bitcore/lib/uri.js

224 lines
5.7 KiB
JavaScript
Raw Normal View History

2014-11-26 13:07:49 -08:00
'use strict';
var _ = require('lodash');
var URL = require('url');
2014-11-28 07:35:53 -08:00
2014-11-26 13:07:49 -08:00
var Address = require('./address');
2014-12-01 12:36:52 -08:00
var Unit = require('./unit');
2014-12-12 08:05:22 -08:00
var JSUtil = require('./util/js');
2014-11-26 13:07:49 -08:00
2014-11-28 07:35:53 -08:00
/**
* Bitcore URI
*
* Instantiate an URI from a bitcoin URI String or an Object. An URI instance
* can be created with a bitcoin uri string or an object. All instances of
* URI are valid, the static method isValid allows checking before instanciation.
*
* All standard parameters can be found as members of the class, the address
* is represented using an {Address} instance and the amount is represented in
* satoshis. Any other non-standard parameters can be found under the extra member.
*
* @example
*
* var uri = new URI('bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu?amount=1.2');
* console.log(uri.address, uri.amount);
*
* @param {string|Object} data - A bitcoin URI string or an Object
* @param {Array.<string>} [knownParams] - Required non-standard params
* @throws {TypeError} Invalid bitcoin address
* @throws {TypeError} Invalid amount
* @throws {Error} Unknown required argument
* @returns {URI} A new valid and frozen instance of URI
*/
var URI = function(data, knownParams) {
2014-11-26 13:07:49 -08:00
this.extras = {};
2014-11-28 07:35:53 -08:00
this.knownParams = knownParams || [];
2014-11-26 13:07:49 -08:00
this.address = this.network = this.amount = this.message = null;
if (typeof(data) === 'string') {
2014-11-28 07:35:53 -08:00
var params = URI.parse(data);
if (params.amount) {
params.amount = this._parseAmount(params.amount);
}
2014-11-26 13:07:49 -08:00
this._fromObject(params);
} else if (typeof(data) === 'object') {
2014-11-28 07:35:53 -08:00
this._fromObject(data);
2014-11-26 13:07:49 -08:00
} else {
throw new TypeError('Unrecognized data format.');
}
};
2014-11-28 07:35:53 -08:00
/**
* Instantiate a URI from a String
*
* @param {String} str - JSON string or object of the URI
* @returns {URI} A new instance of a URI
*/
URI.fromString = function fromString(str) {
if (typeof(str) !== 'string') {
throw TypeError('Expected a string');
}
return new URI(str);
};
/**
* Instantiate a URI from JSON
2014-11-28 07:35:53 -08:00
*
* @param {String|Object} json - JSON string or object of the URI
* @returns {URI} A new instance of a URI
*/
URI.fromJSON = function fromJSON(json) {
2014-12-12 09:07:15 -08:00
if (JSUtil.isValidJSON(json)) {
json = JSON.parse(json);
}
return new URI(json);
};
/**
2014-11-28 07:35:53 -08:00
* Check if an bitcoin URI string is valid
*
* @example
*
* var valid = URI.isValid('bitcoin:12A1MyfXbW6RhdRAZEqofac5jCQQjwEPBu');
* // true
*
* @param {string|Object} data - A bitcoin URI string or an Object
* @param {Array.<string>} [knownParams] - Required non-standard params
* @returns {boolean} Result of uri validation
*/
URI.isValid = function(arg, knownParams) {
2014-11-26 13:07:49 -08:00
try {
2014-11-28 07:35:53 -08:00
var uri = new URI(arg, knownParams);
2014-11-26 13:07:49 -08:00
return true;
2014-12-12 08:23:12 -08:00
} catch (err) {
2014-11-26 13:07:49 -08:00
return false;
}
};
2014-11-28 07:35:53 -08:00
/**
* Convert a bitcoin URI string into a simple object.
*
* @param {string} uri - A bitcoin URI string
* @throws {TypeError} Invalid bitcoin URI
* @returns {Object} An object with the parsed params
*/
2014-11-26 13:07:49 -08:00
URI.parse = function(uri) {
var info = URL.parse(uri, true);
if (info.protocol != 'bitcoin:') {
throw new TypeError('Invalid bitcoin URI');
}
// workaround to host insensitiveness
var group = /[^:]*:\/?\/?([^?]*)/.exec(uri);
info.query.address = group && group[1] || undefined;
return info.query;
};
URI.Members = ['address', 'amount', 'message', 'label', 'r'];
2014-11-28 07:35:53 -08:00
/**
* Internal function to load the URI instance with an object.
*
2014-12-01 12:36:52 -08:00
* @param {Object} obj - Object with the information
2014-11-28 07:35:53 -08:00
* @throws {TypeError} Invalid bitcoin address
* @throws {TypeError} Invalid amount
* @throws {Error} Unknown required argument
*/
2014-11-26 13:07:49 -08:00
URI.prototype._fromObject = function(obj) {
if (!Address.isValid(obj.address)) {
throw new TypeError('Invalid bitcoin address');
}
2014-11-26 13:07:49 -08:00
this.address = new Address(obj.address);
this.network = this.address.network;
2014-12-01 12:36:52 -08:00
this.amount = obj.amount;
2014-11-26 13:07:49 -08:00
for (var key in obj) {
if (key === 'address' || key === 'amount') continue;
2014-11-28 07:35:53 -08:00
if (/^req-/.exec(key) && this.knownParams.indexOf(key) === -1) {
2014-11-26 13:07:49 -08:00
throw Error('Unknown required argument ' + key);
}
var destination = URI.Members.indexOf(key) > -1 ? this : this.extras;
2014-11-26 13:07:49 -08:00
destination[key] = obj[key];
}
};
2014-11-28 07:35:53 -08:00
/**
* Internal function to transform a BTC string amount into satoshis
*
* @param {String} amount - Amount BTC string
* @throws {TypeError} Invalid amount
* @returns {Object} Amount represented in satoshis
*/
2014-11-26 13:07:49 -08:00
URI.prototype._parseAmount = function(amount) {
amount = Number(amount);
if (isNaN(amount)) {
throw new TypeError('Invalid amount');
}
2014-12-01 12:36:52 -08:00
return Unit.fromBTC(amount).toSatoshis();
2014-11-26 13:07:49 -08:00
};
URI.prototype.toObject = function toObject() {
var json = {};
2014-12-12 08:23:12 -08:00
for (var i = 0; i < URI.Members.length; i++) {
var m = URI.Members[i];
2014-12-12 08:23:12 -08:00
if (this.hasOwnProperty(m) && typeof(this[m]) !== 'undefined') {
if (typeof(this[m].toString) === 'function') {
json[m] = this[m].toString();
} else {
json[m] = this[m];
}
}
}
_.extend(json, this.extras);
return json;
};
URI.prototype.toJSON = function toJSON() {
return JSON.stringify(this.toObject());
};
2014-11-28 07:35:53 -08:00
/**
* Will return a the string representation of the URI
*
* @returns {String} Bitcoin URI string
*/
2014-11-26 13:07:49 -08:00
URI.prototype.toString = function() {
var query = {};
if (this.amount) {
query.amount = Unit.fromSatoshis(this.amount).toBTC();
}
if (this.message) {
query.message = this.message;
}
if (this.label) {
query.label = this.label;
}
if (this.r) {
query.r = this.r;
}
_.extend(query, this.extras);
2014-11-26 13:07:49 -08:00
return URL.format({
protocol: 'bitcoin:',
host: this.address,
query: query
});
};
2014-11-28 07:35:53 -08:00
/**
* Will return a string formatted for the console
*
* @returns {String} Bitcoin URI
*/
2014-11-26 13:07:49 -08:00
URI.prototype.inspect = function() {
2014-12-12 08:23:12 -08:00
return '<URI: ' + this.toString() + '>';
};
2014-11-26 13:07:49 -08:00
module.exports = URI;