From dff0803bcf4ed5860cb53aa2cb0c7855388d3d1f Mon Sep 17 00:00:00 2001 From: Alasdair Mercer Date: Wed, 23 Nov 2016 13:42:28 +0000 Subject: [PATCH] release v2.0.0 (#2) :shipit: --- .editorconfig | 12 + .eslintignore | 2 + .eslintrc.json | 15 + .gitattributes | 1 + .gitignore | 6 +- .npmignore | 7 + .travis.yml | 8 + AUTHORS.md | 3 +- CHANGES.md | 19 + CONTRIBUTING.md | 33 ++ Gruntfile.js | 162 ++++++++ LICENSE.md | 4 +- README.md | 365 ++++++++++++++++-- bower.json | 36 ++ dist/yourls.js | 793 ++++++++++++++++++++++++++++++++++++++++ dist/yourls.js.map | 1 + dist/yourls.min.js | 4 + dist/yourls.min.js.map | 1 + docs/docco.css | 186 ---------- docs/yourls.html | 202 ---------- package.json | 57 ++- src/api.js | 138 +++++++ src/request/jsonp.js | 147 ++++++++ src/request/paramify.js | 46 +++ src/util/array.js | 34 ++ src/yourls-db.js | 48 +++ src/yourls-url.js | 76 ++++ src/yourls.js | 279 ++++++++++++++ yourls.js | 186 ---------- yourls.min.js | 6 - 30 files changed, 2239 insertions(+), 638 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitattributes create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 Gruntfile.js create mode 100644 bower.json create mode 100644 dist/yourls.js create mode 100644 dist/yourls.js.map create mode 100644 dist/yourls.min.js create mode 100644 dist/yourls.min.js.map delete mode 100644 docs/docco.css delete mode 100644 docs/yourls.html create mode 100644 src/api.js create mode 100644 src/request/jsonp.js create mode 100644 src/request/paramify.js create mode 100644 src/util/array.js create mode 100644 src/yourls-db.js create mode 100644 src/yourls-url.js create mode 100644 src/yourls.js delete mode 100644 yourls.js delete mode 100644 yourls.min.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8c52ff9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..207b110 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist/ +Gruntfile.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..5042e54 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,15 @@ +{ + "extends": "skelp/v3/es5", + "env": { + "browser": true + }, + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "max-params": [ + "error", + 4 + ] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcadb2c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/.gitignore b/.gitignore index e94e5b1..552f221 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -.DS_Store? -ehthumbs.db -Icon? -Thumbs.db \ No newline at end of file +node_modules/ +*.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..bb179e7 --- /dev/null +++ b/.npmignore @@ -0,0 +1,7 @@ +.* +AUTHORS.md +bower.json +CHANGES.md +CONTRIBUTING.md +Gruntfile.js +README.md diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ba153ad --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - "0.10" + - "0.12" + - "4" + - "6" +script: + - npm test diff --git a/AUTHORS.md b/AUTHORS.md index 86766fd..73ce8ba 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,3 +1,4 @@ # Authors ordered by first contribution -* Alasdair Mercer \ No newline at end of file +* Alasdair Mercer +* Josh Panter diff --git a/CHANGES.md b/CHANGES.md index e69de29..fedc97b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -0,0 +1,19 @@ +## 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) +* Rewrite library to be easier to maintain +* Refactor API methods to be easier to use (incl. breaking changes) +* Add `yourls.disconnect` API method to clear previously stored connection information +* Add `yourls.db.stats` API method to fetch statistics for all links +* Add `yourls.version` API method to fetch YOURLS version information +* Fix bug where query string parameter name and/or value are not always URL-encoded +* Rewrite documentation to be more informative +* Remove `docs` generated by [Docco](https://jashkenas.github.io/docco/) +* Add [EditorConfig](http://editorconfig.org) file +* Add [ESLint](http://eslint.org) +* Bundle library using [Rollup](http://rollupjs.org) +* Build library using [Grunt](http://gruntjs.com) +* Add continuous integration using [Travis](http://travis-ci.org) +* Add contributor guidelines +* Publish to [Bower](http://bower.io) +* Publish to [npm](https://www.npmjs.org) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..09b7c49 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing + +If you have any questions about [YOURLS API](https://github.com/neocotic/yourls-api) please feel free to +[raise an issue](https://github.com/neocotic/yourls-api/issues/new). + +Please [search existing issues](https://github.com/neocotic/yourls-api/issues) for the same feature and/or issue before +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: + +``` bash +# install/update package dependencies +$ npm install +# run test suite +$ 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. + +All pull requests should be made to the `develop` branch. + +Don't forget to add your details to the list of +[AUTHORS.md](https://github.com/neocotic/yourls-api/blob/master/AUTHORS.md) if you want your contribution to be +recognized by others. diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..f407d27 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,162 @@ +/* + * 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. + */ + +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 */' + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + + watch: { + all: { + files: [ 'src/**/*.js' ], + tasks: [ 'test' ] + } + } + }) + + 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/**' ] + }, + + 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' + } + }, + 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.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...') + } + + grunt.loadNpmTasks('grunt-contrib-watch') + + grunt.registerTask('default', [ 'build' ]) + grunt.registerTask('build', buildTasks) + grunt.registerTask('compile', compileTasks) + grunt.registerTask('test', testTasks) +} diff --git a/LICENSE.md b/LICENSE.md index 6220ef1..0b9ca94 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (C) 2011 Alasdair Mercer, http://neocotic.com/ +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 @@ -16,4 +16,4 @@ 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. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 8358223..1fedaaa 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,369 @@ - ___ - /\_ \ __ - __ __ ___ __ __ _ __\//\ \ ____ __ _____ /\_\ - /\ \/\ \ / __`\/\ \/\ \/\`'__\\ \ \ /',__\ _______ /'__`\ /\ '__`\/\ \ - \ \ \_\ \/\ \L\ \ \ \_\ \ \ \/ \_\ \_/\__, `\\______\\ \L\.\_\ \ \L\ \ \ \ - \/`____ \ \____/\ \____/\ \_\ /\____\/\____//______/ \__/.\_\\ \ ,__/\ \_\ - `/___/> \/___/ \/___/ \/_/ \/____/\/___/ \/__/\/_/ \ \ \/ \/_/ - /\___/ \ \_\ - \/__/ \/_/ + Y88b d88P .d88888b. 888 888 8888888b. 888 .d8888b. + Y88b d88P d88P" "Y88b 888 888 888 Y88b 888 d88P Y88b + Y88o88P 888 888 888 888 888 888 888 Y88b. + Y888P 888 888 888 888 888 d88P 888 "Y888b. + 888 888 888 888 888 8888888P" 888 "Y88b. + 888 888 888 888 888 888 T88b 888 "888 + 888 Y88b. .d88P Y88b. .d88P 888 T88b 888 Y88b d88P + 888 "Y88888P" "Y88888P" 888 T88b 88888888 "Y8888P" + d8888 8888888b. 8888888 + d88888 888 Y88b 888 + d88P888 888 888 888 + d88P 888 888 d88P 888 + d88P 888 8888888P" 888 + d88P 888 888 888 + d8888888888 888 888 + d88P 888 888 8888888 -[yourls-api][] is a JavaScript library that provides bindings for the [YOURLS][] -API using JSONP. +[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][] version **1.5.1** or newer. +> This library is only compatible with YOURLS servers running version **1.5.1** or newer as it requires JSONP support. -## Connect +[![Build Status](https://img.shields.io/travis/neocotic/yourls-api/develop.svg?style=flat-square)](https://travis-ci.org/neocotic/yourls-api) +[![Dev Dependency Status](https://img.shields.io/david/dev/neocotic/yourls-api.svg?style=flat-square)](https://david-dm.org/neocotic/yourls-api#info=devDependencies) +[![License](https://img.shields.io/npm/l/yourls-api.svg?style=flat-square)](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md) +[![Release](https://img.shields.io/npm/v/yourls-api.svg?style=flat-square)](https://www.npmjs.com/package/yourls-api) + +* [Install](#install) +* [API](#api) +* [Migrating from v1](#migrating-from-v1) +* [Bugs](#bugs) +* [Contributors](#contributors) +* [License](#license) + +## Install + +Install using the package manager for your desired environment(s): + +``` bash +$ npm install --save yourls-api +# OR: +$ bower install --save yourls-api +``` + +You'll need to have at least [Node.js](https://nodejs.org) installed and you'll only need [Bower](https://bower.io) if +you want to install that way instead of using `npm`. And, although this library can be installed via `npm` it is only +intended for use in the browser. `npm` installations are supported for the many web bundlers out there now. + +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) + +## API + +The API has been designed to be as simple and human-friendly as possible and attempts to hide the majority of work when +dealing with the YOURLS API from you. + +All methods of the API return a reference to the API itself to enable a clean chaining of method calls, if desired. + +All requests that are sent to YOURLS servers are asynchronous and callback methods are used to track these. Callback +methods are passed the most is deemed (by this library) to be the most important information from the response as the +first argument and the entire response as the second argument. + +The following documentation contains some examples of the results that can be expected from YOURLS, however, it doesn't +cover everything. It's recommended that you play around with making requests and inspecting/logging results and +responses to see all of the data that is available. + +### Connecting ``` javascript yourls.connect(url[, credentials]) ``` -## Shorten +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** +point to the `yourls-api.php` file or its equivalent (e.g. if it's been renamed or using URL rewrites). You can only +connect to a single YOURLS server at a time. + +``` javascript +// Simple connection to public server +yourls.connect('https://example.com/yourls-api.php') +``` + +If you're going to be connecting to a private YOURLS server, you'll also need to provide `credentials` that can be used +to authenticate with it. The recommended method is to specify the `signature` token and use the +[passwordless API requests](http://code.google.com/p/yourls/wiki/PasswordlessAPI) as the signature token can be reset +easily. + +``` javascript +// Passwordless connection to private server +yourls.connect('https://example.com/yourls-api.php', { + signature: '3002a61584' +}) +``` + +However, it's even better if you use a time-limited signature token as it's somewhat more secure. That said; this +library leaves it up to you to provide the signature token as an md5 sum (which should be made from a concatenation of +the `timestamp` passed in and the signature token, and in that order). It's also worth noting that the timestamp should +be in seconds, not milliseconds, since Unix Epoch. + +Although it is possible to create md5 sums in the browser with the use of a third-party JavaScript library, at that +point you signature token has probably already been exposed. The best bet is for your server to calculate the md5 sum +and then pass it and the timestamp on which it was based to your frontend to be consumed by this library. + +``` javascript +// Time-limited passwordless connection to private server +yourls.connect('https://example.com/yourls-api.php', { + signature: md5(1477947900 + '3002a61584'), + timestamp: 1477947900 +}) +``` + +This library does also support the more traditional `username`/`password` combination as well. + +``` javascript +// Basic authentication connection to private server +yourls.connect('https://example.com/yourls-api.php', { + username: 'admin', + password: 'qwerty' +}) +``` + +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 +yourls.disconnect() +``` + +Calling this method simply clears any previously stored connection information and, despite the name of this method, no +live connections are actually terminated. + +### Shortening + +``` javascript +yourls.shorten(url[, descriptor], callback(result, response)) +``` + +This method shortens the `url` provided with a keyword/hash that is generated by the YOURLS server. ``` javascript -yourls.shorten(url[, keyword], callback(data)[, context]) +// Shorten a URL with a random keyword +yourls.shorten('https://github.com/neocotic/yourls-api', function(result, response) { + console.log(result.shorturl) + //=> "https://example.com/abc123" + console.log(result.title) + //=> "https://github.com/neocotic/yourls-api" + console.log(result.url.keyword) + //=> "abc123" +}) ``` -## Stats +Optionally, this method can take a `descriptor` containing additional information including the `keyword` to be used for +the short URL that is created and a `title` that is to be associated with it. As a shortcut, the keyword can be passed +as the `descriptor` itself. ``` javascript -yourls.stats([filter][, limit], callback(data)[, context]) +// Shorten a URL with a predefined keyword +yourls.shorten('https://github.com/neocotic/yourls-api', 'yourls', function(result, response) { + console.log(result.shorturl) + //=> "https://example.com/yourls" + console.log(result.title) + //=> "https://github.com/neocotic/yourls-api" + console.log(result.url.keyword) + //=> "yourls" +}) + +// Shorten a URL with a predefined keyword and title +yourls.shorten('https://github.com/neocotic/yourls-api', { keyword: 'yourls', title: 'YOURLS API' }, function(result, response) { + console.log(result.shorturl) + //=> "https://example.com/yourls" + console.log(result.title) + //=> "YOURLS API" + console.log(result.url.keyword) + //=> "yourls" +}) +``` + +### Statistics + +``` javascript +yourls.stats([criteria, ]callback(result, response)) ``` -## URL +This method fetches the statistics for all links. + +``` javascript +// Get link statistics +yourls.stats(function(result, response) { + console.log(result.stats.total_clicks) + //=> "98765" + console.log(result.stats.total_links) + //=> "123" +}) +``` + +Optionally, this method can take a `criteria` containing search criteria for a sub-set of links which can be included in +the result. This includes the `filter` (either `"top"`, `"bottom"`, `"rand"`, or `"last"`), which can be used to control +sorting, as well as `limit` and `start`, which can be used for pagination lookups. As a shortcut, the limit can be +passed as the `criteria` itself. The minimum required in order to do this is for a `limit` to be specified. + +``` javascript +// Get top 10 links +yourls.stats(10, function(result, response) { + console.log(result.links.length) + //=> 10 + console.log(result.links[0].shorturl) + //=> "https://example.com/yourls" + console.log(result.stats.total_links) + //=> "123" +}) + +// Get second page of 5 newest links +yourls.stats({ filter: 'last', limit: 5, start: 5 }, function(result, response) { + console.log(result.links.length) + //=> 5 + console.log(result.links[0].shorturl) + //=> "https://example.com/abc123" + console.log(result.stats.total_links) + //=> "123" +}) +``` + +--- + +``` javascript +yourls.db.stats(callback(result, response)) +``` + +This method does exactly the same as `yourls.stats` except that it *only* returns the statistics for all links. + +``` javascript +// Get link statistics +yourls.db.stats(function(result, response) { + console.log(result.total_clicks) + //=> "98765" + console.log(result.total_links) + //=> "123" +}) +``` + +### URL Information ``` javascript yourls.url(url) ``` -### Expand +Unlike other API calls, this method returns a wrapper for making API calls that specific to a given shortened `url`, +which can be the keyword instead, if desired. + +``` javascript +// Both do the same thing: +var yourls = yourls.url('https://example.com/yourls') +var yourls = yourls.url('yourls') +``` + +Just like the top-level API methods, all of the URL-specific methods return a reference to the URL wrapper to enable a +clean chaining of method calls, if desired. + +#### Expanding ``` javascript -yourls.url.expand(callback(data)[, context]) +yourls.url(url).expand(callback(result, response)) ``` -### Stats +This method expands the shortened `url` into the original (long) URL. ``` javascript -yourls.url.stats(callback(data)[, context]) +yourls.url('https://example.com/yourls').expand(function(result, response) { + console.log(result.keyword) + //=> "yourls" + console.log(result.longurl) + //=> "https://github.com/neocotic/yourls-api" + console.log(result.shorturl) + //=> "https://example.com/yourls" +}) ``` -## Miscellaneous +#### Statistics ``` javascript -yourls.noConflict() -yourls.VERSION +yourls.url(url).stats(callback(result, response)) ``` +This method fetches the statistics for the shortened `url`. + +``` javascript +yourls.url('https://example.com/yourls').stats(function(result, response) { + console.log(result.clicks) + //=> "123" + console.log(result.title) + //=> "neocotic/yourls-api: JavaScript bindings for the YOURLS API" + console.log(result.url) + //=> "https://github.com/neocotic/yourls-api" +}) +``` + +### Versions + +``` javascript +yourls.version([db, ]callback(result, response)) +``` + +This methods fetches the version of YOURLS running on the connected server. + +``` javascript +yourls.version(function(result, response) { + console.log(result.version) + //=> "1.7" +}) +``` + +Optionally, a `db` flag can be enabled for the YOURLS database version to also be included in the result. + +``` javascript +yourls.version(true, function(result, response) { + console.log(result.version) + //=> "1.7" + console.log(result.db_version) + //=> "482" +}) +``` + +--- + +``` javascript +console.log(yourls.VERSION) +//=> "2.0.0" +``` + +The current version of this library. + +## Migrating from v1 + +If you've been using v1 then you can find details about what's changed and a guide on how to migrate to v2 below: + +https://github.com/neocotic/yourls-api/wiki/Migrating-from-v1 + +You can also find the code and documentation for the v1 below: + +https://github.com/neocotic/yourls-api/tree/1.0.0 + ## Bugs -If you have any problems with this library or would like to see the changes -currently in development you can do so here; +If you have any problems with this library or would like to see changes currently in development you can do so +[here](https://github.com/neocotic/yourls-api/issues). -https://github.com/neocotic/yourls-api/issues +However, if you believe that your issue is with YOURLS itself, please take a look a +[their issues](https://github.com/YOURLS/YOURLS/issues) instead. -## Questions? +## Contributors -Take a look at `docs/yourls.html` to get a better understanding of what the code -is doing. +If you want to contribute, you're a legend! Information on how you can do so can be found in +[CONTRIBUTING.md](https://github.com/neocotic/yourls-api/blob/master/CONTRIBUTING.md). We want your suggestions and pull +requests! -If that doesn't help, feel free to follow me on Twitter, [@neocotic][]. +A list of YOURLS API contributors can be found in +[AUTHORS.md](https://github.com/neocotic/yourls-api/blob/master/AUTHORS.md). -However, if you want more information or examples of using this library please -visit the project's homepage; +## License -http://neocotic.com/yourls-api +Copyright © 2016 Alasdair Mercer -[@neocotic]: https://twitter.com/#!/neocotic -[yourls]: http://yourls.org -[yourls-api]: http://neocotic.com/yourls-api \ No newline at end of file +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 new file mode 100644 index 0000000..c96e755 --- /dev/null +++ b/bower.json @@ -0,0 +1,36 @@ +{ + "name": "yourls-api", + "version": "2.0.0", + "description": "Bindings for the YOURLS API", + "homepage": "https://github.com/neocotic/yourls-api", + "authors": [ + { + "name": "Alasdair Mercer", + "email": "mercer.alasdair@gmail.com", + "homepage": "https://neocotic.com" + } + ], + "license": "MIT", + "keywords": [ + "yourls", + "url", + "shortener", + "api", + "jsonp" + ], + "repository": { + "type": "git", + "url": "https://github.com/neocotic/yourls-api.git" + }, + "main": "dist/yourls.js", + "ignore": [ + ".*", + "src/", + "AUTHORS.md", + "CHANGES.md", + "CONTRIBUTING.md", + "Gruntfile.js", + "package.json", + "README.md" + ] +} diff --git a/dist/yourls.js b/dist/yourls.js new file mode 100644 index 0000000..2b3b4e4 --- /dev/null +++ b/dist/yourls.js @@ -0,0 +1,793 @@ +/* + * 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) : + (global.yourls = factory()); +}(this, (function () { 'use strict'; + + /* + * 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. + */ + + /** + * 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; + } + + return result + } + + /** + * 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) + * @protected + * @constructor + */ + function API(url, credentials) { + /** + * The URL of the YOURLS server. + * + * @public + * @type {string} + */ + this.url = url ? url.replace(/\/$/, '') : ''; + /** + * The credentials to be used to authenticate with the YOURLS API. + * + * This may be null if the YOURLS API is public. + * + * @public + * @type {API~Credentials} + */ + this.credentials = sanitizeCredentials(credentials); + } + + /** + * Destroys the singleton instance of {@link API}. + * + * @return {void} + * @public + * @static + */ + API.clear = function() { + instance = null; + }; + + /** + * 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 + }; + + /** + * Stores this {@link API} as the singleton, potentially replacing the existing instance. + * + * @return {void} + * @public + */ + API.prototype.store = function() { + instance = this; + }; + + /** + * The credentials to be used to authenticate with a private YOURLS API. + * + * Authentication can be done with a traditional username and password combination or by using the secret + * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs + * and can be found on the "Tools" page. + * + * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server + * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and + * signature token concatenated, and in that order. + * + * @typedef {Object} API~Credentials + * @property {string} [password] - The password of the user to be authenticated. + * @property {string} [username] - The name of the user to be authenticated. + * @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. + */ + + /* + * 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. + */ + + /** + * Returns whether the specified obj is an array. + * + * This function 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. + * @protected + */ + function isArray(obj) { + return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]' + } + + /* + * 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. + */ + + /** + * Creates a serialized representation of the specified params into a URL query string. + * + * @param {Object} [params] - the hash of parameter key/value pairs to be serialized + * @return {string} A URL query string representing params. + * @protected + */ + function paramify(params) { + if (!params) { + return '' + } + + var results = []; + + for (var key in params) { + if (Object.prototype.hasOwnProperty.call(params, key)) { + if (params[key] != null) { + results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])); + } + } + } + + return results.join('&') + } + + /* + * 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. + */ + + /** + * The key of the callback function holder within the global namespace. + * + * @private + * @type {string} + */ + 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 + } + + /** + * 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 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 + */ + function getResult(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 + } + + /** + * 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 + */ + 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); + }; + + var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' }); + if (api.credentials) { + target += '&' + paramify(api.credentials); + } + if (data) { + target += '&' + paramify(data); + } + + script.setAttribute('src', target); + document.getElementsByTagName('head')[0].appendChild(script); + } + + /* + * 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. + */ + + /** + * Provides the ability to lookup information related to the YOURLS database. + * + * @constructor + * @protected + */ + function DB() { + // Do nothing + } + + /** + * 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' }; + + jsonp(data, 'db-stats', callback); + + return this + }; + + /* + * 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 THE0 + * 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. + */ + + /** + * 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 + * @protected + */ + function URL(url) { + /** + * Either the shortened URL or its keyword for this {@link URL}. + * + * @public + * @type {string} + */ + this.url = url; + } + + /** + * Retrieves the original ("long") URL for this shortened {@link URL}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {URL} A reference to this {@link URL} for chaining purposes. + * @public + */ + URL.prototype.expand = function(callback) { + var data = { + action: 'expand', + shorturl: this.url + }; + + jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback); + + return this + }; + + /** + * Retrieves the statistics for this shortened {@link URL}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {URL} A reference to this {@link URL} for chaining purposes. + * @public + */ + URL.prototype.stats = function(callback) { + var data = { + action: 'url-stats', + shorturl: this.url + }; + + jsonp(data, 'link', callback); + + return this + }; + + /* + * 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. + */ + + /** + * 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. + * + * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure + * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private). + * + * @constructor + * @protected + */ + var YOURLS = function() { + /** + * Provides information on the YOURLS {@link DB}. + * + * @public + * @type {DB} + */ + this.db = new DB(); + + /** + * 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. + * + * @public + * @type {string} + */ + this.VERSION = '2.0.0'; + }; + + /** + * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. + * + * @param {string} [url=''] - the URL for the YOURLS server + * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be + * null) + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ + YOURLS.prototype.connect = function(url, credentials) { + var api = new API(url, credentials); + api.store(); + + 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(); + + 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 + }; + + 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; + } + + jsonp(data, [ 'shorturl', 'title', 'url' ], callback); + + 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 + } + + if (criteria) { + data.filter = criteria.filter; + data.limit = criteria.limit; + data.start = criteria.start; + } + + jsonp(data, [ 'links', 'stats' ], function(result, response) { + callback(sanitizeStatsResult(result), response); + }); + + 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 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 + */ + YOURLS.prototype.version = function(db, callback) { + var data = { action: 'version' }; + + if (typeof db === 'function') { + callback = db; + db = null; + } + + if (db != null) { + data.db = Number(db); + } + + jsonp(data, [ 'db_version', 'version' ], callback); + + return this + }; + + /** + * The singleton instance of {@link YOURLS}. + */ + var yourls = new YOURLS(); + + /** + * Contains criteria which can be used to search for a refined set of shortened URLs. + * + * Pagination can be achieved by using limit and start. + * + * No links will be returned unless limit is specified and has a value that is greater than zero. + * + * @typedef {Object} YOURLS~SearchCriteria + * @property {string} [filter] - The filter to be applied (either "top", "bottom", + * "rand", or "last"). + * @property {number} [limit] - The maximum number of links whose statistical information is to be counted. + * null or 0 will result in no links being included in the result. + * @property {number} [start] - The offset from where the search should begin. + */ + + /** + * Contains additional information which can be used when shortening a URL. + * + * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique + * keyword. + * + * @typedef {Object} YOURLS~UrlDescriptor + * @property {string} [keyword] - The optional keyword to be used for the shortened URL. + * @property {string} [title] - The optional title to be associated with the shortened URL. + */ + + return yourls; + +}))); + +//# sourceMappingURL=yourls.js.map \ No newline at end of file diff --git a/dist/yourls.js.map b/dist/yourls.js.map new file mode 100644 index 0000000..4dc92d2 --- /dev/null +++ b/dist/yourls.js.map @@ -0,0 +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 diff --git a/dist/yourls.min.js b/dist/yourls.min.js new file mode 100644 index 0000000..b7ec2ce --- /dev/null +++ b/dist/yourls.min.js @@ -0,0 +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 diff --git a/docs/docco.css b/docs/docco.css deleted file mode 100644 index 5aa0a8d..0000000 --- a/docs/docco.css +++ /dev/null @@ -1,186 +0,0 @@ -/*--------------------- Layout and Typography ----------------------------*/ -body { - font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; - font-size: 15px; - line-height: 22px; - color: #252519; - margin: 0; padding: 0; -} -a { - color: #261a3b; -} - a:visited { - color: #261a3b; - } -p { - margin: 0 0 15px 0; -} -h1, h2, h3, h4, h5, h6 { - margin: 0px 0 15px 0; -} - h1 { - margin-top: 40px; - } -#container { - position: relative; -} -#background { - position: fixed; - top: 0; left: 525px; right: 0; bottom: 0; - background: #f5f5ff; - border-left: 1px solid #e5e5ee; - z-index: -1; -} -#jump_to, #jump_page { - background: white; - -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; - -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; - font: 10px Arial; - text-transform: uppercase; - cursor: pointer; - text-align: right; -} -#jump_to, #jump_wrapper { - position: fixed; - right: 0; top: 0; - padding: 5px 10px; -} - #jump_wrapper { - padding: 0; - display: none; - } - #jump_to:hover #jump_wrapper { - display: block; - } - #jump_page { - padding: 5px 0 3px; - margin: 0 0 25px 25px; - } - #jump_page .source { - display: block; - padding: 5px 10px; - text-decoration: none; - border-top: 1px solid #eee; - } - #jump_page .source:hover { - background: #f5f5ff; - } - #jump_page .source:first-child { - } -table td { - border: 0; - outline: 0; -} - td.docs, th.docs { - max-width: 450px; - min-width: 450px; - min-height: 5px; - padding: 10px 25px 1px 50px; - overflow-x: hidden; - vertical-align: top; - text-align: left; - } - .docs pre { - margin: 15px 0 15px; - padding-left: 15px; - } - .docs p tt, .docs p code { - background: #f8f8ff; - border: 1px solid #dedede; - font-size: 12px; - padding: 0 0.2em; - } - .pilwrap { - position: relative; - } - .pilcrow { - font: 12px Arial; - text-decoration: none; - color: #454545; - position: absolute; - top: 3px; left: -20px; - padding: 1px 2px; - opacity: 0; - -webkit-transition: opacity 0.2s linear; - } - td.docs:hover .pilcrow { - opacity: 1; - } - td.code, th.code { - padding: 14px 15px 16px 25px; - width: 100%; - vertical-align: top; - background: #f5f5ff; - border-left: 1px solid #e5e5ee; - } - pre, tt, code { - font-size: 12px; line-height: 18px; - font-family: Monaco, Consolas, "Lucida Console", monospace; - margin: 0; padding: 0; - } - - -/*---------------------- Syntax Highlighting -----------------------------*/ -td.linenos { background-color: #f0f0f0; padding-right: 10px; } -span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } -body .hll { background-color: #ffffcc } -body .c { color: #408080; font-style: italic } /* Comment */ -body .err { border: 1px solid #FF0000 } /* Error */ -body .k { color: #954121 } /* Keyword */ -body .o { color: #666666 } /* Operator */ -body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ -body .cp { color: #BC7A00 } /* Comment.Preproc */ -body .c1 { color: #408080; font-style: italic } /* Comment.Single */ -body .cs { color: #408080; font-style: italic } /* Comment.Special */ -body .gd { color: #A00000 } /* Generic.Deleted */ -body .ge { font-style: italic } /* Generic.Emph */ -body .gr { color: #FF0000 } /* Generic.Error */ -body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -body .gi { color: #00A000 } /* Generic.Inserted */ -body .go { color: #808080 } /* Generic.Output */ -body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -body .gs { font-weight: bold } /* Generic.Strong */ -body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -body .gt { color: #0040D0 } /* Generic.Traceback */ -body .kc { color: #954121 } /* Keyword.Constant */ -body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ -body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ -body .kp { color: #954121 } /* Keyword.Pseudo */ -body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ -body .kt { color: #B00040 } /* Keyword.Type */ -body .m { color: #666666 } /* Literal.Number */ -body .s { color: #219161 } /* Literal.String */ -body .na { color: #7D9029 } /* Name.Attribute */ -body .nb { color: #954121 } /* Name.Builtin */ -body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -body .no { color: #880000 } /* Name.Constant */ -body .nd { color: #AA22FF } /* Name.Decorator */ -body .ni { color: #999999; font-weight: bold } /* Name.Entity */ -body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -body .nf { color: #0000FF } /* Name.Function */ -body .nl { color: #A0A000 } /* Name.Label */ -body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -body .nt { color: #954121; font-weight: bold } /* Name.Tag */ -body .nv { color: #19469D } /* Name.Variable */ -body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -body .w { color: #bbbbbb } /* Text.Whitespace */ -body .mf { color: #666666 } /* Literal.Number.Float */ -body .mh { color: #666666 } /* Literal.Number.Hex */ -body .mi { color: #666666 } /* Literal.Number.Integer */ -body .mo { color: #666666 } /* Literal.Number.Oct */ -body .sb { color: #219161 } /* Literal.String.Backtick */ -body .sc { color: #219161 } /* Literal.String.Char */ -body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ -body .s2 { color: #219161 } /* Literal.String.Double */ -body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -body .sh { color: #219161 } /* Literal.String.Heredoc */ -body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -body .sx { color: #954121 } /* Literal.String.Other */ -body .sr { color: #BB6688 } /* Literal.String.Regex */ -body .s1 { color: #219161 } /* Literal.String.Single */ -body .ss { color: #19469D } /* Literal.String.Symbol */ -body .bp { color: #954121 } /* Name.Builtin.Pseudo */ -body .vc { color: #19469D } /* Name.Variable.Class */ -body .vg { color: #19469D } /* Name.Variable.Global */ -body .vi { color: #19469D } /* Name.Variable.Instance */ -body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/yourls.html b/docs/yourls.html deleted file mode 100644 index 6dfeda1..0000000 --- a/docs/yourls.html +++ /dev/null @@ -1,202 +0,0 @@ - yourls.js

yourls.js

yourls-api 1.0.0
-(c) 2011 Alasdair Mercer
-Freely distributable under the MIT license.
-For all details and documentation:
-http://neocotic.com/yourls-api

(function (root) {
-
-
-

Private variables

-
-  var
-
-

URL of YOURLS API stored by yourls.connect.

-    api            = '',
-
-

Authentication credentials stored by yourls.connect.

-    auth           = {},
-
-

Save the previous value of the yourls variable.

-    previousYourls = root.yourls;
-
-
-

Public variables

-
-
-

API to be exposed publicly later on.

-  var yourls = {};
-
-

Callback functions for activate JSONP requests.
-Functions should removed once they have been called.
-This property must be public since the callback is called in global -context.

-  yourls.__jsonp_callbacks = {};
-
-
-

Public constants

-
-
-

Current version of yourls.

-  yourls.VERSION = '1.0.0';
-
-
-

Private functions

-
-
-

Convert the object provided in to a URL parameter string.

-  function paramify(params) {
-    var key, str = '';
-    for (key in params) {
-      if (params.hasOwnProperty(key)) {
-        if (typeof params[key] !== 'undefined') {
-          str += key + '=' + params[key] + '&';
-        }
-      }
-    }
-    return str.replace(/&$/, '');
-  }
-
-
-

Send a JSONP request to the YOURLS API that calls the callback function -with the context specified as this.

-  function jsonp(url, callback, context) {
-    var
-      id     = +new Date(),
-      script = document.createElement('script');
-    while (typeof yourls.__jsonp_callbacks[id] !== 'undefined') {
-      id += Math.random();
-    }
-    yourls.__jsonp_callbacks[id] = function () {
-      delete yourls.__jsonp_callbacks[id];
-      callback.apply(context, arguments);
-    };
-    url = '?format=jsonp&callback=' +
-          encodeURIComponent('yourls.__jsonp_callbacks[' + id + ']') + '&' +
-          url;
-    url += '&' + paramify(auth);
-    script.setAttribute('src', api + url);
-    document.getElementsByTagName('head')[0].appendChild(script);
-  }
-
-
-

Primary functions

-
-
-

Store the URL and user credentials to be used to connect to the YOURLS -API.
-This won't validate the URL or credentials at any point; this is performed -by each individual method.

-  yourls.connect = function (url, credentials) {
-    api = url;
-    auth = {};
-    if (credentials) {
-      if (credentials.signature) {
-        auth.signature = credentials.signature;
-      } else {
-        auth.password = credentials.password;
-        auth.username = credentials.username;
-      }
-    }
-    return this;
-  };
-
-
-

Retrieve the short URL for a long URL.

-  yourls.shorten = function (url, keyword, callback, context) {
-    var data = {
-      action : 'shorturl',
-      url    : url
-    };
-    if (typeof keyword === 'function') {
-      callback = keyword;
-    } else {
-      data.keyword = keyword;
-    }
-    jsonp(paramify(data), callback, context);
-    return this;
-  };
-
-
-

Retrieve the statistics for all the shortened URLs which can be optionally -filtered.

-  yourls.stats = function (filter, limit, callback, context) {
-    var
-      data       = {action: 'stats'},
-      filterType = typeof filter;
-    switch (filterType) {
-    case 'number':
-      data.limit = filter;
-      callback = limit;
-      break;
-    case 'function':
-      callback = filter;
-      break;
-    default:
-      data.filter = filter;
-      if (typeof limit === 'function') {
-        callback = limit;
-      } else {
-        data.limit = limit;
-      }
-    }
-    jsonp(paramify(data), callback, context);
-    return this;
-  };
-
-
-

URL functions

-
-
-

The constructor for url objects.
-Just creating an instance of a url doesn't fetch any data from the YOURLS -API and you'll need to be explicit about what you want to do in order for -that to happen.

-  yourls.url = function (url) {
-    if (!(this instanceof yourls.url)) return new yourls.url(url);
-    this.url = url;
-  };
-
-
-

Retrieve the long URL for a short URL.

-  yourls.url.prototype.expand = function (callback, context) {
-    jsonp(paramify({
-      action   : 'expand',
-      shorturl : this.url
-    }), callback, context);
-    return this;
-  };
-
-
-

Retrieve statistics for a single short URL.

-  yourls.url.prototype.stats = function (callback, context) {
-    jsonp(paramify({
-      action   : 'url-stats',
-      shorturl : this.url
-    }), callback, context);
-    return this;
-  };
-
-
-

Utility functions

-
-
-

Run yourls.js in noConflict mode, returning the yourls variable to its -previous owner.
-Returns a reference to yourls.

-  yourls.noConflict = function () {
-    root.yourls = previousYourls;
-    return this;
-  };
-
-
-

Export yourls for CommonJS.

-  if (typeof define === 'function' && define.amd) {
-    define('yourls', function () {
-      return yourls;
-    });
-  } else {
-    root.yourls = yourls;
-  }
-
-}(this));
-
-
\ No newline at end of file diff --git a/package.json b/package.json index 56216c9..e27c7f3 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,45 @@ { - "name" : "yourls", - "description" : "JavaScript bindings for the YOURLS API", - "version" : "1.0.0", - "homepage" : "http://neocotic.com/yourls-api", - "bugs" : {"url" : "https://github.com/neocotic/yourls-api/issues"}, - "author" : "Alasdair Mercer (http://neocotic.com)", - "licenses" : [{"type" : "MIT", - "url" : "https://github.com/neocotic/yourls-api/raw/master/LICENSE.md"}], - "keywords" : ["yourls", "url", "shortener", "jsonp"], - "repository" : {"type" : "git", - "url" : "git://github.com/neocotic/yourls-api.git"}, - "main" : "yourls.js" -} \ No newline at end of file + "name": "yourls-api", + "version": "2.0.0", + "description": "Bindings for the YOURLS API", + "homepage": "https://github.com/neocotic/yourls-api", + "bugs": { + "url": "https://github.com/neocotic/yourls-api/issues" + }, + "author": { + "name": "Alasdair Mercer", + "email": "mercer.alasdair@gmail.com", + "url": "https://neocotic.com" + }, + "license": "MIT", + "keywords": [ + "yourls", + "url", + "shortener", + "api", + "jsonp" + ], + "repository": { + "type": "git", + "url": "https://github.com/neocotic/yourls-api.git" + }, + "devDependencies": { + "eslint-config-skelp": "^0.1.5", + "grunt": "^1.0.1", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-eslint": "^19.0.0", + "grunt-rollup": "^1.0.1", + "rollup-plugin-commonjs": "^5.0.5", + "rollup-plugin-node-resolve": "^2.0.0", + "rollup-plugin-uglify": "^1.0.1", + "semver": "^5.3.0" + }, + "main": "dist/yourls.js", + "jsnext:main": "src/yourls.js", + "scripts": { + "build": "grunt build", + "test": "grunt test" + } +} diff --git a/src/api.js b/src/api.js new file mode 100644 index 0000000..275469f --- /dev/null +++ b/src/api.js @@ -0,0 +1,138 @@ +/* + * 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. + */ + +/** + * 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 + } + + return result +} + +/** + * 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) + * @protected + * @constructor + */ +export function API(url, credentials) { + /** + * The URL of the YOURLS server. + * + * @public + * @type {string} + */ + this.url = url ? url.replace(/\/$/, '') : '' + /** + * The credentials to be used to authenticate with the YOURLS API. + * + * This may be null if the YOURLS API is public. + * + * @public + * @type {API~Credentials} + */ + this.credentials = sanitizeCredentials(credentials) +} + +/** + * Destroys the singleton instance of {@link API}. + * + * @return {void} + * @public + * @static + */ +API.clear = function() { + instance = null +} + +/** + * 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 +} + +/** + * Stores this {@link API} as the singleton, potentially replacing the existing instance. + * + * @return {void} + * @public + */ +API.prototype.store = function() { + instance = this +} + +/** + * The credentials to be used to authenticate with a private YOURLS API. + * + * Authentication can be done with a traditional username and password combination or by using the secret + * signature token (e.g. 1002a612b4)for the YOURLS API. The latter is not available for public YOURLS APIs + * and can be found on the "Tools" page. + * + * Optionally, a timestamp can accompany the signature token to make it time-limited (depending on the server + * configuration). When a timestamp is provided the signature token must be the md5 sum of the timestamp and + * signature token concatenated, and in that order. + * + * @typedef {Object} API~Credentials + * @property {string} [password] - The password of the user to be authenticated. + * @property {string} [username] - The name of the user to be authenticated. + * @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. + */ diff --git a/src/request/jsonp.js b/src/request/jsonp.js new file mode 100644 index 0000000..ebcb130 --- /dev/null +++ b/src/request/jsonp.js @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { API } from '../api' +import { isArray } from '../util/array' +import { paramify } from './paramify' + +/** + * The key of the callback function holder within the global namespace. + * + * @private + * @type {string} + */ +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 +} + +/** + * 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 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 + */ +function getResult(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 +} + +/** + * 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) + } + + var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' }) + if (api.credentials) { + target += '&' + paramify(api.credentials) + } + if (data) { + target += '&' + paramify(data) + } + + script.setAttribute('src', target) + document.getElementsByTagName('head')[0].appendChild(script) +} diff --git a/src/request/paramify.js b/src/request/paramify.js new file mode 100644 index 0000000..b20c865 --- /dev/null +++ b/src/request/paramify.js @@ -0,0 +1,46 @@ +/* + * 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. + */ + +/** + * Creates a serialized representation of the specified params into a URL query string. + * + * @param {Object} [params] - the hash of parameter key/value pairs to be serialized + * @return {string} A URL query string representing params. + * @protected + */ +export function paramify(params) { + if (!params) { + return '' + } + + var results = [] + + for (var key in params) { + if (Object.prototype.hasOwnProperty.call(params, key)) { + if (params[key] != null) { + results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key])) + } + } + } + + return results.join('&') +} diff --git a/src/util/array.js b/src/util/array.js new file mode 100644 index 0000000..06c97e9 --- /dev/null +++ b/src/util/array.js @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/** + * Returns whether the specified obj is an array. + * + * This function 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. + * @protected + */ +export function isArray(obj) { + return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === '[object Array]' +} diff --git a/src/yourls-db.js b/src/yourls-db.js new file mode 100644 index 0000000..daae956 --- /dev/null +++ b/src/yourls-db.js @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { jsonp } from './request/jsonp' + +/** + * Provides the ability to lookup information related to the YOURLS database. + * + * @constructor + * @protected + */ +export function DB() { + // Do nothing +} + +/** + * 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' } + + jsonp(data, 'db-stats', callback) + + return this +} diff --git a/src/yourls-url.js b/src/yourls-url.js new file mode 100644 index 0000000..0e0159d --- /dev/null +++ b/src/yourls-url.js @@ -0,0 +1,76 @@ +/* + * 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 THE0 + * 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 { jsonp } from './request/jsonp' + +/** + * 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 + * @protected + */ +export function URL(url) { + /** + * Either the shortened URL or its keyword for this {@link URL}. + * + * @public + * @type {string} + */ + this.url = url +} + +/** + * Retrieves the original ("long") URL for this shortened {@link URL}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {URL} A reference to this {@link URL} for chaining purposes. + * @public + */ +URL.prototype.expand = function(callback) { + var data = { + action: 'expand', + shorturl: this.url + } + + jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback) + + return this +} + +/** + * Retrieves the statistics for this shortened {@link URL}. + * + * @param {Function} callback - the callback function to be called with the result + * @return {URL} A reference to this {@link URL} for chaining purposes. + * @public + */ +URL.prototype.stats = function(callback) { + var data = { + action: 'url-stats', + shorturl: this.url + } + + jsonp(data, 'link', callback) + + return this +} diff --git a/src/yourls.js b/src/yourls.js new file mode 100644 index 0000000..a6fb602 --- /dev/null +++ b/src/yourls.js @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2016 Alasdair Mercer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { API } from './api' +import { DB } from './yourls-db' +import { isArray } from './util/array' +import { jsonp } from './request/jsonp' +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. + * + * Before attempting to interact with a YOURLS server, you must call {@link YOURLS#connect} first to configure + * the URL of the YOURLS server and any credentials required to authenticate with its API (only required when private). + * + * @constructor + * @protected + */ +var YOURLS = function() { + /** + * Provides information on the YOURLS {@link DB}. + * + * @public + * @type {DB} + */ + this.db = new DB() + + /** + * 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. + * + * @public + * @type {string} + */ + this.VERSION = '2.0.0' +} + +/** + * Stores the specified information to be used later to connect to and authenticate with a YOURLS server. + * + * @param {string} [url=''] - the URL for the YOURLS server + * @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be + * null) + * @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes. + * @public + */ +YOURLS.prototype.connect = function(url, credentials) { + var api = new API(url, credentials) + api.store() + + 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() + + 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 + } + + 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 + } + + jsonp(data, [ 'shorturl', 'title', 'url' ], callback) + + 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 + } + + if (criteria) { + data.filter = criteria.filter + data.limit = criteria.limit + data.start = criteria.start + } + + jsonp(data, [ 'links', 'stats' ], function(result, response) { + callback(sanitizeStatsResult(result), response) + }) + + 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 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 + */ +YOURLS.prototype.version = function(db, callback) { + var data = { action: 'version' } + + if (typeof db === 'function') { + callback = db + db = null + } + + if (db != null) { + data.db = Number(db) + } + + jsonp(data, [ 'db_version', 'version' ], callback) + + return this +} + +/** + * The singleton instance of {@link YOURLS}. + */ +export default new YOURLS() + +/** + * Contains criteria which can be used to search for a refined set of shortened URLs. + * + * Pagination can be achieved by using limit and start. + * + * No links will be returned unless limit is specified and has a value that is greater than zero. + * + * @typedef {Object} YOURLS~SearchCriteria + * @property {string} [filter] - The filter to be applied (either "top", "bottom", + * "rand", or "last"). + * @property {number} [limit] - The maximum number of links whose statistical information is to be counted. + * null or 0 will result in no links being included in the result. + * @property {number} [start] - The offset from where the search should begin. + */ + +/** + * Contains additional information which can be used when shortening a URL. + * + * If keyword is specified, it must be available and, if not, the YOURLS server will generate a unique + * keyword. + * + * @typedef {Object} YOURLS~UrlDescriptor + * @property {string} [keyword] - The optional keyword to be used for the shortened URL. + * @property {string} [title] - The optional title to be associated with the shortened URL. + */ diff --git a/yourls.js b/yourls.js deleted file mode 100644 index 9723549..0000000 --- a/yourls.js +++ /dev/null @@ -1,186 +0,0 @@ -// [yourls-api](http://neocotic.com/yourls-api) 1.0.0 -// (c) 2011 Alasdair Mercer -// Freely distributable under the MIT license. -// For all details and documentation: -// - -(function (root) { - - // Private variables - // ----------------- - - var - // URL of YOURLS API stored by `yourls.connect`. - api = '', - // Authentication credentials stored by `yourls.connect`. - auth = {}, - // Save the previous value of the `yourls` variable. - previousYourls = root.yourls; - - // Public variables - // ---------------- - - // API to be exposed publicly later on. - var yourls = {}; - // Callback functions for activate JSONP requests. - // Functions should removed once they have been called. - // This property must be public since the callback is called in global - // context. - yourls.__jsonp_callbacks = {}; - - // Public constants - // ---------------- - - // Current version of `yourls`. - yourls.VERSION = '1.0.0'; - - // Private functions - // ----------------- - - // Convert the object provided in to a URL parameter string. - function paramify(params) { - var key, str = ''; - for (key in params) { - if (params.hasOwnProperty(key)) { - if (typeof params[key] !== 'undefined') { - str += key + '=' + params[key] + '&'; - } - } - } - return str.replace(/&$/, ''); - } - - // Send a JSONP request to the YOURLS API that calls the callback function - // with the context specified as `this`. - function jsonp(url, callback, context) { - var - id = +new Date(), - script = document.createElement('script'); - while (typeof yourls.__jsonp_callbacks[id] !== 'undefined') { - id += Math.random(); - } - yourls.__jsonp_callbacks[id] = function () { - delete yourls.__jsonp_callbacks[id]; - callback.apply(context, arguments); - }; - url = '?format=jsonp&callback=' + - encodeURIComponent('yourls.__jsonp_callbacks[' + id + ']') + '&' + - url; - url += '&' + paramify(auth); - script.setAttribute('src', api + url); - document.getElementsByTagName('head')[0].appendChild(script); - } - - // Primary functions - // ----------------- - - // Store the URL and user credentials to be used to connect to the YOURLS - // API. - // This won't validate the URL or credentials at any point; this is performed - // by each individual method. - yourls.connect = function (url, credentials) { - api = url; - auth = {}; - if (credentials) { - if (credentials.signature) { - auth.signature = credentials.signature; - } else { - auth.password = credentials.password; - auth.username = credentials.username; - } - } - return this; - }; - - // Retrieve the short URL for a long URL. - yourls.shorten = function (url, keyword, callback, context) { - var data = { - action : 'shorturl', - url : url - }; - if (typeof keyword === 'function') { - callback = keyword; - } else { - data.keyword = keyword; - } - jsonp(paramify(data), callback, context); - return this; - }; - - // Retrieve the statistics for all the shortened URLs which can be optionally - // filtered. - yourls.stats = function (filter, limit, callback, context) { - var - data = {action: 'stats'}, - filterType = typeof filter; - switch (filterType) { - case 'number': - data.limit = filter; - callback = limit; - break; - case 'function': - callback = filter; - break; - default: - data.filter = filter; - if (typeof limit === 'function') { - callback = limit; - } else { - data.limit = limit; - } - } - jsonp(paramify(data), callback, context); - return this; - }; - - // URL functions - // ------------- - - // The constructor for url objects. - // Just creating an instance of a url doesn't fetch any data from the YOURLS - // API and you'll need to be explicit about what you want to do in order for - // that to happen. - yourls.url = function (url) { - if (!(this instanceof yourls.url)) return new yourls.url(url); - this.url = url; - }; - - // Retrieve the long URL for a short URL. - yourls.url.prototype.expand = function (callback, context) { - jsonp(paramify({ - action : 'expand', - shorturl : this.url - }), callback, context); - return this; - }; - - // Retrieve statistics for a single short URL. - yourls.url.prototype.stats = function (callback, context) { - jsonp(paramify({ - action : 'url-stats', - shorturl : this.url - }), callback, context); - return this; - }; - - // Utility functions - // ----------------- - - // Run yourls.js in *noConflict* mode, returning the `yourls` variable to its - // previous owner. - // Returns a reference to `yourls`. - yourls.noConflict = function () { - root.yourls = previousYourls; - return this; - }; - - // Export `yourls` for CommonJS. - if (typeof define === 'function' && define.amd) { - define('yourls', function () { - return yourls; - }); - } else { - root.yourls = yourls; - } - -}(this)); \ No newline at end of file diff --git a/yourls.min.js b/yourls.min.js deleted file mode 100644 index 620f3fe..0000000 --- a/yourls.min.js +++ /dev/null @@ -1,6 +0,0 @@ -// [yourls-api](http://neocotic.com/yourls-api) 1.0.0 -// (c) 2011 Alasdair Mercer -// Freely distributable under the MIT license. -// For all details and documentation: -// http://neocotic.com/yourls-api -(function(a){function g(a,d,g){var h=+(new Date),i=document.createElement("script");while(typeof e.__jsonp_callbacks[h]!=="undefined"){h+=Math.random()}e.__jsonp_callbacks[h]=function(){delete e.__jsonp_callbacks[h];d.apply(g,arguments)};a="?format=jsonp&callback="+encodeURIComponent("yourls.__jsonp_callbacks["+h+"]")+"&"+a;a+="&"+f(c);i.setAttribute("src",b+a);document.getElementsByTagName("head")[0].appendChild(i)}function f(a){var b,c="";for(b in a){if(a.hasOwnProperty(b)){if(typeof a[b]!=="undefined"){c+=b+"="+a[b]+"&"}}}return c.replace(/&$/,"")}var b="",c={},d=a.yourls;var e={};e.__jsonp_callbacks={};e.VERSION="1.0.0";e.connect=function(a,d){b=a;c={};if(d){if(d.signature){c.signature=d.signature}else{c.password=d.password;c.username=d.username}}return this};e.shorten=function(a,b,c,d){var e={action:"shorturl",url:a};if(typeof b==="function"){c=b}else{e.keyword=b}g(f(e),c,d);return this};e.stats=function(a,b,c,d){var e={action:"stats"},h=typeof a;switch(h){case"number":e.limit=a;c=b;break;case"function":c=a;break;default:e.filter=a;if(typeof b==="function"){c=b}else{e.limit=b}}g(f(e),c,d);return this};e.url=function(a){if(!(this instanceof e.url))return new e.url(a);this.url=a};e.url.prototype.expand=function(a,b){g(f({action:"expand",shorturl:this.url}),a,b);return this};e.url.prototype.stats=function(a,b){g(f({action:"url-stats",shorturl:this.url}),a,b);return this};e.noConflict=function(){a.yourls=d;return this};if(typeof define==="function"&&define.amd){define("yourls",function(){return e})}else{a.yourls=e}})(this) \ No newline at end of file