Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added the currency binding and tests; the currency binding is a one-w…

…ay text binding that displays numeric values as money
  • Loading branch information...
commit cf9cc093a8153faff32f50e0d455e616da9a9065 1 parent c594d49
@politician authored
Showing with 157 additions and 0 deletions.
  1. +91 −0 outback.js
  2. +1 −0  spec/SpecRunner.html
  3. +65 −0 spec/bindings/currency.spec.js
View
91 outback.js
@@ -991,4 +991,95 @@
}
})();
+
+ /* The "currency" binding
+
+ Usage:
+ data-bind="currency: @modelAttr, currencyOptions: { format: <formatspec> }"
+
+ formatspec is a string which describes how a monetary value must be
+ rendered. This string must match the RegExp,
+
+ /^([^\d]*)9([^\d]?)999([^\d])99([^\d]*)$/
+
+ where the capture groups are interpreted as follows:
+
+ $1 is an optional prefix; typically a currency sign ($)
+ $2 is an optional thousands separator
+ $3 is the decimal separator
+ $4 is an optional suffix; typically a currency abbreviation (USD)
+
+ The default format specifier is '$9,999.99'.
+
+ negativeClass is the name of a CSS class to add to the element
+ when the value is less than 0. The default is 'currency-negative'.
+
+ Purpose: The currency binding causes the associated DOM element to
+ display the text value of the bound symbol formatted as a currency.
+ */
+ Backbone.outback.bindingHandlers['currency'] = (function() {
+ function optionsFor(valueAccessor, allBindingsAccessor) {
+ var config, options;
+
+ config = {
+ format: [ '$', ',', '.', '' ]
+ };
+
+ options = allBindingsAccessor('currencyOptions');
+ if (options && hop(options, 'format')) {
+ config.format = parseFormatSpec(options.format) || config.format;
+ }
+
+ return config;
+ }
+
+ function parseFormatSpec(formatSpec) {
+ var re, m;
+ re = /^([^\d]*)9([^\d]?)999([^\d])99([^\d]*)$/;
+ m = re.exec(formatSpec);
+ if (_.isArray(m)) {
+ m.shift();
+ return m;
+ }
+ }
+
+ // http://stackoverflow.com/questions/149055/how-can-i-format-numbers-as-money-in-javascript
+ // with modifications to specify radix in parseInt, to wrap negative values in parens, and
+ // to display a currency symbol if specified
+ function formatMoney (c, prefix, t, d, suffix) {
+ var n = this,
+ c = isNaN(c = Math.abs(c)) ? 2 : c,
+ d = d == undefined ? "," : d,
+ t = t == undefined ? "." : t,
+ s1 = n < 0 ? "(" : "",
+ s2 = n < 0 ? ")" : "",
+ prefix = prefix == undefined ? "" : prefix,
+ suffix = suffix == undefined ? "" : suffix,
+ i = parseInt(n = Math.abs(+n || 0).toFixed(c), 10) + "",
+ j = (j = i.length) > 3 ? j % 3 : 0;
+
+ return s1
+ + prefix
+ + (j ? i.substr(0, j) + t : "")
+ + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t)
+ + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "")
+ + suffix
+ + s2;
+ };
+
+ return {
+ update: function (element, valueAccessor, allBindingsAccessor, view) {
+ var config, value;
+ config = optionsFor(valueAccessor, allBindingsAccessor);
+
+ value = valueAccessor()();
+ value = formatMoney.apply(value, [2].concat(config.format));
+
+ $(element).text(value);
+ },
+
+ parseFormatSpec: parseFormatSpec
+ }
+ })();
+
}));
View
1  spec/SpecRunner.html
@@ -60,6 +60,7 @@
<script type="text/javascript" src="bindings/value.spec.js"></script>
<script type="text/javascript" src="bindings/hasfocus.spec.js"></script>
<script type="text/javascript" src="bindings/checked.spec.js"></script>
+ <script type="text/javascript" src="bindings/currency.spec.js"></script>
<!---->
</head>
<body>
View
65 spec/bindings/currency.spec.js
@@ -0,0 +1,65 @@
+describe('the currency binding', function() {
+
+ beforeEach(function() {
+ this.model = new AModel({price: 0});
+ this.view = new FixtureView({model: this.model});
+ _.extend(this.view, {
+ innerHtml: "<span></span>",
+ modelBindings: {
+ 'span': {
+ currency: Backbone.outback.modelRef('price')
+ }
+ }
+ });
+ });
+
+ afterEach(function() {
+ this.view.remove();
+ })
+
+ it('should update the value of the DOM element when the model changes', function() {
+ this.view.render();
+ this.el = this.view.$('#anchor span');
+
+ expect(this.el.size() > 0).toBeTruthy();
+ expect(this.el.text()).toBe('$0.00');
+
+ this.model.set({price: 1});
+ expect(this.el.text()).toBe('$1.00');
+ });
+
+ it('should correctly parse format specifiers', function() {
+ var match;
+ match = Backbone.outback.bindingHandlers.currency.parseFormatSpec('prefix9!999?99suffix');
+
+ expect(match).toBeDefined();
+ expect(match[0]).toBe('prefix');
+ expect(match[1]).toBe('!');
+ expect(match[2]).toBe('?');
+ expect(match[3]).toBe('suffix');
+ });
+
+ it('should be configurable', function() {
+ this.view.modelBindings['span'].currencyOptions = {
+ format: 'prefix9!999?99suffix'
+ };
+
+ this.view.render();
+ this.el = this.view.$('#anchor span');
+
+ expect(this.el.text()).toBe('prefix0?00suffix');
+
+ this.model.set({price: 2485999.95 });
+
+ expect(this.el.text()).toBe('prefix2!485!999?95suffix');
+ });
+
+ it('should only show two decimal places', function() {
+ this.view.render();
+ this.el = this.view.$('#anchor span');
+
+ this.model.set({price: 3.1415 });
+
+ expect(this.el.text()).toBe('$3.14');
+ });
+});
Please sign in to comment.
Something went wrong with that request. Please try again.