UNPKG

steam-econcc

Version:

Currency conversion and formatting library for Steam economy and real world currencies, based on the format used by backpack.tf.

728 lines (585 loc) 26.2 kB
(function (global, factory) { if (typeof define === "function" && define.amd) { define("EconCC", ["exports", "module"], function (exports, module) { factory(exports); module.exports = exports["default"]; }); } else if (typeof exports !== "undefined") { factory(exports); module.exports = exports["default"]; } else { var mod = { exports: {} }; factory(mod.exports); global.EconCC = mod.exports["default"]; } })(this, function (exports) { "use strict"; var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var nondec = /[^\d\.\-e+]/g; var doubledash = /--/g; var EconCC = (function () { function EconCC(currencies, type) { for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } _classCallCheck(this, EconCC); this.currencies = {}; this.aliases = {}; this.step = EconCC.Disabled; this.trailing = EconCC.Auto; this.range = EconCC.Range.Low; this.separators = { thousand: ",", decimal: "." }; if (typeof currencies === "object") { if (currencies.convert !== false) { type = type || "backpackTF"; this.convert.apply(this, [type, currencies].concat(args)); } else { this.modify(currencies); } } } EconCC.prototype.modify = function modify() { var state = arguments[0] === undefined ? {} : arguments[0]; function copy(o) { var c = {}; for (var v in o) { if (typeof o[v] === "object" && Object.prototype.toString.call(o[v]) !== "[object Array]") { c[v] = copy(o[v]); } else { c[v] = o[v]; } } return c; } function patch(val, withVal) { var c = copy(val); for (var v in withVal) { var wv = withVal[v]; if (typeof wv === "object" && Object.prototype.toString.call(wv) !== "[object Array]") { c[v] = patch(c[v] || {}, wv); } else { c[v] = wv; } } return c; } for (var _name in state) { var val = state[_name]; var _self = this[_name]; if (_name === "convert") continue; if (val !== undefined && typeof _self !== "undefined" && typeof _self !== "function") { if (typeof val === "object") { this[_name] = patch(_self, val); } else { this[_name] = val; } } if (_name === "currencies") this.update(); } return this; }; EconCC.prototype.convert = function convert(type) { for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } var converter = EconCC.converters[type]; if (!converter) { throw new Error("Unknown converter (" + converter + ") | Converters: " + Object.keys(EconCC.converters)); } var converted = converter.apply(undefined, args); for (var _name2 in converted) { if (_name2 !== "convert") { this[_name2] = converted[_name2]; } } return this.update(); }; EconCC.prototype.update = function update() { for (var cname in this.currencies) { var cur = this.currencies[cname]; var low = cur.low; var mid = undefined, high = undefined; if (cur.high) { mid = (low + cur.high) / 2; high = cur.high; } var next = this.currencies[cur.currency]; while (next && !(next.bc || next.rwc)) { low *= next.low; if (high) { mid *= next.high ? EconCC._floatdiv(next.low + next.high, 2, next.round) : next.low; high *= next.high || next.low; } next = this.currencies[next.currency]; } if (!high) { high = mid = low; } cur._bc = { low: low, mid: mid, high: high }; } return this; }; EconCC.prototype.valueFromRange = function valueFromRange(value) { var currency = arguments[1] === undefined ? value.currency : arguments[1]; return (function () { var res = { currency: currency }; if (typeof value.value !== "undefined") { res.value = value.value; return res; } switch (this.range) { case EconCC.Range.Low: res.value = value.low; break; case EconCC.Range.Mid: res.value = value.high ? EconCC._floatdiv(value.low + value.high, 2, this._gc(currency).round) : value.low; break; case EconCC.Range.High: res.value = value.high || value.low; break; } return res; }).apply(this, arguments); }; EconCC.prototype._rc = function _rc(currency) { var co = typeof currency === "object" && currency.internal && this.currencies[currency.internal] ? currency : undefined; return this.aliases[currency] ? this.currencies[this.aliases[currency]] : this.currencies[currency] || co; }; EconCC.prototype._gc = function _gc(currency) { var cur = this._rc(currency); if (!cur || typeof cur !== "object") { throw new TypeError("no such currency: " + currency + " | currencies: " + Object.keys(this.currencies) + " / aliases: " + Object.keys(this.aliases)); } return cur; }; EconCC.prototype._vv = function _vv(value) { if (typeof value === "object") { if (typeof value.low === "number") { return this.valueFromRange(value).value; } else if (typeof value.value === "number") { return value.value; } else { return 0; } } return value || 0; }; EconCC._floatdiv = function _floatdiv(a, b) { var acc = arguments[2] === undefined ? 2 : arguments[2]; var round = arguments[3] === undefined ? null : arguments[3]; var p = Math.pow(10, acc < 2 ? 2 : acc); var fn = round || function (n) { return n; }; return fn(a * p / b) / p; }; EconCC.prototype._brt = function _brt(currency) { return currency._bc[EconCC.RangeTag[this.range]]; }; EconCC.prototype._sepnum = function _sepnum(num) { return ("" + num).replace(/\B(?=(\d{3})+(?!\d))/g, this.separators.thousand).replace(/\./g, this.separators.decimal); }; EconCC.prototype.scope = function scope(state, fn) { var self = { currencies: this.currencies, aliases: this.aliases, range: this.range, step: this.step, trailing: this.trailing, separators: this.separators }; this.modify(state); fn(this); return this.modify(self); }; EconCC.prototype.isCurrency = function isCurrency(cur) { if (typeof cur === "object") { if (cur.currency) cur = cur.currency;else if (!cur.internal) { return false; } } return !!this._rc(cur); }; EconCC.prototype.convertToBC = function convertToBC(value) { var currency = arguments[1] === undefined ? value.currency : arguments[1]; return (function () { var cur = this._gc(currency); var val = this._vv(value); return !cur || cur.bc || cur.rwc ? val : val * this._brt(cur); }).apply(this, arguments); }; EconCC.prototype.convertFromBC = function convertFromBC(value) { var currency = arguments[1] === undefined ? value.currency : arguments[1]; return (function () { var cur = this._gc(currency); var val = this._vv(value); return !cur || cur.bc || cur.rwc ? val : EconCC._floatdiv(val, this._brt(cur), cur.round); }).apply(this, arguments); }; EconCC.prototype.convertToCurrency = function convertToCurrency(value, oldc, newc) { if (arguments.length === 2) { newc = oldc; oldc = value.currency; } var oldcc = this._gc(oldc); newc = this._gc(newc); value = this._vv(value); var ret = { currency: newc.internal }; if (oldcc.rwc) value = EconCC._floatdiv(value, this._brt(oldcc), oldcc.round); if (newc.bc) { ret.value = this.convertToBC(value, oldc); } else if (newc.rwc) { ret.value = this.convertToBC(value, oldc) * this._brt(newc); } else { ret.value = this.convertFromBC(this.convertToBC(value, oldc), newc); } return ret; }; EconCC.prototype.formatCurrencyRange = function formatCurrencyRange(value) { if (value.high) { var _name3 = false; var cur = this._gc(value.currency); if (cur.pos.sym === "start") _name3 = true; return this.formatCurrency(value.low, value.currency, _name3) + "–" + this.formatCurrency(value.high, value.currency, !_name3); } return this.formatCurrency(value.low, value.currency); }; EconCC.prototype.formatCurrency = function formatCurrency(value) { var currency = arguments[1] === undefined ? value.currency : arguments[1]; var name = arguments[2] === undefined ? true : arguments[2]; return (function () { var cur = this._gc(currency); var str = "", val = +this._vv(value).toFixed(cur.round); var trailing = cur.rwc || this.trailing === EconCC.Auto ? cur.trailing : this.trailing; if (this.step !== EconCC.Disabled && cur.step && val >= cur.step) { val = (Math.floor(val) + EconCC._floatdiv(Math.round(val % 1 / cur.step), Math.floor(1 / cur.step), cur.round, Math.floor)).toFixed(cur.round); if (!trailing) val = +val; } else { if (trailing) { val = val.toFixed(cur.round); } } val = this._sepnum(val); if (!name) return val; if (cur.rwc) { switch (cur.pos.sym) { case "start": str = cur.symbol + val; break; case "end": str = val + cur.symbol; break; } } else { str = val + " " + cur.names[+(value.value !== 1)]; } return str; }).apply(this, arguments); }; EconCC.prototype.parse = function parse(str) { var parts = str.split(" "); var range = (parts[0] || "").split(/-|–/); var mode = (parts[1] || "").split(":"); var currency = mode[0]; var fmtmode = mode[1]; if (!this.isCurrency(currency)) { for (var cname in this.currencies) { var cur = this.currencies[cname]; var sym = cur.symbol; if (!sym) continue; var pos = cur.pos.sym; var start = (range[0] || "")[0]; var end = range[1] ? range[1].slice(-1) : ""; if (pos === "start" && sym === start || pos === "end" && sym === end) { currency = cname; break; } } } if (!fmtmode) { fmtmode = range[range.length - 1].split(":")[1]; } return { currency: currency, low: Number(range[0].replace(doubledash, "00").replace(nondec, "")), high: range[1] ? Number(range[1].replace(doubledash, "00").replace(nondec, "")) : undefined, mode: EconCC.Mode[fmtmode] }; }; EconCC.prototype.f = function f(str) { var value = this.parse(str); return this.format(value, value.mode); }; EconCC.prototype.format = function format(value) { var mode = arguments[1] === undefined ? EconCC.Mode.Short : arguments[1]; var primary = undefined; if (mode === EconCC.Mode.ShortRange || mode === EconCC.Mode.LongRange) { primary = this.formatCurrencyRange(value); if (value.low || !value.value) value = this.valueFromRange(value); } else { if (value.low || !value.value) value = this.valueFromRange(value); primary = this.formatCurrency(value); } if (mode === EconCC.Mode.Short || mode === EconCC.Mode.ShortRange) { return primary; } var vc = this._gc(value.currency); var fmt = []; var donecur = {}; var label = mode === EconCC.Mode.Label; var threshold = label ? 1 : 0.8; if (label) { fmt.push({ str: primary, cvalue: this._brt(vc) }); } donecur[vc.internal] = true; for (var cname in this.currencies) { var cur = this.currencies[cname]; if (cur.internal === value.currency || donecur[cname] || label && !cur.label || !label && cur.hidden) { continue; } var val = this.convertToCurrency(value, value.currency, cur); if (!cur.rwc && val.value < threshold) { continue; } var obj = { str: this.formatCurrency(val), fmt: cur.pos.fmt }; if (label) { obj.cvalue = this._brt(cur); } donecur[cname] = true; fmt.push(obj); } if (label) { fmt.sort(function (a, b) { var cv = b.cvalue - a.cvalue; if (cv === 0) { return b.fmt - a.fmt; } return cv; }); return fmt[0].str; } fmt.sort(function (a, b) { return a.fmt - b.fmt; }); return primary + (fmt.length ? " (" + fmt.map(function (o) { return o.str; }).join(", ") + ")" : ""); }; EconCC.prototype.formatRange = function formatRange(value) { var mode = arguments[1] === undefined ? EconCC.Mode.Short : arguments[1]; var cur = value.currency; return this.format({ currency: cur, value: value.low }, mode) + (value.high ? " – " + this.format({ currency: cur, value: value.high }, mode) : ""); }; EconCC.prototype.scm = function scm(val) { var mode = arguments[1] === undefined ? EconCC.SCM.Buyer : arguments[1]; var fees = arguments[2] === undefined ? {} : arguments[2]; var _valueFromRange = this.valueFromRange(val); var value = _valueFromRange.value; var currency = _valueFromRange.currency; var buyer = { currency: currency }; var seller = { currency: currency }; if (mode === EconCC.SCM.Buyer) { var fee = EconCC.stmCalculateFee(value * 100, fees); var svalue = Math.max(EconCC._floatdiv(fee.cents - fee.fees, 100), 0.01); buyer.value = value; seller.value = svalue; } else if (mode === EconCC.SCM.Seller) { seller.value = value; buyer.value = Math.max(EconCC._floatdiv(EconCC.stmCalculateInclFee(value * 100, fees).cents, 100), 0.03); } return { buyer: buyer, seller: seller }; }; EconCC.stmCalculateFee = function stmCalculateFee(cents) { var _ref = arguments[1] === undefined ? {} : arguments[1]; var stmFee = _ref.stmFee; var publisherFee = _ref.publisherFee; var baseFee = _ref.baseFee; var minFee = _ref.minFee; stmFee = typeof stmFee === "number" ? stmFee : 0.05; publisherFee = typeof publisherFee === "number" ? publisherFee : 0.1; baseFee = typeof baseFee === "number" ? baseFee : 0; minFee = typeof minFee === "number" ? minFee : 1; var wallet = { stmFee: stmFee, publisherFee: publisherFee, baseFee: baseFee, minFee: minFee }; var receiver = Math.floor((cents - baseFee) / (stmFee + publisherFee + 1)); var fees = EconCC.stmCalculateInclFee(receiver, wallet); var undershot = false; var iter = 0; while (fees.cents !== cents && iter < 50) { if (fees.cents > cents) { if (undershot) { fees = EconCC.stmCalculateInclFee(receiver - 1, wallet); fees.steamFee += Math.floor(cents - fees.cents); fees.fees += Math.floor(cents - fees.cents); fees.cents = cents; break; } else { receiver -= 1; } } else { undershot = true; receiver += 1; } fees = EconCC.stmCalculateInclFee(receiver, wallet); iter += 1; } return fees; }; EconCC.stmCalculateInclFee = function stmCalculateInclFee(funds) { var _ref2 = arguments[1] === undefined ? {} : arguments[1]; var stmFee = _ref2.stmFee; var publisherFee = _ref2.publisherFee; var baseFee = _ref2.baseFee; var minFee = _ref2.minFee; stmFee = typeof stmFee === "number" ? stmFee : 0.05; publisherFee = typeof publisherFee === "number" ? publisherFee : 0.1; baseFee = typeof baseFee === "number" ? baseFee : 0; minFee = typeof minFee === "number" ? minFee : 1; var steamFee = Math.floor(Math.floor(Math.max(funds * stmFee, minFee)) + baseFee); var pubFee = Math.floor(publisherFee > 0 ? Math.max(funds * publisherFee, 1) : 0); var fees = steamFee + pubFee; return { steamFee: steamFee, publisherFee: pubFee, fees: fees, cents: funds + fees }; }; EconCC.makeCurrency = function makeCurrency(type, _ref3) { var internal = _ref3.internal; var trailing = _ref3.trailing; var low = _ref3.low; var high = _ref3.high; var round = _ref3.round; var hidden = _ref3.hidden; var label = _ref3.label; var symbol = _ref3.symbol; var pos = _ref3.pos; var currency = _ref3.currency; var names = _ref3.names; var step = _ref3.step; if (typeof round === "undefined") round = 2; var cur = { internal: internal, trailing: trailing, low: low, high: high, round: round, hidden: hidden, label: label, pos: pos }; if (type === "gc" || type === "bc") { if (type === "bc") { cur.bc = true; } else { cur.currency = currency; } cur.names = names; cur.step = step; } else if (type === "rwc") { cur.rwc = true; cur.symbol = symbol; if (typeof cur.trailing === "undefined") cur.trailing = true; } return cur; }; return EconCC; })(); var Converters = { backpackTFItem: function backpackTFItem(price) { return { currency: price.currency, low: price.value, high: price.value_high }; }, backpackTF: function backpackTF(currencies) { if (currencies.response) currencies = currencies.response; if (currencies.currencies) currencies = currencies.currencies; var aliases = {}; var pos = 0; var currs = { usd: EconCC.makeCurrency("rwc", { internal: "usd", symbol: "$", pos: { sym: "start", fmt: 99 } }) }; for (var cname in currencies) { var cobj = currencies[cname]; var iprice = cobj.price; var hidden = cobj.blanket || cobj.hidden; if (cname === "earbuds") hidden = true; var curn = currs[cname] = { internal: cname, currency: iprice.currency, low: iprice.value, high: iprice.value_high, names: [cobj.single, cobj.plural], round: cobj.round, pos: { fmt: pos }, hidden: !!hidden, label: !hidden, trailing: false }; aliases[cobj.single] = aliases[cobj.plural] = cname; if (cname === "metal") { curn.bc = true; curn.step = 0.055; curn.trailing = true; currs.usd.low = iprice.value; currs.usd.high = iprice.value_high; } else if (cname === "earbuds" || cname === "keys") { curn.step = 0.05; } pos += 1; } return { currencies: currs, range: EconCC.Range.Mid, aliases: aliases, convert: false }; }, backpackSCM: function backpackSCM(items, spec, rwc) { var notax = arguments[3] === undefined ? false : arguments[3]; if (items.response) items = items.response; if (items.items) items = items.items; var aliases = {}; var pos = 0; var currs = {}; if (!rwc) { rwc = EconCC.makeCurrency("rwc", { internal: "usd", symbol: "$", pos: { sym: "start", fmt: 99 } }); } currs[rwc.internal] = rwc; var fees = typeof notax === "object" ? notax : {}; for (var iname in spec) { var item = spec[iname]; var iinternal = item.internal; var inames = item.names; var iround = item.round; var itrailing = item.trailing; var ihidden = item.hidden; var ilabel = item.label; var ipos = item.pos; var ibc = item.bc; var irwc = item.rwc; if (typeof inames === "string") inames = [inames, inames]; if (!ipos) ipos = { fmt: pos }; var cents = items[iname].value; if (notax) { cents -= EconCC.stmCalculateFee(cents, fees).fees; } var price = cents / 100; if (ibc && !rwc.low) rwc.low = price; currs[iinternal] = { internal: iinternal, names: inames, currency: rwc.internal, round: iround || 2, trailing: itrailing || false, hidden: ihidden || false, label: ilabel || false, low: price, pos: ipos, bc: ibc || false, rwc: irwc || false }; pos += 1; } for (var icur in currs) { var cur = currs[icur]; if (cur.bc || cur.rwc) continue; cur.low = cur.low / rwc.low; } return { currencies: currs, range: EconCC.Range.Mid, aliases: aliases, convert: false }; } }; EconCC.converters = Converters; EconCC.Range = { Low: 0, Mid: 1, High: 2 }; EconCC.Mode = { Short: 1, Long: 2, ShortRange: 3, LongRange: 4, Label: 5 }; EconCC.SCM = { Buyer: 1, Seller: 2 }; EconCC.RangeTag = ["low", "mid", "high"]; EconCC.Auto = 2; EconCC.Enabled = 1; EconCC.Disabled = 0; exports["default"] = EconCC; });