Socket
Socket
Sign inDemoInstall

yourls-api

Package Overview
Dependencies
0
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0 to 2.1.0

src/request/json.js

4

CHANGES.md

@@ -0,1 +1,5 @@

## Version 2.1.0, 2017.03.24
* Support JSON requests (GET & POST) [#6](https://github.com/neocotic/yourls-api/issues/6)
## Version 2.0.0, 2016.11.23

@@ -2,0 +6,0 @@

1281

dist/yourls.js

@@ -1,22 +0,1 @@

/*
* 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) {

@@ -29,3 +8,3 @@ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :

/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2016 Alasdair Mercer, Skelp
*

@@ -52,35 +31,81 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

/**
* The singleton {@link API} instance which is privatized to prevent leaking credentials.
* A bare-bones constructor for surrogate prototype swapping.
*
* @private
* @type {API}
* @constructor
*/
var instance = null;
var Constructor = function() {};
/**
* A reference to <code>Object.prototype.hasOwnProperty</code>.
*
* @private
* @type {Function}
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* A reference to <code>Array.prototype.slice</code>.
*
* @private
* @type {Function}
*/
var slice = Array.prototype.slice;
/**
* Sanitizes the specified <code>credentials</code> by ensuring that only valid properties are present and only when
* appropriate.
* Extends the specified <code>target</code> object with the properties in each of the <code>sources</code> provided.
*
* This function does not modify <code>credentials</code> and instead creates a new object with the sanitized
* properties.
* Nothing happens if <code>target</code> is <code>null</code> and if any source is <code>null</code> it will be
* ignored.
*
* @param {API~Credentials} credentials - the credentials to be sanitized (may be <code>null</code>)
* @return {API~Credentials} A sanitized version of <code>credentials</code> or <code>null</code> if
* <code>credentials</code> is <code>null</code>.
* @param {boolean} own - <code>true</code> to only copy <b>own</b> properties from <code>sources</code> onto
* <code>target</code>; otherwise <code>false</code>
* @param {Object} [target] - the target object which should be extended
* @param {...Object} [sources] - the source objects whose properties are to be copied onto <code>target</code>
* @return {void}
* @private
*/
function sanitizeCredentials(credentials) {
if (!credentials) {
return null
function extend(own, target, sources) {
if (target == null) {
return
}
var result = {};
if (credentials.signature) {
result.signature = credentials.signature;
result.timestamp = credentials.timestamp;
sources = slice.call(arguments, 2);
var property;
var source;
for (var i = 0, length = sources.length; i < length; i++) {
source = sources[i];
for (property in source) {
if (!own || hasOwnProperty.call(source, property)) {
target[property] = source[property];
}
}
}
}
/**
* Creates an object which inherits the given <code>prototype</code>.
*
* Optionally, the created object can be extended further with the specified <code>properties</code>.
*
* @param {Object} prototype - the prototype to be inherited by the created object
* @param {Object} [properties] - the optional properties to be extended by the created object
* @return {Object} The newly created object.
* @private
*/
function create(prototype, properties) {
var result;
if (typeof Object.create === 'function') {
result = Object.create(prototype);
} else {
result.password = credentials.password;
result.username = credentials.username;
Constructor.prototype = prototype;
result = new Constructor();
Constructor.prototype = null;
}
if (properties) {
extend(true, result, properties);
}
return result

@@ -90,2 +115,119 @@ }

/**
* The base constructor from which all others should extend.
*
* @public
* @constructor
*/
function Oopsy() {}
/**
* Extends the constructor to which this method is associated with the <code>prototype</code> and/or
* <code>statics</code> provided.
*
* If <code>constructor</code> is provided, it will be used as the constructor for the child, otherwise a simple
* constructor which only calls the super constructor will be used instead.
*
* The super constructor can be accessed via a special <code>super_</code> property on the child constructor.
*
* @param {Function} [constructor] - the constructor for the child
* @param {Object} [prototype] - the prototype properties to be defined for the child
* @param {Object} [statics] - the static properties to be defined for the child
* @return {Function} The child <code>constructor</code> provided or the one created if none was given.
* @public
* @static
*/
Oopsy.extend = function(constructor, prototype, statics) {
var superConstructor = this;
if (typeof constructor !== 'function') {
statics = prototype;
prototype = constructor;
constructor = function() {
return superConstructor.apply(this, arguments)
};
}
extend(false, constructor, superConstructor, statics);
constructor.prototype = create(superConstructor.prototype, prototype);
constructor.prototype.constructor = constructor;
constructor.super_ = superConstructor;
return constructor
};
/*
* Copyright (C) 2017 Alasdair Mercer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Extends the specified <code>target</code> object with the properties in each of the <code>sources</code> provided.
*
* Any of the <code>sources</code> that are <code>null</code> will simply be ignored.
*
* @param {Object} target - the target object which should be extended
* @param {...Object} [sources] - the source objects whose properties are to be copied onto <code>target</code>
* @return {Object} A reference to <code>target</code>.
* @protected
*/
function extend$1(target, sources) {
sources = Array.prototype.slice.call(arguments, 1);
for (var i = 0, length = sources.length, property, source; i < length; i++) {
source = sources[i];
if (source) {
for (property in source) {
if (Object.prototype.hasOwnProperty.call(source, property)) {
target[property] = source[property];
}
}
}
}
return target
}
/*
* Copyright (C) 2017 Alasdair Mercer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Contains information on how to connect to and authenticate with a YOURLS server.

@@ -96,6 +238,8 @@ *

* <code>null</code>)
* @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be
* <code>null</code>)
* @protected
* @constructor
*/
function API(url, credentials) {
var API = Oopsy.extend(function(url, credentials, options) {
/**

@@ -116,39 +260,94 @@ * The URL of the YOURLS server.

*/
this.credentials = sanitizeCredentials(credentials);
}
this.credentials = API._sanitizeCredentials(credentials);
/**
* The options to be used to send requests to the YOURLS server.
*
* @public
* @type {API~Options}
*/
this.options = API._sanitizeOptions(options);
}, null, {
/**
* Destroys the singleton instance of {@link API}.
*
* @return {void}
* @public
* @static
*/
API.clear = function() {
instance = null;
};
/**
* The default options to be used.
*
* @protected
* @static
* @type {API~Options}
*/
defaultOptions: {
format: 'jsonp',
method: 'GET'
},
/**
* Retrieves the singleton instance of {@link API}.
*
* This function will return <code>null</code> unless an instance is currently stored.
*
* @return {API} The connected {@link API} or <code>null</code> if none exists.
* @public
* @static
*/
API.fetch = function() {
return instance
};
/**
* The singleton {@link API} instance which is privatized to prevent leaking credentials.
*
* @public
* @static
* @type {API}
*/
instance: null,
/**
* Stores this {@link API} as the singleton, potentially replacing the existing instance.
*
* @return {void}
* @public
*/
API.prototype.store = function() {
instance = this;
};
/**
* Sanitizes the specified <code>credentials</code> by ensuring that only valid properties are present and only when
* appropriate.
*
* This method does not modify <code>credentials</code> and instead creates a new object with the sanitized
* properties.
*
* @param {API~Credentials} credentials - the credentials to be sanitized (may be <code>null</code>)
* @return {API~Credentials} A sanitized version of <code>credentials</code> or <code>null</code> if
* <code>credentials</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeCredentials: function(credentials) {
if (!credentials) {
return null
}
var result = {};
if (credentials.signature) {
result.signature = credentials.signature;
result.timestamp = credentials.timestamp;
} else {
result.password = credentials.password;
result.username = credentials.username;
}
return result
},
/**
* Sanitizes the specified <code>options</code> by ensuring that only valid properties are present and in the correct
* format.
*
* This method does not modify <code>options</code> and instead creates a new object with the sanitized properties and
* default values will be used for missing options.
*
* @param {API~Options} options - the options to be sanitized (may be <code>null</code>)
* @return {API~Options} A sanitized version of <code>options</code> which will contain only default values if
* <code>options</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeOptions: function(options) {
var result = extend$1({}, API.defaultOptions);
if (!options) {
return result
}
if (options.format) {
result.format = options.format.toLowerCase();
}
if (options.method) {
result.method = options.method.toUpperCase();
}
return result
}
});
/**

@@ -172,4 +371,16 @@ * The credentials to be used to authenticate with a private YOURLS API.

/**
* The options that determine how requests are sent to the YOURLS server.
*
* If the request <code>format</code> does not support the HTTP <code>method</code>, requests will not be sent and an
* error will be thrown when such attempts occur.
*
* @typedef {Object} API~Options
* @property {string} [format="jsonp"] - The format in which requests are sent (either <code>"json"</code> or
* <code>"jsonp"</code>).
* @property {string} [method="GET"] - The HTTP method to be used for requests.
*/
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -198,3 +409,3 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

*
* This function will use the native <code>Array.isArray</code>, if available.
* This method will use the native <code>Array.isArray</code>, if available.
*

@@ -210,3 +421,3 @@ * @param {*} obj - the object to be checked (may be <code>null</code>)

/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -233,28 +444,194 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

/**
* Creates a serialized representation of the specified <code>params</code> into a URL query string.
* Contains logic to connect with a YOURLS server and send data to its API.
*
* @param {Object} [params] - the hash of parameter key/value pairs to be serialized
* @return {string} A URL query string representing <code>params</code>.
* Due to the nature of HTTP, requests sent using a "GET" HTTP method will include all information in the URL of the
* request. This includes the data that is sent as well as <b>any credentials</b> used to authenticate with the API. You
* have been warned.
*
* @constructor
* @protected
*/
function paramify(params) {
if (!params) {
return ''
var Request = Oopsy.extend({
/**
* Builds the body for this {@link Request} based on the <code>api</code> and <code>data</code> provided.
*
* @param {API} api - the {@link API} to which the request is being sent
* @param {Object} [data] - the data being sent in this request
* @return {Object} The request body.
* @protected
*/
buildBody: function(api, data) {
return extend$1({ format: api.options.format }, api.credentials, data)
},
/**
* Returns the list of the HTTP methods that are supported by this {@link Request}.
*
* By default, this method returns <code>null</code>, so implementations <b>must</b> implement this method to ensure
* that {@link Request#isMethodSupported} works correctly.
*
* @return {string[]} The supported HTTP methods.
* @protected
*/
getSupportedHttpMethods: function() {
return null
},
/**
* Determines whether this {@link Request} supports the specified HTTP <code>method</code>.
*
* @param {string} method - the HTTP method to be checked
* @return {boolean} <code>true</code> if <code>method</code> is supported; otherwise <code>false</code>.
* @public
*/
isMethodSupported: function(method) {
var supportedMethods = this.getSupportedHttpMethods();
return supportedMethods && supportedMethods.indexOf(method) !== -1
},
/**
* Determines whether the data that is to be sent to the YOURLS server in this {@link Request} must be serialized as
* query string parameters.
*
* @param {string} method - the HTTP method to be used
* @return {boolean} <code>true</code> if the data needs to be sent as query string parameters; otherwise
* <code>false</code>.
* @protected
*/
isQueryStringRequired: function(method) {
return method === 'GET'
},
/**
* Processes this {@link Request} by sending it to the specified target <code>url</code> containing the
* <code>body</code> provided.
*
* <code>callback</code> should be called with the response regardless of whether the it was a success or failure.
*
* This method is called internally by {@link Request#send} and does all of the actual work involved to send the
* request and parse the response. All implementations <b>must</b> implement this method.
*
* @param {string} method - the request HTTP method
* @param {string} url - the request URL
* @param {Object} body - the request body (may be <code>null</code>)
* @param {Function} callback - the function to be called with the response
* @return {void}
* @protected
* @abstract
*/
process: function(method, url, body, callback) {
// Must be implemented
},
/**
* Sends the request to the connected YOURLS API with the <code>data</code> provided which should, in turn, call the
* specified <code>callback</code> with the result.
*
* If the request is successful, <code>callback</code> will be passed the value of the named properties from the
* response. If <code>resultNames</code> is a string or only contains a single string, only the value for that named
* property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named
* property will be passed as the first argument. The actual response will always be passed as the second argument.
*
* @param {Object} data - the data to be sent
* @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to
* <code>callback</code> as the first argument
* @param {Function} callback - the function to be called with the result
* @return {void}
* @public
*/
send: function(data, resultNames, callback) {
var api = API.instance;
var body = Request._serializeParameters(this.buildBody(api, data));
var method = api.options.method;
var url = api.url;
if (this.isQueryStringRequired(method)) {
url += '?' + body;
body = null;
}
this.process(method, url, body, function(response) {
callback(Request._extractResult(resultNames, response), response);
});
}
var results = [];
}, {
for (var key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
if (params[key] != null) {
results.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
/**
* Extracts the values of the properties with the specified <code>names</code> from the <code>response</code> provided
* and returns them in a single result.
*
* If <code>names</code> 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 <code>response</code> is <code>null</code> this method will return <code>null</code>.
*
* @param {string|string[]} names - the names of the <code>response</code> properties whose values are to be returned
* as the result
* @param {Object} response - the YOURLS API response
* @return {*} The result extracted from <code>response</code>.
* @private
* @static
*/
_extractResult: function(names, response) {
names = isArray(names) ? names : [ names ];
var i;
var name;
var result = null;
if (!response) {
return result
}
if (names.length === 1) {
result = response[names[0]];
} else {
result = {};
for (i = 0; i < names.length; i++) {
name = names[i];
if (typeof response[name] !== 'undefined') {
result[name] = response[name];
}
}
}
return result
},
/**
* Creates a serialized representation of the specified parameters.
*
* All of the parameter names and values are URL-encoded so that they can be safely included in the query string or
* request body.
*
* @param {Object} [params] - the hash of parameter name/value pairs to be serialized
* @return {string} A URL-encoded representing <code>obj</code> or an empty string if <code>obj</code> is
* <code>null</code>.
* @private
* @static
*/
_serializeParameters: function(params) {
if (!params) {
return ''
}
var results = [];
for (var name in params) {
if (Object.prototype.hasOwnProperty.call(params, name) && params[name] != null) {
results.push(encodeURIComponent(name) + '=' + encodeURIComponent(params[name]));
}
}
return results.join('&')
}
return results.join('&')
}
});
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -281,125 +658,272 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

/**
* The key of the callback function holder within the global namespace.
* An implementation of {@link Request} that provides support for JSON requests to the YOURLS API.
*
* @private
* @type {string}
* JSON requests can only be sent using the "GET" or "POST" HTTP methods.
*
* @constructor
* @extends Request
* @protected
*/
var callbackHolderKey = '__yourls' + Date.now() + '_jsonp';
/**
* Contains the callback functions for active JSONP requests.
var JSONRequest = Request.extend({
/**
* @inheritDoc
* @override
*/
getSupportedHttpMethods: function() {
return [ 'GET', 'POST' ]
},
/**
* @inheritDoc
* @override
*/
process: function(method, url, body, callback) {
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onreadystatechange = function() {
var response;
if (xhr.readyState === 4) {
try {
response = JSON.parse(xhr.responseText);
callback(response);
} catch (e) {
throw new Error('Unable to parse response: ' + e)
}
}
};
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (body != null) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.send(body);
}
});
/*
* Copyright (C) 2017 Alasdair Mercer
*
* Callback references should be removed immediately once they have been called.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* Due to the nature of JSON, a reference to this object <b>must</b> be publicly available (i.e. global).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* @private
* @type {Object<number, Function>}
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
var callbackHolder = window[callbackHolderKey] = {};
/**
* Generates a quick and dirty unique ID for a callback.
* The seed to be used to generate IDs.
*
* @return {number} The generated callback ID.
* @private
* @type {number}
*/
function generateCallbackId() {
var id = Date.now();
while (callbackHolder[id]) {
id++;
}
var seed = new Date().getTime();
return id
}
/**
* Extracts the values of the properties with the specified <code>names</code> from the <code>response</code> provided
* and returns them in a single result.
* An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API.
*
* If <code>names</code> is a string or only contains a single string, only the value for that named property will be
* returned. Otherwise, an object containing the key/value pairs for each named property will be returned.
* JSONP requests can only be sent using the "GET" HTTP method.
*
* If <code>response</code> is <code>null</code> this function will return <code>null</code>.
*
* @param {string|string[]} names - the names of the <code>response</code> properties whose values are to be returned as
* the result
* @param {Object} response - the YOURLS API response
* @return {*} The result extracted from <code>response</code>.
* @private
* @constructor
* @extends Request
* @protected
*/
function getResult(names, response) {
names = isArray(names) ? names : [ names ];
var JSONPRequest = Request.extend(function() {
JSONPRequest.super_.call(this);
var i;
var name;
var result = null;
if (!window[JSONPRequest._callbackHolderKey]) {
window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder;
}
if (!response) {
return result
/**
* The generated ID which is used to store a reference to the callback function within the holder so that the JSONP
* payload can find it in the global namespace.
*
* @private
* @type {number}
*/
this._id = JSONPRequest._generateId();
}, {
/**
* @inheritDoc
* @override
*/
getSupportedHttpMethods: function() {
return [ 'GET' ]
},
/**
* @inheritDoc
* @override
*/
buildBody: function(api, data) {
var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data);
body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']';
return body
},
/**
* @inheritDoc
* @override
*/
process: function(method, url, body, callback) {
var script = document.createElement('script');
var self = this;
JSONPRequest._callbackHolder[this._id] = function(response) {
delete JSONPRequest._callbackHolder[self._id];
script.parentNode.removeChild(script);
callback(response);
};
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
}
if (names.length === 1) {
result = response[names[0]];
} else {
result = {};
}, {
for (i = 0; i < names.length; i++) {
name = names[i];
/**
* The key of the callback function holder within the global namespace.
*
* @private
* @static
* @type {string}
*/
_callbackHolderKey: '__yourls' + seed + '_jsonp',
if (typeof response[name] !== 'undefined') {
result[name] = response[name];
}
}
/**
* Contains the callback functions for active JSONP requests.
*
* Callback references should be removed immediately once they have been called.
*
* Due to the nature of JSON, a reference to this object <b>must</b> be publicly available (i.e. global).
*
* @private
* @static
* @type {Object<number, Function>}
*/
_callbackHolder: {},
/**
* Generates an ID to be used when storing a reference to a callback function.
*
* @return {number} The generated ID.
* @private
*/
_generateId: function() {
do {
seed++;
} while (JSONPRequest._callbackHolder[seed])
return seed
}
return result
}
});
/**
* Sends a JSONP request to the connected YOURLS API with the <code>data</code> provided which should, in turn, call the
* specified <code>callback</code> with the result.
/*
* Copyright (C) 2017 Alasdair Mercer
*
* If the request is successful, <code>callback</code> will be passed the value of the named properties from the
* response. If <code>resultNames</code> is a string or only contains a single string, only the value for that named
* property will be passed as the first argument. Otherwise, an object containing the key/value pairs for each named
* property will be passed as the first argument. The actual response will always be passed as the second argument.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* Due to the nature of JSONP, all information will be included in the URL of the request. This includes
* <code>data</code> as well as <b>any credentials</b> used to authenticate with the API. You have been warned.
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* @param {Object} data - the data to be sent
* @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to
* <code>callback</code> as the first argument
* @param {Function} callback - the function to be called with the result
* @return {void}
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Can make requests to the connected YOURLS API.
*
* @constructor
* @protected
*/
function jsonp(data, resultNames, callback) {
var api = API.fetch();
var id = generateCallbackId();
var script = document.createElement('script');
var Requestor = Oopsy.extend({
callbackHolder[id] = function(response) {
var result = getResult(resultNames, response);
/**
* Sends the request to the connected YOURLS API with the <code>data</code> provided which should, in turn, call the
* specified <code>callback</code> with the result.
*
* This method is primarily a proxy to {@link Request#send} but does validate the state of the connection information
* to ensure that is is valid before making the request.
*
* @param {Object} data - the data to be sent
* @param {string|string[]} resultNames - the names of the response properties whose values are to be passed to
* <code>callback</code> as the first argument
* @param {Function} callback - the function to be called with the result
* @return {void}
* @throws {Error} - If either no connection is present, the request format is not supported, or the configured HTTP
* method is not supported by the {@link Request}.
* @protected
*/
sendRequest: function(data, resultNames, callback) {
var api = API.instance;
delete callbackHolder[id];
script.parentNode.removeChild(script);
if (!api) {
throw new Error('No connection has been made')
}
callback(result, response);
};
var format = api.options.format;
var method = api.options.method;
var Request = Requestor._requestFormatMap[format];
var target = api.url + '?' + paramify({ callback: callbackHolderKey + '[' + id + ']', format: 'jsonp' });
if (api.credentials) {
target += '&' + paramify(api.credentials);
if (!Request) {
throw new Error('Request format not supported: ' + format)
}
var request = new Request();
if (!request.isMethodSupported(method)) {
throw new Error('HTTP method not supported: ' + method)
}
request.send(data, resultNames, callback);
}
if (data) {
target += '&' + paramify(data);
}, {
/**
* The mapping of supported request formats to {@link Request} constructors.
*
* @private
* @static
* @type {Object<string, Function>}
*/
_requestFormatMap: {
json: JSONRequest,
jsonp: JSONPRequest
}
script.setAttribute('src', target);
document.getElementsByTagName('head')[0].appendChild(script);
}
});
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -429,25 +953,26 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

* @constructor
* @extends Requestor
* @protected
*/
function DB() {
// Do nothing
}
var DB = Requestor.extend({
/**
* Retrieves the statistics for this {@link DB}.
*
* @param {Function} callback - the callback function to be called with the result
* @return {DB} A reference to this {@link DB} for chaining purposes.
* @public
*/
DB.prototype.stats = function(callback) {
var data = { action: 'db-stats' };
/**
* Retrieves the statistics for this {@link DB}.
*
* @param {Function} callback - the callback function to be called with the result
* @return {DB} A reference to this {@link DB} for chaining purposes.
* @public
*/
stats: function(callback) {
var data = { action: 'db-stats' };
jsonp(data, 'db-stats', callback);
this.sendRequest(data, 'db-stats', callback);
return this
};
return this
}
});
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -478,5 +1003,8 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

* @constructor
* @extends Requestor
* @protected
*/
function URL(url) {
var URL = Requestor.extend(function(url) {
URL.super_.call(this);
/**

@@ -489,3 +1017,3 @@ * Either the shortened URL or its keyword for this {@link URL}.

this.url = url;
}
});

@@ -505,3 +1033,3 @@ /**

jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback);
this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback);

@@ -524,3 +1052,3 @@ return this

jsonp(data, 'link', callback);
this.sendRequest(data, 'link', callback);

@@ -531,3 +1059,3 @@ return this

/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -554,34 +1082,2 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

/**
* 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 <code>result</code> if it is <code>null</code>, has no links property or one that is
* already an array (future-proofing).
*
* @param {Object} result - the result to be sanitized (may be <code>null</code>)
* @param {Object} [result.links] - the links to be transformed into an array (may be <code>null</code>)
* @return {Object} The modified <code>result</code> or <code>null</code> if <code>result</code> is <code>null</code>.
* @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.

@@ -593,5 +1089,8 @@ *

* @constructor
* @extends Requestor
* @protected
*/
var YOURLS = function() {
var YOURLS = Requestor.extend(function() {
YOURLS.super_.call(this);
/**

@@ -608,4 +1107,4 @@ * Provides information on the YOURLS {@link DB}.

*
* This is <b>not</b> the same as the version of YOURLS that is being connected to. The {@link YOURLS#version}
* function should be used to provide that information.
* This is <b>not</b> the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method
* should be used to provide that information.
*

@@ -615,164 +1114,202 @@ * @public

*/
this.VERSION = '2.0.0';
};
this.VERSION = '2.1.0';
}, {
/**
* Stores the specified information to be used later to connect to and authenticate with a YOURLS server.
*
* @param {string} [url=''] - the URL for the YOURLS server
* @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be
* <code>null</code>)
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.connect = function(url, credentials) {
var api = new API(url, credentials);
api.store();
/**
* Stores the specified information to be used later to connect to and authenticate with a YOURLS server.
*
* @param {string} [url=''] - the URL for the YOURLS server
* @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be
* <code>null</code>)
* @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be
* <code>null</code>)
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
connect: function(url, credentials, options) {
API.instance = new API(url, credentials, options);
return this
};
return this
},
/**
* Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS
* server.
*
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.disconnect = function() {
API.clear();
/**
* Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS
* server.
*
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
disconnect: function() {
API.instance = null;
return this
};
return this
},
/**
* Creates a short URL for the specified long <code>url</code>.
*
* Optionally, a <code>descriptor</code> 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 <code>descriptor</code> is a string, it will be treated as the keyword.
*
* @param {string} url - the long URL to be shortened
* @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used
* for the short URL
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.shorten = function(url, descriptor, callback) {
var data = {
action: 'shorturl',
url: url
};
/**
* Creates a short URL for the specified long <code>url</code>.
*
* Optionally, a <code>descriptor</code> 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 <code>descriptor</code> is a string, it will be treated as the keyword.
*
* @param {string} url - the long URL to be shortened
* @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be
* used for the short URL
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
shorten: function(url, descriptor, callback) {
var data = {
action: 'shorturl',
url: url
};
switch (typeof descriptor) {
case 'function':
callback = descriptor;
descriptor = null;
break
case 'string':
descriptor = { keyword: descriptor };
break
default:
// Do nothing
}
switch (typeof descriptor) {
case 'function':
callback = descriptor;
descriptor = null;
break
case 'string':
descriptor = { keyword: descriptor };
break
default:
// Do nothing
}
if (descriptor) {
data.keyword = descriptor.keyword;
data.title = descriptor.title;
}
if (descriptor) {
data.keyword = descriptor.keyword;
data.title = descriptor.title;
}
jsonp(data, [ 'shorturl', 'title', 'url' ], callback);
this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback);
return this
};
return this
},
/**
* Retrieves the statistics for all shortened URLs.
*
* Optionally, <code>criteria</code> 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
* <code>criteria</code> 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' };
/**
* Retrieves the statistics for all shortened URLs.
*
* Optionally, <code>criteria</code> 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 <code>criteria</code> is a number, it will be treated as the limit.
*
* No links will be included in the result unless a limit is specified that is greater than zero. In that case, this
* method would effectively be doing the same as {@link DB#stats}.
*
* @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to
* search for links to be included in the result
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
stats: function(criteria, callback) {
var data = { action: 'stats' };
switch (typeof criteria) {
case 'function':
callback = criteria;
criteria = null;
break
case 'number':
criteria = { limit: criteria };
break
default:
// Do nothing
}
switch (typeof criteria) {
case 'function':
callback = criteria;
criteria = null;
break
case 'number':
criteria = { limit: criteria };
break
default:
// Do nothing
}
if (criteria) {
data.filter = criteria.filter;
data.limit = criteria.limit;
data.start = criteria.start;
}
if (criteria) {
data.filter = criteria.filter;
data.limit = criteria.limit;
data.start = criteria.start;
}
jsonp(data, [ 'links', 'stats' ], function(result, response) {
callback(sanitizeStatsResult(result), response);
});
this.sendRequest(data, [ 'links', 'stats' ], function(result, response) {
callback(YOURLS._sanitizeStatsResult(result), response);
});
return this
};
return this
},
/**
* Creates an instance of {@link URL} for the specified shortened <code>url</code> 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 <code>url</code> or <code>null</code> if <code>url</code> is
* <code>null</code>.
* @public
*/
YOURLS.prototype.url = function(url) {
return url ? new URL(url) : null
};
/**
* Creates an instance of {@link URL} for the specified shortened <code>url</code> which can be used to lookup more
* detailed information relating to it.
*
* No data is fetched just by calling this method; one of the methods on the returned instance need to be called for
* that to happen.
*
* @param {string} url - the shortened URL (or its keyword)
* @return {URL} The {@link URL} created for the shortened <code>url</code> or <code>null</code> if <code>url</code>
* is <code>null</code>.
* @public
*/
url: function(url) {
return url ? new URL(url) : null
},
/**
* Retrieves the version of the connected YOURLS API.
*
* Optionally, <code>db</code> can be passed to indicate that the YOURLS database version should also be included in the
* result.
*
* @param {boolean} [db] - <code>true</code> to include the database version; otherwise <code>false</code>
* @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' };
/**
* Retrieves the version of the connected YOURLS API.
*
* Optionally, <code>db</code> can be passed to indicate that the YOURLS database version should also be included in
* the result.
*
* @param {boolean} [db] - <code>true</code> to include the database version; otherwise <code>false</code>
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
version: function(db, callback) {
var data = { action: 'version' };
if (typeof db === 'function') {
callback = db;
db = null;
if (typeof db === 'function') {
callback = db;
db = null;
}
if (db != null) {
data.db = Number(db);
}
this.sendRequest(data, [ 'db_version', 'version' ], callback);
return this
}
if (db != null) {
data.db = Number(db);
}, {
/**
* Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links
* from an object mapping into an array.
*
* This method simply returns <code>result</code> if it is <code>null</code>, has no links property or one that is
* already an array (future-proofing).
*
* @param {Object} result - the result to be sanitized (may be <code>null</code>)
* @param {Object} [result.links] - the links to be transformed into an array (may be <code>null</code>)
* @return {Object} The modified <code>result</code> or <code>null</code> if <code>result</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeStatsResult: function(result) {
// Future-proofing by sanitizing links *only* when not already an array
if (!result || !result.links || isArray(result.links)) {
return result
}
var index = 1;
var link;
var links = [];
while ((link = result.links['link_' + index]) != null) {
links.push(link);
index++;
}
result.links = links;
return result
}
jsonp(data, [ 'db_version', 'version' ], callback);
});
return this
};
/**

@@ -779,0 +1316,0 @@ * The singleton instance of {@link YOURLS}.

@@ -1,4 +0,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;e<t.length;e++)o=t[e],"undefined"!=typeof n[o]&&(i[o]=n[o]);return i}function u(t,r,u){var s=n.fetch(),a=o(),l=document.createElement("script");p[a]=function(t){var n=i(r,t);delete p[a],l.parentNode.removeChild(l),u(n,t)};var c=s.url+"?"+e({callback:f+"["+a+"]",format:"jsonp"});s.credentials&&(c+="&"+e(s.credentials)),t&&(c+="&"+e(t)),l.setAttribute("src",c),document.getElementsByTagName("head")[0].appendChild(l)}function s(){}function a(t){this.url=t}function l(t){if(!t||!t.links||r(t.links))return t;for(var n,e=1,o=[];null!=(n=t.links["link_"+e]);)o.push(n),e++;return t.links=o,t}var c=null;n.clear=function(){c=null},n.fetch=function(){return c},n.prototype.store=function(){c=this};var f="__yourls"+Date.now()+"_jsonp",p=window[f]={};s.prototype.stats=function(t){var n={action:"db-stats"};return u(n,"db-stats",t),this},a.prototype.expand=function(t){var n={action:"expand",shorturl:this.url};return u(n,["keyword","longurl","shorturl"],t),this},a.prototype.stats=function(t){var n={action:"url-stats",shorturl:this.url};return u(n,"link",t),this};var d=function(){this.db=new s,this.VERSION="2.0.0"};d.prototype.connect=function(t,r){var e=new n(t,r);return e.store(),this},d.prototype.disconnect=function(){return n.clear(),this},d.prototype.shorten=function(t,n,r){var e={action:"shorturl",url:t};switch(typeof n){case"function":r=n,n=null;break;case"string":n={keyword:n}}return n&&(e.keyword=n.keyword,e.title=n.title),u(e,["shorturl","title","url"],r),this},d.prototype.stats=function(t,n){var r={action:"stats"};switch(typeof t){case"function":n=t,t=null;break;case"number":t={limit:t}}return t&&(r.filter=t.filter,r.limit=t.limit,r.start=t.start),u(r,["links","stats"],function(t,r){n(l(t),r)}),this},d.prototype.url=function(t){return t?new a(t):null},d.prototype.version=function(t,n){var r={action:"version"};return"function"==typeof t&&(n=t,t=null),null!=t&&(r.db=Number(t)),u(r,["db_version","version"],n),this};var h=new d;return h});
/*! YOURLS API v2.1.0 | (C) 2017 Alasdair Mercer | MIT License */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define("yourls-api",e):t.yourls=e()}(this,function(){"use strict";function t(t,e,n){if(null!=e){n=u.call(arguments,2);for(var r,o,i=0,a=n.length;i<a;i++){o=n[i];for(r in o)t&&!s.call(o,r)||(e[r]=o[r])}}}function e(e,n){var r;return"function"==typeof Object.create?r=Object.create(e):(i.prototype=e,r=new i,i.prototype=null),n&&t(!0,r,n),r}function n(){}function r(t,e){e=Array.prototype.slice.call(arguments,1);for(var n,r,o=0,i=e.length;o<i;o++)if(r=e[o])for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(t[n]=r[n]);return t}function o(t){return Array.isArray?Array.isArray(t):"[object Array]"===Object.prototype.toString.call(t)}var i=function(){},s=Object.prototype.hasOwnProperty,u=Array.prototype.slice;n.extend=function(n,r,o){var i=this;return"function"!=typeof n&&(o=r,r=n,n=function(){return i.apply(this,arguments)}),t(!1,n,i,o),n.prototype=e(i.prototype,r),n.prototype.constructor=n,n.super_=i,n};var a=n.extend(function(t,e,n){this.url=t?t.replace(/\/$/,""):"",this.credentials=a._sanitizeCredentials(e),this.options=a._sanitizeOptions(n)},null,{defaultOptions:{format:"jsonp",method:"GET"},instance:null,_sanitizeCredentials:function(t){if(!t)return null;var e={};return t.signature?(e.signature=t.signature,e.timestamp=t.timestamp):(e.password=t.password,e.username=t.username),e},_sanitizeOptions:function(t){var e=r({},a.defaultOptions);return t?(t.format&&(e.format=t.format.toLowerCase()),t.method&&(e.method=t.method.toUpperCase()),e):e}}),l=n.extend({buildBody:function(t,e){return r({format:t.options.format},t.credentials,e)},getSupportedHttpMethods:function(){return null},isMethodSupported:function(t){var e=this.getSupportedHttpMethods();return e&&e.indexOf(t)!==-1},isQueryStringRequired:function(t){return"GET"===t},process:function(t,e,n,r){},send:function(t,e,n){var r=a.instance,o=l._serializeParameters(this.buildBody(r,t)),i=r.options.method,s=r.url;this.isQueryStringRequired(i)&&(s+="?"+o,o=null),this.process(i,s,o,function(t){n(l._extractResult(e,t),t)})}},{_extractResult:function(t,e){t=o(t)?t:[t];var n,r,i=null;if(!e)return i;if(1===t.length)i=e[t[0]];else for(i={},n=0;n<t.length;n++)r=t[n],void 0!==e[r]&&(i[r]=e[r]);return i},_serializeParameters:function(t){if(!t)return"";var e=[];for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&null!=t[n]&&e.push(encodeURIComponent(n)+"="+encodeURIComponent(t[n]));return e.join("&")}}),c=l.extend({getSupportedHttpMethods:function(){return["GET","POST"]},process:function(t,e,n,r){var o=new XMLHttpRequest;o.open(t,e,!0),o.onreadystatechange=function(){var t;if(4===o.readyState)try{t=JSON.parse(o.responseText),r(t)}catch(t){throw new Error("Unable to parse response: "+t)}},o.setRequestHeader("X-Requested-With","XMLHttpRequest"),null!=n&&o.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),o.send(n)}}),d=(new Date).getTime(),p=l.extend(function(){p.super_.call(this),window[p._callbackHolderKey]||(window[p._callbackHolderKey]=p._callbackHolder),this._id=p._generateId()},{getSupportedHttpMethods:function(){return["GET"]},buildBody:function(t,e){var n=p.super_.prototype.buildBody.call(this,t,e);return n.callback=p._callbackHolderKey+"["+this._id+"]",n},process:function(t,e,n,r){var o=document.createElement("script"),i=this;p._callbackHolder[this._id]=function(t){delete p._callbackHolder[i._id],o.parentNode.removeChild(o),r(t)},o.setAttribute("src",e),document.getElementsByTagName("head")[0].appendChild(o)}},{_callbackHolderKey:"__yourls"+d+"_jsonp",_callbackHolder:{},_generateId:function(){do{d++}while(p._callbackHolder[d]);return d}}),f=n.extend({sendRequest:function(t,e,n){var r=a.instance;if(!r)throw new Error("No connection has been made");var o=r.options.format,i=r.options.method,s=f._requestFormatMap[o];if(!s)throw new Error("Request format not supported: "+o);var u=new s;if(!u.isMethodSupported(i))throw new Error("HTTP method not supported: "+i);u.send(t,e,n)}},{_requestFormatMap:{json:c,jsonp:p}}),h=f.extend({stats:function(t){var e={action:"db-stats"};return this.sendRequest(e,"db-stats",t),this}}),y=f.extend(function(t){y.super_.call(this),this.url=t});y.prototype.expand=function(t){var e={action:"expand",shorturl:this.url};return this.sendRequest(e,["keyword","longurl","shorturl"],t),this},y.prototype.stats=function(t){var e={action:"url-stats",shorturl:this.url};return this.sendRequest(e,"link",t),this};var m=f.extend(function(){m.super_.call(this),this.db=new h,this.VERSION="2.1.0"},{connect:function(t,e,n){return a.instance=new a(t,e,n),this},disconnect:function(){return a.instance=null,this},shorten:function(t,e,n){var r={action:"shorturl",url:t};switch(typeof e){case"function":n=e,e=null;break;case"string":e={keyword:e}}return e&&(r.keyword=e.keyword,r.title=e.title),this.sendRequest(r,["shorturl","title","url"],n),this},stats:function(t,e){var n={action:"stats"};switch(typeof t){case"function":e=t,t=null;break;case"number":t={limit:t}}return t&&(n.filter=t.filter,n.limit=t.limit,n.start=t.start),this.sendRequest(n,["links","stats"],function(t,n){e(m._sanitizeStatsResult(t),n)}),this},url:function(t){return t?new y(t):null},version:function(t,e){var n={action:"version"};return"function"==typeof t&&(e=t,t=null),null!=t&&(n.db=Number(t)),this.sendRequest(n,["db_version","version"],e),this}},{_sanitizeStatsResult:function(t){if(!t||!t.links||o(t.links))return t;for(var e,n=1,r=[];null!=(e=t.links["link_"+n]);)r.push(e),n++;return t.links=r,t}});return new m});
//# sourceMappingURL=yourls.min.js.map

@@ -1,2 +0,2 @@

Copyright (C) 2016 Alasdair Mercer
Copyright (C) 2017 Alasdair Mercer

@@ -3,0 +3,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "yourls-api",
"version": "2.0.0",
"version": "2.1.0",
"description": "Bindings for the YOURLS API",

@@ -20,2 +20,3 @@ "homepage": "https://github.com/neocotic/yourls-api",

"api",
"json",
"jsonp"

@@ -35,13 +36,15 @@ ],

"grunt-rollup": "^1.0.1",
"rollup-plugin-commonjs": "^5.0.5",
"load-grunt-tasks": "^3.5.2",
"oopsy": "^0.2.0",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-uglify": "^1.0.1",
"semver": "^5.3.0"
"rollup-plugin-uglify": "^1.0.1"
},
"main": "dist/yourls.js",
"browser": "dist/yourls.js",
"jsnext:main": "src/yourls.js",
"scripts": {
"build": "grunt build",
"ci": "grunt ci",
"test": "grunt test"
}
}

@@ -18,12 +18,10 @@ Y88b d88P .d88888b. 888 888 8888888b. 888 .d8888b.

[YOURLS API](https://github.com/neocotic/yourls-api) is a JavaScript library that provides bindings for
[YOURLS](https://yourls.org) URL shortener servers.
> This library is only compatible with YOURLS servers running version **1.5.1** or newer as it requires JSONP support.
[![Build Status](https://img.shields.io/travis/neocotic/yourls-api/develop.svg?style=flat-square)](https://travis-ci.org/neocotic/yourls-api)
[![Dev Dependency Status](https://img.shields.io/david/dev/neocotic/yourls-api.svg?style=flat-square)](https://david-dm.org/neocotic/yourls-api#info=devDependencies)
[![Dev Dependency Status](https://img.shields.io/david/dev/neocotic/yourls-api.svg?style=flat-square)](https://david-dm.org/neocotic/yourls-api?type=dev)
[![License](https://img.shields.io/npm/l/yourls-api.svg?style=flat-square)](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md)
[![Release](https://img.shields.io/npm/v/yourls-api.svg?style=flat-square)](https://www.npmjs.com/package/yourls-api)
[YOURLS API](https://github.com/neocotic/yourls-api) is a JavaScript library that provides bindings for
[YOURLS](https://yourls.org) URL shortener servers.
* [Install](#install)

@@ -52,4 +50,4 @@ * [API](#api)

* [Development Version](https://github.com/neocotic/yourls-api/blob/master/dist/yourls.js)
* [Production Version](https://github.com/neocotic/yourls-api/blob/master/dist/yourls.min.js)
* [Development Version](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.js) (TODO - [Source Map](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.js.map))
* [Production Version](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.min.js) (TODO - [Source Map](https://cdn.rawgit.com/neocotic/yourls-api/master/dist/yourls.min.js.map))

@@ -74,3 +72,3 @@ ## API

``` javascript
yourls.connect(url[, credentials])
yourls.connect(url[, credentials][, options])
```

@@ -126,8 +124,44 @@

> **IMPORTANT:** When sending `GET` requests, by design, all information will be included in the URL of the request
> This includes data as well as *any credentials* used to authenticate with the API. You have been warned.
> This is unavoidable when sending requests in the JSONP format but, when using the JSON format, you can send `POST`
> requests, which means that your data is sent inside the body of the request. Combine this with HTTPS and your data and
> credentials cannot be sniffed over the network.
As you may have noticed; this method also accepts the following entirely optional `options`:
Option | Description | Default
------ | ----------------------------------- | ---------
format | Format in which requests are sent | `"jsonp"`
method | HTTP method to be used for requests | `"GET"`
``` javascript
// Does the same as specifying no options (i.e. using defaults)
yourls.connect('https://example.com/yourls-api.php', null, {
format: 'jsonp',
method: 'GET'
})
// Best practice if you want to secure the data you're transmitting and you've setup CORS, if needed
yourls.connect('https://example.com/yourls-api.php', {
signature: '3002a61584'
}, {
format: 'json',
method: 'POST'
})
```
The following formats are supported with the corresponding HTTP methods:
Format | HTTP Methods
------ | ------------
json | GET, POST
jsonp | GET
> **IMPORTANT:** The YOURLS server must be running version **1.5.1** or newer in order to send requests in the JSONP
> format.
Despite the name of this method, no connection or authentication is carried out at this point and this initial method
simply stores these values to prevent you from having to specify them with every API call.
> When using JSONP to send requests, by design, all information will be included in the URL of the request. This
> includes data as well as **any credentials** used to authenticate with the API. You have been warned.
### Disconnecting

@@ -278,2 +312,3 @@

``` javascript
// Get more details for link
yourls.url('https://example.com/yourls').expand(function(result, response) {

@@ -298,2 +333,3 @@ console.log(result.keyword)

``` javascript
// Get statistics only for this link
yourls.url('https://example.com/yourls').stats(function(result, response) {

@@ -318,2 +354,3 @@ console.log(result.clicks)

``` javascript
// Get YOURLS version
yourls.version(function(result, response) {

@@ -328,2 +365,3 @@ console.log(result.version)

``` javascript
// Get YOURLS database version as well
yourls.version(true, function(result, response) {

@@ -340,4 +378,5 @@ console.log(result.version)

``` javascript
// Get version of this library
console.log(yourls.VERSION)
//=> "2.0.0"
//=> "2.1.0"
```

@@ -376,4 +415,4 @@

Copyright © 2016 Alasdair Mercer
Copyright © 2017 Alasdair Mercer
See [LICENSE.md](https://github.com/neocotic/yourls-api/blob/master/LICENSE.md) for more information on our MIT license.
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -23,39 +23,6 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

/**
* The singleton {@link API} instance which is privatized to prevent leaking credentials.
*
* @private
* @type {API}
*/
var instance = null
import Oopsy from 'oopsy'
/**
* Sanitizes the specified <code>credentials</code> by ensuring that only valid properties are present and only when
* appropriate.
*
* This function does not modify <code>credentials</code> and instead creates a new object with the sanitized
* properties.
*
* @param {API~Credentials} credentials - the credentials to be sanitized (may be <code>null</code>)
* @return {API~Credentials} A sanitized version of <code>credentials</code> or <code>null</code> if
* <code>credentials</code> is <code>null</code>.
* @private
*/
function sanitizeCredentials(credentials) {
if (!credentials) {
return null
}
import { extend } from './util/extend'
var result = {}
if (credentials.signature) {
result.signature = credentials.signature
result.timestamp = credentials.timestamp
} else {
result.password = credentials.password
result.username = credentials.username
}
return result
}
/**

@@ -67,6 +34,8 @@ * Contains information on how to connect to and authenticate with a YOURLS server.

* <code>null</code>)
* @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be
* <code>null</code>)
* @protected
* @constructor
*/
export function API(url, credentials) {
export var API = Oopsy.extend(function(url, credentials, options) {
/**

@@ -87,39 +56,94 @@ * The URL of the YOURLS server.

*/
this.credentials = sanitizeCredentials(credentials)
}
this.credentials = API._sanitizeCredentials(credentials)
/**
* The options to be used to send requests to the YOURLS server.
*
* @public
* @type {API~Options}
*/
this.options = API._sanitizeOptions(options)
}, null, {
/**
* Destroys the singleton instance of {@link API}.
*
* @return {void}
* @public
* @static
*/
API.clear = function() {
instance = null
}
/**
* The default options to be used.
*
* @protected
* @static
* @type {API~Options}
*/
defaultOptions: {
format: 'jsonp',
method: 'GET'
},
/**
* Retrieves the singleton instance of {@link API}.
*
* This function will return <code>null</code> unless an instance is currently stored.
*
* @return {API} The connected {@link API} or <code>null</code> if none exists.
* @public
* @static
*/
API.fetch = function() {
return instance
}
/**
* The singleton {@link API} instance which is privatized to prevent leaking credentials.
*
* @public
* @static
* @type {API}
*/
instance: null,
/**
* Stores this {@link API} as the singleton, potentially replacing the existing instance.
*
* @return {void}
* @public
*/
API.prototype.store = function() {
instance = this
}
/**
* Sanitizes the specified <code>credentials</code> by ensuring that only valid properties are present and only when
* appropriate.
*
* This method does not modify <code>credentials</code> and instead creates a new object with the sanitized
* properties.
*
* @param {API~Credentials} credentials - the credentials to be sanitized (may be <code>null</code>)
* @return {API~Credentials} A sanitized version of <code>credentials</code> or <code>null</code> if
* <code>credentials</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeCredentials: function(credentials) {
if (!credentials) {
return null
}
var result = {}
if (credentials.signature) {
result.signature = credentials.signature
result.timestamp = credentials.timestamp
} else {
result.password = credentials.password
result.username = credentials.username
}
return result
},
/**
* Sanitizes the specified <code>options</code> by ensuring that only valid properties are present and in the correct
* format.
*
* This method does not modify <code>options</code> and instead creates a new object with the sanitized properties and
* default values will be used for missing options.
*
* @param {API~Options} options - the options to be sanitized (may be <code>null</code>)
* @return {API~Options} A sanitized version of <code>options</code> which will contain only default values if
* <code>options</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeOptions: function(options) {
var result = extend({}, API.defaultOptions)
if (!options) {
return result
}
if (options.format) {
result.format = options.format.toLowerCase()
}
if (options.method) {
result.method = options.method.toUpperCase()
}
return result
}
})
/**

@@ -142,1 +166,13 @@ * The credentials to be used to authenticate with a private YOURLS API.

*/
/**
* The options that determine how requests are sent to the YOURLS server.
*
* If the request <code>format</code> does not support the HTTP <code>method</code>, requests will not be sent and an
* error will be thrown when such attempts occur.
*
* @typedef {Object} API~Options
* @property {string} [format="jsonp"] - The format in which requests are sent (either <code>"json"</code> or
* <code>"jsonp"</code>).
* @property {string} [method="GET"] - The HTTP method to be used for requests.
*/
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -23,126 +23,114 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

import { API } from '../api'
import { isArray } from '../util/array'
import { paramify } from './paramify'
import { Request } from './request'
/**
* The key of the callback function holder within the global namespace.
* The seed to be used to generate IDs.
*
* @private
* @type {string}
* @type {number}
*/
var callbackHolderKey = '__yourls' + Date.now() + '_jsonp'
var seed = new Date().getTime()
/**
* Contains the callback functions for active JSONP requests.
* An implementation of {@link Request} that provides support for JSONP requests to the YOURLS API.
*
* Callback references should be removed immediately once they have been called.
* JSONP requests can only be sent using the "GET" HTTP method.
*
* Due to the nature of JSON, a reference to this object <b>must</b> be publicly available (i.e. global).
*
* @private
* @type {Object<number, Function>}
* @constructor
* @extends Request
* @protected
*/
var callbackHolder = window[callbackHolderKey] = {}
export var JSONPRequest = Request.extend(function() {
JSONPRequest.super_.call(this)
/**
* 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++
if (!window[JSONPRequest._callbackHolderKey]) {
window[JSONPRequest._callbackHolderKey] = JSONPRequest._callbackHolder
}
return id
}
/**
* The generated ID which is used to store a reference to the callback function within the holder so that the JSONP
* payload can find it in the global namespace.
*
* @private
* @type {number}
*/
this._id = JSONPRequest._generateId()
}, {
/**
* Extracts the values of the properties with the specified <code>names</code> from the <code>response</code> provided
* and returns them in a single result.
*
* If <code>names</code> 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 <code>response</code> is <code>null</code> this function will return <code>null</code>.
*
* @param {string|string[]} names - the names of the <code>response</code> properties whose values are to be returned as
* the result
* @param {Object} response - the YOURLS API response
* @return {*} The result extracted from <code>response</code>.
* @private
*/
function getResult(names, response) {
names = isArray(names) ? names : [ names ]
/**
* @inheritDoc
* @override
*/
getSupportedHttpMethods: function() {
return [ 'GET' ]
},
var i
var name
var result = null
/**
* @inheritDoc
* @override
*/
buildBody: function(api, data) {
var body = JSONPRequest.super_.prototype.buildBody.call(this, api, data)
body.callback = JSONPRequest._callbackHolderKey + '[' + this._id + ']'
if (!response) {
return result
}
return body
},
if (names.length === 1) {
result = response[names[0]]
} else {
result = {}
/**
* @inheritDoc
* @override
*/
process: function(method, url, body, callback) {
var script = document.createElement('script')
for (i = 0; i < names.length; i++) {
name = names[i]
var self = this
JSONPRequest._callbackHolder[this._id] = function(response) {
delete JSONPRequest._callbackHolder[self._id]
script.parentNode.removeChild(script)
if (typeof response[name] !== 'undefined') {
result[name] = response[name]
}
callback(response)
}
script.setAttribute('src', url)
document.getElementsByTagName('head')[0].appendChild(script)
}
return result
}
}, {
/**
* Sends a JSONP request to the connected YOURLS API with the <code>data</code> provided which should, in turn, call the
* specified <code>callback</code> with the result.
*
* If the request is successful, <code>callback</code> will be passed the value of the named properties from the
* response. If <code>resultNames</code> 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
* <code>data</code> as well as <b>any credentials</b> 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
* <code>callback</code> 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')
/**
* The key of the callback function holder within the global namespace.
*
* @private
* @static
* @type {string}
*/
_callbackHolderKey: '__yourls' + seed + '_jsonp',
callbackHolder[id] = function(response) {
var result = getResult(resultNames, response)
/**
* 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 <b>must</b> be publicly available (i.e. global).
*
* @private
* @static
* @type {Object<number, Function>}
*/
_callbackHolder: {},
delete callbackHolder[id]
script.parentNode.removeChild(script)
/**
* Generates an ID to be used when storing a reference to a callback function.
*
* @return {number} The generated ID.
* @private
*/
_generateId: function() {
do {
seed++
} while (JSONPRequest._callbackHolder[seed])
callback(result, response)
return seed
}
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
* Copyright (C) 2017 Alasdair Mercer
*

@@ -26,3 +26,3 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

*
* This function will use the native <code>Array.isArray</code>, if available.
* This method will use the native <code>Array.isArray</code>, if available.
*

@@ -29,0 +29,0 @@ * @param {*} obj - the object to be checked (may be <code>null</code>)

/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -23,3 +23,3 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

import { jsonp } from './request/jsonp'
import { Requestor } from './request/requestor'

@@ -30,21 +30,22 @@ /**

* @constructor
* @extends Requestor
* @protected
*/
export function DB() {
// Do nothing
}
export var DB = Requestor.extend({
/**
* Retrieves the statistics for this {@link DB}.
*
* @param {Function} callback - the callback function to be called with the result
* @return {DB} A reference to this {@link DB} for chaining purposes.
* @public
*/
DB.prototype.stats = function(callback) {
var data = { action: 'db-stats' }
/**
* Retrieves the statistics for this {@link DB}.
*
* @param {Function} callback - the callback function to be called with the result
* @return {DB} A reference to this {@link DB} for chaining purposes.
* @public
*/
stats: function(callback) {
var data = { action: 'db-stats' }
jsonp(data, 'db-stats', callback)
this.sendRequest(data, 'db-stats', callback)
return this
}
return this
}
})
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -23,3 +23,3 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

import { jsonp } from './request/jsonp'
import { Requestor } from './request/requestor'

@@ -31,5 +31,8 @@ /**

* @constructor
* @extends Requestor
* @protected
*/
export function URL(url) {
export var URL = Requestor.extend(function(url) {
URL.super_.call(this)
/**

@@ -42,3 +45,3 @@ * Either the shortened URL or its keyword for this {@link URL}.

this.url = url
}
})

@@ -58,3 +61,3 @@ /**

jsonp(data, [ 'keyword', 'longurl', 'shorturl' ], callback)
this.sendRequest(data, [ 'keyword', 'longurl', 'shorturl' ], callback)

@@ -77,5 +80,5 @@ return this

jsonp(data, 'link', callback)
this.sendRequest(data, 'link', callback)
return this
}
/*
* Copyright (C) 2016 Alasdair Mercer
* Copyright (C) 2017 Alasdair Mercer
*

@@ -26,38 +26,6 @@ * Permission is hereby granted, free of charge, to any person obtaining a copy

import { isArray } from './util/array'
import { jsonp } from './request/jsonp'
import { Requestor } from './request/requestor'
import { URL } from './yourls-url'
/**
* Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links
* from an object mapping into an array.
*
* This function simply returns <code>result</code> if it is <code>null</code>, has no links property or one that is
* already an array (future-proofing).
*
* @param {Object} result - the result to be sanitized (may be <code>null</code>)
* @param {Object} [result.links] - the links to be transformed into an array (may be <code>null</code>)
* @return {Object} The modified <code>result</code> or <code>null</code> if <code>result</code> is <code>null</code>.
* @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.

@@ -69,5 +37,8 @@ *

* @constructor
* @extends Requestor
* @protected
*/
var YOURLS = function() {
var YOURLS = Requestor.extend(function() {
YOURLS.super_.call(this)
/**

@@ -84,4 +55,4 @@ * Provides information on the YOURLS {@link DB}.

*
* This is <b>not</b> the same as the version of YOURLS that is being connected to. The {@link YOURLS#version}
* function should be used to provide that information.
* This is <b>not</b> the same as the version of YOURLS that is being connected to. The {@link YOURLS#version} method
* should be used to provide that information.
*

@@ -91,164 +62,202 @@ * @public

*/
this.VERSION = '2.0.0'
}
this.VERSION = '2.1.0'
}, {
/**
* Stores the specified information to be used later to connect to and authenticate with a YOURLS server.
*
* @param {string} [url=''] - the URL for the YOURLS server
* @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be
* <code>null</code>)
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.connect = function(url, credentials) {
var api = new API(url, credentials)
api.store()
/**
* Stores the specified information to be used later to connect to and authenticate with a YOURLS server.
*
* @param {string} [url=''] - the URL for the YOURLS server
* @param {API~Credentials} [credentials] - the credentials to be used to authenticate with the YOURLS API (may be
* <code>null</code>)
* @param {API~Options} [options] - the options to be used to send requests to the YOURLS server (may be
* <code>null</code>)
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
connect: function(url, credentials, options) {
API.instance = new API(url, credentials, options)
return this
}
return this
},
/**
* Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS
* server.
*
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.disconnect = function() {
API.clear()
/**
* Clears any information that may have been previously stored for connecting to and authenticating with a YOURLS
* server.
*
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
disconnect: function() {
API.instance = null
return this
}
return this
},
/**
* Creates a short URL for the specified long <code>url</code>.
*
* Optionally, a <code>descriptor</code> 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 <code>descriptor</code> is a string, it will be treated as the keyword.
*
* @param {string} url - the long URL to be shortened
* @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be used
* for the short URL
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
YOURLS.prototype.shorten = function(url, descriptor, callback) {
var data = {
action: 'shorturl',
url: url
}
/**
* Creates a short URL for the specified long <code>url</code>.
*
* Optionally, a <code>descriptor</code> 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 <code>descriptor</code> is a string, it will be treated as the keyword.
*
* @param {string} url - the long URL to be shortened
* @param {YOURLS~UrlDescriptor|string} [descriptor] - the optional descriptor (or keyword, if it's a string) to be
* used for the short URL
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
shorten: function(url, descriptor, callback) {
var data = {
action: 'shorturl',
url: url
}
switch (typeof descriptor) {
case 'function':
callback = descriptor
descriptor = null
break
case 'string':
descriptor = { keyword: descriptor }
break
default:
// Do nothing
}
switch (typeof descriptor) {
case 'function':
callback = descriptor
descriptor = null
break
case 'string':
descriptor = { keyword: descriptor }
break
default:
// Do nothing
}
if (descriptor) {
data.keyword = descriptor.keyword
data.title = descriptor.title
}
if (descriptor) {
data.keyword = descriptor.keyword
data.title = descriptor.title
}
jsonp(data, [ 'shorturl', 'title', 'url' ], callback)
this.sendRequest(data, [ 'shorturl', 'title', 'url' ], callback)
return this
}
return this
},
/**
* Retrieves the statistics for all shortened URLs.
*
* Optionally, <code>criteria</code> 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
* <code>criteria</code> 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' }
/**
* Retrieves the statistics for all shortened URLs.
*
* Optionally, <code>criteria</code> 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 <code>criteria</code> is a number, it will be treated as the limit.
*
* No links will be included in the result unless a limit is specified that is greater than zero. In that case, this
* method would effectively be doing the same as {@link DB#stats}.
*
* @param {YOURLS~SearchCriteria|number} [criteria] - the optional criteria (or limit, if it's a number) to be used to
* search for links to be included in the result
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
stats: function(criteria, callback) {
var data = { action: 'stats' }
switch (typeof criteria) {
case 'function':
callback = criteria
criteria = null
break
case 'number':
criteria = { limit: criteria }
break
default:
// Do nothing
}
switch (typeof criteria) {
case 'function':
callback = criteria
criteria = null
break
case 'number':
criteria = { limit: criteria }
break
default:
// Do nothing
}
if (criteria) {
data.filter = criteria.filter
data.limit = criteria.limit
data.start = criteria.start
}
if (criteria) {
data.filter = criteria.filter
data.limit = criteria.limit
data.start = criteria.start
}
jsonp(data, [ 'links', 'stats' ], function(result, response) {
callback(sanitizeStatsResult(result), response)
})
this.sendRequest(data, [ 'links', 'stats' ], function(result, response) {
callback(YOURLS._sanitizeStatsResult(result), response)
})
return this
}
return this
},
/**
* Creates an instance of {@link URL} for the specified shortened <code>url</code> 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 <code>url</code> or <code>null</code> if <code>url</code> is
* <code>null</code>.
* @public
*/
YOURLS.prototype.url = function(url) {
return url ? new URL(url) : null
}
/**
* Creates an instance of {@link URL} for the specified shortened <code>url</code> which can be used to lookup more
* detailed information relating to it.
*
* No data is fetched just by calling this method; one of the methods on the returned instance need to be called for
* that to happen.
*
* @param {string} url - the shortened URL (or its keyword)
* @return {URL} The {@link URL} created for the shortened <code>url</code> or <code>null</code> if <code>url</code>
* is <code>null</code>.
* @public
*/
url: function(url) {
return url ? new URL(url) : null
},
/**
* Retrieves the version of the connected YOURLS API.
*
* Optionally, <code>db</code> can be passed to indicate that the YOURLS database version should also be included in the
* result.
*
* @param {boolean} [db] - <code>true</code> to include the database version; otherwise <code>false</code>
* @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' }
/**
* Retrieves the version of the connected YOURLS API.
*
* Optionally, <code>db</code> can be passed to indicate that the YOURLS database version should also be included in
* the result.
*
* @param {boolean} [db] - <code>true</code> to include the database version; otherwise <code>false</code>
* @param {Function} callback - the callback function to be called with the result
* @return {YOURLS} A reference to this {@link YOURLS} for chaining purposes.
* @public
*/
version: function(db, callback) {
var data = { action: 'version' }
if (typeof db === 'function') {
callback = db
db = null
if (typeof db === 'function') {
callback = db
db = null
}
if (db != null) {
data.db = Number(db)
}
this.sendRequest(data, [ 'db_version', 'version' ], callback)
return this
}
if (db != null) {
data.db = Number(db)
}, {
/**
* Sanitizes the result of {@link YOURLS#stats} so that it's more usable in application code by transforming the links
* from an object mapping into an array.
*
* This method simply returns <code>result</code> if it is <code>null</code>, has no links property or one that is
* already an array (future-proofing).
*
* @param {Object} result - the result to be sanitized (may be <code>null</code>)
* @param {Object} [result.links] - the links to be transformed into an array (may be <code>null</code>)
* @return {Object} The modified <code>result</code> or <code>null</code> if <code>result</code> is <code>null</code>.
* @private
* @static
*/
_sanitizeStatsResult: function(result) {
// Future-proofing by sanitizing links *only* when not already an array
if (!result || !result.links || isArray(result.links)) {
return result
}
var index = 1
var link
var links = []
while ((link = result.links['link_' + index]) != null) {
links.push(link)
index++
}
result.links = links
return result
}
jsonp(data, [ 'db_version', 'version' ], callback)
})
return this
}
/**

@@ -255,0 +264,0 @@ * The singleton instance of {@link YOURLS}.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc