├── jasmine-core
├── spec
├── version.rb
├── jasmine.css
├── json2.js
├── jasmine-html.js
└── jasmine.js
├── .gitignore
├── specs
├── SpecHelper.js
├── NumberToPercentageSpec.js
├── NumberToPhoneSpec.js
├── NumberWithDelimiterSpec.js
├── NumberToHumanSizeSpec.js
├── NumberWithPrecisionSpec.js
├── NumberToCurrencySpec.js
├── NumberToHumanSpec.js
└── NumberToRoundedSpec.js
├── package.json
├── LICENSE.md
├── index.html
├── README.md
├── src
└── number_helpers.coffee
└── lib
└── number_helpers.js
/jasmine-core/spec:
--------------------------------------------------------------------------------
1 | ../../spec
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | node_modules
3 |
--------------------------------------------------------------------------------
/jasmine-core/version.rb:
--------------------------------------------------------------------------------
1 | module Jasmine
2 | module Core
3 | VERSION = "1.2.0"
4 | end
5 | end
6 |
7 |
--------------------------------------------------------------------------------
/specs/SpecHelper.js:
--------------------------------------------------------------------------------
1 | // THIS IS FOR jasmine-node
2 | // DO NOT include this in the non node jasmine spec runner
3 |
4 | NumberHelpers = require("../lib/number_helpers")
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "number_helpers",
3 | "version": "0.1.2",
4 | "description": "Rails like number helpers in JS",
5 | "author": {
6 | "name": "Christopher Altman",
7 | "email": "caltman@emcien.com"
8 | },
9 | "main": "./lib/number_helpers.js",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/emcien/number-helpers-coffeescript.git"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/specs/NumberToPercentageSpec.js:
--------------------------------------------------------------------------------
1 | describe("Number To Percentage", function() {
2 | it('Defaults', function() {
3 | var out = NumberHelpers.number_to_percentage(100);
4 | expect(out).toBe('100.000%');
5 | });
6 |
7 | it('Two Digit Default', function() {
8 | var out = NumberHelpers.number_to_percentage(98);
9 | expect(out).toBe('98.000%');
10 | });
11 |
12 | it('With Percision', function() {
13 | var out = NumberHelpers.number_to_percentage(100, {precision: 0});
14 | expect(out).toBe('100%');
15 | });
16 |
17 | it('Separator and Delimiter', function() {
18 | var out = NumberHelpers.number_to_percentage(1000, {delimiter: '.', separator: ','});
19 | expect(out).toBe('1.000,000%');
20 | });
21 |
22 | it('Precision', function() {
23 | var out = NumberHelpers.number_to_percentage(302.24398923423, {precision: 5});
24 | expect(out).toBe('302.24399%');
25 | });
26 |
27 | it('Non Digit', function() {
28 | var out = NumberHelpers.number_to_percentage('98a');
29 | expect(out).toBe('98a%');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012, Emcien All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/specs/NumberToPhoneSpec.js:
--------------------------------------------------------------------------------
1 | describe("Number To Phone", function() {
2 | it('7 digits', function() {
3 | var out = NumberHelpers.number_to_phone(5551234);
4 | expect(out).toBe('555-1234');
5 | });
6 | it('10 digits', function() {
7 | var out = NumberHelpers.number_to_phone(1235551234);
8 | expect(out).toBe('123-555-1234');
9 | });
10 | it('Area Code', function() {
11 | var out = NumberHelpers.number_to_phone(1235551234, {area_code: true});
12 | expect(out).toBe('(123) 555-1234');
13 | });
14 | it('Delimiter', function() {
15 | var out = NumberHelpers.number_to_phone(1235551234, {delimiter: ' '});
16 | expect(out).toBe('123 555 1234');
17 | });
18 | it('Extension', function() {
19 | var out = NumberHelpers.number_to_phone(1235551234, {area_code: true, extension: 555});
20 | expect(out).toBe('(123) 555-1234 x 555');
21 | });
22 | it('Country Code', function() {
23 | var out = NumberHelpers.number_to_phone(1235551234, {country_code: 1});
24 | expect(out).toBe('+1-123-555-1234');
25 | });
26 | it('NaN', function() {
27 | var out = NumberHelpers.number_to_phone('123a456');
28 | expect(out).toBe('123a456');
29 | });
30 | it('Complex: +1.123.555.1234 x 1343', function() {
31 | var out = NumberHelpers.number_to_phone(1235551234, {country_code: 1, extension: 1343, delimiter: '.'});
32 | expect(out).toBe('+1.123.555.1234 x 1343');
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/specs/NumberWithDelimiterSpec.js:
--------------------------------------------------------------------------------
1 | describe('Number With Delimiter', function() {
2 | it('Numeric Type Value', function() {
3 | var out = NumberHelpers.number_with_delimiter(12345678);
4 | expect(out).toBe('12,345,678');
5 | });
6 | it('String Type Value', function() {
7 | var out = NumberHelpers.number_with_delimiter('123456');
8 | expect(out).toBe('123,456');
9 | });
10 | it('Decimal Value', function() {
11 | var out = NumberHelpers.number_with_delimiter(12345678.05);
12 | expect(out).toBe('12,345,678.05');
13 | });
14 | it('Delimiter As Period', function() {
15 | var out = NumberHelpers.number_with_delimiter(12345678, {delimiter:'.'});
16 | expect(out).toBe('12.345.678');
17 | });
18 | it('Delimiter As Comma', function() {
19 | var out = NumberHelpers.number_with_delimiter(12345678, {delimiter:','});
20 | expect(out).toBe('12,345,678');
21 | });
22 | it('Seperatar as Space', function() {
23 | var out = NumberHelpers.number_with_delimiter(12345678.05, {separator:' '});
24 | expect(out).toBe('12,345,678 05');
25 | });
26 | it('Non-numeric Value', function() {
27 | var out = NumberHelpers.number_with_delimiter('112a');
28 | expect(out).toBe('112a');
29 | });
30 | it('Delimiter and Separator Combination', function() {
31 | var out = NumberHelpers.number_with_delimiter(98765432.98, {delimiter:' ',separator:','});
32 | expect(out).toBe('98 765 432,98');
33 | });
34 | });
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Emcien NumbersHelpers Spec Runner
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/specs/NumberToHumanSizeSpec.js:
--------------------------------------------------------------------------------
1 | describe("Number To Human Size", function() {
2 | it('Bytes', function() {
3 | var out = NumberHelpers.number_to_human_size(123);
4 | expect(out).toBe('123 Bytes');
5 | });
6 | it('1.21 KB', function() {
7 | var out = NumberHelpers.number_to_human_size(1234);
8 | expect(out).toBe('1.21 KB');
9 | });
10 | it('12.1 KB', function() {
11 | var out = NumberHelpers.number_to_human_size(12345);
12 | expect(out).toBe('12.1 KB');
13 | });
14 | it('1.18 MB', function() {
15 | var out = NumberHelpers.number_to_human_size(1234567);
16 | expect(out).toBe('1.18 MB');
17 | });
18 | it('1.15 GB', function() {
19 | var out = NumberHelpers.number_to_human_size(1234567890);
20 | expect(out).toBe('1.15 GB');
21 | });
22 | it('1.12 TB', function() {
23 | var out = NumberHelpers.number_to_human_size(1234567890123);
24 | expect(out).toBe('1.12 TB');
25 | });
26 | it('1.2 MB, Precision 2', function() {
27 | var out = NumberHelpers.number_to_human_size(1234567, {precision: 2});
28 | expect(out).toBe('1.2 MB');
29 | });
30 | it('470 KB, Precision 2', function() {
31 | var out = NumberHelpers.number_to_human_size(483989, {precision: 2});
32 | expect(out).toBe('470 KB');
33 | });
34 | it('1,2 MB, Precision 2, Separator ,', function() {
35 | var out = NumberHelpers.number_to_human_size(1234567, {precision: 2, separator: ','});
36 | expect(out).toBe('1,2 MB');
37 | });
38 | it('Precision 5', function() {
39 | var out = NumberHelpers.number_to_human_size(1234567890123, {precision: 5});
40 | expect(out).toBe('1.1228 TB');
41 | });
42 | it('500 MB, Precision 5', function() {
43 | var out = NumberHelpers.number_to_human_size(524288000, {precision: 5, strip_insignificant_zeros: true});
44 | expect(out).toBe('500 MB');
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/specs/NumberWithPrecisionSpec.js:
--------------------------------------------------------------------------------
1 | describe("Number With Precision", function() {
2 | it('Defaults', function() {
3 | var out = NumberHelpers.number_with_precision(111.2345);
4 | expect(out).toBe('111.235');
5 | });
6 | it('Precision: 2', function() {
7 | var out = NumberHelpers.number_with_precision(111.2345, {precision: 2});
8 | expect(out).toBe('111.23');
9 | });
10 | it('Precision: 5', function() {
11 | var out = NumberHelpers.number_with_precision(13, {precision: 5});
12 | expect(out).toBe('13.00000');
13 | });
14 | it('Precision: 0', function() {
15 | var out = NumberHelpers.number_with_precision(389.32314, {precision: 0});
16 | expect(out).toBe('389');
17 | });
18 | it('Significant: 0', function() {
19 | var out = NumberHelpers.number_with_precision(111.2345, {significant: true});
20 | expect(out).toBe('111');
21 | });
22 | it('Significant: 1', function() {
23 | var out = NumberHelpers.number_with_precision(111.2345, {significant: true, precision: 1});
24 | expect(out).toBe('100');
25 | });
26 | it('Significant: 4', function() {
27 | var out = NumberHelpers.number_with_precision(389.32314, {significant: true, precision: 4});
28 | expect(out).toBe('389.3');
29 | });
30 | it('Significant: 5', function() {
31 | var out = NumberHelpers.number_with_precision(13, {significant: true, precision: 5});
32 | expect(out).toBe('13.00000');
33 | });
34 | it('Significant: 6', function() {
35 | var out = NumberHelpers.number_with_precision(13.00000004, {significant: true, precision: 6});
36 | expect(out).toBe('13.000000');
37 | });
38 | it('Precision: 2, Seperator: ,, Delimiter: .', function() {
39 | var out = NumberHelpers.number_with_precision(1111.2345, {separator: ',', precision: 2, delimiter: '.'});
40 | expect(out).toBe('1.111,23');
41 | });
42 | it('strip_insignificant_zeros', function() {
43 | var out = NumberHelpers.number_with_precision(13.0, {significant: true, precision: 5, strip_insignificant_zeros: true});
44 | expect(out).toBe('13');
45 | });
46 | it('leaves_significant_zeros', function() {
47 | var out = NumberHelpers.number_with_precision(130.0, {significant: true, precision: 5, strip_insignificant_zeros: true});
48 | expect(out).toBe('130');
49 | });
50 | it('strips out insignificant zeroes after nonzero digits', function() {
51 | var out = NumberHelpers.number_with_precision(13.01000, {significant: true, precision: 5, strip_insignificant_zeros: true});
52 | expect(out).toBe('13.01');
53 | });
54 | it('can strip insignificant decimal parts if requested', function() {
55 | var a = NumberHelpers.number_with_precision(13.010, { strip_empty_fractional_parts: true });
56 | expect(a).toBe('13.010');
57 |
58 | var b = NumberHelpers.number_with_precision(13.000, { strip_empty_fractional_parts: true });
59 | expect(b).toBe('13');
60 |
61 | var c = NumberHelpers.number_with_precision(13.000, { strip_empty_fractional_parts: true, precision: 4 });
62 | expect(c).toBe('13');
63 |
64 | var d = NumberHelpers.number_with_precision(13.000001, { strip_empty_fractional_parts: true, precision: 4 });
65 | expect(d).toBe('13');
66 |
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/specs/NumberToCurrencySpec.js:
--------------------------------------------------------------------------------
1 | // number_to_currency(-1234567890.50, :negative_format => "(%u%n)") # => ($1,234,567,890.50)
2 | // number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u") # => 1234567890,50 £
3 |
4 | describe("Number To Currency", function() {
5 | it('Defaults', function() {
6 | var out = NumberHelpers.number_to_currency(1234567890.50);
7 | expect(out).toBe('$1,234,567,890.50');
8 | });
9 | it('String', function() {
10 | var out = NumberHelpers.number_to_currency('1234567890.50');
11 | expect(out).toBe('$1,234,567,890.50');
12 | });
13 | it('Negative number', function() {
14 | var out = NumberHelpers.number_to_currency(-2.99, {precision: 0});
15 | expect(out).toBe('-$3');
16 | });
17 | it('Negative number with precision rounding', function() {
18 | var out = NumberHelpers.number_to_currency(-2.999, {precision: 2});
19 | expect(out).toBe('-$3.00');
20 | });
21 | it('Negative number with precision', function() {
22 | var out = NumberHelpers.number_to_currency(-2.503, {precision: 2});
23 | expect(out).toBe('-$2.50');
24 | });
25 | it('Rounding Up', function() {
26 | var out = NumberHelpers.number_to_currency(1234567890.506);
27 | expect(out).toBe('$1,234,567,890.51');
28 | });
29 | it('Simple rounding Up With 9s Precision 0', function() {
30 | var out = NumberHelpers.number_to_currency(2.99, {precision: 0});
31 | expect(out).toBe('$3');
32 | });
33 | it('Simple Precision 2 with 9s', function() {
34 | var out = NumberHelpers.number_to_currency(2.99, {precision: 2});
35 | expect(out).toBe('$2.99');
36 | });
37 | it('Rounding Up With 9s Precision 0', function() {
38 | var out = NumberHelpers.number_to_currency(1234567890.99, {precision: 0});
39 | expect(out).toBe('$1,234,567,891');
40 | });
41 | it('Rounding Up With 9s Precision 2', function() {
42 | var out = NumberHelpers.number_to_currency(1234567890.99, {precision: 2});
43 | expect(out).toBe('$1,234,567,890.99');
44 | });
45 | it('Precision 0', function() {
46 | var out = NumberHelpers.number_to_currency(1234567890.506, {precision: 0});
47 | expect(out).toBe('$1,234,567,891');
48 | });
49 | it('Precision 1', function() {
50 | var out = NumberHelpers.number_to_currency(1234567890.506, {precision: 1});
51 | expect(out).toBe('$1,234,567,890.5');
52 | });
53 | it('Precision 3', function() {
54 | var out = NumberHelpers.number_to_currency(1234567890.506, {precision: 3});
55 | expect(out).toBe('$1,234,567,890.506');
56 | });
57 | it('Precision 4', function() {
58 | var out = NumberHelpers.number_to_currency(1234567890.506, {precision: 4});
59 | expect(out).toBe('$1,234,567,890.5060');
60 | });
61 | it('String', function() {
62 | var out = NumberHelpers.number_to_currency('123a456');
63 | expect(out).toBe('$123a456');
64 | });
65 | it('Unit, Separator, Delimiter Combo', function() {
66 | var out = NumberHelpers.number_to_currency(1234567890.50, {unit:"£", separator:",", delimiter:""});
67 | expect(out).toBe('£1234567890,50');
68 | });
69 | it('allows "after" unit positioning', function() {
70 | var out = NumberHelpers.number_to_currency(123.507, {unit: " CZK", unit_position: "end", precision: 2});
71 | expect(out).toBe('123.51 CZK');
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/specs/NumberToHumanSpec.js:
--------------------------------------------------------------------------------
1 | // Unsignificant zeros after the decimal separator are stripped out by default (set :strip_insignificant_zeros to false to change that):
2 | // number_to_human(12345012345, :significant_digits => 6) # => "12.345 Billion"
3 |
4 | describe("Number To Human", function() {
5 | it('Hundreds', function() {
6 | var out = NumberHelpers.number_to_human(123);
7 | expect(out).toBe('123');
8 | });
9 | it('Thousands', function() {
10 | var out = NumberHelpers.number_to_human(1234);
11 | expect(out).toBe('1.23 Thousand');
12 | });
13 | it('Thousands', function() {
14 | var out = NumberHelpers.number_to_human(1234);
15 | expect(out).toBe('1.23 Thousand');
16 | });
17 | it('Ten Thousands', function() {
18 | var out = NumberHelpers.number_to_human(12345);
19 | expect(out).toBe('12.3 Thousand');
20 | });
21 | it('Million', function() {
22 | var out = NumberHelpers.number_to_human(1234567);
23 | expect(out).toBe('1.23 Million');
24 | });
25 | it('Billion', function() {
26 | var out = NumberHelpers.number_to_human(1234567890);
27 | expect(out).toBe('1.23 Billion');
28 | });
29 | it('Tillion', function() {
30 | var out = NumberHelpers.number_to_human(1234567890123);
31 | expect(out).toBe('1.23 Trillion');
32 | });
33 | it('Quadrillion', function() {
34 | var out = NumberHelpers.number_to_human(1234567890123456);
35 | expect(out).toBe('1.23 Quadrillion');
36 | });
37 | it('1230 Quadrillion', function() {
38 | var out = NumberHelpers.number_to_human(1234567890123456789);
39 | expect(out).toBe('1230 Quadrillion');
40 | });
41 | it('Precision 2', function() {
42 | var out = NumberHelpers.number_to_human(489939, {precision: 2});
43 | expect(out).toBe('490 Thousand');
44 | });
45 | it('Precision 4', function() {
46 | var out = NumberHelpers.number_to_human(489939, {precision: 4});
47 | expect(out).toBe('489.9 Thousand');
48 | });
49 | it('Precision 4, Significant false', function() {
50 | var out = NumberHelpers.number_to_human(1234567, {precision: 4, significant: false});
51 | expect(out).toBe('1.2346 Million');
52 | });
53 | it('Precision 1, Significant false, Seperator ,', function() {
54 | var out = NumberHelpers.number_to_human(1234567, {precision: 1, significant: false, separator: ','});
55 | expect(out).toBe('1,2 Million');
56 | });
57 | it('500 Million', function() {
58 | var out = NumberHelpers.number_to_human(500000000, {precision: 5, strip_insignificant_zeros: true});
59 | expect(out).toBe('500 Million');
60 | });
61 | it('500 Million', function() {
62 | var out = NumberHelpers.number_to_human(500000000, {precision: 5, strip_insignificant_zeros: true});
63 | expect(out).toBe('500 Million');
64 | });
65 | it('Custom labels: K', function() {
66 | var out = NumberHelpers.number_to_human(1234, {space_label: false, labels:{thousand: 'K'}});
67 | expect(out).toBe('1.23K')
68 | });
69 | it('Custom labels: M', function() {
70 | var out = NumberHelpers.number_to_human(1234567, {labels:{million: 'M'}});
71 | expect(out).toBe('1.23 M')
72 | });
73 | it('Custom labels: B', function() {
74 | var out = NumberHelpers.number_to_human(1234567890, {space_label: false, labels:{billion: 'B'}});
75 | expect(out).toBe('1.23B')
76 | });
77 | it('Custom labels: T', function() {
78 | var out = NumberHelpers.number_to_human(1234567890123, {labels:{trillion: 'T'}});
79 | expect(out).toBe('1.23 T')
80 | });
81 | it('Custom labels: P', function() {
82 | var out = NumberHelpers.number_to_human(1234567890123456, {labels:{quadrillion: 'P'}});
83 | expect(out).toBe('1.23 P')
84 | });
85 | it('No label space', function() {
86 | var out = NumberHelpers.number_to_human(1234, {space_label: false});
87 | expect(out).toBe('1.23Thousand');
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/specs/NumberToRoundedSpec.js:
--------------------------------------------------------------------------------
1 | // These tests were converted from Rails tests. Higher precision tests were ommitted as they are currently not supported in JavaScript
2 |
3 | describe("NumberToRounded", function() {
4 | it("should reconstitute exponentials", function() {
5 | var num = 34534534;
6 | var exps = num.toExponential().split('e');
7 | expect(NumberHelpers.reconstitute_exponential(exps[0],exps[1])).toEqual(num+"");
8 | expect(NumberHelpers.reconstitute_exponential(num.toExponential())).toEqual(num+"");
9 | num = 0.000034544;
10 | expect(NumberHelpers.reconstitute_exponential(num.toExponential())).toEqual(num+"");
11 | num = 988.45
12 | expect(NumberHelpers.reconstitute_exponential(num.toExponential())).toEqual(num+"");
13 | num = 9080943000
14 | expect(NumberHelpers.reconstitute_exponential(num.toExponential())).toEqual(num+"");
15 | });
16 | it("to_rounded", function() {
17 | expect(NumberHelpers.number_to_rounded(-111.2346 )).toEqual("-111.235");
18 | expect(NumberHelpers.number_to_rounded(111.2346 )).toEqual("111.235");
19 | expect(NumberHelpers.number_to_rounded(31.825 , { precision : 2 } )).toEqual("31.83");
20 | expect(NumberHelpers.number_to_rounded(111.2346 , { precision : 2 } )).toEqual("111.23");
21 | expect(NumberHelpers.number_to_rounded(111 , { precision : 2 } )).toEqual("111.00");
22 | expect(NumberHelpers.number_to_rounded("111.2346" )).toEqual("111.235");
23 | expect(NumberHelpers.number_to_rounded("31.825" , { precision : 2 } )).toEqual("31.83");
24 | expect(NumberHelpers.number_to_rounded(32.6751 * 100.00, {precision: 0} )).toEqual("3268");
25 | expect(NumberHelpers.number_to_rounded(111.50 , { precision : 0 } )).toEqual("112");
26 | expect(NumberHelpers.number_to_rounded(1234567891.50 , { precision : 0 } )).toEqual("1234567892");
27 | expect(NumberHelpers.number_to_rounded(0 , { precision : 0 } )).toEqual("0");
28 | expect(NumberHelpers.number_to_rounded(0.001 , { precision : 5 } )).toEqual("0.00100");
29 | expect(NumberHelpers.number_to_rounded(0.00111 , { precision : 3 } )).toEqual("0.001");
30 | expect(NumberHelpers.number_to_rounded(9.995 , { precision : 2 } )).toEqual("10.00");
31 | expect(NumberHelpers.number_to_rounded(10.995 , { precision : 2 } )).toEqual("11.00");
32 | expect(NumberHelpers.number_to_rounded(-0.001 , { precision : 2 } )).toEqual("0.00");
33 |
34 | });
35 | it("to_rounded_with_custom_delimiter_and_separator", function() {
36 | expect(NumberHelpers.number_to_rounded(31.825 , { precision : 2, separator : ',' } )).toEqual( '31,83');
37 | expect(NumberHelpers.number_to_rounded(1231.825 , { precision : 2, separator : ',', delimiter : '.' } )).toEqual('1.231,83');
38 | });
39 | it("to_rounded_with_significant_digits", function() {
40 | expect(NumberHelpers.number_to_rounded(123987 , { precision : 3, significant : true } )).toEqual("124000");
41 | expect(NumberHelpers.number_to_rounded(123987876 , { precision : 2, significant : true } )).toEqual("120000000");
42 | expect(NumberHelpers.number_to_rounded("43523" , { precision : 1, significant : true } )).toEqual("40000");
43 | expect(NumberHelpers.number_to_rounded(9775 , { precision : 4, significant : true } )).toEqual("9775");
44 | expect(NumberHelpers.number_to_rounded(5.3923 , { precision : 2, significant : true } )).toEqual("5.4");
45 | expect(NumberHelpers.number_to_rounded(5.3923 , { precision : 1, significant : true } )).toEqual("5");
46 | expect(NumberHelpers.number_to_rounded(1.232 , { precision : 1, significant : true } )).toEqual("1");
47 | expect(NumberHelpers.number_to_rounded(7 , { precision : 1, significant : true } )).toEqual("7");
48 | expect(NumberHelpers.number_to_rounded(1 , { precision : 1, significant : true } )).toEqual("1");
49 | expect(NumberHelpers.number_to_rounded(52.7923 , { precision : 2, significant : true } )).toEqual("53");
50 | expect(NumberHelpers.number_to_rounded(9775 , { precision : 6, significant : true } )).toEqual("9775.00");
51 | expect(NumberHelpers.number_to_rounded(5.3929 , { precision : 7, significant : true } )).toEqual("5.392900");
52 | expect(NumberHelpers.number_to_rounded(0 , { precision : 2, significant : true } )).toEqual("0.0");
53 | expect(NumberHelpers.number_to_rounded(0 , { precision : 1, significant : true } )).toEqual("0");
54 | expect(NumberHelpers.number_to_rounded(0.0001 , { precision : 1, significant : true } )).toEqual("0.0001");
55 | expect(NumberHelpers.number_to_rounded(0.0001 , { precision : 3, significant : true } )).toEqual("0.000100");
56 | expect(NumberHelpers.number_to_rounded(0.0001111 , { precision : 1, significant : true } )).toEqual("0.0001");
57 | expect(NumberHelpers.number_to_rounded(9.995 , { precision : 3, significant : true } )).toEqual("10.0");
58 | expect(NumberHelpers.number_to_rounded(9.994 , { precision : 3, significant : true } )).toEqual("9.99");
59 | expect(NumberHelpers.number_to_rounded(10.995 , { precision : 3, significant : true } )).toEqual("11.0");
60 |
61 | });
62 | it("to_rounded_with_strip_insignificant_zeros", function() {
63 | expect(NumberHelpers.number_to_rounded(9775.43 , { precision : 4, strip_insignificant_zeros : true } )).toEqual("9775.43");
64 | expect(NumberHelpers.number_to_rounded(9775.2 , { precision : 6, significant : true, strip_insignificant_zeros : true } )).toEqual("9775.2");
65 | expect(NumberHelpers.number_to_rounded(0 , { precision : 6, significant : true, strip_insignificant_zeros : true } )).toEqual("0");
66 | });
67 | // Zero precision with significant is a mistake (would always return zero),
68 | // so we treat it as if significant was false (increases backwards compatibility for number_to_human_size)
69 |
70 | it("to_rounded_with_significant_true_and_zero_precision", function() {
71 | expect(NumberHelpers.number_to_rounded(123.987 , { precision : 0, significant : true } )).toEqual("124");
72 | expect(NumberHelpers.number_to_rounded(12 , { precision : 0, significant : true } )).toEqual("12");
73 | expect(NumberHelpers.number_to_rounded("12.3" , { precision : 0, significant : true } )).toEqual("12");
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/jasmine-core/jasmine.css:
--------------------------------------------------------------------------------
1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
2 |
3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
4 | #HTMLReporter a { text-decoration: none; }
5 | #HTMLReporter a:hover { text-decoration: underline; }
6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; }
9 | #HTMLReporter .version { color: #aaaaaa; }
10 | #HTMLReporter .banner { margin-top: 14px; }
11 | #HTMLReporter .duration { color: #aaaaaa; float: right; }
12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; }
15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; }
17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; }
21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
24 | #HTMLReporter .runningAlert { background-color: #666666; }
25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; }
26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; }
27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
28 | #HTMLReporter .passingAlert { background-color: #a6b779; }
29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
30 | #HTMLReporter .failingAlert { background-color: #cf867e; }
31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; }
32 | #HTMLReporter .results { margin-top: 14px; }
33 | #HTMLReporter #details { display: none; }
34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
38 | #HTMLReporter.showDetails .summary { display: none; }
39 | #HTMLReporter.showDetails #details { display: block; }
40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
41 | #HTMLReporter .summary { margin-top: 14px; }
42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; }
45 | #HTMLReporter .description + .suite { margin-top: 0; }
46 | #HTMLReporter .suite { margin-top: 14px; }
47 | #HTMLReporter .suite a { color: #333333; }
48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; }
49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
51 | #HTMLReporter .resultMessage span.result { display: block; }
52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
53 |
54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; }
56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
61 | #TrivialReporter .runner.running { background-color: yellow; }
62 | #TrivialReporter .options { text-align: right; font-size: .8em; }
63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
64 | #TrivialReporter .suite .suite { margin: 5px; }
65 | #TrivialReporter .suite.passed { background-color: #dfd; }
66 | #TrivialReporter .suite.failed { background-color: #fdd; }
67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
71 | #TrivialReporter .spec.skipped { background-color: #bbb; }
72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
73 | #TrivialReporter .passed { background-color: #cfc; display: none; }
74 | #TrivialReporter .failed { background-color: #fbb; }
75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
77 | #TrivialReporter .resultMessage .mismatch { color: black; }
78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; }
82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | (A better formatted version of this documentation can
2 | be viewed [here](http://emcien.github.com/number-helpers-coffeescript))
3 |
4 | ## Background
5 |
6 | At Emcien, we found more of our application View Code migrating into a
7 | BackBone (or JS) rendered context. The useful Rails NumberHelpers
8 | we have grown accustomed to are not available. Out of necessity, we created a
9 | CoffeeScript implementation of the Rails NumberHelpers to help display
10 | numeric data in a fomatted fashion.
11 |
12 | ## Details
13 |
14 | This library uses the
15 | [Rails NumberHelpers Documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html)
16 | as the recipe for all method names, parameters, and test cases. For testing,
17 | the Jasmine Test Framework implements the same test specs as the Rails code.
18 |
19 | ## Testing
20 |
21 | * jasmine
22 | - Just open the index.html file in your browser
23 | * jasmine-node
24 | - Install jasmine-node `npm install -g jasmine-node`
25 | - Run: `$ jasmine-node specs`
26 |
27 | ### Methods:
28 |
29 | #### `number_to_currency`
30 |
31 | Formats a number into a currency string (e.g., $13.65).
32 | You can customize the format in the options hash.
33 |
34 | `NumberHelpers.number_to_currency(123, {option: value})`
35 |
36 | - `{ precision: 2 }` - Sets the level of precision (defaults to 2).
37 | - `{ unit: '$' }` - Sets the denomination of the currency (defaults to "$").
38 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
39 | - `{ delimiter: ',' }` - Sets the thousands delimiter (defaults to ",").
40 |
41 | *Examples:*
42 | - `NumberHelpers.number_to_currency(1234567890.50)`: `$1,234,567,890.50`
43 | - `NumberHelpers.number_to_currency('1234567890.50')`: `$1,234,567,890.50`
44 | - `NumberHelpers.number_to_currency(1234567890.506)`: `$1,234,567,890.51`
45 | - `NumberHelpers.number_to_currency(1234567890.506, {precision: 0})`: `$1,234,567,890`
46 | - `NumberHelpers.number_to_currency(1234567890.506, {precision: 1})`: `$1,234,567,890.5`
47 | - `NumberHelpers.number_to_currency(1234567890.506, {precision: 3})`: `$1,234,567,890.506`
48 | - `NumberHelpers.number_to_currency(1234567890.506, {precision: 4})`: `$1,234,567,890.5060`
49 | - `NumberHelpers.number_to_currency('123a456')`: `$123a456`
50 | - `NumberHelpers.number_to_currency(1234567890.50, {unit:"£", separator:",", delimiter:""})`: `£1234567890,50`
51 |
52 | ---------
53 |
54 | #### `number_to_human`
55 |
56 | Pretty prints (formats and approximates) a number in a way it is more
57 | readable by humans (eg.: 1200000000 becomes “1.2 Billion”). This is useful
58 | for numbers that can get very large (and too hard to read).
59 |
60 | `NumberHelpers.number_to_human(123, {option: value})`
61 |
62 | - `{ precision: 3 }` - Sets the level of precision (defaults to 3).
63 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
64 | - `{ delimiter: ',' }` - Sets the thousands delimiter (defaults to ",").
65 | - `{ space_label: false }` - Omit the space between the value and the label.
66 | - If false, then 1234 would be output as 1.23Thousand
67 | - `{ strip_insignificant_zeros: true}` - If true removes insignificant zeros
68 | after the decimal separator (defaults to true)
69 | - `{ significant: true }`
70 | - If true, precision will be the # of significant_digits.
71 | - If false, the # of fractional digits (defaults to true)
72 | - `{ labels: { thousand: 'K', million: 'M' } }`
73 | - Customize the label used for the output value.
74 | - Possible keys are:
75 | - `thousand`
76 | - `million`
77 | - `billion`
78 | - `trillion`
79 | - `quadrillion`
80 |
81 | *Examples:*
82 | - `NumberHelpers.number_to_human(123)`: `123`
83 | - `NumberHelpers.number_to_human(1234)`: `1.23 Thousand`
84 | - `NumberHelpers.number_to_human(12345)`: `12.3 Thousand`
85 | - `NumberHelpers.number_to_human(1234567)`: `1.23 Million`
86 | - `NumberHelpers.number_to_human(1234567890)`: `1.23 Billion`
87 | - `NumberHelpers.number_to_human(1234567890123)`: `1.23 Trillion`
88 | - `NumberHelpers.number_to_human(1234567890123456)`: `1.23 Quadrillion`
89 | - `NumberHelpers.number_to_human(1234567890123456789)`: `1230 Quadrillion`
90 | - `NumberHelpers.number_to_human(489939, {precision: 2})`: `490 Thousand`
91 | - `NumberHelpers.number_to_human(489939, {precision: 4})`: `489.9 Thousand`
92 | - `NumberHelpers.number_to_human(1234567, {precision: 4, significant: false})`: `1.2346 Million`
93 | - `NumberHelpers.number_to_human(1234567, {precision: 1, significant: false, separator: ','})`: `1,2 Million`
94 | - `NumberHelpers.number_to_human(500000000, {precision: 5, strip_insignificant_zeros: true})`: `500 Million`
95 |
96 | ----------
97 |
98 | ## `number_to_human_size`
99 |
100 | Formats the bytes in number into a more understandable representation
101 | (e.g., giving it 1500 yields 1.5 KB). This method is useful for reporting
102 | file sizes to users. You can customize the format in the options hash.
103 |
104 | `NumberHelpers.number_to_human_size(123, {option: value})`
105 |
106 | - `{ precision: 3 }` - Sets the level of precision (defaults to 3).
107 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
108 | - `{ delimiter: ',' }` - Sets the thousands delimiter (defaults to ",").
109 | - `{ strip_insignificant_zeros: true}` - If true removes insignificant zeros
110 | after the decimal separator (defaults to true)
111 | - `{ significant: true }` (defaults to true)
112 | - If true, precision will be the # of significant_digits.
113 | - If false, the # of fractional digits
114 |
115 | *Examples:*
116 | - `NumberHelpers.number_to_human_size(123)`: `123 Bytes`
117 | - `NumberHelpers.number_to_human_size(1234)`: `1.21 KB`
118 | - `NumberHelpers.number_to_human_size(12345)`: `12.1 KB`
119 | - `NumberHelpers.number_to_human_size(1234567)`: `1.18 MB`
120 | - `NumberHelpers.number_to_human_size(1234567890)`: `1.15 GB`
121 | - `NumberHelpers.number_to_human_size(1234567890123)`: `1.12 TB`
122 | - `NumberHelpers.number_to_human_size(1234567, {precision: 2})`: `1.2 MB`
123 | - `NumberHelpers.number_to_human_size(483989, {precision: 2})`: `470 KB`
124 | - `NumberHelpers.number_to_human_size(1234567, {precision: 2, separator: ','})`: `1,2 MB`
125 | - `NumberHelpers.number_to_human_size(1234567890123, {precision: 5})`: `1.1228 TB`
126 | - `NumberHelpers.number_to_human_size(524288000, {precision: 5, strip_insignificant_zeros: true})`: `500 MB`
127 |
128 | -------------
129 |
130 | ## `number_to_percentage`
131 |
132 | Formats a number as a percentage string (e.g., 65%). You can
133 | customize the format in the options hash.
134 |
135 | `NumberHelpers.number_to_percentage(123, {option: value})`
136 |
137 | - `{ precision: 3 }` - Sets the level of precision (defaults to 3).
138 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
139 | - `{ delimiter: '' }` - Sets the thousands delimiter (defaults to "").
140 | - `{ strip_insignificant_zeros: false}` - If true removes insignificant zeros
141 | after the decimal separator (defaults to false)
142 | - `{ significant: false }` - If true, precision will be the # of significant_digits
143 | If false, the # of fractional digits (defaults to false)
144 |
145 | *Examples:*
146 | - `NumberHelpers.number_to_percentage(100)`: `100.000%`
147 | - `NumberHelpers.number_to_percentage(98)`: `98.000%`
148 | - `NumberHelpers.number_to_percentage(100, {precision: 0})`: `100%`
149 | - `NumberHelpers.number_to_percentage(1000, {delimiter: '.', separator: ','})`: `1.000,000%`
150 | - `NumberHelpers.number_to_percentage(302.24398923423, {precision: 5})`: `302.24399%`
151 | - `NumberHelpers.number_to_percentage('98a')`: `98a%`
152 |
153 | ----------
154 |
155 | ## `number_to_phone`
156 |
157 | Formats a number into a US phone number (e.g., (555) 123-9876).
158 | You can customize the format in the options hash.
159 |
160 | `NumberHelpers.number_to_phone(5551234, {option: value})`
161 |
162 | - `{ area_code: false }` - Adds parentheses around the area code.
163 | - `{ delimiter: '-' }` - Sets the thousands delimiter (defaults to "-").
164 | - `{ extension: false }` - Specifies an extension to add to the end of the generated number.
165 | - `{ country_code: false }` - Sets the country code for the phone number.
166 |
167 | *Examples:*
168 | - `NumberHelpers.number_to_phone(5551234)`: `555-1234`
169 | - `NumberHelpers.number_to_phone(1235551234)`: `123-555-1234`
170 | - `NumberHelpers.number_to_phone(1235551234, {area_code: true})`: `(123) 555-1234`
171 | - `NumberHelpers.number_to_phone(1235551234, {delimiter: ' '})`: `123 555 1234`
172 | - `NumberHelpers.number_to_phone(1235551234, {area_code: true, extension: 555})`: `(123) 555-1234 x 555`
173 | - `NumberHelpers.number_to_phone(1235551234, {country_code: 1})`: `+1-123-555-1234`
174 | - `NumberHelpers.number_to_phone(123a456)`: `123a456`
175 | - `NumberHelpers.number_to_phone(1235551234, {country_code: 1, extension: 1343, delimiter: '.'})`: `+1.123.555.1234 x 1343`
176 |
177 | ----------
178 |
179 | ## `number_with_delimiter`
180 |
181 | Formats a number with grouped thousands using delimiter (e.g., 12,324).
182 | You can customize the format in the options hash.
183 |
184 | `NumberHelpers.number_with_delimiter(12345678, {option: value})`
185 |
186 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
187 | - `{ delimiter: ',' }` - Sets the thousands delimiter (defaults to ",").
188 |
189 | *Examples:*
190 | - `NumberHelpers.number_with_delimiter(12345678)`: `12,345,678`
191 | - `NumberHelpers.number_with_delimiter('123456')`: `123,456`
192 | - `NumberHelpers.number_with_delimiter(12345678.05)`: `12,345,678.05`
193 | - `NumberHelpers.number_with_delimiter(12345678, {delimiter:'.'})`: `12.345.678`
194 | - `NumberHelpers.number_with_delimiter(12345678, {delimiter:','})`: `12,345,678`
195 | - `NumberHelpers.number_with_delimiter(12345678.05, {separator:' '})`: `12,345,678 05`
196 | - `NumberHelpers.number_with_delimiter('112a')`: `112a`
197 | - `NumberHelpers.number_with_delimiter(98765432.98, {delimiter:' ',separator:','})`: `98 765 432,98`
198 |
199 | ------
200 |
201 | ## `number_with_precision`
202 |
203 | Formats a number with the specified level of :precision (e.g., 112.32 has a
204 | precision of 2 if :significant is false, and 5 if :significant is true).
205 | You can customize the format in the options hash.
206 |
207 | `NumberHelpers.number_with_precision(111.2345, {option: value})`
208 |
209 | - `{ precision: 3 }` - Sets the level of precision (defaults to 3).
210 | - `{ separator: '.' }` - Sets the separator between the units (defaults to ".").
211 | - `{ delimiter: ',' }` - Sets the thousands delimiter (defaults to ",").
212 | - `{ strip_insignificant_zeros: false}` - If true removes insignificant zeros after
213 | the decimal separator (defaults to false)
214 | - `{ significant: false }`
215 | - If true, precision will be the # of significant_digits.
216 | - If false, the # of fractional digits (defaults to false)
217 | - `{ strip_empty_fractional_parts: false }` - If true, the number will be expressed as an integer
218 | if the fractional part is empty (if it rounds to 15.00, only '15' is produced). (defaults to false)
219 |
220 | *Examples:*
221 | - `NumberHelpers.number_with_precision(111.2345)`: `111.235`
222 | - `NumberHelpers.number_with_precision(111.2345, {precision: 2})`: `111.23`
223 | - `NumberHelpers.number_with_precision(13, {precision: 5})`: `13.00000`
224 | - `NumberHelpers.number_with_precision(389.32314, {precision: 0})`: `389`
225 | - `NumberHelpers.number_with_precision(111.2345, {significant: true})`: `111`
226 | - `NumberHelpers.number_with_precision(111.2345, {significant: true, precision: 1})`: `100`
227 | - `NumberHelpers.number_with_precision(389.32314, {significant: true, precision: 4})`: `389.3`
228 | - `NumberHelpers.number_with_precision(13, {significant: true, precision: 5})`: `13.00000`
229 | - `NumberHelpers.number_with_precision(13.00000004, {significant: true, precision: 6})`: `13.000000`
230 | - `NumberHelpers.number_with_precision(1111.2345, {separator: ',', precision: 2, delimiter: '.'})`: `1.111,23`
231 | - `NumberHelpers.number_with_precision(13, {significant: true, precision: 5, strip_insignificant_zeros: true})`: `13`
232 |
233 |
234 | This project is maintained by [Emcien](https://github.com/emcien).
235 |
236 | We are always looking for good engineers -
237 | [send](mailto:devjobs@emcien.com) us your github profile and resume!
238 |
239 |
--------------------------------------------------------------------------------
/src/number_helpers.coffee:
--------------------------------------------------------------------------------
1 | class NumberHelpers
2 | @ZEROS = new Array(200).join('0');
3 | @number_to_currency = (float, opts={}) ->
4 | _precision = opts.precision ? 2
5 | _unit = opts.unit ? '$'
6 | _separator = opts.separator ? '.'
7 | _delimiter = opts.delimiter ? ','
8 | _unit_pos = opts.unit_position ? 'start'
9 |
10 | sign = ''
11 |
12 | # Non-number values return zero precision
13 | if isNaN(float)
14 | result = float
15 | else
16 | float = parseFloat(float) # Arg might be a string, integer
17 | sign = '-' if float < 0
18 | fixedWidth = Math.abs(float).toFixed(_precision)
19 | delimited = NumberHelpers.number_with_delimiter(fixedWidth, {delimiter: _delimiter})
20 | result = delimited.split('.').join(_separator)
21 |
22 | if _unit_pos == 'end'
23 | return "#{sign}#{result}#{_unit}"
24 | else
25 | return "#{sign}#{_unit}#{result}"
26 |
27 | @number_with_delimiter = (float, opts={}) ->
28 | _separator = opts.separator ? '.'
29 | _delimiter = opts.delimiter ? ','
30 |
31 | number = float.toString().split(".")
32 | integer = number[0]
33 | decimal = number[1] ? ''
34 |
35 | # Remove separator if no decimal
36 | _separator = '' unless decimal
37 |
38 | rgx = /(\d+)(\d{3})/
39 | integer = integer.replace(rgx, "$1" + _delimiter + "$2") while rgx.test(integer) if _delimiter
40 |
41 | return "#{integer}#{_separator}#{decimal}"
42 |
43 | @safe_round = (float, _significant, _precision) ->
44 | # Rounding based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
45 | #Shift
46 | return float unless float
47 | value = (+float).toExponential().toString().split('e');
48 | _round = if _significant then (-1 - value[1] + _precision) else _precision
49 |
50 | value = Math.round(+(value[0] + 'e' + (+value[1] + _round)));
51 | # Shift back
52 | value = value.toExponential().toString().split('e');
53 | value = +(value[0] + 'e' + ( +value[1] - _round) );
54 |
55 |
56 | @number_with_precision = (float, opts={}) ->
57 | _precision = opts.precision ? 3
58 | _delimiter = opts.delimiter ? ','
59 | _separator = opts.separator ? '.'
60 | _significant = opts.significant ? false
61 | _strip_insignificant_zeros = opts.strip_insignificant_zeros ? false
62 | _skip_empty_fractionals = opts.strip_empty_fractional_parts
63 |
64 | # Break number into inspectable pieces
65 | number = float.toString().split('.')
66 | integer = number[0]
67 | decimal = number[1] ? ''
68 |
69 | # Significant Digits need rounding to match number of sig digits
70 | if _significant
71 | rnd = _precision - integer.length
72 | else
73 | rnd = _precision
74 |
75 | # Make sure rounding is not to a negative place
76 | rnd = 0 if rnd < 1
77 |
78 | # Round
79 | multiple = Math.pow(10, rnd)
80 | if multiple > 1
81 | rounded = @safe_round(float, _significant, _precision)
82 | else
83 | rounded = float
84 |
85 | # Break number into inspectable pieces
86 | number = rounded.toString().split('.')
87 | integer = number[0]
88 | decimal = number[1] ? ''
89 |
90 | decimal = parseFloat("0.#{decimal}").toFixed(_precision)
91 | decimal = decimal.toString().split('.')
92 | decimal = decimal[1] ? ''
93 |
94 | # Reconstitute the number with correct decimal
95 | number = "#{integer}.#{decimal}" * 1
96 | num_array = number.toString().split('')
97 | num_lngth = num_array.length
98 |
99 | # Count Non-zero Digits
100 | i = 0; sigs = 0
101 | while i < num_lngth
102 | sigs++ unless num_array[i] is '.' or num_array[i] is '0'
103 | i++
104 |
105 | # Significant Digits
106 | if _significant and sigs >= _precision and _precision > 0
107 | significant = number.toPrecision(_precision) * 1
108 | significant = significant.toString().split('.')
109 | integer = significant[0]
110 | decimal = significant[1] ? ''
111 |
112 |
113 |
114 |
115 | # Delimiter Integer
116 | integer = NumberHelpers.number_with_delimiter(integer, {delimiter: _delimiter})
117 |
118 | # Strip Insignificant Digits
119 | if _strip_insignificant_zeros
120 | dlen = decimal.length
121 | newlen = dlen
122 |
123 | while newlen > 0 and decimal[newlen - 1] == '0'
124 | newlen = newlen - 1
125 |
126 | if newlen == 0
127 | decimal = ''
128 | else if newlen != dlen
129 | decimal = decimal.slice(0, newlen)
130 |
131 | if _skip_empty_fractionals
132 | i = 0; zcount = 0
133 | num_array = decimal.split('')
134 | dlen = decimal.length
135 | while i < dlen
136 | zcount++ if num_array[i] is '0'
137 | i++
138 | if zcount == dlen
139 | decimal = ''
140 |
141 | # Remove separator if no decimal
142 | _separator = '' unless decimal
143 |
144 | return "#{integer}#{_separator}#{decimal}"
145 |
146 | @number_to_human = (float, opts={}) ->
147 | _precision = opts.precision ? 3
148 | _separator = opts.separator ? '.'
149 | _significant = opts.significant ? true
150 | _delimiter = opts.delimiter ? ','
151 | _strip_insignificant_zeros = opts.strip_insignificant_zeros ? false
152 | _space_label = if opts.space_label is false then '' else ' '
153 | _labels =
154 | thousand: opts.labels?.thousand ? 'Thousand'
155 | million: opts.labels?.million ? 'Million'
156 | billion: opts.labels?.billion ? 'Billion'
157 | trillion: opts.labels?.trillion ? 'Trillion'
158 | quadrillion: opts.labels?.quadrillion ? 'Quadrillion'
159 |
160 | # Remove the sign of the number for easier comparision
161 | abs_float = Math.abs(float)
162 |
163 | # Less than Thousand does not need text or a insignifiant digits
164 | if abs_float < Math.pow(10, 3)
165 | denom = 1
166 | label = false
167 | else if abs_float >= Math.pow(10, 3) and abs_float < Math.pow(10, 6)
168 | denom = Math.pow(10, 3)
169 | label = _labels.thousand
170 | else if abs_float >= Math.pow(10, 6) and abs_float < Math.pow(10, 9)
171 | denom = Math.pow(10, 6)
172 | label = _labels.million
173 | else if abs_float >= Math.pow(10, 9) and abs_float < Math.pow(10, 12)
174 | denom = Math.pow(10, 9)
175 | label = _labels.billion
176 | else if abs_float >= Math.pow(10, 12) and abs_float < Math.pow(10, 15)
177 | denom = Math.pow(10, 12)
178 | label = _labels.trillion
179 | else if abs_float >= Math.pow(10, 15)
180 | denom = Math.pow(10, 15)
181 | label = _labels.quadrillion
182 |
183 | # Process the number into a presentable format
184 | number = float / denom
185 | precise = NumberHelpers.number_with_precision(number,
186 | precision: _precision
187 | significant: _significant
188 | delimiter: if label is 'Quadrillion' then '' else _delimiter
189 | separator: _separator
190 | strip_insignificant_zeros: unless label then true else _strip_insignificant_zeros
191 | )
192 |
193 | #No label needed for less than thousand
194 | if label
195 | return "#{precise}#{_space_label}#{label}"
196 | else
197 | return precise
198 |
199 | @number_to_human_size = (float, opts={}) ->
200 | _precision = opts.precision ? 3
201 | _separator = opts.separator ? '.'
202 | _significant = opts.significant ? true
203 | _delimiter = opts.delimiter ? ','
204 | _strip_insignificant_zeros = opts.strip_insignificant_zeros ? false
205 |
206 | # Power of 10 to Bytes Converter
207 | float = float / 1.024 if float > 1000
208 | float = float / 1.024 if float > 1000000
209 | float = float / 1.024 if float > 1000000000
210 | float = float / 1.024 if float > 1000000000000
211 |
212 | # Remove the sign of the number for easier comparision
213 | abs_float = Math.abs(float)
214 |
215 | # Less than Thousand does not need text or a insignifiant digits
216 | if abs_float < Math.pow(10, 3)
217 | denom = 1
218 | label = 'Bytes'
219 | else if abs_float >= Math.pow(10, 3) and abs_float < Math.pow(10, 6)
220 | denom = Math.pow(10, 3)
221 | label = 'KB'
222 | else if abs_float >= Math.pow(10, 6) and abs_float < Math.pow(10, 9)
223 | denom = Math.pow(10, 6)
224 | label = 'MB'
225 | else if abs_float >= Math.pow(10, 9) and abs_float < Math.pow(10, 12)
226 | denom = Math.pow(10, 9)
227 | label = 'GB'
228 | else if abs_float >= Math.pow(10, 12) and abs_float < Math.pow(10, 15)
229 | denom = Math.pow(10, 12)
230 | label = 'TB'
231 |
232 | # Process the number into a presentable format
233 | number = float / denom
234 |
235 | precise = NumberHelpers.number_with_precision(number,
236 | precision: _precision
237 | significant: _significant
238 | delimiter: _delimiter
239 | separator: _separator
240 | strip_insignificant_zeros: if label is 'Bytes' then true else _strip_insignificant_zeros
241 | )
242 |
243 | return "#{precise} #{label}"
244 |
245 | @number_to_phone = (number, opts={}) ->
246 | _area_code = opts.area_code ? false
247 | _delimiter = opts.delimiter ? '-'
248 | _country_code = opts.country_code ? false
249 | _extension = opts.extension ? false
250 |
251 | # Not a numerical value
252 | return number if isNaN(number)
253 |
254 | str = number.toString()
255 | lng = str.length
256 |
257 | # Last Four Digits
258 | last = str.substr(lng - 4, lng)
259 |
260 | if lng < 8
261 | first = str.substr(0, 3)
262 | else
263 | first = str.substr(0, 3)
264 | second = str.substr(3,3)
265 |
266 | # Area Code
267 | if _area_code
268 | first = "(#{first}) #{second}"
269 | else
270 | first = "#{first}#{_delimiter}#{second}"
271 |
272 | # Extension Code
273 | _extension = if _extension then " x #{opts.extension}" else ''
274 |
275 | # Country Code
276 | _country_code = if _country_code then "+#{_country_code}#{_delimiter}" else ''
277 |
278 | return "#{_country_code}#{first}#{_delimiter}#{last}#{_extension}"
279 |
280 | @number_to_percentage = (float, opts={}) ->
281 | _precision = opts.precision ? 3
282 | _separator = opts.separator ? '.'
283 | _significant = opts.significant ? false
284 | _delimiter = opts.delimiter ? ''
285 | _strip_insignificant_zeros = opts.strip_insignificant_zeros ? false
286 |
287 | unless isNaN(float)
288 | float = NumberHelpers.number_with_precision(float,
289 | precision: _precision
290 | significant: _significant
291 | delimiter: _delimiter
292 | separator: _separator
293 | strip_insignificant_zeros: _strip_insignificant_zeros
294 | )
295 |
296 | return "#{float}%"
297 |
298 | @reconstitute_exponential = (num, exp) ->
299 | if num.indexOf('e') != -1
300 | vals = num.split('e')
301 | num = vals[0]
302 | exp = vals[1]
303 | exp = +exp
304 | num +=''
305 | return num if exp == 0
306 | # format is n.nnnnn or n; decimal implied after first digit
307 | num = num.replace(/\./,'')
308 | numlength = num.length
309 | if exp > 0
310 | if (exp + 1) < numlength
311 | num = num.substr(0,exp+1)+'.'+num.substr(exp+1)
312 | else
313 | # no decimal, but we need added zeros
314 | num = (num + @ZEROS).substr(0,exp+1)
315 | else
316 | #smaller, just move the decimal over
317 | num = "0."+(@ZEROS + num).substr(@ZEROS.length+1+exp)
318 | return num
319 |
320 |
321 |
322 | # Number to rounded handles singificant digits differently than number_with_precision
323 | @number_to_rounded = (float, opts={}) ->
324 | _precision = opts.precision ? 3
325 | _significant = opts.significant ? false
326 | # Be consistent with Ruby implementation
327 | _delimiter = opts.delimiter ? ''
328 | _separator = opts.separator ? '.'
329 | _strip_insignificant_zeros = opts.strip_insignificant_zeros ? false
330 | _skip_empty_fractionals = opts.strip_empty_fractional_parts
331 | # Zero precision with significant is a mistake (would always return zero),
332 | # so we treat it as if significant was false (increases backwards compatibility for number_to_human_size)
333 |
334 | if _significant && !_precision
335 | _significant = false
336 |
337 | rounded = @safe_round(float, _significant, _precision)
338 |
339 | if _significant && _precision
340 | value = rounded.toExponential().split('e');
341 | significant_val = (+value[0]).toFixed(_precision-1)
342 | rounded = @reconstitute_exponential(significant_val,value[1])
343 |
344 | else if _precision
345 | # Break number into inspectable pieces
346 | rounded = rounded.toFixed(_precision)
347 |
348 | rounded +=''
349 | number = rounded.split('.')
350 | integer = number[0]
351 | decimal = number[1] ? ''
352 |
353 | # Delimiter Integer
354 | integer = NumberHelpers.number_with_delimiter(integer, {delimiter: _delimiter})
355 |
356 | # Strip Insignificant Digits
357 | if _strip_insignificant_zeros
358 | dlen = decimal.length
359 | newlen = dlen
360 |
361 | while newlen > 0 and decimal[newlen - 1] == '0'
362 | newlen = newlen - 1
363 |
364 | if newlen == 0
365 | decimal = ''
366 | else if newlen != dlen
367 | decimal = decimal.slice(0, newlen)
368 |
369 | if _skip_empty_fractionals
370 | i = 0; zcount = 0
371 | num_array = decimal.split('')
372 | dlen = decimal.length
373 | while i < dlen
374 | zcount++ if num_array[i] is '0'
375 | i++
376 | if zcount == dlen
377 | decimal = ''
378 |
379 | # Remove separator if no decimal
380 | _separator = '' unless decimal
381 |
382 | return "#{integer}#{_separator}#{decimal}"
383 |
384 |
385 |
386 |
387 | if typeof module isnt 'undefined' and typeof module.exports isnt 'undefined'
388 | module.exports = NumberHelpers
389 | else
390 | window.NumberHelpers = NumberHelpers
391 |
--------------------------------------------------------------------------------
/lib/number_helpers.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.10.0
2 | (function() {
3 | var NumberHelpers;
4 |
5 | NumberHelpers = (function() {
6 | function NumberHelpers() {}
7 |
8 | NumberHelpers.ZEROS = new Array(200).join('0');
9 |
10 | NumberHelpers.number_to_currency = function(float, opts) {
11 | var _delimiter, _precision, _separator, _unit, _unit_pos, delimited, fixedWidth, ref, ref1, ref2, ref3, ref4, result, sign;
12 | if (opts == null) {
13 | opts = {};
14 | }
15 | _precision = (ref = opts.precision) != null ? ref : 2;
16 | _unit = (ref1 = opts.unit) != null ? ref1 : '$';
17 | _separator = (ref2 = opts.separator) != null ? ref2 : '.';
18 | _delimiter = (ref3 = opts.delimiter) != null ? ref3 : ',';
19 | _unit_pos = (ref4 = opts.unit_position) != null ? ref4 : 'start';
20 | sign = '';
21 | if (isNaN(float)) {
22 | result = float;
23 | } else {
24 | float = parseFloat(float);
25 | if (float < 0) {
26 | sign = '-';
27 | }
28 | fixedWidth = Math.abs(float).toFixed(_precision);
29 | delimited = NumberHelpers.number_with_delimiter(fixedWidth, {
30 | delimiter: _delimiter
31 | });
32 | result = delimited.split('.').join(_separator);
33 | }
34 | if (_unit_pos === 'end') {
35 | return "" + sign + result + _unit;
36 | } else {
37 | return "" + sign + _unit + result;
38 | }
39 | };
40 |
41 | NumberHelpers.number_with_delimiter = function(float, opts) {
42 | var _delimiter, _separator, decimal, integer, number, ref, ref1, ref2, rgx;
43 | if (opts == null) {
44 | opts = {};
45 | }
46 | _separator = (ref = opts.separator) != null ? ref : '.';
47 | _delimiter = (ref1 = opts.delimiter) != null ? ref1 : ',';
48 | number = float.toString().split(".");
49 | integer = number[0];
50 | decimal = (ref2 = number[1]) != null ? ref2 : '';
51 | if (!decimal) {
52 | _separator = '';
53 | }
54 | rgx = /(\d+)(\d{3})/;
55 | if (_delimiter) {
56 | while (rgx.test(integer)) {
57 | integer = integer.replace(rgx, "$1" + _delimiter + "$2");
58 | }
59 | }
60 | return "" + integer + _separator + decimal;
61 | };
62 |
63 | NumberHelpers.safe_round = function(float, _significant, _precision) {
64 | var _round, value;
65 | if (!float) {
66 | return float;
67 | }
68 | value = (+float).toExponential().toString().split('e');
69 | _round = _significant ? -1 - value[1] + _precision : _precision;
70 | value = Math.round(+(value[0] + 'e' + (+value[1] + _round)));
71 | value = value.toExponential().toString().split('e');
72 | return value = +(value[0] + 'e' + (+value[1] - _round));
73 | };
74 |
75 | NumberHelpers.number_with_precision = function(float, opts) {
76 | var _delimiter, _precision, _separator, _significant, _skip_empty_fractionals, _strip_insignificant_zeros, decimal, dlen, i, integer, multiple, newlen, num_array, num_lngth, number, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, rnd, rounded, significant, sigs, zcount;
77 | if (opts == null) {
78 | opts = {};
79 | }
80 | _precision = (ref = opts.precision) != null ? ref : 3;
81 | _delimiter = (ref1 = opts.delimiter) != null ? ref1 : ',';
82 | _separator = (ref2 = opts.separator) != null ? ref2 : '.';
83 | _significant = (ref3 = opts.significant) != null ? ref3 : false;
84 | _strip_insignificant_zeros = (ref4 = opts.strip_insignificant_zeros) != null ? ref4 : false;
85 | _skip_empty_fractionals = opts.strip_empty_fractional_parts;
86 | number = float.toString().split('.');
87 | integer = number[0];
88 | decimal = (ref5 = number[1]) != null ? ref5 : '';
89 | if (_significant) {
90 | rnd = _precision - integer.length;
91 | } else {
92 | rnd = _precision;
93 | }
94 | if (rnd < 1) {
95 | rnd = 0;
96 | }
97 | multiple = Math.pow(10, rnd);
98 | if (multiple > 1) {
99 | rounded = this.safe_round(float, _significant, _precision);
100 | } else {
101 | rounded = float;
102 | }
103 | number = rounded.toString().split('.');
104 | integer = number[0];
105 | decimal = (ref6 = number[1]) != null ? ref6 : '';
106 | decimal = parseFloat("0." + decimal).toFixed(_precision);
107 | decimal = decimal.toString().split('.');
108 | decimal = (ref7 = decimal[1]) != null ? ref7 : '';
109 | number = (integer + "." + decimal) * 1;
110 | num_array = number.toString().split('');
111 | num_lngth = num_array.length;
112 | i = 0;
113 | sigs = 0;
114 | while (i < num_lngth) {
115 | if (!(num_array[i] === '.' || num_array[i] === '0')) {
116 | sigs++;
117 | }
118 | i++;
119 | }
120 | if (_significant && sigs >= _precision && _precision > 0) {
121 | significant = number.toPrecision(_precision) * 1;
122 | significant = significant.toString().split('.');
123 | integer = significant[0];
124 | decimal = (ref8 = significant[1]) != null ? ref8 : '';
125 | }
126 | integer = NumberHelpers.number_with_delimiter(integer, {
127 | delimiter: _delimiter
128 | });
129 | if (_strip_insignificant_zeros) {
130 | dlen = decimal.length;
131 | newlen = dlen;
132 | while (newlen > 0 && decimal[newlen - 1] === '0') {
133 | newlen = newlen - 1;
134 | }
135 | if (newlen === 0) {
136 | decimal = '';
137 | } else if (newlen !== dlen) {
138 | decimal = decimal.slice(0, newlen);
139 | }
140 | }
141 | if (_skip_empty_fractionals) {
142 | i = 0;
143 | zcount = 0;
144 | num_array = decimal.split('');
145 | dlen = decimal.length;
146 | while (i < dlen) {
147 | if (num_array[i] === '0') {
148 | zcount++;
149 | }
150 | i++;
151 | }
152 | if (zcount === dlen) {
153 | decimal = '';
154 | }
155 | }
156 | if (!decimal) {
157 | _separator = '';
158 | }
159 | return "" + integer + _separator + decimal;
160 | };
161 |
162 | NumberHelpers.number_to_human = function(float, opts) {
163 | var _delimiter, _labels, _precision, _separator, _significant, _space_label, _strip_insignificant_zeros, abs_float, denom, label, number, precise, ref, ref1, ref10, ref11, ref12, ref13, ref14, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9;
164 | if (opts == null) {
165 | opts = {};
166 | }
167 | _precision = (ref = opts.precision) != null ? ref : 3;
168 | _separator = (ref1 = opts.separator) != null ? ref1 : '.';
169 | _significant = (ref2 = opts.significant) != null ? ref2 : true;
170 | _delimiter = (ref3 = opts.delimiter) != null ? ref3 : ',';
171 | _strip_insignificant_zeros = (ref4 = opts.strip_insignificant_zeros) != null ? ref4 : false;
172 | _space_label = opts.space_label === false ? '' : ' ';
173 | _labels = {
174 | thousand: (ref5 = (ref6 = opts.labels) != null ? ref6.thousand : void 0) != null ? ref5 : 'Thousand',
175 | million: (ref7 = (ref8 = opts.labels) != null ? ref8.million : void 0) != null ? ref7 : 'Million',
176 | billion: (ref9 = (ref10 = opts.labels) != null ? ref10.billion : void 0) != null ? ref9 : 'Billion',
177 | trillion: (ref11 = (ref12 = opts.labels) != null ? ref12.trillion : void 0) != null ? ref11 : 'Trillion',
178 | quadrillion: (ref13 = (ref14 = opts.labels) != null ? ref14.quadrillion : void 0) != null ? ref13 : 'Quadrillion'
179 | };
180 | abs_float = Math.abs(float);
181 | if (abs_float < Math.pow(10, 3)) {
182 | denom = 1;
183 | label = false;
184 | } else if (abs_float >= Math.pow(10, 3) && abs_float < Math.pow(10, 6)) {
185 | denom = Math.pow(10, 3);
186 | label = _labels.thousand;
187 | } else if (abs_float >= Math.pow(10, 6) && abs_float < Math.pow(10, 9)) {
188 | denom = Math.pow(10, 6);
189 | label = _labels.million;
190 | } else if (abs_float >= Math.pow(10, 9) && abs_float < Math.pow(10, 12)) {
191 | denom = Math.pow(10, 9);
192 | label = _labels.billion;
193 | } else if (abs_float >= Math.pow(10, 12) && abs_float < Math.pow(10, 15)) {
194 | denom = Math.pow(10, 12);
195 | label = _labels.trillion;
196 | } else if (abs_float >= Math.pow(10, 15)) {
197 | denom = Math.pow(10, 15);
198 | label = _labels.quadrillion;
199 | }
200 | number = float / denom;
201 | precise = NumberHelpers.number_with_precision(number, {
202 | precision: _precision,
203 | significant: _significant,
204 | delimiter: label === 'Quadrillion' ? '' : _delimiter,
205 | separator: _separator,
206 | strip_insignificant_zeros: !label ? true : _strip_insignificant_zeros
207 | });
208 | if (label) {
209 | return "" + precise + _space_label + label;
210 | } else {
211 | return precise;
212 | }
213 | };
214 |
215 | NumberHelpers.number_to_human_size = function(float, opts) {
216 | var _delimiter, _precision, _separator, _significant, _strip_insignificant_zeros, abs_float, denom, label, number, precise, ref, ref1, ref2, ref3, ref4;
217 | if (opts == null) {
218 | opts = {};
219 | }
220 | _precision = (ref = opts.precision) != null ? ref : 3;
221 | _separator = (ref1 = opts.separator) != null ? ref1 : '.';
222 | _significant = (ref2 = opts.significant) != null ? ref2 : true;
223 | _delimiter = (ref3 = opts.delimiter) != null ? ref3 : ',';
224 | _strip_insignificant_zeros = (ref4 = opts.strip_insignificant_zeros) != null ? ref4 : false;
225 | if (float > 1000) {
226 | float = float / 1.024;
227 | }
228 | if (float > 1000000) {
229 | float = float / 1.024;
230 | }
231 | if (float > 1000000000) {
232 | float = float / 1.024;
233 | }
234 | if (float > 1000000000000) {
235 | float = float / 1.024;
236 | }
237 | abs_float = Math.abs(float);
238 | if (abs_float < Math.pow(10, 3)) {
239 | denom = 1;
240 | label = 'Bytes';
241 | } else if (abs_float >= Math.pow(10, 3) && abs_float < Math.pow(10, 6)) {
242 | denom = Math.pow(10, 3);
243 | label = 'KB';
244 | } else if (abs_float >= Math.pow(10, 6) && abs_float < Math.pow(10, 9)) {
245 | denom = Math.pow(10, 6);
246 | label = 'MB';
247 | } else if (abs_float >= Math.pow(10, 9) && abs_float < Math.pow(10, 12)) {
248 | denom = Math.pow(10, 9);
249 | label = 'GB';
250 | } else if (abs_float >= Math.pow(10, 12) && abs_float < Math.pow(10, 15)) {
251 | denom = Math.pow(10, 12);
252 | label = 'TB';
253 | }
254 | number = float / denom;
255 | precise = NumberHelpers.number_with_precision(number, {
256 | precision: _precision,
257 | significant: _significant,
258 | delimiter: _delimiter,
259 | separator: _separator,
260 | strip_insignificant_zeros: label === 'Bytes' ? true : _strip_insignificant_zeros
261 | });
262 | return precise + " " + label;
263 | };
264 |
265 | NumberHelpers.number_to_phone = function(number, opts) {
266 | var _area_code, _country_code, _delimiter, _extension, first, last, lng, ref, ref1, ref2, ref3, second, str;
267 | if (opts == null) {
268 | opts = {};
269 | }
270 | _area_code = (ref = opts.area_code) != null ? ref : false;
271 | _delimiter = (ref1 = opts.delimiter) != null ? ref1 : '-';
272 | _country_code = (ref2 = opts.country_code) != null ? ref2 : false;
273 | _extension = (ref3 = opts.extension) != null ? ref3 : false;
274 | if (isNaN(number)) {
275 | return number;
276 | }
277 | str = number.toString();
278 | lng = str.length;
279 | last = str.substr(lng - 4, lng);
280 | if (lng < 8) {
281 | first = str.substr(0, 3);
282 | } else {
283 | first = str.substr(0, 3);
284 | second = str.substr(3, 3);
285 | if (_area_code) {
286 | first = "(" + first + ") " + second;
287 | } else {
288 | first = "" + first + _delimiter + second;
289 | }
290 | }
291 | _extension = _extension ? " x " + opts.extension : '';
292 | _country_code = _country_code ? "+" + _country_code + _delimiter : '';
293 | return "" + _country_code + first + _delimiter + last + _extension;
294 | };
295 |
296 | NumberHelpers.number_to_percentage = function(float, opts) {
297 | var _delimiter, _precision, _separator, _significant, _strip_insignificant_zeros, ref, ref1, ref2, ref3, ref4;
298 | if (opts == null) {
299 | opts = {};
300 | }
301 | _precision = (ref = opts.precision) != null ? ref : 3;
302 | _separator = (ref1 = opts.separator) != null ? ref1 : '.';
303 | _significant = (ref2 = opts.significant) != null ? ref2 : false;
304 | _delimiter = (ref3 = opts.delimiter) != null ? ref3 : '';
305 | _strip_insignificant_zeros = (ref4 = opts.strip_insignificant_zeros) != null ? ref4 : false;
306 | if (!isNaN(float)) {
307 | float = NumberHelpers.number_with_precision(float, {
308 | precision: _precision,
309 | significant: _significant,
310 | delimiter: _delimiter,
311 | separator: _separator,
312 | strip_insignificant_zeros: _strip_insignificant_zeros
313 | });
314 | }
315 | return float + "%";
316 | };
317 |
318 | NumberHelpers.reconstitute_exponential = function(num, exp) {
319 | var numlength, vals;
320 | if (num.indexOf('e') !== -1) {
321 | vals = num.split('e');
322 | num = vals[0];
323 | exp = vals[1];
324 | }
325 | exp = +exp;
326 | num += '';
327 | if (exp === 0) {
328 | return num;
329 | }
330 | num = num.replace(/\./, '');
331 | numlength = num.length;
332 | if (exp > 0) {
333 | if ((exp + 1) < numlength) {
334 | num = num.substr(0, exp + 1) + '.' + num.substr(exp + 1);
335 | } else {
336 | num = (num + this.ZEROS).substr(0, exp + 1);
337 | }
338 | } else {
339 | num = "0." + (this.ZEROS + num).substr(this.ZEROS.length + 1 + exp);
340 | }
341 | return num;
342 | };
343 |
344 | NumberHelpers.number_to_rounded = function(float, opts) {
345 | var _delimiter, _precision, _separator, _significant, _skip_empty_fractionals, _strip_insignificant_zeros, decimal, dlen, i, integer, newlen, num_array, number, ref, ref1, ref2, ref3, ref4, ref5, rounded, significant_val, value, zcount;
346 | if (opts == null) {
347 | opts = {};
348 | }
349 | _precision = (ref = opts.precision) != null ? ref : 3;
350 | _significant = (ref1 = opts.significant) != null ? ref1 : false;
351 | _delimiter = (ref2 = opts.delimiter) != null ? ref2 : '';
352 | _separator = (ref3 = opts.separator) != null ? ref3 : '.';
353 | _strip_insignificant_zeros = (ref4 = opts.strip_insignificant_zeros) != null ? ref4 : false;
354 | _skip_empty_fractionals = opts.strip_empty_fractional_parts;
355 | if (_significant && !_precision) {
356 | _significant = false;
357 | }
358 | rounded = this.safe_round(float, _significant, _precision);
359 | if (_significant && _precision) {
360 | value = rounded.toExponential().split('e');
361 | significant_val = (+value[0]).toFixed(_precision - 1);
362 | rounded = this.reconstitute_exponential(significant_val, value[1]);
363 | } else if (_precision) {
364 | rounded = rounded.toFixed(_precision);
365 | }
366 | rounded += '';
367 | number = rounded.split('.');
368 | integer = number[0];
369 | decimal = (ref5 = number[1]) != null ? ref5 : '';
370 | integer = NumberHelpers.number_with_delimiter(integer, {
371 | delimiter: _delimiter
372 | });
373 | if (_strip_insignificant_zeros) {
374 | dlen = decimal.length;
375 | newlen = dlen;
376 | while (newlen > 0 && decimal[newlen - 1] === '0') {
377 | newlen = newlen - 1;
378 | }
379 | if (newlen === 0) {
380 | decimal = '';
381 | } else if (newlen !== dlen) {
382 | decimal = decimal.slice(0, newlen);
383 | }
384 | }
385 | if (_skip_empty_fractionals) {
386 | i = 0;
387 | zcount = 0;
388 | num_array = decimal.split('');
389 | dlen = decimal.length;
390 | while (i < dlen) {
391 | if (num_array[i] === '0') {
392 | zcount++;
393 | }
394 | i++;
395 | }
396 | if (zcount === dlen) {
397 | decimal = '';
398 | }
399 | }
400 | if (!decimal) {
401 | _separator = '';
402 | }
403 | return "" + integer + _separator + decimal;
404 | };
405 |
406 | return NumberHelpers;
407 |
408 | })();
409 |
410 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
411 | module.exports = NumberHelpers;
412 | } else {
413 | window.NumberHelpers = NumberHelpers;
414 | }
415 |
416 | }).call(this);
417 |
--------------------------------------------------------------------------------
/jasmine-core/json2.js:
--------------------------------------------------------------------------------
1 | /*
2 | http://www.JSON.org/json2.js
3 | 2009-08-17
4 |
5 | Public Domain.
6 |
7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8 |
9 | See http://www.JSON.org/js.html
10 |
11 | This file creates a global JSON object containing two methods: stringify
12 | and parse.
13 |
14 | JSON.stringify(value, replacer, space)
15 | value any JavaScript value, usually an object or array.
16 |
17 | replacer an optional parameter that determines how object
18 | values are stringified for objects. It can be a
19 | function or an array of strings.
20 |
21 | space an optional parameter that specifies the indentation
22 | of nested structures. If it is omitted, the text will
23 | be packed without extra whitespace. If it is a number,
24 | it will specify the number of spaces to indent at each
25 | level. If it is a string (such as '\t' or ' '),
26 | it contains the characters used to indent at each level.
27 |
28 | This method produces a JSON text from a JavaScript value.
29 |
30 | When an object value is found, if the object contains a toJSON
31 | method, its toJSON method will be called and the result will be
32 | stringified. A toJSON method does not serialize: it returns the
33 | value represented by the name/value pair that should be serialized,
34 | or undefined if nothing should be serialized. The toJSON method
35 | will be passed the key associated with the value, and this will be
36 | bound to the value
37 |
38 | For example, this would serialize Dates as ISO strings.
39 |
40 | Date.prototype.toJSON = function (key) {
41 | function f(n) {
42 | // Format integers to have at least two digits.
43 | return n < 10 ? '0' + n : n;
44 | }
45 |
46 | return this.getUTCFullYear() + '-' +
47 | f(this.getUTCMonth() + 1) + '-' +
48 | f(this.getUTCDate()) + 'T' +
49 | f(this.getUTCHours()) + ':' +
50 | f(this.getUTCMinutes()) + ':' +
51 | f(this.getUTCSeconds()) + 'Z';
52 | };
53 |
54 | You can provide an optional replacer method. It will be passed the
55 | key and value of each member, with this bound to the containing
56 | object. The value that is returned from your method will be
57 | serialized. If your method returns undefined, then the member will
58 | be excluded from the serialization.
59 |
60 | If the replacer parameter is an array of strings, then it will be
61 | used to select the members to be serialized. It filters the results
62 | such that only members with keys listed in the replacer array are
63 | stringified.
64 |
65 | Values that do not have JSON representations, such as undefined or
66 | functions, will not be serialized. Such values in objects will be
67 | dropped; in arrays they will be replaced with null. You can use
68 | a replacer function to replace those with JSON values.
69 | JSON.stringify(undefined) returns undefined.
70 |
71 | The optional space parameter produces a stringification of the
72 | value that is filled with line breaks and indentation to make it
73 | easier to read.
74 |
75 | If the space parameter is a non-empty string, then that string will
76 | be used for indentation. If the space parameter is a number, then
77 | the indentation will be that many spaces.
78 |
79 | Example:
80 |
81 | text = JSON.stringify(['e', {pluribus: 'unum'}]);
82 | // text is '["e",{"pluribus":"unum"}]'
83 |
84 |
85 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87 |
88 | text = JSON.stringify([new Date()], function (key, value) {
89 | return this[key] instanceof Date ?
90 | 'Date(' + this[key] + ')' : value;
91 | });
92 | // text is '["Date(---current time---)"]'
93 |
94 |
95 | JSON.parse(text, reviver)
96 | This method parses a JSON text to produce an object or array.
97 | It can throw a SyntaxError exception.
98 |
99 | The optional reviver parameter is a function that can filter and
100 | transform the results. It receives each of the keys and values,
101 | and its return value is used instead of the original value.
102 | If it returns what it received, then the structure is not modified.
103 | If it returns undefined then the member is deleted.
104 |
105 | Example:
106 |
107 | // Parse the text. Values that look like ISO date strings will
108 | // be converted to Date objects.
109 |
110 | myData = JSON.parse(text, function (key, value) {
111 | var a;
112 | if (typeof value === 'string') {
113 | a =
114 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115 | if (a) {
116 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117 | +a[5], +a[6]));
118 | }
119 | }
120 | return value;
121 | });
122 |
123 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124 | var d;
125 | if (typeof value === 'string' &&
126 | value.slice(0, 5) === 'Date(' &&
127 | value.slice(-1) === ')') {
128 | d = new Date(value.slice(5, -1));
129 | if (d) {
130 | return d;
131 | }
132 | }
133 | return value;
134 | });
135 |
136 |
137 | This is a reference implementation. You are free to copy, modify, or
138 | redistribute.
139 |
140 | This code should be minified before deployment.
141 | See http://javascript.crockford.com/jsmin.html
142 |
143 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144 | NOT CONTROL.
145 | */
146 |
147 | /*jslint evil: true */
148 |
149 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152 | lastIndex, length, parse, prototype, push, replace, slice, stringify,
153 | test, toJSON, toString, valueOf
154 | */
155 |
156 | "use strict";
157 |
158 | // Create a JSON object only if one does not already exist. We create the
159 | // methods in a closure to avoid creating global variables.
160 |
161 | if (!this.JSON) {
162 | this.JSON = {};
163 | }
164 |
165 | (function () {
166 |
167 | function f(n) {
168 | // Format integers to have at least two digits.
169 | return n < 10 ? '0' + n : n;
170 | }
171 |
172 | if (typeof Date.prototype.toJSON !== 'function') {
173 |
174 | Date.prototype.toJSON = function (key) {
175 |
176 | return isFinite(this.valueOf()) ?
177 | this.getUTCFullYear() + '-' +
178 | f(this.getUTCMonth() + 1) + '-' +
179 | f(this.getUTCDate()) + 'T' +
180 | f(this.getUTCHours()) + ':' +
181 | f(this.getUTCMinutes()) + ':' +
182 | f(this.getUTCSeconds()) + 'Z' : null;
183 | };
184 |
185 | String.prototype.toJSON =
186 | Number.prototype.toJSON =
187 | Boolean.prototype.toJSON = function (key) {
188 | return this.valueOf();
189 | };
190 | }
191 |
192 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
193 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
194 | gap,
195 | indent,
196 | meta = { // table of character substitutions
197 | '\b': '\\b',
198 | '\t': '\\t',
199 | '\n': '\\n',
200 | '\f': '\\f',
201 | '\r': '\\r',
202 | '"' : '\\"',
203 | '\\': '\\\\'
204 | },
205 | rep;
206 |
207 |
208 | function quote(string) {
209 |
210 | // If the string contains no control characters, no quote characters, and no
211 | // backslash characters, then we can safely slap some quotes around it.
212 | // Otherwise we must also replace the offending characters with safe escape
213 | // sequences.
214 |
215 | escapable.lastIndex = 0;
216 | return escapable.test(string) ?
217 | '"' + string.replace(escapable, function (a) {
218 | var c = meta[a];
219 | return typeof c === 'string' ? c :
220 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
221 | }) + '"' :
222 | '"' + string + '"';
223 | }
224 |
225 |
226 | function str(key, holder) {
227 | // Produce a string from holder[key].
228 |
229 | var i, // The loop counter.
230 | k, // The member key.
231 | v, // The member value.
232 | length,
233 | mind = gap,
234 | partial,
235 | value = holder[key];
236 |
237 | // If the value has a toJSON method, call it to obtain a replacement value.
238 |
239 | if (value && typeof value === 'object' &&
240 | typeof value.toJSON === 'function') {
241 | value = value.toJSON(key);
242 | }
243 |
244 | // If we were called with a replacer function, then call the replacer to
245 | // obtain a replacement value.
246 |
247 | if (typeof rep === 'function') {
248 | value = rep.call(holder, key, value);
249 | }
250 |
251 | // What happens next depends on the value's type.
252 |
253 | switch (typeof value) {
254 | case 'string':
255 | return quote(value);
256 |
257 | case 'number':
258 |
259 | // JSON numbers must be finite. Encode non-finite numbers as null.
260 |
261 | return isFinite(value) ? String(value) : 'null';
262 |
263 | case 'boolean':
264 | case 'null':
265 |
266 | // If the value is a boolean or null, convert it to a string. Note:
267 | // typeof null does not produce 'null'. The case is included here in
268 | // the remote chance that this gets fixed someday.
269 |
270 | return String(value);
271 |
272 | // If the type is 'object', we might be dealing with an object or an array or
273 | // null.
274 |
275 | case 'object':
276 |
277 | // Due to a specification blunder in ECMAScript, typeof null is 'object',
278 | // so watch out for that case.
279 |
280 | if (!value) {
281 | return 'null';
282 | }
283 |
284 | // Make an array to hold the partial results of stringifying this object value.
285 |
286 | gap += indent;
287 | partial = [];
288 |
289 | // Is the value an array?
290 |
291 | if (Object.prototype.toString.apply(value) === '[object Array]') {
292 |
293 | // The value is an array. Stringify every element. Use null as a placeholder
294 | // for non-JSON values.
295 |
296 | length = value.length;
297 | for (i = 0; i < length; i += 1) {
298 | partial[i] = str(i, value) || 'null';
299 | }
300 |
301 | // Join all of the elements together, separated with commas, and wrap them in
302 | // brackets.
303 |
304 | v = partial.length === 0 ? '[]' :
305 | gap ? '[\n' + gap +
306 | partial.join(',\n' + gap) + '\n' +
307 | mind + ']' :
308 | '[' + partial.join(',') + ']';
309 | gap = mind;
310 | return v;
311 | }
312 |
313 | // If the replacer is an array, use it to select the members to be stringified.
314 |
315 | if (rep && typeof rep === 'object') {
316 | length = rep.length;
317 | for (i = 0; i < length; i += 1) {
318 | k = rep[i];
319 | if (typeof k === 'string') {
320 | v = str(k, value);
321 | if (v) {
322 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
323 | }
324 | }
325 | }
326 | } else {
327 |
328 | // Otherwise, iterate through all of the keys in the object.
329 |
330 | for (k in value) {
331 | if (Object.hasOwnProperty.call(value, k)) {
332 | v = str(k, value);
333 | if (v) {
334 | partial.push(quote(k) + (gap ? ': ' : ':') + v);
335 | }
336 | }
337 | }
338 | }
339 |
340 | // Join all of the member texts together, separated with commas,
341 | // and wrap them in braces.
342 |
343 | v = partial.length === 0 ? '{}' :
344 | gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
345 | mind + '}' : '{' + partial.join(',') + '}';
346 | gap = mind;
347 | return v;
348 | }
349 | }
350 |
351 | // If the JSON object does not yet have a stringify method, give it one.
352 |
353 | if (typeof JSON.stringify !== 'function') {
354 | JSON.stringify = function (value, replacer, space) {
355 | // The stringify method takes a value and an optional replacer, and an optional
356 | // space parameter, and returns a JSON text. The replacer can be a function
357 | // that can replace values, or an array of strings that will select the keys.
358 | // A default replacer method can be provided. Use of the space parameter can
359 | // produce text that is more easily readable.
360 |
361 | var i;
362 | gap = '';
363 | indent = '';
364 |
365 | // If the space parameter is a number, make an indent string containing that
366 | // many spaces.
367 |
368 | if (typeof space === 'number') {
369 | for (i = 0; i < space; i += 1) {
370 | indent += ' ';
371 | }
372 |
373 | // If the space parameter is a string, it will be used as the indent string.
374 |
375 | } else if (typeof space === 'string') {
376 | indent = space;
377 | }
378 |
379 | // If there is a replacer, it must be a function or an array.
380 | // Otherwise, throw an error.
381 |
382 | rep = replacer;
383 | if (replacer && typeof replacer !== 'function' &&
384 | (typeof replacer !== 'object' ||
385 | typeof replacer.length !== 'number')) {
386 | throw new Error('JSON.stringify');
387 | }
388 |
389 | // Make a fake root object containing our value under the key of ''.
390 | // Return the result of stringifying the value.
391 |
392 | return str('', {'': value});
393 | };
394 | }
395 |
396 |
397 | // If the JSON object does not yet have a parse method, give it one.
398 |
399 | if (typeof JSON.parse !== 'function') {
400 | JSON.parse = function (text, reviver) {
401 |
402 | // The parse method takes a text and an optional reviver function, and returns
403 | // a JavaScript value if the text is a valid JSON text.
404 |
405 | var j;
406 |
407 | function walk(holder, key) {
408 |
409 | // The walk method is used to recursively walk the resulting structure so
410 | // that modifications can be made.
411 |
412 | var k, v, value = holder[key];
413 | if (value && typeof value === 'object') {
414 | for (k in value) {
415 | if (Object.hasOwnProperty.call(value, k)) {
416 | v = walk(value, k);
417 | if (v !== undefined) {
418 | value[k] = v;
419 | } else {
420 | delete value[k];
421 | }
422 | }
423 | }
424 | }
425 | return reviver.call(holder, key, value);
426 | }
427 |
428 |
429 | // Parsing happens in four stages. In the first stage, we replace certain
430 | // Unicode characters with escape sequences. JavaScript handles many characters
431 | // incorrectly, either silently deleting them, or treating them as line endings.
432 |
433 | cx.lastIndex = 0;
434 | if (cx.test(text)) {
435 | text = text.replace(cx, function (a) {
436 | return '\\u' +
437 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
438 | });
439 | }
440 |
441 | // In the second stage, we run the text against regular expressions that look
442 | // for non-JSON patterns. We are especially concerned with '()' and 'new'
443 | // because they can cause invocation, and '=' because it can cause mutation.
444 | // But just to be safe, we want to reject all unexpected forms.
445 |
446 | // We split the second stage into 4 regexp operations in order to work around
447 | // crippling inefficiencies in IE's and Safari's regexp engines. First we
448 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
449 | // replace all simple value tokens with ']' characters. Third, we delete all
450 | // open brackets that follow a colon or comma or that begin the text. Finally,
451 | // we look to see that the remaining characters are only whitespace or ']' or
452 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
453 |
454 | if (/^[\],:{}\s]*$/.
455 | test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
456 | replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
457 | replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
458 |
459 | // In the third stage we use the eval function to compile the text into a
460 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
461 | // in JavaScript: it can begin a block or an object literal. We wrap the text
462 | // in parens to eliminate the ambiguity.
463 |
464 | j = eval('(' + text + ')');
465 |
466 | // In the optional fourth stage, we recursively walk the new structure, passing
467 | // each name/value pair to a reviver function for possible transformation.
468 |
469 | return typeof reviver === 'function' ?
470 | walk({'': j}, '') : j;
471 | }
472 |
473 | // If the text is not JSON parseable, then a SyntaxError is thrown.
474 |
475 | throw new SyntaxError('JSON.parse');
476 | };
477 | }
478 | }());
479 |
--------------------------------------------------------------------------------
/jasmine-core/jasmine-html.js:
--------------------------------------------------------------------------------
1 | jasmine.HtmlReporterHelpers = {};
2 |
3 | jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
4 | var el = document.createElement(type);
5 |
6 | for (var i = 2; i < arguments.length; i++) {
7 | var child = arguments[i];
8 |
9 | if (typeof child === 'string') {
10 | el.appendChild(document.createTextNode(child));
11 | } else {
12 | if (child) {
13 | el.appendChild(child);
14 | }
15 | }
16 | }
17 |
18 | for (var attr in attrs) {
19 | if (attr == "className") {
20 | el[attr] = attrs[attr];
21 | } else {
22 | el.setAttribute(attr, attrs[attr]);
23 | }
24 | }
25 |
26 | return el;
27 | };
28 |
29 | jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
30 | var results = child.results();
31 | var status = results.passed() ? 'passed' : 'failed';
32 | if (results.skipped) {
33 | status = 'skipped';
34 | }
35 |
36 | return status;
37 | };
38 |
39 | jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
40 | var parentDiv = this.dom.summary;
41 | var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
42 | var parent = child[parentSuite];
43 |
44 | if (parent) {
45 | if (typeof this.views.suites[parent.id] == 'undefined') {
46 | this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
47 | }
48 | parentDiv = this.views.suites[parent.id].element;
49 | }
50 |
51 | parentDiv.appendChild(childElement);
52 | };
53 |
54 |
55 | jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
56 | for(var fn in jasmine.HtmlReporterHelpers) {
57 | ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
58 | }
59 | };
60 |
61 | jasmine.HtmlReporter = function(_doc) {
62 | var self = this;
63 | var doc = _doc || window.document;
64 |
65 | var reporterView;
66 |
67 | var dom = {};
68 |
69 | // Jasmine Reporter Public Interface
70 | self.logRunningSpecs = false;
71 |
72 | self.reportRunnerStarting = function(runner) {
73 | var specs = runner.specs() || [];
74 |
75 | if (specs.length == 0) {
76 | return;
77 | }
78 |
79 | createReporterDom(runner.env.versionString());
80 | doc.body.appendChild(dom.reporter);
81 | setExceptionHandling();
82 |
83 | reporterView = new jasmine.HtmlReporter.ReporterView(dom);
84 | reporterView.addSpecs(specs, self.specFilter);
85 | };
86 |
87 | self.reportRunnerResults = function(runner) {
88 | reporterView && reporterView.complete();
89 | };
90 |
91 | self.reportSuiteResults = function(suite) {
92 | reporterView.suiteComplete(suite);
93 | };
94 |
95 | self.reportSpecStarting = function(spec) {
96 | if (self.logRunningSpecs) {
97 | self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
98 | }
99 | };
100 |
101 | self.reportSpecResults = function(spec) {
102 | reporterView.specComplete(spec);
103 | };
104 |
105 | self.log = function() {
106 | var console = jasmine.getGlobal().console;
107 | if (console && console.log) {
108 | if (console.log.apply) {
109 | console.log.apply(console, arguments);
110 | } else {
111 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
112 | }
113 | }
114 | };
115 |
116 | self.specFilter = function(spec) {
117 | if (!focusedSpecName()) {
118 | return true;
119 | }
120 |
121 | return spec.getFullName().indexOf(focusedSpecName()) === 0;
122 | };
123 |
124 | return self;
125 |
126 | function focusedSpecName() {
127 | var specName;
128 |
129 | (function memoizeFocusedSpec() {
130 | if (specName) {
131 | return;
132 | }
133 |
134 | var paramMap = [];
135 | var params = jasmine.HtmlReporter.parameters(doc);
136 |
137 | for (var i = 0; i < params.length; i++) {
138 | var p = params[i].split('=');
139 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
140 | }
141 |
142 | specName = paramMap.spec;
143 | })();
144 |
145 | return specName;
146 | }
147 |
148 | function createReporterDom(version) {
149 | dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
150 | dom.banner = self.createDom('div', { className: 'banner' },
151 | self.createDom('span', { className: 'title' }, "Jasmine "),
152 | self.createDom('span', { className: 'version' }, version)),
153 |
154 | dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
155 | dom.alert = self.createDom('div', {className: 'alert'},
156 | self.createDom('span', { className: 'exceptions' },
157 | self.createDom('label', { className: 'label', for: 'no_try_catch' }, 'No try/catch'),
158 | self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
159 | dom.results = self.createDom('div', {className: 'results'},
160 | dom.summary = self.createDom('div', { className: 'summary' }),
161 | dom.details = self.createDom('div', { id: 'details' }))
162 | );
163 | }
164 |
165 | function noTryCatch() {
166 | return window.location.search.match(/catch=false/);
167 | }
168 |
169 | function searchWithCatch() {
170 | var params = jasmine.HtmlReporter.parameters(window.document);
171 | var removed = false;
172 | var i = 0;
173 |
174 | while (!removed && i < params.length) {
175 | if (params[i].match(/catch=/)) {
176 | params.splice(i, 1);
177 | removed = true;
178 | }
179 | i++;
180 | }
181 | if (jasmine.CATCH_EXCEPTIONS) {
182 | params.push("catch=false");
183 | }
184 |
185 | return params.join("&");
186 | }
187 |
188 | function setExceptionHandling() {
189 | var chxCatch = document.getElementById('no_try_catch');
190 |
191 | if (noTryCatch()) {
192 | chxCatch.setAttribute('checked', true);
193 | jasmine.CATCH_EXCEPTIONS = false;
194 | }
195 | chxCatch.onclick = function() {
196 | window.location.search = searchWithCatch();
197 | };
198 | }
199 | };
200 | jasmine.HtmlReporter.parameters = function(doc) {
201 | var paramStr = doc.location.search.substring(1);
202 | var params = [];
203 |
204 | if (paramStr.length > 0) {
205 | params = paramStr.split('&');
206 | }
207 | return params;
208 | }
209 | jasmine.HtmlReporter.sectionLink = function(sectionName) {
210 | var link = '?';
211 | var params = [];
212 |
213 | if (sectionName) {
214 | params.push('spec=' + encodeURIComponent(sectionName));
215 | }
216 | if (!jasmine.CATCH_EXCEPTIONS) {
217 | params.push("catch=false");
218 | }
219 | if (params.length > 0) {
220 | link += params.join("&");
221 | }
222 |
223 | return link;
224 | };
225 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
226 | jasmine.HtmlReporter.ReporterView = function(dom) {
227 | this.startedAt = new Date();
228 | this.runningSpecCount = 0;
229 | this.completeSpecCount = 0;
230 | this.passedCount = 0;
231 | this.failedCount = 0;
232 | this.skippedCount = 0;
233 |
234 | this.createResultsMenu = function() {
235 | this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
236 | this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
237 | ' | ',
238 | this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
239 |
240 | this.summaryMenuItem.onclick = function() {
241 | dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
242 | };
243 |
244 | this.detailsMenuItem.onclick = function() {
245 | showDetails();
246 | };
247 | };
248 |
249 | this.addSpecs = function(specs, specFilter) {
250 | this.totalSpecCount = specs.length;
251 |
252 | this.views = {
253 | specs: {},
254 | suites: {}
255 | };
256 |
257 | for (var i = 0; i < specs.length; i++) {
258 | var spec = specs[i];
259 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
260 | if (specFilter(spec)) {
261 | this.runningSpecCount++;
262 | }
263 | }
264 | };
265 |
266 | this.specComplete = function(spec) {
267 | this.completeSpecCount++;
268 |
269 | if (isUndefined(this.views.specs[spec.id])) {
270 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
271 | }
272 |
273 | var specView = this.views.specs[spec.id];
274 |
275 | switch (specView.status()) {
276 | case 'passed':
277 | this.passedCount++;
278 | break;
279 |
280 | case 'failed':
281 | this.failedCount++;
282 | break;
283 |
284 | case 'skipped':
285 | this.skippedCount++;
286 | break;
287 | }
288 |
289 | specView.refresh();
290 | this.refresh();
291 | };
292 |
293 | this.suiteComplete = function(suite) {
294 | var suiteView = this.views.suites[suite.id];
295 | if (isUndefined(suiteView)) {
296 | return;
297 | }
298 | suiteView.refresh();
299 | };
300 |
301 | this.refresh = function() {
302 |
303 | if (isUndefined(this.resultsMenu)) {
304 | this.createResultsMenu();
305 | }
306 |
307 | // currently running UI
308 | if (isUndefined(this.runningAlert)) {
309 | this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
310 | dom.alert.appendChild(this.runningAlert);
311 | }
312 | this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
313 |
314 | // skipped specs UI
315 | if (isUndefined(this.skippedAlert)) {
316 | this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
317 | }
318 |
319 | this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
320 |
321 | if (this.skippedCount === 1 && isDefined(dom.alert)) {
322 | dom.alert.appendChild(this.skippedAlert);
323 | }
324 |
325 | // passing specs UI
326 | if (isUndefined(this.passedAlert)) {
327 | this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
328 | }
329 | this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
330 |
331 | // failing specs UI
332 | if (isUndefined(this.failedAlert)) {
333 | this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
334 | }
335 | this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
336 |
337 | if (this.failedCount === 1 && isDefined(dom.alert)) {
338 | dom.alert.appendChild(this.failedAlert);
339 | dom.alert.appendChild(this.resultsMenu);
340 | }
341 |
342 | // summary info
343 | this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
344 | this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
345 | };
346 |
347 | this.complete = function() {
348 | dom.alert.removeChild(this.runningAlert);
349 |
350 | this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
351 |
352 | if (this.failedCount === 0) {
353 | dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
354 | } else {
355 | showDetails();
356 | }
357 |
358 | dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
359 | };
360 |
361 | return this;
362 |
363 | function showDetails() {
364 | if (dom.reporter.className.search(/showDetails/) === -1) {
365 | dom.reporter.className += " showDetails";
366 | }
367 | }
368 |
369 | function isUndefined(obj) {
370 | return typeof obj === 'undefined';
371 | }
372 |
373 | function isDefined(obj) {
374 | return !isUndefined(obj);
375 | }
376 |
377 | function specPluralizedFor(count) {
378 | var str = count + " spec";
379 | if (count > 1) {
380 | str += "s"
381 | }
382 | return str;
383 | }
384 |
385 | };
386 |
387 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
388 |
389 |
390 | jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
391 | this.spec = spec;
392 | this.dom = dom;
393 | this.views = views;
394 |
395 | this.symbol = this.createDom('li', { className: 'pending' });
396 | this.dom.symbolSummary.appendChild(this.symbol);
397 |
398 | this.summary = this.createDom('div', { className: 'specSummary' },
399 | this.createDom('a', {
400 | className: 'description',
401 | href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
402 | title: this.spec.getFullName()
403 | }, this.spec.description)
404 | );
405 |
406 | this.detail = this.createDom('div', { className: 'specDetail' },
407 | this.createDom('a', {
408 | className: 'description',
409 | href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
410 | title: this.spec.getFullName()
411 | }, this.spec.getFullName())
412 | );
413 | };
414 |
415 | jasmine.HtmlReporter.SpecView.prototype.status = function() {
416 | return this.getSpecStatus(this.spec);
417 | };
418 |
419 | jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
420 | this.symbol.className = this.status();
421 |
422 | switch (this.status()) {
423 | case 'skipped':
424 | break;
425 |
426 | case 'passed':
427 | this.appendSummaryToSuiteDiv();
428 | break;
429 |
430 | case 'failed':
431 | this.appendSummaryToSuiteDiv();
432 | this.appendFailureDetail();
433 | break;
434 | }
435 | };
436 |
437 | jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
438 | this.summary.className += ' ' + this.status();
439 | this.appendToSummary(this.spec, this.summary);
440 | };
441 |
442 | jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
443 | this.detail.className += ' ' + this.status();
444 |
445 | var resultItems = this.spec.results().getItems();
446 | var messagesDiv = this.createDom('div', { className: 'messages' });
447 |
448 | for (var i = 0; i < resultItems.length; i++) {
449 | var result = resultItems[i];
450 |
451 | if (result.type == 'log') {
452 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
453 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
454 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
455 |
456 | if (result.trace.stack) {
457 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
458 | }
459 | }
460 | }
461 |
462 | if (messagesDiv.childNodes.length > 0) {
463 | this.detail.appendChild(messagesDiv);
464 | this.dom.details.appendChild(this.detail);
465 | }
466 | };
467 |
468 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
469 | this.suite = suite;
470 | this.dom = dom;
471 | this.views = views;
472 |
473 | this.element = this.createDom('div', { className: 'suite' },
474 | this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
475 | );
476 |
477 | this.appendToSummary(this.suite, this.element);
478 | };
479 |
480 | jasmine.HtmlReporter.SuiteView.prototype.status = function() {
481 | return this.getSpecStatus(this.suite);
482 | };
483 |
484 | jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
485 | this.element.className += " " + this.status();
486 | };
487 |
488 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
489 |
490 | /* @deprecated Use jasmine.HtmlReporter instead
491 | */
492 | jasmine.TrivialReporter = function(doc) {
493 | this.document = doc || document;
494 | this.suiteDivs = {};
495 | this.logRunningSpecs = false;
496 | };
497 |
498 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
499 | var el = document.createElement(type);
500 |
501 | for (var i = 2; i < arguments.length; i++) {
502 | var child = arguments[i];
503 |
504 | if (typeof child === 'string') {
505 | el.appendChild(document.createTextNode(child));
506 | } else {
507 | if (child) { el.appendChild(child); }
508 | }
509 | }
510 |
511 | for (var attr in attrs) {
512 | if (attr == "className") {
513 | el[attr] = attrs[attr];
514 | } else {
515 | el.setAttribute(attr, attrs[attr]);
516 | }
517 | }
518 |
519 | return el;
520 | };
521 |
522 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
523 | var showPassed, showSkipped;
524 |
525 | this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
526 | this.createDom('div', { className: 'banner' },
527 | this.createDom('div', { className: 'logo' },
528 | this.createDom('span', { className: 'title' }, "Jasmine"),
529 | this.createDom('span', { className: 'version' }, runner.env.versionString())),
530 | this.createDom('div', { className: 'options' },
531 | "Show ",
532 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
533 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
534 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
535 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
536 | )
537 | ),
538 |
539 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
540 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
541 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
542 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
543 | );
544 |
545 | this.document.body.appendChild(this.outerDiv);
546 |
547 | var suites = runner.suites();
548 | for (var i = 0; i < suites.length; i++) {
549 | var suite = suites[i];
550 | var suiteDiv = this.createDom('div', { className: 'suite' },
551 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
552 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
553 | this.suiteDivs[suite.id] = suiteDiv;
554 | var parentDiv = this.outerDiv;
555 | if (suite.parentSuite) {
556 | parentDiv = this.suiteDivs[suite.parentSuite.id];
557 | }
558 | parentDiv.appendChild(suiteDiv);
559 | }
560 |
561 | this.startedAt = new Date();
562 |
563 | var self = this;
564 | showPassed.onclick = function(evt) {
565 | if (showPassed.checked) {
566 | self.outerDiv.className += ' show-passed';
567 | } else {
568 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
569 | }
570 | };
571 |
572 | showSkipped.onclick = function(evt) {
573 | if (showSkipped.checked) {
574 | self.outerDiv.className += ' show-skipped';
575 | } else {
576 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
577 | }
578 | };
579 | };
580 |
581 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
582 | var results = runner.results();
583 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
584 | this.runnerDiv.setAttribute("class", className);
585 | //do it twice for IE
586 | this.runnerDiv.setAttribute("className", className);
587 | var specs = runner.specs();
588 | var specCount = 0;
589 | for (var i = 0; i < specs.length; i++) {
590 | if (this.specFilter(specs[i])) {
591 | specCount++;
592 | }
593 | }
594 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
595 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
596 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
597 |
598 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
599 | };
600 |
601 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
602 | var results = suite.results();
603 | var status = results.passed() ? 'passed' : 'failed';
604 | if (results.totalCount === 0) { // todo: change this to check results.skipped
605 | status = 'skipped';
606 | }
607 | this.suiteDivs[suite.id].className += " " + status;
608 | };
609 |
610 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
611 | if (this.logRunningSpecs) {
612 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
613 | }
614 | };
615 |
616 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
617 | var results = spec.results();
618 | var status = results.passed() ? 'passed' : 'failed';
619 | if (results.skipped) {
620 | status = 'skipped';
621 | }
622 | var specDiv = this.createDom('div', { className: 'spec ' + status },
623 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
624 | this.createDom('a', {
625 | className: 'description',
626 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
627 | title: spec.getFullName()
628 | }, spec.description));
629 |
630 |
631 | var resultItems = results.getItems();
632 | var messagesDiv = this.createDom('div', { className: 'messages' });
633 | for (var i = 0; i < resultItems.length; i++) {
634 | var result = resultItems[i];
635 |
636 | if (result.type == 'log') {
637 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
638 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
639 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
640 |
641 | if (result.trace.stack) {
642 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
643 | }
644 | }
645 | }
646 |
647 | if (messagesDiv.childNodes.length > 0) {
648 | specDiv.appendChild(messagesDiv);
649 | }
650 |
651 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
652 | };
653 |
654 | jasmine.TrivialReporter.prototype.log = function() {
655 | var console = jasmine.getGlobal().console;
656 | if (console && console.log) {
657 | if (console.log.apply) {
658 | console.log.apply(console, arguments);
659 | } else {
660 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
661 | }
662 | }
663 | };
664 |
665 | jasmine.TrivialReporter.prototype.getLocation = function() {
666 | return this.document.location;
667 | };
668 |
669 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
670 | var paramMap = {};
671 | var params = this.getLocation().search.substring(1).split('&');
672 | for (var i = 0; i < params.length; i++) {
673 | var p = params[i].split('=');
674 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
675 | }
676 |
677 | if (!paramMap.spec) {
678 | return true;
679 | }
680 | return spec.getFullName().indexOf(paramMap.spec) === 0;
681 | };
682 |
--------------------------------------------------------------------------------
/jasmine-core/jasmine.js:
--------------------------------------------------------------------------------
1 | var isCommonJS = typeof window == "undefined";
2 |
3 | /**
4 | * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
5 | *
6 | * @namespace
7 | */
8 | var jasmine = {};
9 | if (isCommonJS) exports.jasmine = jasmine;
10 | /**
11 | * @private
12 | */
13 | jasmine.unimplementedMethod_ = function() {
14 | throw new Error("unimplemented method");
15 | };
16 |
17 | /**
18 | * Use jasmine.undefined instead of undefined, since undefined is just
19 | * a plain old variable and may be redefined by somebody else.
20 | *
21 | * @private
22 | */
23 | jasmine.undefined = jasmine.___undefined___;
24 |
25 | /**
26 | * Show diagnostic messages in the console if set to true
27 | *
28 | */
29 | jasmine.VERBOSE = false;
30 |
31 | /**
32 | * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
33 | *
34 | */
35 | jasmine.DEFAULT_UPDATE_INTERVAL = 250;
36 |
37 | /**
38 | * Default timeout interval in milliseconds for waitsFor() blocks.
39 | */
40 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
41 |
42 | /**
43 | * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
44 | * Set to false to let the exception bubble up in the browser.
45 | *
46 | */
47 | jasmine.CATCH_EXCEPTIONS = true;
48 |
49 | jasmine.getGlobal = function() {
50 | function getGlobal() {
51 | return this;
52 | }
53 |
54 | return getGlobal();
55 | };
56 |
57 | /**
58 | * Allows for bound functions to be compared. Internal use only.
59 | *
60 | * @ignore
61 | * @private
62 | * @param base {Object} bound 'this' for the function
63 | * @param name {Function} function to find
64 | */
65 | jasmine.bindOriginal_ = function(base, name) {
66 | var original = base[name];
67 | if (original.apply) {
68 | return function() {
69 | return original.apply(base, arguments);
70 | };
71 | } else {
72 | // IE support
73 | return jasmine.getGlobal()[name];
74 | }
75 | };
76 |
77 | jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
78 | jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
79 | jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
80 | jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
81 |
82 | jasmine.MessageResult = function(values) {
83 | this.type = 'log';
84 | this.values = values;
85 | this.trace = new Error(); // todo: test better
86 | };
87 |
88 | jasmine.MessageResult.prototype.toString = function() {
89 | var text = "";
90 | for (var i = 0; i < this.values.length; i++) {
91 | if (i > 0) text += " ";
92 | if (jasmine.isString_(this.values[i])) {
93 | text += this.values[i];
94 | } else {
95 | text += jasmine.pp(this.values[i]);
96 | }
97 | }
98 | return text;
99 | };
100 |
101 | jasmine.ExpectationResult = function(params) {
102 | this.type = 'expect';
103 | this.matcherName = params.matcherName;
104 | this.passed_ = params.passed;
105 | this.expected = params.expected;
106 | this.actual = params.actual;
107 | this.message = this.passed_ ? 'Passed.' : params.message;
108 |
109 | var trace = (params.trace || new Error(this.message));
110 | this.trace = this.passed_ ? '' : trace;
111 | };
112 |
113 | jasmine.ExpectationResult.prototype.toString = function () {
114 | return this.message;
115 | };
116 |
117 | jasmine.ExpectationResult.prototype.passed = function () {
118 | return this.passed_;
119 | };
120 |
121 | /**
122 | * Getter for the Jasmine environment. Ensures one gets created
123 | */
124 | jasmine.getEnv = function() {
125 | var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
126 | return env;
127 | };
128 |
129 | /**
130 | * @ignore
131 | * @private
132 | * @param value
133 | * @returns {Boolean}
134 | */
135 | jasmine.isArray_ = function(value) {
136 | return jasmine.isA_("Array", value);
137 | };
138 |
139 | /**
140 | * @ignore
141 | * @private
142 | * @param value
143 | * @returns {Boolean}
144 | */
145 | jasmine.isString_ = function(value) {
146 | return jasmine.isA_("String", value);
147 | };
148 |
149 | /**
150 | * @ignore
151 | * @private
152 | * @param value
153 | * @returns {Boolean}
154 | */
155 | jasmine.isNumber_ = function(value) {
156 | return jasmine.isA_("Number", value);
157 | };
158 |
159 | /**
160 | * @ignore
161 | * @private
162 | * @param {String} typeName
163 | * @param value
164 | * @returns {Boolean}
165 | */
166 | jasmine.isA_ = function(typeName, value) {
167 | return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
168 | };
169 |
170 | /**
171 | * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
172 | *
173 | * @param value {Object} an object to be outputted
174 | * @returns {String}
175 | */
176 | jasmine.pp = function(value) {
177 | var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
178 | stringPrettyPrinter.format(value);
179 | return stringPrettyPrinter.string;
180 | };
181 |
182 | /**
183 | * Returns true if the object is a DOM Node.
184 | *
185 | * @param {Object} obj object to check
186 | * @returns {Boolean}
187 | */
188 | jasmine.isDomNode = function(obj) {
189 | return obj.nodeType > 0;
190 | };
191 |
192 | /**
193 | * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
194 | *
195 | * @example
196 | * // don't care about which function is passed in, as long as it's a function
197 | * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
198 | *
199 | * @param {Class} clazz
200 | * @returns matchable object of the type clazz
201 | */
202 | jasmine.any = function(clazz) {
203 | return new jasmine.Matchers.Any(clazz);
204 | };
205 |
206 | /**
207 | * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
208 | * attributes on the object.
209 | *
210 | * @example
211 | * // don't care about any other attributes than foo.
212 | * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
213 | *
214 | * @param sample {Object} sample
215 | * @returns matchable object for the sample
216 | */
217 | jasmine.objectContaining = function (sample) {
218 | return new jasmine.Matchers.ObjectContaining(sample);
219 | };
220 |
221 | /**
222 | * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
223 | *
224 | * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
225 | * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
226 | *
227 | * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
228 | *
229 | * Spies are torn down at the end of every spec.
230 | *
231 | * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
232 | *
233 | * @example
234 | * // a stub
235 | * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
236 | *
237 | * // spy example
238 | * var foo = {
239 | * not: function(bool) { return !bool; }
240 | * }
241 | *
242 | * // actual foo.not will not be called, execution stops
243 | * spyOn(foo, 'not');
244 |
245 | // foo.not spied upon, execution will continue to implementation
246 | * spyOn(foo, 'not').andCallThrough();
247 | *
248 | * // fake example
249 | * var foo = {
250 | * not: function(bool) { return !bool; }
251 | * }
252 | *
253 | * // foo.not(val) will return val
254 | * spyOn(foo, 'not').andCallFake(function(value) {return value;});
255 | *
256 | * // mock example
257 | * foo.not(7 == 7);
258 | * expect(foo.not).toHaveBeenCalled();
259 | * expect(foo.not).toHaveBeenCalledWith(true);
260 | *
261 | * @constructor
262 | * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
263 | * @param {String} name
264 | */
265 | jasmine.Spy = function(name) {
266 | /**
267 | * The name of the spy, if provided.
268 | */
269 | this.identity = name || 'unknown';
270 | /**
271 | * Is this Object a spy?
272 | */
273 | this.isSpy = true;
274 | /**
275 | * The actual function this spy stubs.
276 | */
277 | this.plan = function() {
278 | };
279 | /**
280 | * Tracking of the most recent call to the spy.
281 | * @example
282 | * var mySpy = jasmine.createSpy('foo');
283 | * mySpy(1, 2);
284 | * mySpy.mostRecentCall.args = [1, 2];
285 | */
286 | this.mostRecentCall = {};
287 |
288 | /**
289 | * Holds arguments for each call to the spy, indexed by call count
290 | * @example
291 | * var mySpy = jasmine.createSpy('foo');
292 | * mySpy(1, 2);
293 | * mySpy(7, 8);
294 | * mySpy.mostRecentCall.args = [7, 8];
295 | * mySpy.argsForCall[0] = [1, 2];
296 | * mySpy.argsForCall[1] = [7, 8];
297 | */
298 | this.argsForCall = [];
299 | this.calls = [];
300 | };
301 |
302 | /**
303 | * Tells a spy to call through to the actual implemenatation.
304 | *
305 | * @example
306 | * var foo = {
307 | * bar: function() { // do some stuff }
308 | * }
309 | *
310 | * // defining a spy on an existing property: foo.bar
311 | * spyOn(foo, 'bar').andCallThrough();
312 | */
313 | jasmine.Spy.prototype.andCallThrough = function() {
314 | this.plan = this.originalValue;
315 | return this;
316 | };
317 |
318 | /**
319 | * For setting the return value of a spy.
320 | *
321 | * @example
322 | * // defining a spy from scratch: foo() returns 'baz'
323 | * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
324 | *
325 | * // defining a spy on an existing property: foo.bar() returns 'baz'
326 | * spyOn(foo, 'bar').andReturn('baz');
327 | *
328 | * @param {Object} value
329 | */
330 | jasmine.Spy.prototype.andReturn = function(value) {
331 | this.plan = function() {
332 | return value;
333 | };
334 | return this;
335 | };
336 |
337 | /**
338 | * For throwing an exception when a spy is called.
339 | *
340 | * @example
341 | * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
342 | * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
343 | *
344 | * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
345 | * spyOn(foo, 'bar').andThrow('baz');
346 | *
347 | * @param {String} exceptionMsg
348 | */
349 | jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
350 | this.plan = function() {
351 | throw exceptionMsg;
352 | };
353 | return this;
354 | };
355 |
356 | /**
357 | * Calls an alternate implementation when a spy is called.
358 | *
359 | * @example
360 | * var baz = function() {
361 | * // do some stuff, return something
362 | * }
363 | * // defining a spy from scratch: foo() calls the function baz
364 | * var foo = jasmine.createSpy('spy on foo').andCall(baz);
365 | *
366 | * // defining a spy on an existing property: foo.bar() calls an anonymnous function
367 | * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
368 | *
369 | * @param {Function} fakeFunc
370 | */
371 | jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
372 | this.plan = fakeFunc;
373 | return this;
374 | };
375 |
376 | /**
377 | * Resets all of a spy's the tracking variables so that it can be used again.
378 | *
379 | * @example
380 | * spyOn(foo, 'bar');
381 | *
382 | * foo.bar();
383 | *
384 | * expect(foo.bar.callCount).toEqual(1);
385 | *
386 | * foo.bar.reset();
387 | *
388 | * expect(foo.bar.callCount).toEqual(0);
389 | */
390 | jasmine.Spy.prototype.reset = function() {
391 | this.wasCalled = false;
392 | this.callCount = 0;
393 | this.argsForCall = [];
394 | this.calls = [];
395 | this.mostRecentCall = {};
396 | };
397 |
398 | jasmine.createSpy = function(name) {
399 |
400 | var spyObj = function() {
401 | spyObj.wasCalled = true;
402 | spyObj.callCount++;
403 | var args = jasmine.util.argsToArray(arguments);
404 | spyObj.mostRecentCall.object = this;
405 | spyObj.mostRecentCall.args = args;
406 | spyObj.argsForCall.push(args);
407 | spyObj.calls.push({object: this, args: args});
408 | return spyObj.plan.apply(this, arguments);
409 | };
410 |
411 | var spy = new jasmine.Spy(name);
412 |
413 | for (var prop in spy) {
414 | spyObj[prop] = spy[prop];
415 | }
416 |
417 | spyObj.reset();
418 |
419 | return spyObj;
420 | };
421 |
422 | /**
423 | * Determines whether an object is a spy.
424 | *
425 | * @param {jasmine.Spy|Object} putativeSpy
426 | * @returns {Boolean}
427 | */
428 | jasmine.isSpy = function(putativeSpy) {
429 | return putativeSpy && putativeSpy.isSpy;
430 | };
431 |
432 | /**
433 | * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
434 | * large in one call.
435 | *
436 | * @param {String} baseName name of spy class
437 | * @param {Array} methodNames array of names of methods to make spies
438 | */
439 | jasmine.createSpyObj = function(baseName, methodNames) {
440 | if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
441 | throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
442 | }
443 | var obj = {};
444 | for (var i = 0; i < methodNames.length; i++) {
445 | obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
446 | }
447 | return obj;
448 | };
449 |
450 | /**
451 | * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
452 | *
453 | * Be careful not to leave calls to jasmine.log in production code.
454 | */
455 | jasmine.log = function() {
456 | var spec = jasmine.getEnv().currentSpec;
457 | spec.log.apply(spec, arguments);
458 | };
459 |
460 | /**
461 | * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
462 | *
463 | * @example
464 | * // spy example
465 | * var foo = {
466 | * not: function(bool) { return !bool; }
467 | * }
468 | * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
469 | *
470 | * @see jasmine.createSpy
471 | * @param obj
472 | * @param methodName
473 | * @returns a Jasmine spy that can be chained with all spy methods
474 | */
475 | var spyOn = function(obj, methodName) {
476 | return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
477 | };
478 | if (isCommonJS) exports.spyOn = spyOn;
479 |
480 | /**
481 | * Creates a Jasmine spec that will be added to the current suite.
482 | *
483 | * // TODO: pending tests
484 | *
485 | * @example
486 | * it('should be true', function() {
487 | * expect(true).toEqual(true);
488 | * });
489 | *
490 | * @param {String} desc description of this specification
491 | * @param {Function} func defines the preconditions and expectations of the spec
492 | */
493 | var it = function(desc, func) {
494 | return jasmine.getEnv().it(desc, func);
495 | };
496 | if (isCommonJS) exports.it = it;
497 |
498 | /**
499 | * Creates a disabled Jasmine spec.
500 | *
501 | * A convenience method that allows existing specs to be disabled temporarily during development.
502 | *
503 | * @param {String} desc description of this specification
504 | * @param {Function} func defines the preconditions and expectations of the spec
505 | */
506 | var xit = function(desc, func) {
507 | return jasmine.getEnv().xit(desc, func);
508 | };
509 | if (isCommonJS) exports.xit = xit;
510 |
511 | /**
512 | * Starts a chain for a Jasmine expectation.
513 | *
514 | * It is passed an Object that is the actual value and should chain to one of the many
515 | * jasmine.Matchers functions.
516 | *
517 | * @param {Object} actual Actual value to test against and expected value
518 | */
519 | var expect = function(actual) {
520 | return jasmine.getEnv().currentSpec.expect(actual);
521 | };
522 | if (isCommonJS) exports.expect = expect;
523 |
524 | /**
525 | * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
526 | *
527 | * @param {Function} func Function that defines part of a jasmine spec.
528 | */
529 | var runs = function(func) {
530 | jasmine.getEnv().currentSpec.runs(func);
531 | };
532 | if (isCommonJS) exports.runs = runs;
533 |
534 | /**
535 | * Waits a fixed time period before moving to the next block.
536 | *
537 | * @deprecated Use waitsFor() instead
538 | * @param {Number} timeout milliseconds to wait
539 | */
540 | var waits = function(timeout) {
541 | jasmine.getEnv().currentSpec.waits(timeout);
542 | };
543 | if (isCommonJS) exports.waits = waits;
544 |
545 | /**
546 | * Waits for the latchFunction to return true before proceeding to the next block.
547 | *
548 | * @param {Function} latchFunction
549 | * @param {String} optional_timeoutMessage
550 | * @param {Number} optional_timeout
551 | */
552 | var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
553 | jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
554 | };
555 | if (isCommonJS) exports.waitsFor = waitsFor;
556 |
557 | /**
558 | * A function that is called before each spec in a suite.
559 | *
560 | * Used for spec setup, including validating assumptions.
561 | *
562 | * @param {Function} beforeEachFunction
563 | */
564 | var beforeEach = function(beforeEachFunction) {
565 | jasmine.getEnv().beforeEach(beforeEachFunction);
566 | };
567 | if (isCommonJS) exports.beforeEach = beforeEach;
568 |
569 | /**
570 | * A function that is called after each spec in a suite.
571 | *
572 | * Used for restoring any state that is hijacked during spec execution.
573 | *
574 | * @param {Function} afterEachFunction
575 | */
576 | var afterEach = function(afterEachFunction) {
577 | jasmine.getEnv().afterEach(afterEachFunction);
578 | };
579 | if (isCommonJS) exports.afterEach = afterEach;
580 |
581 | /**
582 | * Defines a suite of specifications.
583 | *
584 | * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
585 | * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
586 | * of setup in some tests.
587 | *
588 | * @example
589 | * // TODO: a simple suite
590 | *
591 | * // TODO: a simple suite with a nested describe block
592 | *
593 | * @param {String} description A string, usually the class under test.
594 | * @param {Function} specDefinitions function that defines several specs.
595 | */
596 | var describe = function(description, specDefinitions) {
597 | return jasmine.getEnv().describe(description, specDefinitions);
598 | };
599 | if (isCommonJS) exports.describe = describe;
600 |
601 | /**
602 | * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
603 | *
604 | * @param {String} description A string, usually the class under test.
605 | * @param {Function} specDefinitions function that defines several specs.
606 | */
607 | var xdescribe = function(description, specDefinitions) {
608 | return jasmine.getEnv().xdescribe(description, specDefinitions);
609 | };
610 | if (isCommonJS) exports.xdescribe = xdescribe;
611 |
612 |
613 | // Provide the XMLHttpRequest class for IE 5.x-6.x:
614 | jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
615 | function tryIt(f) {
616 | try {
617 | return f();
618 | } catch(e) {
619 | }
620 | return null;
621 | }
622 |
623 | var xhr = tryIt(function() {
624 | return new ActiveXObject("Msxml2.XMLHTTP.6.0");
625 | }) ||
626 | tryIt(function() {
627 | return new ActiveXObject("Msxml2.XMLHTTP.3.0");
628 | }) ||
629 | tryIt(function() {
630 | return new ActiveXObject("Msxml2.XMLHTTP");
631 | }) ||
632 | tryIt(function() {
633 | return new ActiveXObject("Microsoft.XMLHTTP");
634 | });
635 |
636 | if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
637 |
638 | return xhr;
639 | } : XMLHttpRequest;
640 | /**
641 | * @namespace
642 | */
643 | jasmine.util = {};
644 |
645 | /**
646 | * Declare that a child class inherit it's prototype from the parent class.
647 | *
648 | * @private
649 | * @param {Function} childClass
650 | * @param {Function} parentClass
651 | */
652 | jasmine.util.inherit = function(childClass, parentClass) {
653 | /**
654 | * @private
655 | */
656 | var subclass = function() {
657 | };
658 | subclass.prototype = parentClass.prototype;
659 | childClass.prototype = new subclass();
660 | };
661 |
662 | jasmine.util.formatException = function(e) {
663 | var lineNumber;
664 | if (e.line) {
665 | lineNumber = e.line;
666 | }
667 | else if (e.lineNumber) {
668 | lineNumber = e.lineNumber;
669 | }
670 |
671 | var file;
672 |
673 | if (e.sourceURL) {
674 | file = e.sourceURL;
675 | }
676 | else if (e.fileName) {
677 | file = e.fileName;
678 | }
679 |
680 | var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
681 |
682 | if (file && lineNumber) {
683 | message += ' in ' + file + ' (line ' + lineNumber + ')';
684 | }
685 |
686 | return message;
687 | };
688 |
689 | jasmine.util.htmlEscape = function(str) {
690 | if (!str) return str;
691 | return str.replace(/&/g, '&')
692 | .replace(//g, '>');
694 | };
695 |
696 | jasmine.util.argsToArray = function(args) {
697 | var arrayOfArgs = [];
698 | for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
699 | return arrayOfArgs;
700 | };
701 |
702 | jasmine.util.extend = function(destination, source) {
703 | for (var property in source) destination[property] = source[property];
704 | return destination;
705 | };
706 |
707 | /**
708 | * Environment for Jasmine
709 | *
710 | * @constructor
711 | */
712 | jasmine.Env = function() {
713 | this.currentSpec = null;
714 | this.currentSuite = null;
715 | this.currentRunner_ = new jasmine.Runner(this);
716 |
717 | this.reporter = new jasmine.MultiReporter();
718 |
719 | this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
720 | this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
721 | this.lastUpdate = 0;
722 | this.specFilter = function() {
723 | return true;
724 | };
725 |
726 | this.nextSpecId_ = 0;
727 | this.nextSuiteId_ = 0;
728 | this.equalityTesters_ = [];
729 |
730 | // wrap matchers
731 | this.matchersClass = function() {
732 | jasmine.Matchers.apply(this, arguments);
733 | };
734 | jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
735 |
736 | jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
737 | };
738 |
739 |
740 | jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
741 | jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
742 | jasmine.Env.prototype.setInterval = jasmine.setInterval;
743 | jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
744 |
745 | /**
746 | * @returns an object containing jasmine version build info, if set.
747 | */
748 | jasmine.Env.prototype.version = function () {
749 | if (jasmine.version_) {
750 | return jasmine.version_;
751 | } else {
752 | throw new Error('Version not set');
753 | }
754 | };
755 |
756 | /**
757 | * @returns string containing jasmine version build info, if set.
758 | */
759 | jasmine.Env.prototype.versionString = function() {
760 | if (!jasmine.version_) {
761 | return "version unknown";
762 | }
763 |
764 | var version = this.version();
765 | var versionString = version.major + "." + version.minor + "." + version.build;
766 | if (version.release_candidate) {
767 | versionString += ".rc" + version.release_candidate;
768 | }
769 | versionString += " revision " + version.revision;
770 | return versionString;
771 | };
772 |
773 | /**
774 | * @returns a sequential integer starting at 0
775 | */
776 | jasmine.Env.prototype.nextSpecId = function () {
777 | return this.nextSpecId_++;
778 | };
779 |
780 | /**
781 | * @returns a sequential integer starting at 0
782 | */
783 | jasmine.Env.prototype.nextSuiteId = function () {
784 | return this.nextSuiteId_++;
785 | };
786 |
787 | /**
788 | * Register a reporter to receive status updates from Jasmine.
789 | * @param {jasmine.Reporter} reporter An object which will receive status updates.
790 | */
791 | jasmine.Env.prototype.addReporter = function(reporter) {
792 | this.reporter.addReporter(reporter);
793 | };
794 |
795 | jasmine.Env.prototype.execute = function() {
796 | this.currentRunner_.execute();
797 | };
798 |
799 | jasmine.Env.prototype.describe = function(description, specDefinitions) {
800 | var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
801 |
802 | var parentSuite = this.currentSuite;
803 | if (parentSuite) {
804 | parentSuite.add(suite);
805 | } else {
806 | this.currentRunner_.add(suite);
807 | }
808 |
809 | this.currentSuite = suite;
810 |
811 | var declarationError = null;
812 | try {
813 | specDefinitions.call(suite);
814 | } catch(e) {
815 | declarationError = e;
816 | }
817 |
818 | if (declarationError) {
819 | this.it("encountered a declaration exception", function() {
820 | throw declarationError;
821 | });
822 | }
823 |
824 | this.currentSuite = parentSuite;
825 |
826 | return suite;
827 | };
828 |
829 | jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
830 | if (this.currentSuite) {
831 | this.currentSuite.beforeEach(beforeEachFunction);
832 | } else {
833 | this.currentRunner_.beforeEach(beforeEachFunction);
834 | }
835 | };
836 |
837 | jasmine.Env.prototype.currentRunner = function () {
838 | return this.currentRunner_;
839 | };
840 |
841 | jasmine.Env.prototype.afterEach = function(afterEachFunction) {
842 | if (this.currentSuite) {
843 | this.currentSuite.afterEach(afterEachFunction);
844 | } else {
845 | this.currentRunner_.afterEach(afterEachFunction);
846 | }
847 |
848 | };
849 |
850 | jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
851 | return {
852 | execute: function() {
853 | }
854 | };
855 | };
856 |
857 | jasmine.Env.prototype.it = function(description, func) {
858 | var spec = new jasmine.Spec(this, this.currentSuite, description);
859 | this.currentSuite.add(spec);
860 | this.currentSpec = spec;
861 |
862 | if (func) {
863 | spec.runs(func);
864 | }
865 |
866 | return spec;
867 | };
868 |
869 | jasmine.Env.prototype.xit = function(desc, func) {
870 | return {
871 | id: this.nextSpecId(),
872 | runs: function() {
873 | }
874 | };
875 | };
876 |
877 | jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
878 | if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
879 | return true;
880 | }
881 |
882 | a.__Jasmine_been_here_before__ = b;
883 | b.__Jasmine_been_here_before__ = a;
884 |
885 | var hasKey = function(obj, keyName) {
886 | return obj !== null && obj[keyName] !== jasmine.undefined;
887 | };
888 |
889 | for (var property in b) {
890 | if (!hasKey(a, property) && hasKey(b, property)) {
891 | mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
892 | }
893 | }
894 | for (property in a) {
895 | if (!hasKey(b, property) && hasKey(a, property)) {
896 | mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
897 | }
898 | }
899 | for (property in b) {
900 | if (property == '__Jasmine_been_here_before__') continue;
901 | if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
902 | mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
903 | }
904 | }
905 |
906 | if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
907 | mismatchValues.push("arrays were not the same length");
908 | }
909 |
910 | delete a.__Jasmine_been_here_before__;
911 | delete b.__Jasmine_been_here_before__;
912 | return (mismatchKeys.length === 0 && mismatchValues.length === 0);
913 | };
914 |
915 | jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
916 | mismatchKeys = mismatchKeys || [];
917 | mismatchValues = mismatchValues || [];
918 |
919 | for (var i = 0; i < this.equalityTesters_.length; i++) {
920 | var equalityTester = this.equalityTesters_[i];
921 | var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
922 | if (result !== jasmine.undefined) return result;
923 | }
924 |
925 | if (a === b) return true;
926 |
927 | if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
928 | return (a == jasmine.undefined && b == jasmine.undefined);
929 | }
930 |
931 | if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
932 | return a === b;
933 | }
934 |
935 | if (a instanceof Date && b instanceof Date) {
936 | return a.getTime() == b.getTime();
937 | }
938 |
939 | if (a.jasmineMatches) {
940 | return a.jasmineMatches(b);
941 | }
942 |
943 | if (b.jasmineMatches) {
944 | return b.jasmineMatches(a);
945 | }
946 |
947 | if (a instanceof jasmine.Matchers.ObjectContaining) {
948 | return a.matches(b);
949 | }
950 |
951 | if (b instanceof jasmine.Matchers.ObjectContaining) {
952 | return b.matches(a);
953 | }
954 |
955 | if (jasmine.isString_(a) && jasmine.isString_(b)) {
956 | return (a == b);
957 | }
958 |
959 | if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
960 | return (a == b);
961 | }
962 |
963 | if (typeof a === "object" && typeof b === "object") {
964 | return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
965 | }
966 |
967 | //Straight check
968 | return (a === b);
969 | };
970 |
971 | jasmine.Env.prototype.contains_ = function(haystack, needle) {
972 | if (jasmine.isArray_(haystack)) {
973 | for (var i = 0; i < haystack.length; i++) {
974 | if (this.equals_(haystack[i], needle)) return true;
975 | }
976 | return false;
977 | }
978 | return haystack.indexOf(needle) >= 0;
979 | };
980 |
981 | jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
982 | this.equalityTesters_.push(equalityTester);
983 | };
984 | /** No-op base class for Jasmine reporters.
985 | *
986 | * @constructor
987 | */
988 | jasmine.Reporter = function() {
989 | };
990 |
991 | //noinspection JSUnusedLocalSymbols
992 | jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
993 | };
994 |
995 | //noinspection JSUnusedLocalSymbols
996 | jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
997 | };
998 |
999 | //noinspection JSUnusedLocalSymbols
1000 | jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
1001 | };
1002 |
1003 | //noinspection JSUnusedLocalSymbols
1004 | jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
1005 | };
1006 |
1007 | //noinspection JSUnusedLocalSymbols
1008 | jasmine.Reporter.prototype.reportSpecResults = function(spec) {
1009 | };
1010 |
1011 | //noinspection JSUnusedLocalSymbols
1012 | jasmine.Reporter.prototype.log = function(str) {
1013 | };
1014 |
1015 | /**
1016 | * Blocks are functions with executable code that make up a spec.
1017 | *
1018 | * @constructor
1019 | * @param {jasmine.Env} env
1020 | * @param {Function} func
1021 | * @param {jasmine.Spec} spec
1022 | */
1023 | jasmine.Block = function(env, func, spec) {
1024 | this.env = env;
1025 | this.func = func;
1026 | this.spec = spec;
1027 | };
1028 |
1029 | jasmine.Block.prototype.execute = function(onComplete) {
1030 | if (!jasmine.CATCH_EXCEPTIONS) {
1031 | this.func.apply(this.spec);
1032 | }
1033 | else {
1034 | try {
1035 | this.func.apply(this.spec);
1036 | } catch (e) {
1037 | this.spec.fail(e);
1038 | }
1039 | }
1040 | onComplete();
1041 | };
1042 | /** JavaScript API reporter.
1043 | *
1044 | * @constructor
1045 | */
1046 | jasmine.JsApiReporter = function() {
1047 | this.started = false;
1048 | this.finished = false;
1049 | this.suites_ = [];
1050 | this.results_ = {};
1051 | };
1052 |
1053 | jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
1054 | this.started = true;
1055 | var suites = runner.topLevelSuites();
1056 | for (var i = 0; i < suites.length; i++) {
1057 | var suite = suites[i];
1058 | this.suites_.push(this.summarize_(suite));
1059 | }
1060 | };
1061 |
1062 | jasmine.JsApiReporter.prototype.suites = function() {
1063 | return this.suites_;
1064 | };
1065 |
1066 | jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
1067 | var isSuite = suiteOrSpec instanceof jasmine.Suite;
1068 | var summary = {
1069 | id: suiteOrSpec.id,
1070 | name: suiteOrSpec.description,
1071 | type: isSuite ? 'suite' : 'spec',
1072 | children: []
1073 | };
1074 |
1075 | if (isSuite) {
1076 | var children = suiteOrSpec.children();
1077 | for (var i = 0; i < children.length; i++) {
1078 | summary.children.push(this.summarize_(children[i]));
1079 | }
1080 | }
1081 | return summary;
1082 | };
1083 |
1084 | jasmine.JsApiReporter.prototype.results = function() {
1085 | return this.results_;
1086 | };
1087 |
1088 | jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
1089 | return this.results_[specId];
1090 | };
1091 |
1092 | //noinspection JSUnusedLocalSymbols
1093 | jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
1094 | this.finished = true;
1095 | };
1096 |
1097 | //noinspection JSUnusedLocalSymbols
1098 | jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
1099 | };
1100 |
1101 | //noinspection JSUnusedLocalSymbols
1102 | jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
1103 | this.results_[spec.id] = {
1104 | messages: spec.results().getItems(),
1105 | result: spec.results().failedCount > 0 ? "failed" : "passed"
1106 | };
1107 | };
1108 |
1109 | //noinspection JSUnusedLocalSymbols
1110 | jasmine.JsApiReporter.prototype.log = function(str) {
1111 | };
1112 |
1113 | jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
1114 | var results = {};
1115 | for (var i = 0; i < specIds.length; i++) {
1116 | var specId = specIds[i];
1117 | results[specId] = this.summarizeResult_(this.results_[specId]);
1118 | }
1119 | return results;
1120 | };
1121 |
1122 | jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
1123 | var summaryMessages = [];
1124 | var messagesLength = result.messages.length;
1125 | for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
1126 | var resultMessage = result.messages[messageIndex];
1127 | summaryMessages.push({
1128 | text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
1129 | passed: resultMessage.passed ? resultMessage.passed() : true,
1130 | type: resultMessage.type,
1131 | message: resultMessage.message,
1132 | trace: {
1133 | stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
1134 | }
1135 | });
1136 | }
1137 |
1138 | return {
1139 | result : result.result,
1140 | messages : summaryMessages
1141 | };
1142 | };
1143 |
1144 | /**
1145 | * @constructor
1146 | * @param {jasmine.Env} env
1147 | * @param actual
1148 | * @param {jasmine.Spec} spec
1149 | */
1150 | jasmine.Matchers = function(env, actual, spec, opt_isNot) {
1151 | this.env = env;
1152 | this.actual = actual;
1153 | this.spec = spec;
1154 | this.isNot = opt_isNot || false;
1155 | this.reportWasCalled_ = false;
1156 | };
1157 |
1158 | // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1159 | jasmine.Matchers.pp = function(str) {
1160 | throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1161 | };
1162 |
1163 | // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1164 | jasmine.Matchers.prototype.report = function(result, failing_message, details) {
1165 | throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1166 | };
1167 |
1168 | jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
1169 | for (var methodName in prototype) {
1170 | if (methodName == 'report') continue;
1171 | var orig = prototype[methodName];
1172 | matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
1173 | }
1174 | };
1175 |
1176 | jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
1177 | return function() {
1178 | var matcherArgs = jasmine.util.argsToArray(arguments);
1179 | var result = matcherFunction.apply(this, arguments);
1180 |
1181 | if (this.isNot) {
1182 | result = !result;
1183 | }
1184 |
1185 | if (this.reportWasCalled_) return result;
1186 |
1187 | var message;
1188 | if (!result) {
1189 | if (this.message) {
1190 | message = this.message.apply(this, arguments);
1191 | if (jasmine.isArray_(message)) {
1192 | message = message[this.isNot ? 1 : 0];
1193 | }
1194 | } else {
1195 | var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
1196 | message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
1197 | if (matcherArgs.length > 0) {
1198 | for (var i = 0; i < matcherArgs.length; i++) {
1199 | if (i > 0) message += ",";
1200 | message += " " + jasmine.pp(matcherArgs[i]);
1201 | }
1202 | }
1203 | message += ".";
1204 | }
1205 | }
1206 | var expectationResult = new jasmine.ExpectationResult({
1207 | matcherName: matcherName,
1208 | passed: result,
1209 | expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
1210 | actual: this.actual,
1211 | message: message
1212 | });
1213 | this.spec.addMatcherResult(expectationResult);
1214 | return jasmine.undefined;
1215 | };
1216 | };
1217 |
1218 |
1219 |
1220 |
1221 | /**
1222 | * toBe: compares the actual to the expected using ===
1223 | * @param expected
1224 | */
1225 | jasmine.Matchers.prototype.toBe = function(expected) {
1226 | return this.actual === expected;
1227 | };
1228 |
1229 | /**
1230 | * toNotBe: compares the actual to the expected using !==
1231 | * @param expected
1232 | * @deprecated as of 1.0. Use not.toBe() instead.
1233 | */
1234 | jasmine.Matchers.prototype.toNotBe = function(expected) {
1235 | return this.actual !== expected;
1236 | };
1237 |
1238 | /**
1239 | * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1240 | *
1241 | * @param expected
1242 | */
1243 | jasmine.Matchers.prototype.toEqual = function(expected) {
1244 | return this.env.equals_(this.actual, expected);
1245 | };
1246 |
1247 | /**
1248 | * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1249 | * @param expected
1250 | * @deprecated as of 1.0. Use not.toEqual() instead.
1251 | */
1252 | jasmine.Matchers.prototype.toNotEqual = function(expected) {
1253 | return !this.env.equals_(this.actual, expected);
1254 | };
1255 |
1256 | /**
1257 | * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
1258 | * a pattern or a String.
1259 | *
1260 | * @param expected
1261 | */
1262 | jasmine.Matchers.prototype.toMatch = function(expected) {
1263 | return new RegExp(expected).test(this.actual);
1264 | };
1265 |
1266 | /**
1267 | * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1268 | * @param expected
1269 | * @deprecated as of 1.0. Use not.toMatch() instead.
1270 | */
1271 | jasmine.Matchers.prototype.toNotMatch = function(expected) {
1272 | return !(new RegExp(expected).test(this.actual));
1273 | };
1274 |
1275 | /**
1276 | * Matcher that compares the actual to jasmine.undefined.
1277 | */
1278 | jasmine.Matchers.prototype.toBeDefined = function() {
1279 | return (this.actual !== jasmine.undefined);
1280 | };
1281 |
1282 | /**
1283 | * Matcher that compares the actual to jasmine.undefined.
1284 | */
1285 | jasmine.Matchers.prototype.toBeUndefined = function() {
1286 | return (this.actual === jasmine.undefined);
1287 | };
1288 |
1289 | /**
1290 | * Matcher that compares the actual to null.
1291 | */
1292 | jasmine.Matchers.prototype.toBeNull = function() {
1293 | return (this.actual === null);
1294 | };
1295 |
1296 | /**
1297 | * Matcher that boolean not-nots the actual.
1298 | */
1299 | jasmine.Matchers.prototype.toBeTruthy = function() {
1300 | return !!this.actual;
1301 | };
1302 |
1303 |
1304 | /**
1305 | * Matcher that boolean nots the actual.
1306 | */
1307 | jasmine.Matchers.prototype.toBeFalsy = function() {
1308 | return !this.actual;
1309 | };
1310 |
1311 |
1312 | /**
1313 | * Matcher that checks to see if the actual, a Jasmine spy, was called.
1314 | */
1315 | jasmine.Matchers.prototype.toHaveBeenCalled = function() {
1316 | if (arguments.length > 0) {
1317 | throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1318 | }
1319 |
1320 | if (!jasmine.isSpy(this.actual)) {
1321 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1322 | }
1323 |
1324 | this.message = function() {
1325 | return [
1326 | "Expected spy " + this.actual.identity + " to have been called.",
1327 | "Expected spy " + this.actual.identity + " not to have been called."
1328 | ];
1329 | };
1330 |
1331 | return this.actual.wasCalled;
1332 | };
1333 |
1334 | /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1335 | jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
1336 |
1337 | /**
1338 | * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1339 | *
1340 | * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1341 | */
1342 | jasmine.Matchers.prototype.wasNotCalled = function() {
1343 | if (arguments.length > 0) {
1344 | throw new Error('wasNotCalled does not take arguments');
1345 | }
1346 |
1347 | if (!jasmine.isSpy(this.actual)) {
1348 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1349 | }
1350 |
1351 | this.message = function() {
1352 | return [
1353 | "Expected spy " + this.actual.identity + " to not have been called.",
1354 | "Expected spy " + this.actual.identity + " to have been called."
1355 | ];
1356 | };
1357 |
1358 | return !this.actual.wasCalled;
1359 | };
1360 |
1361 | /**
1362 | * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1363 | *
1364 | * @example
1365 | *
1366 | */
1367 | jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
1368 | var expectedArgs = jasmine.util.argsToArray(arguments);
1369 | if (!jasmine.isSpy(this.actual)) {
1370 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1371 | }
1372 | this.message = function() {
1373 | if (this.actual.callCount === 0) {
1374 | // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
1375 | return [
1376 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
1377 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
1378 | ];
1379 | } else {
1380 | return [
1381 | "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
1382 | "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
1383 | ];
1384 | }
1385 | };
1386 |
1387 | return this.env.contains_(this.actual.argsForCall, expectedArgs);
1388 | };
1389 |
1390 | /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1391 | jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
1392 |
1393 | /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1394 | jasmine.Matchers.prototype.wasNotCalledWith = function() {
1395 | var expectedArgs = jasmine.util.argsToArray(arguments);
1396 | if (!jasmine.isSpy(this.actual)) {
1397 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
1398 | }
1399 |
1400 | this.message = function() {
1401 | return [
1402 | "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
1403 | "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
1404 | ];
1405 | };
1406 |
1407 | return !this.env.contains_(this.actual.argsForCall, expectedArgs);
1408 | };
1409 |
1410 | /**
1411 | * Matcher that checks that the expected item is an element in the actual Array.
1412 | *
1413 | * @param {Object} expected
1414 | */
1415 | jasmine.Matchers.prototype.toContain = function(expected) {
1416 | return this.env.contains_(this.actual, expected);
1417 | };
1418 |
1419 | /**
1420 | * Matcher that checks that the expected item is NOT an element in the actual Array.
1421 | *
1422 | * @param {Object} expected
1423 | * @deprecated as of 1.0. Use not.toContain() instead.
1424 | */
1425 | jasmine.Matchers.prototype.toNotContain = function(expected) {
1426 | return !this.env.contains_(this.actual, expected);
1427 | };
1428 |
1429 | jasmine.Matchers.prototype.toBeLessThan = function(expected) {
1430 | return this.actual < expected;
1431 | };
1432 |
1433 | jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
1434 | return this.actual > expected;
1435 | };
1436 |
1437 | /**
1438 | * Matcher that checks that the expected item is equal to the actual item
1439 | * up to a given level of decimal precision (default 2).
1440 | *
1441 | * @param {Number} expected
1442 | * @param {Number} precision
1443 | */
1444 | jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
1445 | if (!(precision === 0)) {
1446 | precision = precision || 2;
1447 | }
1448 | var multiplier = Math.pow(10, precision);
1449 | var actual = Math.round(this.actual * multiplier);
1450 | expected = Math.round(expected * multiplier);
1451 | return expected == actual;
1452 | };
1453 |
1454 | /**
1455 | * Matcher that checks that the expected exception was thrown by the actual.
1456 | *
1457 | * @param {String} expected
1458 | */
1459 | jasmine.Matchers.prototype.toThrow = function(expected) {
1460 | var result = false;
1461 | var exception;
1462 | if (typeof this.actual != 'function') {
1463 | throw new Error('Actual is not a function');
1464 | }
1465 | try {
1466 | this.actual();
1467 | } catch (e) {
1468 | exception = e;
1469 | }
1470 | if (exception) {
1471 | result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
1472 | }
1473 |
1474 | var not = this.isNot ? "not " : "";
1475 |
1476 | this.message = function() {
1477 | if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
1478 | return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
1479 | } else {
1480 | return "Expected function to throw an exception.";
1481 | }
1482 | };
1483 |
1484 | return result;
1485 | };
1486 |
1487 | jasmine.Matchers.Any = function(expectedClass) {
1488 | this.expectedClass = expectedClass;
1489 | };
1490 |
1491 | jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
1492 | if (this.expectedClass == String) {
1493 | return typeof other == 'string' || other instanceof String;
1494 | }
1495 |
1496 | if (this.expectedClass == Number) {
1497 | return typeof other == 'number' || other instanceof Number;
1498 | }
1499 |
1500 | if (this.expectedClass == Function) {
1501 | return typeof other == 'function' || other instanceof Function;
1502 | }
1503 |
1504 | if (this.expectedClass == Object) {
1505 | return typeof other == 'object';
1506 | }
1507 |
1508 | return other instanceof this.expectedClass;
1509 | };
1510 |
1511 | jasmine.Matchers.Any.prototype.jasmineToString = function() {
1512 | return '';
1513 | };
1514 |
1515 | jasmine.Matchers.ObjectContaining = function (sample) {
1516 | this.sample = sample;
1517 | };
1518 |
1519 | jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
1520 | mismatchKeys = mismatchKeys || [];
1521 | mismatchValues = mismatchValues || [];
1522 |
1523 | var env = jasmine.getEnv();
1524 |
1525 | var hasKey = function(obj, keyName) {
1526 | return obj != null && obj[keyName] !== jasmine.undefined;
1527 | };
1528 |
1529 | for (var property in this.sample) {
1530 | if (!hasKey(other, property) && hasKey(this.sample, property)) {
1531 | mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
1532 | }
1533 | else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
1534 | mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
1535 | }
1536 | }
1537 |
1538 | return (mismatchKeys.length === 0 && mismatchValues.length === 0);
1539 | };
1540 |
1541 | jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
1542 | return "";
1543 | };
1544 | // Mock setTimeout, clearTimeout
1545 | // Contributed by Pivotal Computer Systems, www.pivotalsf.com
1546 |
1547 | jasmine.FakeTimer = function() {
1548 | this.reset();
1549 |
1550 | var self = this;
1551 | self.setTimeout = function(funcToCall, millis) {
1552 | self.timeoutsMade++;
1553 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
1554 | return self.timeoutsMade;
1555 | };
1556 |
1557 | self.setInterval = function(funcToCall, millis) {
1558 | self.timeoutsMade++;
1559 | self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
1560 | return self.timeoutsMade;
1561 | };
1562 |
1563 | self.clearTimeout = function(timeoutKey) {
1564 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
1565 | };
1566 |
1567 | self.clearInterval = function(timeoutKey) {
1568 | self.scheduledFunctions[timeoutKey] = jasmine.undefined;
1569 | };
1570 |
1571 | };
1572 |
1573 | jasmine.FakeTimer.prototype.reset = function() {
1574 | this.timeoutsMade = 0;
1575 | this.scheduledFunctions = {};
1576 | this.nowMillis = 0;
1577 | };
1578 |
1579 | jasmine.FakeTimer.prototype.tick = function(millis) {
1580 | var oldMillis = this.nowMillis;
1581 | var newMillis = oldMillis + millis;
1582 | this.runFunctionsWithinRange(oldMillis, newMillis);
1583 | this.nowMillis = newMillis;
1584 | };
1585 |
1586 | jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
1587 | var scheduledFunc;
1588 | var funcsToRun = [];
1589 | for (var timeoutKey in this.scheduledFunctions) {
1590 | scheduledFunc = this.scheduledFunctions[timeoutKey];
1591 | if (scheduledFunc != jasmine.undefined &&
1592 | scheduledFunc.runAtMillis >= oldMillis &&
1593 | scheduledFunc.runAtMillis <= nowMillis) {
1594 | funcsToRun.push(scheduledFunc);
1595 | this.scheduledFunctions[timeoutKey] = jasmine.undefined;
1596 | }
1597 | }
1598 |
1599 | if (funcsToRun.length > 0) {
1600 | funcsToRun.sort(function(a, b) {
1601 | return a.runAtMillis - b.runAtMillis;
1602 | });
1603 | for (var i = 0; i < funcsToRun.length; ++i) {
1604 | try {
1605 | var funcToRun = funcsToRun[i];
1606 | this.nowMillis = funcToRun.runAtMillis;
1607 | funcToRun.funcToCall();
1608 | if (funcToRun.recurring) {
1609 | this.scheduleFunction(funcToRun.timeoutKey,
1610 | funcToRun.funcToCall,
1611 | funcToRun.millis,
1612 | true);
1613 | }
1614 | } catch(e) {
1615 | }
1616 | }
1617 | this.runFunctionsWithinRange(oldMillis, nowMillis);
1618 | }
1619 | };
1620 |
1621 | jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
1622 | this.scheduledFunctions[timeoutKey] = {
1623 | runAtMillis: this.nowMillis + millis,
1624 | funcToCall: funcToCall,
1625 | recurring: recurring,
1626 | timeoutKey: timeoutKey,
1627 | millis: millis
1628 | };
1629 | };
1630 |
1631 | /**
1632 | * @namespace
1633 | */
1634 | jasmine.Clock = {
1635 | defaultFakeTimer: new jasmine.FakeTimer(),
1636 |
1637 | reset: function() {
1638 | jasmine.Clock.assertInstalled();
1639 | jasmine.Clock.defaultFakeTimer.reset();
1640 | },
1641 |
1642 | tick: function(millis) {
1643 | jasmine.Clock.assertInstalled();
1644 | jasmine.Clock.defaultFakeTimer.tick(millis);
1645 | },
1646 |
1647 | runFunctionsWithinRange: function(oldMillis, nowMillis) {
1648 | jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
1649 | },
1650 |
1651 | scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
1652 | jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
1653 | },
1654 |
1655 | useMock: function() {
1656 | if (!jasmine.Clock.isInstalled()) {
1657 | var spec = jasmine.getEnv().currentSpec;
1658 | spec.after(jasmine.Clock.uninstallMock);
1659 |
1660 | jasmine.Clock.installMock();
1661 | }
1662 | },
1663 |
1664 | installMock: function() {
1665 | jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
1666 | },
1667 |
1668 | uninstallMock: function() {
1669 | jasmine.Clock.assertInstalled();
1670 | jasmine.Clock.installed = jasmine.Clock.real;
1671 | },
1672 |
1673 | real: {
1674 | setTimeout: jasmine.getGlobal().setTimeout,
1675 | clearTimeout: jasmine.getGlobal().clearTimeout,
1676 | setInterval: jasmine.getGlobal().setInterval,
1677 | clearInterval: jasmine.getGlobal().clearInterval
1678 | },
1679 |
1680 | assertInstalled: function() {
1681 | if (!jasmine.Clock.isInstalled()) {
1682 | throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
1683 | }
1684 | },
1685 |
1686 | isInstalled: function() {
1687 | return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
1688 | },
1689 |
1690 | installed: null
1691 | };
1692 | jasmine.Clock.installed = jasmine.Clock.real;
1693 |
1694 | //else for IE support
1695 | jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
1696 | if (jasmine.Clock.installed.setTimeout.apply) {
1697 | return jasmine.Clock.installed.setTimeout.apply(this, arguments);
1698 | } else {
1699 | return jasmine.Clock.installed.setTimeout(funcToCall, millis);
1700 | }
1701 | };
1702 |
1703 | jasmine.getGlobal().setInterval = function(funcToCall, millis) {
1704 | if (jasmine.Clock.installed.setInterval.apply) {
1705 | return jasmine.Clock.installed.setInterval.apply(this, arguments);
1706 | } else {
1707 | return jasmine.Clock.installed.setInterval(funcToCall, millis);
1708 | }
1709 | };
1710 |
1711 | jasmine.getGlobal().clearTimeout = function(timeoutKey) {
1712 | if (jasmine.Clock.installed.clearTimeout.apply) {
1713 | return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
1714 | } else {
1715 | return jasmine.Clock.installed.clearTimeout(timeoutKey);
1716 | }
1717 | };
1718 |
1719 | jasmine.getGlobal().clearInterval = function(timeoutKey) {
1720 | if (jasmine.Clock.installed.clearTimeout.apply) {
1721 | return jasmine.Clock.installed.clearInterval.apply(this, arguments);
1722 | } else {
1723 | return jasmine.Clock.installed.clearInterval(timeoutKey);
1724 | }
1725 | };
1726 |
1727 | /**
1728 | * @constructor
1729 | */
1730 | jasmine.MultiReporter = function() {
1731 | this.subReporters_ = [];
1732 | };
1733 | jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
1734 |
1735 | jasmine.MultiReporter.prototype.addReporter = function(reporter) {
1736 | this.subReporters_.push(reporter);
1737 | };
1738 |
1739 | (function() {
1740 | var functionNames = [
1741 | "reportRunnerStarting",
1742 | "reportRunnerResults",
1743 | "reportSuiteResults",
1744 | "reportSpecStarting",
1745 | "reportSpecResults",
1746 | "log"
1747 | ];
1748 | for (var i = 0; i < functionNames.length; i++) {
1749 | var functionName = functionNames[i];
1750 | jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
1751 | return function() {
1752 | for (var j = 0; j < this.subReporters_.length; j++) {
1753 | var subReporter = this.subReporters_[j];
1754 | if (subReporter[functionName]) {
1755 | subReporter[functionName].apply(subReporter, arguments);
1756 | }
1757 | }
1758 | };
1759 | })(functionName);
1760 | }
1761 | })();
1762 | /**
1763 | * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
1764 | *
1765 | * @constructor
1766 | */
1767 | jasmine.NestedResults = function() {
1768 | /**
1769 | * The total count of results
1770 | */
1771 | this.totalCount = 0;
1772 | /**
1773 | * Number of passed results
1774 | */
1775 | this.passedCount = 0;
1776 | /**
1777 | * Number of failed results
1778 | */
1779 | this.failedCount = 0;
1780 | /**
1781 | * Was this suite/spec skipped?
1782 | */
1783 | this.skipped = false;
1784 | /**
1785 | * @ignore
1786 | */
1787 | this.items_ = [];
1788 | };
1789 |
1790 | /**
1791 | * Roll up the result counts.
1792 | *
1793 | * @param result
1794 | */
1795 | jasmine.NestedResults.prototype.rollupCounts = function(result) {
1796 | this.totalCount += result.totalCount;
1797 | this.passedCount += result.passedCount;
1798 | this.failedCount += result.failedCount;
1799 | };
1800 |
1801 | /**
1802 | * Adds a log message.
1803 | * @param values Array of message parts which will be concatenated later.
1804 | */
1805 | jasmine.NestedResults.prototype.log = function(values) {
1806 | this.items_.push(new jasmine.MessageResult(values));
1807 | };
1808 |
1809 | /**
1810 | * Getter for the results: message & results.
1811 | */
1812 | jasmine.NestedResults.prototype.getItems = function() {
1813 | return this.items_;
1814 | };
1815 |
1816 | /**
1817 | * Adds a result, tracking counts (total, passed, & failed)
1818 | * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
1819 | */
1820 | jasmine.NestedResults.prototype.addResult = function(result) {
1821 | if (result.type != 'log') {
1822 | if (result.items_) {
1823 | this.rollupCounts(result);
1824 | } else {
1825 | this.totalCount++;
1826 | if (result.passed()) {
1827 | this.passedCount++;
1828 | } else {
1829 | this.failedCount++;
1830 | }
1831 | }
1832 | }
1833 | this.items_.push(result);
1834 | };
1835 |
1836 | /**
1837 | * @returns {Boolean} True if everything below passed
1838 | */
1839 | jasmine.NestedResults.prototype.passed = function() {
1840 | return this.passedCount === this.totalCount;
1841 | };
1842 | /**
1843 | * Base class for pretty printing for expectation results.
1844 | */
1845 | jasmine.PrettyPrinter = function() {
1846 | this.ppNestLevel_ = 0;
1847 | };
1848 |
1849 | /**
1850 | * Formats a value in a nice, human-readable string.
1851 | *
1852 | * @param value
1853 | */
1854 | jasmine.PrettyPrinter.prototype.format = function(value) {
1855 | if (this.ppNestLevel_ > 40) {
1856 | throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
1857 | }
1858 |
1859 | this.ppNestLevel_++;
1860 | try {
1861 | if (value === jasmine.undefined) {
1862 | this.emitScalar('undefined');
1863 | } else if (value === null) {
1864 | this.emitScalar('null');
1865 | } else if (value === jasmine.getGlobal()) {
1866 | this.emitScalar('');
1867 | } else if (value.jasmineToString) {
1868 | this.emitScalar(value.jasmineToString());
1869 | } else if (typeof value === 'string') {
1870 | this.emitString(value);
1871 | } else if (jasmine.isSpy(value)) {
1872 | this.emitScalar("spy on " + value.identity);
1873 | } else if (value instanceof RegExp) {
1874 | this.emitScalar(value.toString());
1875 | } else if (typeof value === 'function') {
1876 | this.emitScalar('Function');
1877 | } else if (typeof value.nodeType === 'number') {
1878 | this.emitScalar('HTMLNode');
1879 | } else if (value instanceof Date) {
1880 | this.emitScalar('Date(' + value + ')');
1881 | } else if (value.__Jasmine_been_here_before__) {
1882 | this.emitScalar('');
1883 | } else if (jasmine.isArray_(value) || typeof value == 'object') {
1884 | value.__Jasmine_been_here_before__ = true;
1885 | if (jasmine.isArray_(value)) {
1886 | this.emitArray(value);
1887 | } else {
1888 | this.emitObject(value);
1889 | }
1890 | delete value.__Jasmine_been_here_before__;
1891 | } else {
1892 | this.emitScalar(value.toString());
1893 | }
1894 | } finally {
1895 | this.ppNestLevel_--;
1896 | }
1897 | };
1898 |
1899 | jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
1900 | for (var property in obj) {
1901 | if (property == '__Jasmine_been_here_before__') continue;
1902 | fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
1903 | obj.__lookupGetter__(property) !== null) : false);
1904 | }
1905 | };
1906 |
1907 | jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
1908 | jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
1909 | jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
1910 | jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
1911 |
1912 | jasmine.StringPrettyPrinter = function() {
1913 | jasmine.PrettyPrinter.call(this);
1914 |
1915 | this.string = '';
1916 | };
1917 | jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
1918 |
1919 | jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
1920 | this.append(value);
1921 | };
1922 |
1923 | jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
1924 | this.append("'" + value + "'");
1925 | };
1926 |
1927 | jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
1928 | this.append('[ ');
1929 | for (var i = 0; i < array.length; i++) {
1930 | if (i > 0) {
1931 | this.append(', ');
1932 | }
1933 | this.format(array[i]);
1934 | }
1935 | this.append(' ]');
1936 | };
1937 |
1938 | jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
1939 | var self = this;
1940 | this.append('{ ');
1941 | var first = true;
1942 |
1943 | this.iterateObject(obj, function(property, isGetter) {
1944 | if (first) {
1945 | first = false;
1946 | } else {
1947 | self.append(', ');
1948 | }
1949 |
1950 | self.append(property);
1951 | self.append(' : ');
1952 | if (isGetter) {
1953 | self.append('');
1954 | } else {
1955 | self.format(obj[property]);
1956 | }
1957 | });
1958 |
1959 | this.append(' }');
1960 | };
1961 |
1962 | jasmine.StringPrettyPrinter.prototype.append = function(value) {
1963 | this.string += value;
1964 | };
1965 | jasmine.Queue = function(env) {
1966 | this.env = env;
1967 | this.blocks = [];
1968 | this.running = false;
1969 | this.index = 0;
1970 | this.offset = 0;
1971 | this.abort = false;
1972 | };
1973 |
1974 | jasmine.Queue.prototype.addBefore = function(block) {
1975 | this.blocks.unshift(block);
1976 | };
1977 |
1978 | jasmine.Queue.prototype.add = function(block) {
1979 | this.blocks.push(block);
1980 | };
1981 |
1982 | jasmine.Queue.prototype.insertNext = function(block) {
1983 | this.blocks.splice((this.index + this.offset + 1), 0, block);
1984 | this.offset++;
1985 | };
1986 |
1987 | jasmine.Queue.prototype.start = function(onComplete) {
1988 | this.running = true;
1989 | this.onComplete = onComplete;
1990 | this.next_();
1991 | };
1992 |
1993 | jasmine.Queue.prototype.isRunning = function() {
1994 | return this.running;
1995 | };
1996 |
1997 | jasmine.Queue.LOOP_DONT_RECURSE = true;
1998 |
1999 | jasmine.Queue.prototype.next_ = function() {
2000 | var self = this;
2001 | var goAgain = true;
2002 |
2003 | while (goAgain) {
2004 | goAgain = false;
2005 |
2006 | if (self.index < self.blocks.length && !this.abort) {
2007 | var calledSynchronously = true;
2008 | var completedSynchronously = false;
2009 |
2010 | var onComplete = function () {
2011 | if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
2012 | completedSynchronously = true;
2013 | return;
2014 | }
2015 |
2016 | if (self.blocks[self.index].abort) {
2017 | self.abort = true;
2018 | }
2019 |
2020 | self.offset = 0;
2021 | self.index++;
2022 |
2023 | var now = new Date().getTime();
2024 | if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
2025 | self.env.lastUpdate = now;
2026 | self.env.setTimeout(function() {
2027 | self.next_();
2028 | }, 0);
2029 | } else {
2030 | if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
2031 | goAgain = true;
2032 | } else {
2033 | self.next_();
2034 | }
2035 | }
2036 | };
2037 | self.blocks[self.index].execute(onComplete);
2038 |
2039 | calledSynchronously = false;
2040 | if (completedSynchronously) {
2041 | onComplete();
2042 | }
2043 |
2044 | } else {
2045 | self.running = false;
2046 | if (self.onComplete) {
2047 | self.onComplete();
2048 | }
2049 | }
2050 | }
2051 | };
2052 |
2053 | jasmine.Queue.prototype.results = function() {
2054 | var results = new jasmine.NestedResults();
2055 | for (var i = 0; i < this.blocks.length; i++) {
2056 | if (this.blocks[i].results) {
2057 | results.addResult(this.blocks[i].results());
2058 | }
2059 | }
2060 | return results;
2061 | };
2062 |
2063 |
2064 | /**
2065 | * Runner
2066 | *
2067 | * @constructor
2068 | * @param {jasmine.Env} env
2069 | */
2070 | jasmine.Runner = function(env) {
2071 | var self = this;
2072 | self.env = env;
2073 | self.queue = new jasmine.Queue(env);
2074 | self.before_ = [];
2075 | self.after_ = [];
2076 | self.suites_ = [];
2077 | };
2078 |
2079 | jasmine.Runner.prototype.execute = function() {
2080 | var self = this;
2081 | if (self.env.reporter.reportRunnerStarting) {
2082 | self.env.reporter.reportRunnerStarting(this);
2083 | }
2084 | self.queue.start(function () {
2085 | self.finishCallback();
2086 | });
2087 | };
2088 |
2089 | jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
2090 | beforeEachFunction.typeName = 'beforeEach';
2091 | this.before_.splice(0,0,beforeEachFunction);
2092 | };
2093 |
2094 | jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
2095 | afterEachFunction.typeName = 'afterEach';
2096 | this.after_.splice(0,0,afterEachFunction);
2097 | };
2098 |
2099 |
2100 | jasmine.Runner.prototype.finishCallback = function() {
2101 | this.env.reporter.reportRunnerResults(this);
2102 | };
2103 |
2104 | jasmine.Runner.prototype.addSuite = function(suite) {
2105 | this.suites_.push(suite);
2106 | };
2107 |
2108 | jasmine.Runner.prototype.add = function(block) {
2109 | if (block instanceof jasmine.Suite) {
2110 | this.addSuite(block);
2111 | }
2112 | this.queue.add(block);
2113 | };
2114 |
2115 | jasmine.Runner.prototype.specs = function () {
2116 | var suites = this.suites();
2117 | var specs = [];
2118 | for (var i = 0; i < suites.length; i++) {
2119 | specs = specs.concat(suites[i].specs());
2120 | }
2121 | return specs;
2122 | };
2123 |
2124 | jasmine.Runner.prototype.suites = function() {
2125 | return this.suites_;
2126 | };
2127 |
2128 | jasmine.Runner.prototype.topLevelSuites = function() {
2129 | var topLevelSuites = [];
2130 | for (var i = 0; i < this.suites_.length; i++) {
2131 | if (!this.suites_[i].parentSuite) {
2132 | topLevelSuites.push(this.suites_[i]);
2133 | }
2134 | }
2135 | return topLevelSuites;
2136 | };
2137 |
2138 | jasmine.Runner.prototype.results = function() {
2139 | return this.queue.results();
2140 | };
2141 | /**
2142 | * Internal representation of a Jasmine specification, or test.
2143 | *
2144 | * @constructor
2145 | * @param {jasmine.Env} env
2146 | * @param {jasmine.Suite} suite
2147 | * @param {String} description
2148 | */
2149 | jasmine.Spec = function(env, suite, description) {
2150 | if (!env) {
2151 | throw new Error('jasmine.Env() required');
2152 | }
2153 | if (!suite) {
2154 | throw new Error('jasmine.Suite() required');
2155 | }
2156 | var spec = this;
2157 | spec.id = env.nextSpecId ? env.nextSpecId() : null;
2158 | spec.env = env;
2159 | spec.suite = suite;
2160 | spec.description = description;
2161 | spec.queue = new jasmine.Queue(env);
2162 |
2163 | spec.afterCallbacks = [];
2164 | spec.spies_ = [];
2165 |
2166 | spec.results_ = new jasmine.NestedResults();
2167 | spec.results_.description = description;
2168 | spec.matchersClass = null;
2169 | };
2170 |
2171 | jasmine.Spec.prototype.getFullName = function() {
2172 | return this.suite.getFullName() + ' ' + this.description + '.';
2173 | };
2174 |
2175 |
2176 | jasmine.Spec.prototype.results = function() {
2177 | return this.results_;
2178 | };
2179 |
2180 | /**
2181 | * All parameters are pretty-printed and concatenated together, then written to the spec's output.
2182 | *
2183 | * Be careful not to leave calls to jasmine.log in production code.
2184 | */
2185 | jasmine.Spec.prototype.log = function() {
2186 | return this.results_.log(arguments);
2187 | };
2188 |
2189 | jasmine.Spec.prototype.runs = function (func) {
2190 | var block = new jasmine.Block(this.env, func, this);
2191 | this.addToQueue(block);
2192 | return this;
2193 | };
2194 |
2195 | jasmine.Spec.prototype.addToQueue = function (block) {
2196 | if (this.queue.isRunning()) {
2197 | this.queue.insertNext(block);
2198 | } else {
2199 | this.queue.add(block);
2200 | }
2201 | };
2202 |
2203 | /**
2204 | * @param {jasmine.ExpectationResult} result
2205 | */
2206 | jasmine.Spec.prototype.addMatcherResult = function(result) {
2207 | this.results_.addResult(result);
2208 | };
2209 |
2210 | jasmine.Spec.prototype.expect = function(actual) {
2211 | var positive = new (this.getMatchersClass_())(this.env, actual, this);
2212 | positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
2213 | return positive;
2214 | };
2215 |
2216 | /**
2217 | * Waits a fixed time period before moving to the next block.
2218 | *
2219 | * @deprecated Use waitsFor() instead
2220 | * @param {Number} timeout milliseconds to wait
2221 | */
2222 | jasmine.Spec.prototype.waits = function(timeout) {
2223 | var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
2224 | this.addToQueue(waitsFunc);
2225 | return this;
2226 | };
2227 |
2228 | /**
2229 | * Waits for the latchFunction to return true before proceeding to the next block.
2230 | *
2231 | * @param {Function} latchFunction
2232 | * @param {String} optional_timeoutMessage
2233 | * @param {Number} optional_timeout
2234 | */
2235 | jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
2236 | var latchFunction_ = null;
2237 | var optional_timeoutMessage_ = null;
2238 | var optional_timeout_ = null;
2239 |
2240 | for (var i = 0; i < arguments.length; i++) {
2241 | var arg = arguments[i];
2242 | switch (typeof arg) {
2243 | case 'function':
2244 | latchFunction_ = arg;
2245 | break;
2246 | case 'string':
2247 | optional_timeoutMessage_ = arg;
2248 | break;
2249 | case 'number':
2250 | optional_timeout_ = arg;
2251 | break;
2252 | }
2253 | }
2254 |
2255 | var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
2256 | this.addToQueue(waitsForFunc);
2257 | return this;
2258 | };
2259 |
2260 | jasmine.Spec.prototype.fail = function (e) {
2261 | var expectationResult = new jasmine.ExpectationResult({
2262 | passed: false,
2263 | message: e ? jasmine.util.formatException(e) : 'Exception',
2264 | trace: { stack: e.stack }
2265 | });
2266 | this.results_.addResult(expectationResult);
2267 | };
2268 |
2269 | jasmine.Spec.prototype.getMatchersClass_ = function() {
2270 | return this.matchersClass || this.env.matchersClass;
2271 | };
2272 |
2273 | jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
2274 | var parent = this.getMatchersClass_();
2275 | var newMatchersClass = function() {
2276 | parent.apply(this, arguments);
2277 | };
2278 | jasmine.util.inherit(newMatchersClass, parent);
2279 | jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
2280 | this.matchersClass = newMatchersClass;
2281 | };
2282 |
2283 | jasmine.Spec.prototype.finishCallback = function() {
2284 | this.env.reporter.reportSpecResults(this);
2285 | };
2286 |
2287 | jasmine.Spec.prototype.finish = function(onComplete) {
2288 | this.removeAllSpies();
2289 | this.finishCallback();
2290 | if (onComplete) {
2291 | onComplete();
2292 | }
2293 | };
2294 |
2295 | jasmine.Spec.prototype.after = function(doAfter) {
2296 | if (this.queue.isRunning()) {
2297 | this.queue.add(new jasmine.Block(this.env, doAfter, this));
2298 | } else {
2299 | this.afterCallbacks.unshift(doAfter);
2300 | }
2301 | };
2302 |
2303 | jasmine.Spec.prototype.execute = function(onComplete) {
2304 | var spec = this;
2305 | if (!spec.env.specFilter(spec)) {
2306 | spec.results_.skipped = true;
2307 | spec.finish(onComplete);
2308 | return;
2309 | }
2310 |
2311 | this.env.reporter.reportSpecStarting(this);
2312 |
2313 | spec.env.currentSpec = spec;
2314 |
2315 | spec.addBeforesAndAftersToQueue();
2316 |
2317 | spec.queue.start(function () {
2318 | spec.finish(onComplete);
2319 | });
2320 | };
2321 |
2322 | jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
2323 | var runner = this.env.currentRunner();
2324 | var i;
2325 |
2326 | for (var suite = this.suite; suite; suite = suite.parentSuite) {
2327 | for (i = 0; i < suite.before_.length; i++) {
2328 | this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
2329 | }
2330 | }
2331 | for (i = 0; i < runner.before_.length; i++) {
2332 | this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
2333 | }
2334 | for (i = 0; i < this.afterCallbacks.length; i++) {
2335 | this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
2336 | }
2337 | for (suite = this.suite; suite; suite = suite.parentSuite) {
2338 | for (i = 0; i < suite.after_.length; i++) {
2339 | this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
2340 | }
2341 | }
2342 | for (i = 0; i < runner.after_.length; i++) {
2343 | this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
2344 | }
2345 | };
2346 |
2347 | jasmine.Spec.prototype.explodes = function() {
2348 | throw 'explodes function should not have been called';
2349 | };
2350 |
2351 | jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
2352 | if (obj == jasmine.undefined) {
2353 | throw "spyOn could not find an object to spy upon for " + methodName + "()";
2354 | }
2355 |
2356 | if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
2357 | throw methodName + '() method does not exist';
2358 | }
2359 |
2360 | if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
2361 | throw new Error(methodName + ' has already been spied upon');
2362 | }
2363 |
2364 | var spyObj = jasmine.createSpy(methodName);
2365 |
2366 | this.spies_.push(spyObj);
2367 | spyObj.baseObj = obj;
2368 | spyObj.methodName = methodName;
2369 | spyObj.originalValue = obj[methodName];
2370 |
2371 | obj[methodName] = spyObj;
2372 |
2373 | return spyObj;
2374 | };
2375 |
2376 | jasmine.Spec.prototype.removeAllSpies = function() {
2377 | for (var i = 0; i < this.spies_.length; i++) {
2378 | var spy = this.spies_[i];
2379 | spy.baseObj[spy.methodName] = spy.originalValue;
2380 | }
2381 | this.spies_ = [];
2382 | };
2383 |
2384 | /**
2385 | * Internal representation of a Jasmine suite.
2386 | *
2387 | * @constructor
2388 | * @param {jasmine.Env} env
2389 | * @param {String} description
2390 | * @param {Function} specDefinitions
2391 | * @param {jasmine.Suite} parentSuite
2392 | */
2393 | jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
2394 | var self = this;
2395 | self.id = env.nextSuiteId ? env.nextSuiteId() : null;
2396 | self.description = description;
2397 | self.queue = new jasmine.Queue(env);
2398 | self.parentSuite = parentSuite;
2399 | self.env = env;
2400 | self.before_ = [];
2401 | self.after_ = [];
2402 | self.children_ = [];
2403 | self.suites_ = [];
2404 | self.specs_ = [];
2405 | };
2406 |
2407 | jasmine.Suite.prototype.getFullName = function() {
2408 | var fullName = this.description;
2409 | for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
2410 | fullName = parentSuite.description + ' ' + fullName;
2411 | }
2412 | return fullName;
2413 | };
2414 |
2415 | jasmine.Suite.prototype.finish = function(onComplete) {
2416 | this.env.reporter.reportSuiteResults(this);
2417 | this.finished = true;
2418 | if (typeof(onComplete) == 'function') {
2419 | onComplete();
2420 | }
2421 | };
2422 |
2423 | jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
2424 | beforeEachFunction.typeName = 'beforeEach';
2425 | this.before_.unshift(beforeEachFunction);
2426 | };
2427 |
2428 | jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
2429 | afterEachFunction.typeName = 'afterEach';
2430 | this.after_.unshift(afterEachFunction);
2431 | };
2432 |
2433 | jasmine.Suite.prototype.results = function() {
2434 | return this.queue.results();
2435 | };
2436 |
2437 | jasmine.Suite.prototype.add = function(suiteOrSpec) {
2438 | this.children_.push(suiteOrSpec);
2439 | if (suiteOrSpec instanceof jasmine.Suite) {
2440 | this.suites_.push(suiteOrSpec);
2441 | this.env.currentRunner().addSuite(suiteOrSpec);
2442 | } else {
2443 | this.specs_.push(suiteOrSpec);
2444 | }
2445 | this.queue.add(suiteOrSpec);
2446 | };
2447 |
2448 | jasmine.Suite.prototype.specs = function() {
2449 | return this.specs_;
2450 | };
2451 |
2452 | jasmine.Suite.prototype.suites = function() {
2453 | return this.suites_;
2454 | };
2455 |
2456 | jasmine.Suite.prototype.children = function() {
2457 | return this.children_;
2458 | };
2459 |
2460 | jasmine.Suite.prototype.execute = function(onComplete) {
2461 | var self = this;
2462 | this.queue.start(function () {
2463 | self.finish(onComplete);
2464 | });
2465 | };
2466 | jasmine.WaitsBlock = function(env, timeout, spec) {
2467 | this.timeout = timeout;
2468 | jasmine.Block.call(this, env, null, spec);
2469 | };
2470 |
2471 | jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
2472 |
2473 | jasmine.WaitsBlock.prototype.execute = function (onComplete) {
2474 | if (jasmine.VERBOSE) {
2475 | this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
2476 | }
2477 | this.env.setTimeout(function () {
2478 | onComplete();
2479 | }, this.timeout);
2480 | };
2481 | /**
2482 | * A block which waits for some condition to become true, with timeout.
2483 | *
2484 | * @constructor
2485 | * @extends jasmine.Block
2486 | * @param {jasmine.Env} env The Jasmine environment.
2487 | * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
2488 | * @param {Function} latchFunction A function which returns true when the desired condition has been met.
2489 | * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
2490 | * @param {jasmine.Spec} spec The Jasmine spec.
2491 | */
2492 | jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
2493 | this.timeout = timeout || env.defaultTimeoutInterval;
2494 | this.latchFunction = latchFunction;
2495 | this.message = message;
2496 | this.totalTimeSpentWaitingForLatch = 0;
2497 | jasmine.Block.call(this, env, null, spec);
2498 | };
2499 | jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
2500 |
2501 | jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
2502 |
2503 | jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
2504 | if (jasmine.VERBOSE) {
2505 | this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
2506 | }
2507 | var latchFunctionResult;
2508 | try {
2509 | latchFunctionResult = this.latchFunction.apply(this.spec);
2510 | } catch (e) {
2511 | this.spec.fail(e);
2512 | onComplete();
2513 | return;
2514 | }
2515 |
2516 | if (latchFunctionResult) {
2517 | onComplete();
2518 | } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
2519 | var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
2520 | this.spec.fail({
2521 | name: 'timeout',
2522 | message: message
2523 | });
2524 |
2525 | this.abort = true;
2526 | onComplete();
2527 | } else {
2528 | this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
2529 | var self = this;
2530 | this.env.setTimeout(function() {
2531 | self.execute(onComplete);
2532 | }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
2533 | }
2534 | };
2535 |
2536 | jasmine.version_= {
2537 | "major": 1,
2538 | "minor": 2,
2539 | "build": 0,
2540 | "revision": 1337006083
2541 | };
2542 |
--------------------------------------------------------------------------------