├── test
├── index.html
├── resources
│ ├── qunit-1.14.0.css
│ └── qunit-1.14.0.js
└── tests.js
├── README.md
├── dist
└── jquery.turkLirasi.min.js
└── src
└── jquery.turkLirasi.js
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | QUnit turkLirasiMaskesi.JS
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jQuery TürkLirası İnput Maskesi
2 | ===============================
3 |
4 | jQuery eklentisi olarak hazırlanan basit bir Türk Lirası giriş maskesi.
5 |
6 | ## Bu proje aktif bir şekilde geliştirilmiyor. Lütfen production ortamında kullanmayın.
7 |
8 | ***
9 | ### Download
10 |
11 | Son versiyon(minified) için [tıklayınız](https://raw.githubusercontent.com/ozziest/turkLirasiMaskesi/master/dist/jquery.turkLirasi.min.js).
12 |
13 | ***
14 | ### Kullanımı
15 | ```html
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 | ```
29 |
30 | ***
31 | ### Ayarlar:
32 |
33 | Opsiyonel olarak yapılabilecek ayarlar şu şekildedir;
34 |
35 |
36 | * `allowedDecimal`: Ondalıklı sayı girişine izin verileceğini belirtir. Varsayılan: true
37 | * `maxDecimalCount`: Ondalıklı bölüme girilebilecek en fazla sayı adedidir. Varsayılan: 2
38 | * `autoFillDecimal`: Ondalıklı bölüm girilmediğinde otomatik olarak doldurulması ayarıdır. Varsayılan: true
39 | * `suffix`: Input sonuna eklenecek olan metini belirten değerdir. Varsayılan: TL
40 | * `formatted`: Girilen değerin basamaklarının formatlanması durumunu belirten değerdir. Varsayılan: true
41 | * `autoSelect`: Input seçildiğinde otomatik olarak seçilmesi (hızlı kopyala-yapıştır işlemleri için) ayarını tutan değerdir. Varsayılan: true
42 | * `align`: Input içine girilen değerlerin ne yana yaslı olacağını belirten değerdir. Varsayılan: right
43 | * `debug`: Çalışma anında logların gösterilmesi ayarıdır. Varsayılan: false
44 | * `_eventAfterSet`: Her girilen rakamdan sonra çağırılacak fonksiyon ayarıdır. Varsayılan: false
45 | * `_eventFocusOut`: FocusOut olayından sonra çağırılacak fonksiyon ayarıdır. Varsayılan: false
46 |
47 | ***
48 | ### Metodlar:
49 |
50 | * `setAgain`: Çalışma anında javascript ile değer güncellenmek istenildiğinde kullanılır.
51 |
52 | $('#inputName').trigger('setAgain', [newValue]);
53 |
54 |
55 | ***
56 | ### Önemli Notlar ve Geliştirme Planı
57 |
58 | - Varsayılan olarak maske formatı 999.999,99 olarak hazırlanmıştır.
59 | - Sayı girişine alt ve üst değer konulabilmesi.
60 | - Negatif sayıların girilebilmesi.
61 |
62 |
63 |
--------------------------------------------------------------------------------
/test/resources/qunit-1.14.0.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * QUnit 1.14.0
3 | * http://qunitjs.com/
4 | *
5 | * Copyright 2013 jQuery Foundation and other contributors
6 | * Released under the MIT license
7 | * http://jquery.org/license
8 | *
9 | * Date: 2014-01-31T16:40Z
10 | */
11 |
12 | /** Font Family and Sizes */
13 |
14 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
16 | }
17 |
18 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
19 | #qunit-tests { font-size: smaller; }
20 |
21 |
22 | /** Resets */
23 |
24 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
25 | margin: 0;
26 | padding: 0;
27 | }
28 |
29 |
30 | /** Header */
31 |
32 | #qunit-header {
33 | padding: 0.5em 0 0.5em 1em;
34 |
35 | color: #8699A4;
36 | background-color: #0D3349;
37 |
38 | font-size: 1.5em;
39 | line-height: 1em;
40 | font-weight: 400;
41 |
42 | border-radius: 5px 5px 0 0;
43 | }
44 |
45 | #qunit-header a {
46 | text-decoration: none;
47 | color: #C2CCD1;
48 | }
49 |
50 | #qunit-header a:hover,
51 | #qunit-header a:focus {
52 | color: #FFF;
53 | }
54 |
55 | #qunit-testrunner-toolbar label {
56 | display: inline-block;
57 | padding: 0 0.5em 0 0.1em;
58 | }
59 |
60 | #qunit-banner {
61 | height: 5px;
62 | }
63 |
64 | #qunit-testrunner-toolbar {
65 | padding: 0.5em 0 0.5em 2em;
66 | color: #5E740B;
67 | background-color: #EEE;
68 | overflow: hidden;
69 | }
70 |
71 | #qunit-userAgent {
72 | padding: 0.5em 0 0.5em 2.5em;
73 | background-color: #2B81AF;
74 | color: #FFF;
75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
76 | }
77 |
78 | #qunit-modulefilter-container {
79 | float: right;
80 | }
81 |
82 | /** Tests: Pass/Fail */
83 |
84 | #qunit-tests {
85 | list-style-position: inside;
86 | }
87 |
88 | #qunit-tests li {
89 | padding: 0.4em 0.5em 0.4em 2.5em;
90 | border-bottom: 1px solid #FFF;
91 | list-style-position: inside;
92 | }
93 |
94 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
95 | display: none;
96 | }
97 |
98 | #qunit-tests li strong {
99 | cursor: pointer;
100 | }
101 |
102 | #qunit-tests li a {
103 | padding: 0.5em;
104 | color: #C2CCD1;
105 | text-decoration: none;
106 | }
107 | #qunit-tests li a:hover,
108 | #qunit-tests li a:focus {
109 | color: #000;
110 | }
111 |
112 | #qunit-tests li .runtime {
113 | float: right;
114 | font-size: smaller;
115 | }
116 |
117 | .qunit-assert-list {
118 | margin-top: 0.5em;
119 | padding: 0.5em;
120 |
121 | background-color: #FFF;
122 |
123 | border-radius: 5px;
124 | }
125 |
126 | .qunit-collapsed {
127 | display: none;
128 | }
129 |
130 | #qunit-tests table {
131 | border-collapse: collapse;
132 | margin-top: 0.2em;
133 | }
134 |
135 | #qunit-tests th {
136 | text-align: right;
137 | vertical-align: top;
138 | padding: 0 0.5em 0 0;
139 | }
140 |
141 | #qunit-tests td {
142 | vertical-align: top;
143 | }
144 |
145 | #qunit-tests pre {
146 | margin: 0;
147 | white-space: pre-wrap;
148 | word-wrap: break-word;
149 | }
150 |
151 | #qunit-tests del {
152 | background-color: #E0F2BE;
153 | color: #374E0C;
154 | text-decoration: none;
155 | }
156 |
157 | #qunit-tests ins {
158 | background-color: #FFCACA;
159 | color: #500;
160 | text-decoration: none;
161 | }
162 |
163 | /*** Test Counts */
164 |
165 | #qunit-tests b.counts { color: #000; }
166 | #qunit-tests b.passed { color: #5E740B; }
167 | #qunit-tests b.failed { color: #710909; }
168 |
169 | #qunit-tests li li {
170 | padding: 5px;
171 | background-color: #FFF;
172 | border-bottom: none;
173 | list-style-position: inside;
174 | }
175 |
176 | /*** Passing Styles */
177 |
178 | #qunit-tests li li.pass {
179 | color: #3C510C;
180 | background-color: #FFF;
181 | border-left: 10px solid #C6E746;
182 | }
183 |
184 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
185 | #qunit-tests .pass .test-name { color: #366097; }
186 |
187 | #qunit-tests .pass .test-actual,
188 | #qunit-tests .pass .test-expected { color: #999; }
189 |
190 | #qunit-banner.qunit-pass { background-color: #C6E746; }
191 |
192 | /*** Failing Styles */
193 |
194 | #qunit-tests li li.fail {
195 | color: #710909;
196 | background-color: #FFF;
197 | border-left: 10px solid #EE5757;
198 | white-space: pre;
199 | }
200 |
201 | #qunit-tests > li:last-child {
202 | border-radius: 0 0 5px 5px;
203 | }
204 |
205 | #qunit-tests .fail { color: #000; background-color: #EE5757; }
206 | #qunit-tests .fail .test-name,
207 | #qunit-tests .fail .module-name { color: #000; }
208 |
209 | #qunit-tests .fail .test-actual { color: #EE5757; }
210 | #qunit-tests .fail .test-expected { color: #008000; }
211 |
212 | #qunit-banner.qunit-fail { background-color: #EE5757; }
213 |
214 |
215 | /** Result */
216 |
217 | #qunit-testresult {
218 | padding: 0.5em 0.5em 0.5em 2.5em;
219 |
220 | color: #2B81AF;
221 | background-color: #D2E0E6;
222 |
223 | border-bottom: 1px solid #FFF;
224 | }
225 | #qunit-testresult .module-name {
226 | font-weight: 700;
227 | }
228 |
229 | /** Fixture */
230 |
231 | #qunit-fixture {
232 | position: absolute;
233 | top: -10000px;
234 | left: -10000px;
235 | width: 1000px;
236 | height: 1000px;
237 | }
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module("Genel İşlemler");
4 |
5 | function sleep(milliseconds) {
6 | var start = new Date().getTime();
7 | for (var i = 0; i < 1e7; i++) {
8 | if ((new Date().getTime() - start) > milliseconds){
9 | break;
10 | }
11 | }
12 | }
13 |
14 | // Input
15 | var input = $('#inputMoney');
16 | input.turkLirasi({
17 | debug: true
18 | });
19 |
20 |
21 | test("Kurulum", function() {
22 |
23 | ok( input.val() == "0,00 TL", "Kurulum başarılı." );
24 |
25 | });
26 |
27 |
28 | test("Rakam", function() {
29 |
30 | input.trigger('focus');
31 | input.trigger($.Event("keydown", {keyCode: 49}));
32 | input.trigger($.Event("keydown", {keyCode: 49}));
33 | input.trigger($.Event("keydown", {keyCode: 49}));
34 | input.trigger('focusout');
35 | ok( input.val() == "111,00 TL", "Rakam yazma işlemi başarılı." );
36 |
37 | });
38 |
39 | test("Seçili Silme", function() {
40 |
41 | input.trigger('click');
42 | input.trigger($.Event("keydown", {keyCode: 8}));
43 | ok( input.val() == "0,00 TL", "Seçili haldeyken topluca silme işlemi başarılı." );
44 |
45 | });
46 |
47 | test("setAgain Methodu", function() {
48 | input.trigger('click');
49 | input.trigger('setAgain', ['123,3']);
50 | input.trigger('focusout');
51 | ok( input.val() == "123,30 TL", "JS ile değer güncelleme methodu test başarılı." );
52 | });
53 |
54 |
55 | test("Virgüllü Sayı", function() {
56 | input.trigger('click');
57 | input.trigger($.Event("keydown", {keyCode: 49}));
58 | input.trigger($.Event("keydown", {keyCode: 188}));
59 | input.trigger($.Event("keydown", {keyCode: 50}));
60 | input.trigger($.Event("keydown", {keyCode: 50}));
61 | input.trigger('focusout');
62 | ok( input.val() == "1,22 TL", "Virgüllü sayı başarılı." );
63 | });
64 |
65 | test("Virgüllü Sayı (Otomatik Tamamlamalı)", function() {
66 | input.trigger('click');
67 | input.trigger($.Event("keydown", {keyCode: 49}));
68 | input.trigger($.Event("keydown", {keyCode: 188}));
69 | input.trigger($.Event("keydown", {keyCode: 53}));
70 | input.trigger('focusout');
71 | ok( input.val() == "1,50 TL", "Virgüllü sayı otomatik tamamlama işlemi başarılı." );
72 | });
73 |
74 | test("Binler Basamağı", function() {
75 | input.trigger('click');
76 | input.trigger($.Event("keydown", {keyCode: 49}));
77 | input.trigger($.Event("keydown", {keyCode: 48}));
78 | input.trigger($.Event("keydown", {keyCode: 48}));
79 | input.trigger($.Event("keydown", {keyCode: 48}));
80 | input.trigger('focusout');
81 | ok( input.val() == "1.000,00 TL", "Binler basamağının formatlı yazdırılması işlemi başarılı." );
82 | });
83 |
84 | test("Fazladan Ondalık Yazma", function() {
85 | input.trigger('click');
86 | input.trigger($.Event("keydown", {keyCode: 49}));
87 | input.trigger($.Event("keydown", {keyCode: 188}));
88 | input.trigger($.Event("keydown", {keyCode: 53}));
89 | input.trigger($.Event("keydown", {keyCode: 53}));
90 | input.trigger($.Event("keydown", {keyCode: 53}));
91 | input.trigger('focusout');
92 | ok( input.val() == "1,55 TL", "Ondalık sınırı başarılı." );
93 | });
94 |
95 | module("Son Ek");
96 |
97 | test("Değiştirme", function() {
98 | input.data('options').suffix = 'YTL';
99 | input.trigger('click');
100 | input.trigger($.Event("keydown", {keyCode: 49}));
101 | input.trigger('focusout');
102 | ok( input.val() == "1,00 YTL", "Son ek değiştirme işlemi başarılı." );
103 | });
104 |
105 | test("Gizleme", function() {
106 | input.data('options').suffix = false;
107 | input.trigger('click');
108 | input.trigger($.Event("keydown", {keyCode: 49}));
109 | input.trigger('focusout');
110 | ok( input.val() == "1,00", "Son ek gizleme işlemi başarılı." );
111 | });
112 |
113 | module("Ondalık");
114 |
115 | test("Değiştirme", function() {
116 | input.data('options').maxDecimalCount = 3;
117 | input.trigger('click');
118 | input.trigger($.Event("keydown", {keyCode: 49}));
119 | input.trigger('focusout');
120 | ok( input.val() == "1,000", "Maksimum ondalık limiti değiştirme işlemi başarılı." );
121 | });
122 |
123 | test("Gizleme", function() {
124 | input.data('options').allowedDecimal = false;
125 | input.trigger('click');
126 | input.trigger($.Event("keydown", {keyCode: 49}));
127 | input.trigger('focusout');
128 | ok( input.val() == "1", "Ondalık gizleme başarılı." );
129 | });
130 |
131 | test("Engelleme", function() {
132 | input.data('options').allowedDecimal = false;
133 | input.trigger('click');
134 | input.trigger($.Event("keydown", {keyCode: 49}));
135 | input.trigger($.Event("keydown", {keyCode: 188}));
136 | input.trigger('focusout');
137 | ok( input.val() == "1", "Ondalık girişi engelleme işlemi başarılı." );
138 | });
139 |
140 | test("Otomatik Ondalık Tamamlama (Kapama)", function() {
141 | input.data('options').allowedDecimal = true;
142 | input.data('options').maxDecimalCount = 2;
143 | input.data('options').autoFillDecimal = false;
144 | input.trigger('click');
145 | input.trigger($.Event("keydown", {keyCode: 49}));
146 | input.trigger('focusout');
147 | ok( input.val() == "1", "Otomatik ondalık tamamlama işleminin kapatılması başarılı." );
148 | });
149 |
150 | test("Otomatik Ondalık Tamamlama (Açma)", function() {
151 | input.data('options').allowedDecimal = true;
152 | input.data('options').maxDecimalCount = 2;
153 | input.data('options').autoFillDecimal = true;
154 | input.trigger('click');
155 | input.trigger($.Event("keydown", {keyCode: 49}));
156 | input.trigger('focusout');
157 | ok( input.val() == "1,00", "Otomatik ondalık tamamlama işleminin açılması başarılı." );
158 | });
159 |
160 | module("Formatlama");
161 |
162 | test("Kapalı", function() {
163 | input.data('options').formatted = false;
164 | input.trigger('click');
165 | input.trigger($.Event("keydown", {keyCode: 49}));
166 | input.trigger($.Event("keydown", {keyCode: 48}));
167 | input.trigger($.Event("keydown", {keyCode: 48}));
168 | input.trigger($.Event("keydown", {keyCode: 48}));
169 | input.trigger('focusout');
170 | ok( input.val() == "1000,00", "Formasız yazdırma işlemi başarılı." );
171 | });
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
--------------------------------------------------------------------------------
/dist/jquery.turkLirasi.min.js:
--------------------------------------------------------------------------------
1 | $.fn.turkLirasi=function(options){var settings=$.extend({allowedDecimal:true,maxDecimalCount:2,autoFillDecimal:true,suffix:'TL',formatted:true,autoSelect:true,align:'right',_eventAfterSet:false,_eventFocusOut:false,debug:false},options);var number_format=function(number,decimals,dec_point,thousands_sep){number=(number+'').replace(/[^0-9+\-Ee.]/g,'');var n=!isFinite(+number)?0:+number,prec=!isFinite(+decimals)?0:Math.abs(decimals),sep=(typeof thousands_sep==='undefined')?',':thousands_sep,dec=(typeof dec_point==='undefined')?'.':dec_point,s='',toFixedFix=function(n,prec){var k=Math.pow(10,prec);return''+Math.round(n*k)/k};s=(prec?toFixedFix(n,prec):''+Math.round(n)).split('.');if(s[0].length>3){s[0]=s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,sep)}if((s[1]||'').length=48&&key<=57)||(key>=96&&key<=105)){checkSelectAll(activeElement);var realValue=checkValue(activeElement.data('value'));var sections=realValue.split(',');if(getDecimalPoint(activeElement)){if(typeof sections[1]!='undefined'&§ions[1].length==options.maxDecimalCount){return false}}if(key>=48&&key<=57){realValue=realValue+String.fromCharCode(e.keyCode)}else{var values={96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"};realValue=realValue+values[key]}realValue=completeValue(realValue);setValue(activeElement,realValue);log(activeElement,activeElement.data())}else{return false}return false})})};
--------------------------------------------------------------------------------
/src/jquery.turkLirasi.js:
--------------------------------------------------------------------------------
1 | $.fn.turkLirasi = function (options) {
2 | /**
3 | * Genel Ayarlar
4 | */
5 | var settings = $.extend({
6 | allowedDecimal: true,
7 | maxDecimalCount: 2,
8 | autoFillDecimal: true,
9 | suffix: 'TL',
10 | formatted: true,
11 | autoSelect: true,
12 | align: 'right',
13 | _eventAfterSet: false,
14 | _eventFocusOut: false,
15 | debug: false
16 | }, options );
17 | // Sayıları formatlı yazdırma işlemi
18 | var number_format = function (number, decimals, dec_point, thousands_sep) {
19 | number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
20 | var n = !isFinite(+number) ? 0 : +number,
21 | prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
22 | sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
23 | dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
24 | s = '',
25 | toFixedFix = function (n, prec) {
26 | var k = Math.pow(10, prec);
27 | return '' + Math.round(n * k) / k;
28 | };
29 | // Fix for IE parseFloat(0.55).toFixed(0) = 0;
30 | s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
31 | if (s[0].length > 3) {
32 | s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
33 | }
34 | if ((s[1] || '').length < prec) {
35 | s[1] = s[1] || '';
36 | s[1] += new Array(prec - s[1].length + 1).join('0');
37 | }
38 | return s.join(dec);
39 | };
40 |
41 | var log = function(activeElement, value)
42 | {
43 | if (activeElement.data('options').debug) {
44 | console.log(value);
45 | }
46 | };
47 |
48 | // Değer kontrolü
49 | var checkValue = function (value)
50 | {
51 | if (value == 0) return '';
52 | return value;
53 | };
54 |
55 | // Görsel kontrlü
56 | var completeValue = function (value)
57 | {
58 | if (value == '') return 0;
59 | return value;
60 | };
61 |
62 | /**
63 | * Get Suffix
64 | *
65 | * Son ek ayarlanarak geriye gönderilir.
66 | */
67 | var getSuffix = function(suffix)
68 | {
69 | if (suffix == false) {
70 | return '';
71 | }
72 | return ' ' + suffix;
73 | };
74 |
75 | /**
76 | * Set Decimal Point
77 | *
78 | * Ondalık bölüm yazılmış mı kontrol edilir.
79 | */
80 | var setDecimalPoint = function(element, value)
81 | {
82 | element.data('decimalPoint', value);
83 | };
84 |
85 | /**
86 | * Get Decimal Point
87 | */
88 | var getDecimalPoint = function(element) {
89 | return element.data('decimalPoint');
90 | };
91 |
92 | /**
93 | * Set Default Value
94 | *
95 | * Elemente varsayılan değerin atanması işlemi
96 | */
97 | var setDefaultValue = function(element)
98 | {
99 | // Auto Fill işlemi kontrol edilir.
100 | if (element.data('options').allowedDecimal && element.data('options').autoFillDecimal) {
101 | // Veriler yazılır
102 | element.data('value', '0');
103 | element.data('visualValue', '0,00');
104 | // Artık sayı virgüllü olacağından değer set edilir.
105 | setDecimalPoint(element, true);
106 | element.val(element.data('visualValue') + getSuffix(element.data('options').suffix));
107 | } else {
108 | // Veriler yazılır
109 | element.data('value', 0);
110 | element.data('visualValue', '0');
111 | setDecimalPoint(element, false);
112 | // Görünen değer yazılır.
113 | element.val(element.data('visualValue') + getSuffix(element.data('options').suffix));
114 | }
115 |
116 | };
117 |
118 | var methodDefined = function(element, method)
119 | {
120 | if (element.data('options')[method] != false) {
121 | return true;
122 | }
123 | return false;
124 | };
125 |
126 | /**
127 | * Set Value
128 | *
129 | * Elemetin değerinin dğeiştirilmesi işlemi.
130 | */
131 | var setValue = function(element, value, initialization)
132 | {
133 | // Elementin data kısmına değer yazılır.
134 | value = '' + value;
135 | // Format dönüşümü yapılır.
136 | value = value.replace('.', ',');
137 | // Değer atanır.
138 | element.data('value', value);
139 | // Sayı bir daha parçalanır.
140 | sections = value.split(',');
141 | // Sayı formatlı mı yazdırılacak?
142 | if (element.data('options').formatted) {
143 | // Sayı formatlanır
144 | var visualValue = number_format(sections[0], 0, ',', '.');
145 | } else {
146 | // Sayı olduğu gibi yazılır.
147 | var visualValue = value;
148 | }
149 | // Ondalık kısım var mı_
150 | if (typeof sections[1] != 'undefined') {
151 | setDecimalPoint(element, true);
152 | // Ondalık kısım da birleştirilir
153 | visualValue += ',' + sections[1];
154 | } else {
155 | setDecimalPoint(element, false);
156 | }
157 | element.data('visualValue', visualValue);
158 | // Görünen değer yazılır.
159 | element.val(element.data('visualValue') + getSuffix(element.data('options').suffix));
160 | log(element, element.data());
161 | // Herhangi bir set sonrası parametre set edilmiş mi kontrol edilir.
162 | if (typeof initialization == 'undefined' && methodDefined(element, '_eventAfterSet')) {
163 | // Olay tetiklenir.
164 | element.trigger('change', element.data('options')._eventAfterSet);
165 | }
166 | };
167 |
168 | var checkSelectAll = function(element)
169 | {
170 | if (element.data('selectAll') == true) {
171 | setDefaultValue(element);
172 | element.data('selectAll', false);
173 | return true;
174 | }
175 | return false;
176 | };
177 |
178 | /**
179 | * Init
180 | *
181 | * Başlangıç aşamasında elementin kurulması işlemi.
182 | */
183 | var init = function(element, options)
184 | {
185 | // Ayarlar yapılır.
186 | element.data('options', options);
187 | // Elementin içindeki değer alınır.
188 | var firstValue = element.val();
189 | // Değer uygun formata göre düzenlenir.
190 | firstValue = firstValue.replace(/\./g,'').replace(',', '.');
191 | // Tip dönüşümü yapılır
192 | firstValue = parseFloat(firstValue);
193 | // Değer kontrol edilir.
194 | if (isNaN(firstValue)) {
195 | // Varsayılan değer atanır.
196 | setDefaultValue(element);
197 | } else {
198 | // Değer atanır.
199 | setValue(element, firstValue, true)
200 | }
201 | };
202 |
203 | // Ana bölüm
204 | return this.each(function () {
205 | // Aktif element belirlenir.
206 | var activeElement = $(this);
207 | // Varsayılan ayarlar alınır.
208 | var options = $.meta ? $.extend({}, settings, activeElement.data()) : settings;
209 |
210 | // Ondalık atayı yaplılır.
211 | setDecimalPoint(activeElement, false);
212 | // Sitil ayarı yapılır.
213 | activeElement.css('text-align', options.align);
214 | // Varsayılan olaran hiç seçilmemiş gibi işaretlenir.
215 | activeElement.data('selectAll', true);
216 | // Element kurulur
217 | init(activeElement, options);
218 |
219 | /**
220 | * Otomatik seçme fonksiyonu tanımlanır.
221 | */
222 | $(this).click(function () {
223 | // Otomatik olarak input içeriğinin seçilmesi
224 | if (activeElement.data('options').autoSelect) {
225 | activeElement.select();
226 | activeElement.data('selectAll', true);
227 | log(activeElement, 'Tüm metin seçildi.');
228 | }
229 | });
230 |
231 | /**
232 | * Focus Out
233 | */
234 | $(this).focusout(function() {
235 | // Auto Fill işlemi kontrol edilir.
236 | if (activeElement.data('options').allowedDecimal && activeElement.data('options').autoFillDecimal) {
237 | // Değer var mı kontrol edilir. Değer yoksa işlem yapılmaz.
238 | if (activeElement.data('value') != '' && activeElement.data('value') != '0') {
239 | // Değer parçalanırç
240 | sections = activeElement.data('value').split(',');
241 | // Virgüllü kısım var mı?
242 | if (typeof sections[1] == 'undefined') {
243 | // Yoksa istenilen ondalık kısım fark olur
244 | var fark = activeElement.data('options').maxDecimalCount;
245 | } else {
246 | // Virgüllü kısım varsa aradaki fark hesaplanır.
247 | var fark = activeElement.data('options').maxDecimalCount - sections[1].length;
248 | }
249 | var fillText = '';
250 | // Fark kadar 0 hazırlanır.
251 | for (var i=1; i<=fark; i ++) {
252 | fillText += '0';
253 | }
254 | // Daha önceden ondalık için virgül yazılmamışza yazılır.
255 | if (!getDecimalPoint(activeElement)) {
256 | fillText = ',' + fillText;
257 | }
258 | // Artık sayı virgüllü olacağından değer set edilir.
259 | setDecimalPoint(activeElement, true);
260 | // Veriler yazılır
261 | activeElement.data('value', activeElement.data('value') + fillText );
262 | activeElement.data('visualValue', activeElement.data('visualValue') + fillText );
263 | activeElement.val(activeElement.data('visualValue') + getSuffix(activeElement.data('options').suffix));
264 | }
265 | }
266 |
267 | // Herhangi bir parametre set edilmiş mi kontrol edilir.
268 | if (methodDefined(activeElement, '_eventFocusOut')) {
269 | // Olay tetiklenir.
270 | activeElement.trigger('change', activeElement.data('options')._eventFocusOut);
271 | }
272 | });
273 |
274 | /**
275 | * Focus
276 | */
277 | $(this).focus(function() {
278 | $(this).trigger('click');
279 | });
280 |
281 | /**
282 | * Javascript ile sonradan değer düzenleme bölümü.
283 | */
284 | $(this).on('setAgain', function(event, newValue) {
285 | // Veriler temizlenir.
286 | newValue = '' + newValue;
287 | newValue = newValue.replace(/\./g,'').replace(',', '.');
288 | setValue($(this), newValue);
289 | });
290 |
291 | // Keydown fonksiyonu tanımlanır.
292 | $(this).keydown(function (e) {
293 |
294 | var activeElement = $(this);
295 | /**
296 | * Suffix
297 | *
298 | * Görsel olarak sayı yazdırılırken bir son ek kullanılacak mı?
299 | */
300 | var suffix = '';
301 | if (options.suffix != '' && options.suffix != false) {
302 | // Son ek hazırlanır
303 | suffix = ' ' + options.suffix;
304 | }
305 | // Basılan tuş kaydedilir.
306 | var key = e.charCode || e.keyCode || 0;
307 | // Log işlemi yapılır.
308 | log(activeElement, 'Pressed Key Code: #' + key);
309 |
310 | // Karakter Kodu Kontrolü
311 | if ((e.ctrlKey || e.metaKey) && key == 86) {
312 | /**
313 | * Copy-Paste işlemi
314 | */
315 | // Elementin içeriği boşaltılıyor
316 | activeElement.val('');
317 | // Zamanlı bir fonksiyon oluşturuluyor.
318 | setTimeout(function (e) {
319 | // Yapıştırılan içerik alınır
320 | var realValue = activeElement.val();
321 | // Başka yerlerden alınan değerler düzeltilir.
322 | realValue = realValue.replace(/\./g,'').replace(',', '.');
323 | // Floata çevrilir
324 | realValue = parseFloat(realValue);
325 | // Değer set edilir.
326 | setValue(activeElement, realValue);
327 | }, 0);
328 | return true;
329 | } else if((e.ctrlKey || e.metaKey) && key == 67) {
330 | /**
331 | * Kopyala tuş kombinasyonu
332 | */
333 | return true;
334 | } else if (key == 9 || key == 39 || key == 40) {
335 | /**
336 | * Tab ya da sağ yön tuşu
337 | */
338 | activeElement.data('selectAll', false);
339 | return true;
340 | } else if (key == 8) {
341 | /**
342 | * Backspace tuşu
343 | */
344 | // Tüm metin seçiliyse metin silinir.
345 | if (checkSelectAll(activeElement)) {
346 | return false;
347 | }
348 | // Gerçek değer öğrenilir.
349 | var realValue = checkValue(activeElement.data('value'));
350 | // Silinmeye çalışılan değer decimal point mi?
351 | if (realValue.substr(realValue.length - 1) == ',') {
352 | // Yeniden decimal point eklenmesine izin verilir.
353 | setDecimalPoint(activeElement, false);
354 | }
355 | // Bir karakter silinir.
356 | realValue = realValue.substr(0, realValue.length - 1);
357 | // Değer kontrol edilir.
358 | realValue = completeValue(realValue);
359 | // Değer set edilir.
360 | setValue(activeElement, realValue);
361 | // Log
362 | log(activeElement, activeElement.data());
363 | } else if (key == 188 && activeElement.data('options').allowedDecimal && getDecimalPoint(activeElement) == false) {
364 | /**
365 | * Decimal Point (,) tuşu
366 | */
367 | // Gerçek değer alınır.
368 | var realValue = checkValue(activeElement.data('value'));
369 | // Virgül ilave edilir.
370 | realValue = realValue + ',';
371 | // Değer set edilir.
372 | setValue(activeElement, realValue);
373 | // Log işlemi
374 | log(activeElement, activeElement.data());
375 | } else if ( (key >= 48 && key <= 57) ||
376 | (key >= 96 && key <= 105) ) {
377 | /**
378 | * Sayısal Karakter
379 | */
380 | // Tüm metin seçiliyse metin silinir.
381 | checkSelectAll(activeElement);
382 | // Gerçek değer alınır.
383 | var realValue = checkValue(activeElement.data('value'));
384 | // Sayı tam ve ondalık olarak parçalanır
385 | var sections = realValue.split(',');
386 | // Şuana kadar yazılan değer ondalık dilim içeriyor mu?
387 | if (getDecimalPoint(activeElement)) {
388 | // Ondalık dilimin uzunluğu, izin verilen kadar mı?
389 | if (typeof sections[1] != 'undefined' && sections[1].length == options.maxDecimalCount) {
390 | // İzin verilenden daha fazla sayı ondalık yazılamaz.
391 | return false;
392 | }
393 | }
394 | // Basılan tuşun sayısal değeri ilave edilir.
395 | if (key >= 48 && key <= 57) {
396 | realValue = realValue + String.fromCharCode(e.keyCode);
397 | } else {
398 | var values = {96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8", 105: "9"};
399 | realValue = realValue + values[key];
400 | }
401 | // Değer kontrol edilir.
402 | realValue = completeValue(realValue);
403 | // Değer yazdırılır.
404 | setValue(activeElement, realValue);
405 | // Log işlemi.
406 | log(activeElement, activeElement.data());
407 | } else {
408 | return false;
409 | }
410 | return false;
411 | });
412 | });
413 |
414 | };
--------------------------------------------------------------------------------
/test/resources/qunit-1.14.0.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * QUnit 1.14.0
3 | * http://qunitjs.com/
4 | *
5 | * Copyright 2013 jQuery Foundation and other contributors
6 | * Released under the MIT license
7 | * http://jquery.org/license
8 | *
9 | * Date: 2014-01-31T16:40Z
10 | */
11 |
12 | (function( window ) {
13 |
14 | var QUnit,
15 | assert,
16 | config,
17 | onErrorFnPrev,
18 | testId = 0,
19 | fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
20 | toString = Object.prototype.toString,
21 | hasOwn = Object.prototype.hasOwnProperty,
22 | // Keep a local reference to Date (GH-283)
23 | Date = window.Date,
24 | setTimeout = window.setTimeout,
25 | clearTimeout = window.clearTimeout,
26 | defined = {
27 | document: typeof window.document !== "undefined",
28 | setTimeout: typeof window.setTimeout !== "undefined",
29 | sessionStorage: (function() {
30 | var x = "qunit-test-string";
31 | try {
32 | sessionStorage.setItem( x, x );
33 | sessionStorage.removeItem( x );
34 | return true;
35 | } catch( e ) {
36 | return false;
37 | }
38 | }())
39 | },
40 | /**
41 | * Provides a normalized error string, correcting an issue
42 | * with IE 7 (and prior) where Error.prototype.toString is
43 | * not properly implemented
44 | *
45 | * Based on http://es5.github.com/#x15.11.4.4
46 | *
47 | * @param {String|Error} error
48 | * @return {String} error message
49 | */
50 | errorString = function( error ) {
51 | var name, message,
52 | errorString = error.toString();
53 | if ( errorString.substring( 0, 7 ) === "[object" ) {
54 | name = error.name ? error.name.toString() : "Error";
55 | message = error.message ? error.message.toString() : "";
56 | if ( name && message ) {
57 | return name + ": " + message;
58 | } else if ( name ) {
59 | return name;
60 | } else if ( message ) {
61 | return message;
62 | } else {
63 | return "Error";
64 | }
65 | } else {
66 | return errorString;
67 | }
68 | },
69 | /**
70 | * Makes a clone of an object using only Array or Object as base,
71 | * and copies over the own enumerable properties.
72 | *
73 | * @param {Object} obj
74 | * @return {Object} New object with only the own properties (recursively).
75 | */
76 | objectValues = function( obj ) {
77 | // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
78 | /*jshint newcap: false */
79 | var key, val,
80 | vals = QUnit.is( "array", obj ) ? [] : {};
81 | for ( key in obj ) {
82 | if ( hasOwn.call( obj, key ) ) {
83 | val = obj[key];
84 | vals[key] = val === Object(val) ? objectValues(val) : val;
85 | }
86 | }
87 | return vals;
88 | };
89 |
90 |
91 | // Root QUnit object.
92 | // `QUnit` initialized at top of scope
93 | QUnit = {
94 |
95 | // call on start of module test to prepend name to all tests
96 | module: function( name, testEnvironment ) {
97 | config.currentModule = name;
98 | config.currentModuleTestEnvironment = testEnvironment;
99 | config.modules[name] = true;
100 | },
101 |
102 | asyncTest: function( testName, expected, callback ) {
103 | if ( arguments.length === 2 ) {
104 | callback = expected;
105 | expected = null;
106 | }
107 |
108 | QUnit.test( testName, expected, callback, true );
109 | },
110 |
111 | test: function( testName, expected, callback, async ) {
112 | var test,
113 | nameHtml = "" + escapeText( testName ) + "";
114 |
115 | if ( arguments.length === 2 ) {
116 | callback = expected;
117 | expected = null;
118 | }
119 |
120 | if ( config.currentModule ) {
121 | nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml;
122 | }
123 |
124 | test = new Test({
125 | nameHtml: nameHtml,
126 | testName: testName,
127 | expected: expected,
128 | async: async,
129 | callback: callback,
130 | module: config.currentModule,
131 | moduleTestEnvironment: config.currentModuleTestEnvironment,
132 | stack: sourceFromStacktrace( 2 )
133 | });
134 |
135 | if ( !validTest( test ) ) {
136 | return;
137 | }
138 |
139 | test.queue();
140 | },
141 |
142 | // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
143 | expect: function( asserts ) {
144 | if (arguments.length === 1) {
145 | config.current.expected = asserts;
146 | } else {
147 | return config.current.expected;
148 | }
149 | },
150 |
151 | start: function( count ) {
152 | // QUnit hasn't been initialized yet.
153 | // Note: RequireJS (et al) may delay onLoad
154 | if ( config.semaphore === undefined ) {
155 | QUnit.begin(function() {
156 | // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
157 | setTimeout(function() {
158 | QUnit.start( count );
159 | });
160 | });
161 | return;
162 | }
163 |
164 | config.semaphore -= count || 1;
165 | // don't start until equal number of stop-calls
166 | if ( config.semaphore > 0 ) {
167 | return;
168 | }
169 | // ignore if start is called more often then stop
170 | if ( config.semaphore < 0 ) {
171 | config.semaphore = 0;
172 | QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
173 | return;
174 | }
175 | // A slight delay, to avoid any current callbacks
176 | if ( defined.setTimeout ) {
177 | setTimeout(function() {
178 | if ( config.semaphore > 0 ) {
179 | return;
180 | }
181 | if ( config.timeout ) {
182 | clearTimeout( config.timeout );
183 | }
184 |
185 | config.blocking = false;
186 | process( true );
187 | }, 13);
188 | } else {
189 | config.blocking = false;
190 | process( true );
191 | }
192 | },
193 |
194 | stop: function( count ) {
195 | config.semaphore += count || 1;
196 | config.blocking = true;
197 |
198 | if ( config.testTimeout && defined.setTimeout ) {
199 | clearTimeout( config.timeout );
200 | config.timeout = setTimeout(function() {
201 | QUnit.ok( false, "Test timed out" );
202 | config.semaphore = 1;
203 | QUnit.start();
204 | }, config.testTimeout );
205 | }
206 | }
207 | };
208 |
209 | // We use the prototype to distinguish between properties that should
210 | // be exposed as globals (and in exports) and those that shouldn't
211 | (function() {
212 | function F() {}
213 | F.prototype = QUnit;
214 | QUnit = new F();
215 | // Make F QUnit's constructor so that we can add to the prototype later
216 | QUnit.constructor = F;
217 | }());
218 |
219 | /**
220 | * Config object: Maintain internal state
221 | * Later exposed as QUnit.config
222 | * `config` initialized at top of scope
223 | */
224 | config = {
225 | // The queue of tests to run
226 | queue: [],
227 |
228 | // block until document ready
229 | blocking: true,
230 |
231 | // when enabled, show only failing tests
232 | // gets persisted through sessionStorage and can be changed in UI via checkbox
233 | hidepassed: false,
234 |
235 | // by default, run previously failed tests first
236 | // very useful in combination with "Hide passed tests" checked
237 | reorder: true,
238 |
239 | // by default, modify document.title when suite is done
240 | altertitle: true,
241 |
242 | // by default, scroll to top of the page when suite is done
243 | scrolltop: true,
244 |
245 | // when enabled, all tests must call expect()
246 | requireExpects: false,
247 |
248 | // add checkboxes that are persisted in the query-string
249 | // when enabled, the id is set to `true` as a `QUnit.config` property
250 | urlConfig: [
251 | {
252 | id: "noglobals",
253 | label: "Check for Globals",
254 | tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
255 | },
256 | {
257 | id: "notrycatch",
258 | label: "No try-catch",
259 | tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
260 | }
261 | ],
262 |
263 | // Set of all modules.
264 | modules: {},
265 |
266 | // logging callback queues
267 | begin: [],
268 | done: [],
269 | log: [],
270 | testStart: [],
271 | testDone: [],
272 | moduleStart: [],
273 | moduleDone: []
274 | };
275 |
276 | // Initialize more QUnit.config and QUnit.urlParams
277 | (function() {
278 | var i, current,
279 | location = window.location || { search: "", protocol: "file:" },
280 | params = location.search.slice( 1 ).split( "&" ),
281 | length = params.length,
282 | urlParams = {};
283 |
284 | if ( params[ 0 ] ) {
285 | for ( i = 0; i < length; i++ ) {
286 | current = params[ i ].split( "=" );
287 | current[ 0 ] = decodeURIComponent( current[ 0 ] );
288 |
289 | // allow just a key to turn on a flag, e.g., test.html?noglobals
290 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
291 | if ( urlParams[ current[ 0 ] ] ) {
292 | urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
293 | } else {
294 | urlParams[ current[ 0 ] ] = current[ 1 ];
295 | }
296 | }
297 | }
298 |
299 | QUnit.urlParams = urlParams;
300 |
301 | // String search anywhere in moduleName+testName
302 | config.filter = urlParams.filter;
303 |
304 | // Exact match of the module name
305 | config.module = urlParams.module;
306 |
307 | config.testNumber = [];
308 | if ( urlParams.testNumber ) {
309 |
310 | // Ensure that urlParams.testNumber is an array
311 | urlParams.testNumber = [].concat( urlParams.testNumber );
312 | for ( i = 0; i < urlParams.testNumber.length; i++ ) {
313 | current = urlParams.testNumber[ i ];
314 | config.testNumber.push( parseInt( current, 10 ) );
315 | }
316 | }
317 |
318 | // Figure out if we're running the tests from a server or not
319 | QUnit.isLocal = location.protocol === "file:";
320 | }());
321 |
322 | extend( QUnit, {
323 |
324 | config: config,
325 |
326 | // Initialize the configuration options
327 | init: function() {
328 | extend( config, {
329 | stats: { all: 0, bad: 0 },
330 | moduleStats: { all: 0, bad: 0 },
331 | started: +new Date(),
332 | updateRate: 1000,
333 | blocking: false,
334 | autostart: true,
335 | autorun: false,
336 | filter: "",
337 | queue: [],
338 | semaphore: 1
339 | });
340 |
341 | var tests, banner, result,
342 | qunit = id( "qunit" );
343 |
344 | if ( qunit ) {
345 | qunit.innerHTML =
346 | "" +
347 | "" +
348 | "" +
349 | "" +
350 | "
";
351 | }
352 |
353 | tests = id( "qunit-tests" );
354 | banner = id( "qunit-banner" );
355 | result = id( "qunit-testresult" );
356 |
357 | if ( tests ) {
358 | tests.innerHTML = "";
359 | }
360 |
361 | if ( banner ) {
362 | banner.className = "";
363 | }
364 |
365 | if ( result ) {
366 | result.parentNode.removeChild( result );
367 | }
368 |
369 | if ( tests ) {
370 | result = document.createElement( "p" );
371 | result.id = "qunit-testresult";
372 | result.className = "result";
373 | tests.parentNode.insertBefore( result, tests );
374 | result.innerHTML = "Running...
";
375 | }
376 | },
377 |
378 | // Resets the test setup. Useful for tests that modify the DOM.
379 | /*
380 | DEPRECATED: Use multiple tests instead of resetting inside a test.
381 | Use testStart or testDone for custom cleanup.
382 | This method will throw an error in 2.0, and will be removed in 2.1
383 | */
384 | reset: function() {
385 | var fixture = id( "qunit-fixture" );
386 | if ( fixture ) {
387 | fixture.innerHTML = config.fixture;
388 | }
389 | },
390 |
391 | // Safe object type checking
392 | is: function( type, obj ) {
393 | return QUnit.objectType( obj ) === type;
394 | },
395 |
396 | objectType: function( obj ) {
397 | if ( typeof obj === "undefined" ) {
398 | return "undefined";
399 | }
400 |
401 | // Consider: typeof null === object
402 | if ( obj === null ) {
403 | return "null";
404 | }
405 |
406 | var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
407 | type = match && match[1] || "";
408 |
409 | switch ( type ) {
410 | case "Number":
411 | if ( isNaN(obj) ) {
412 | return "nan";
413 | }
414 | return "number";
415 | case "String":
416 | case "Boolean":
417 | case "Array":
418 | case "Date":
419 | case "RegExp":
420 | case "Function":
421 | return type.toLowerCase();
422 | }
423 | if ( typeof obj === "object" ) {
424 | return "object";
425 | }
426 | return undefined;
427 | },
428 |
429 | push: function( result, actual, expected, message ) {
430 | if ( !config.current ) {
431 | throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
432 | }
433 |
434 | var output, source,
435 | details = {
436 | module: config.current.module,
437 | name: config.current.testName,
438 | result: result,
439 | message: message,
440 | actual: actual,
441 | expected: expected
442 | };
443 |
444 | message = escapeText( message ) || ( result ? "okay" : "failed" );
445 | message = "" + message + "";
446 | output = message;
447 |
448 | if ( !result ) {
449 | expected = escapeText( QUnit.jsDump.parse(expected) );
450 | actual = escapeText( QUnit.jsDump.parse(actual) );
451 | output += "| Expected: | " + expected + " |
";
452 |
453 | if ( actual !== expected ) {
454 | output += "| Result: | " + actual + " |
";
455 | output += "| Diff: | " + QUnit.diff( expected, actual ) + " |
";
456 | }
457 |
458 | source = sourceFromStacktrace();
459 |
460 | if ( source ) {
461 | details.source = source;
462 | output += "| Source: | " + escapeText( source ) + " |
";
463 | }
464 |
465 | output += "
";
466 | }
467 |
468 | runLoggingCallbacks( "log", QUnit, details );
469 |
470 | config.current.assertions.push({
471 | result: !!result,
472 | message: output
473 | });
474 | },
475 |
476 | pushFailure: function( message, source, actual ) {
477 | if ( !config.current ) {
478 | throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
479 | }
480 |
481 | var output,
482 | details = {
483 | module: config.current.module,
484 | name: config.current.testName,
485 | result: false,
486 | message: message
487 | };
488 |
489 | message = escapeText( message ) || "error";
490 | message = "" + message + "";
491 | output = message;
492 |
493 | output += "";
494 |
495 | if ( actual ) {
496 | output += "| Result: | " + escapeText( actual ) + " |
";
497 | }
498 |
499 | if ( source ) {
500 | details.source = source;
501 | output += "| Source: | " + escapeText( source ) + " |
";
502 | }
503 |
504 | output += "
";
505 |
506 | runLoggingCallbacks( "log", QUnit, details );
507 |
508 | config.current.assertions.push({
509 | result: false,
510 | message: output
511 | });
512 | },
513 |
514 | url: function( params ) {
515 | params = extend( extend( {}, QUnit.urlParams ), params );
516 | var key,
517 | querystring = "?";
518 |
519 | for ( key in params ) {
520 | if ( hasOwn.call( params, key ) ) {
521 | querystring += encodeURIComponent( key ) + "=" +
522 | encodeURIComponent( params[ key ] ) + "&";
523 | }
524 | }
525 | return window.location.protocol + "//" + window.location.host +
526 | window.location.pathname + querystring.slice( 0, -1 );
527 | },
528 |
529 | extend: extend,
530 | id: id,
531 | addEvent: addEvent,
532 | addClass: addClass,
533 | hasClass: hasClass,
534 | removeClass: removeClass
535 | // load, equiv, jsDump, diff: Attached later
536 | });
537 |
538 | /**
539 | * @deprecated: Created for backwards compatibility with test runner that set the hook function
540 | * into QUnit.{hook}, instead of invoking it and passing the hook function.
541 | * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
542 | * Doing this allows us to tell if the following methods have been overwritten on the actual
543 | * QUnit object.
544 | */
545 | extend( QUnit.constructor.prototype, {
546 |
547 | // Logging callbacks; all receive a single argument with the listed properties
548 | // run test/logs.html for any related changes
549 | begin: registerLoggingCallback( "begin" ),
550 |
551 | // done: { failed, passed, total, runtime }
552 | done: registerLoggingCallback( "done" ),
553 |
554 | // log: { result, actual, expected, message }
555 | log: registerLoggingCallback( "log" ),
556 |
557 | // testStart: { name }
558 | testStart: registerLoggingCallback( "testStart" ),
559 |
560 | // testDone: { name, failed, passed, total, runtime }
561 | testDone: registerLoggingCallback( "testDone" ),
562 |
563 | // moduleStart: { name }
564 | moduleStart: registerLoggingCallback( "moduleStart" ),
565 |
566 | // moduleDone: { name, failed, passed, total }
567 | moduleDone: registerLoggingCallback( "moduleDone" )
568 | });
569 |
570 | if ( !defined.document || document.readyState === "complete" ) {
571 | config.autorun = true;
572 | }
573 |
574 | QUnit.load = function() {
575 | runLoggingCallbacks( "begin", QUnit, {} );
576 |
577 | // Initialize the config, saving the execution queue
578 | var banner, filter, i, j, label, len, main, ol, toolbar, val, selection,
579 | urlConfigContainer, moduleFilter, userAgent,
580 | numModules = 0,
581 | moduleNames = [],
582 | moduleFilterHtml = "",
583 | urlConfigHtml = "",
584 | oldconfig = extend( {}, config );
585 |
586 | QUnit.init();
587 | extend(config, oldconfig);
588 |
589 | config.blocking = false;
590 |
591 | len = config.urlConfig.length;
592 |
593 | for ( i = 0; i < len; i++ ) {
594 | val = config.urlConfig[i];
595 | if ( typeof val === "string" ) {
596 | val = {
597 | id: val,
598 | label: val
599 | };
600 | }
601 | config[ val.id ] = QUnit.urlParams[ val.id ];
602 | if ( !val.value || typeof val.value === "string" ) {
603 | urlConfigHtml += "";
611 | } else {
612 | urlConfigHtml += "";
646 | }
647 | }
648 | for ( i in config.modules ) {
649 | if ( config.modules.hasOwnProperty( i ) ) {
650 | moduleNames.push(i);
651 | }
652 | }
653 | numModules = moduleNames.length;
654 | moduleNames.sort( function( a, b ) {
655 | return a.localeCompare( b );
656 | });
657 | moduleFilterHtml += "";
668 |
669 | // `userAgent` initialized at top of scope
670 | userAgent = id( "qunit-userAgent" );
671 | if ( userAgent ) {
672 | userAgent.innerHTML = navigator.userAgent;
673 | }
674 |
675 | // `banner` initialized at top of scope
676 | banner = id( "qunit-header" );
677 | if ( banner ) {
678 | banner.innerHTML = "" + banner.innerHTML + " ";
679 | }
680 |
681 | // `toolbar` initialized at top of scope
682 | toolbar = id( "qunit-testrunner-toolbar" );
683 | if ( toolbar ) {
684 | // `filter` initialized at top of scope
685 | filter = document.createElement( "input" );
686 | filter.type = "checkbox";
687 | filter.id = "qunit-filter-pass";
688 |
689 | addEvent( filter, "click", function() {
690 | var tmp,
691 | ol = id( "qunit-tests" );
692 |
693 | if ( filter.checked ) {
694 | ol.className = ol.className + " hidepass";
695 | } else {
696 | tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
697 | ol.className = tmp.replace( / hidepass /, " " );
698 | }
699 | if ( defined.sessionStorage ) {
700 | if (filter.checked) {
701 | sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
702 | } else {
703 | sessionStorage.removeItem( "qunit-filter-passed-tests" );
704 | }
705 | }
706 | });
707 |
708 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
709 | filter.checked = true;
710 | // `ol` initialized at top of scope
711 | ol = id( "qunit-tests" );
712 | ol.className = ol.className + " hidepass";
713 | }
714 | toolbar.appendChild( filter );
715 |
716 | // `label` initialized at top of scope
717 | label = document.createElement( "label" );
718 | label.setAttribute( "for", "qunit-filter-pass" );
719 | label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
720 | label.innerHTML = "Hide passed tests";
721 | toolbar.appendChild( label );
722 |
723 | urlConfigContainer = document.createElement("span");
724 | urlConfigContainer.innerHTML = urlConfigHtml;
725 | // For oldIE support:
726 | // * Add handlers to the individual elements instead of the container
727 | // * Use "click" instead of "change" for checkboxes
728 | // * Fallback from event.target to event.srcElement
729 | addEvents( urlConfigContainer.getElementsByTagName("input"), "click", function( event ) {
730 | var params = {},
731 | target = event.target || event.srcElement;
732 | params[ target.name ] = target.checked ?
733 | target.defaultValue || true :
734 | undefined;
735 | window.location = QUnit.url( params );
736 | });
737 | addEvents( urlConfigContainer.getElementsByTagName("select"), "change", function( event ) {
738 | var params = {},
739 | target = event.target || event.srcElement;
740 | params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
741 | window.location = QUnit.url( params );
742 | });
743 | toolbar.appendChild( urlConfigContainer );
744 |
745 | if (numModules > 1) {
746 | moduleFilter = document.createElement( "span" );
747 | moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
748 | moduleFilter.innerHTML = moduleFilterHtml;
749 | addEvent( moduleFilter.lastChild, "change", function() {
750 | var selectBox = moduleFilter.getElementsByTagName("select")[0],
751 | selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
752 |
753 | window.location = QUnit.url({
754 | module: ( selectedModule === "" ) ? undefined : selectedModule,
755 | // Remove any existing filters
756 | filter: undefined,
757 | testNumber: undefined
758 | });
759 | });
760 | toolbar.appendChild(moduleFilter);
761 | }
762 | }
763 |
764 | // `main` initialized at top of scope
765 | main = id( "qunit-fixture" );
766 | if ( main ) {
767 | config.fixture = main.innerHTML;
768 | }
769 |
770 | if ( config.autostart ) {
771 | QUnit.start();
772 | }
773 | };
774 |
775 | if ( defined.document ) {
776 | addEvent( window, "load", QUnit.load );
777 | }
778 |
779 | // `onErrorFnPrev` initialized at top of scope
780 | // Preserve other handlers
781 | onErrorFnPrev = window.onerror;
782 |
783 | // Cover uncaught exceptions
784 | // Returning true will suppress the default browser handler,
785 | // returning false will let it run.
786 | window.onerror = function ( error, filePath, linerNr ) {
787 | var ret = false;
788 | if ( onErrorFnPrev ) {
789 | ret = onErrorFnPrev( error, filePath, linerNr );
790 | }
791 |
792 | // Treat return value as window.onerror itself does,
793 | // Only do our handling if not suppressed.
794 | if ( ret !== true ) {
795 | if ( QUnit.config.current ) {
796 | if ( QUnit.config.current.ignoreGlobalErrors ) {
797 | return true;
798 | }
799 | QUnit.pushFailure( error, filePath + ":" + linerNr );
800 | } else {
801 | QUnit.test( "global failure", extend( function() {
802 | QUnit.pushFailure( error, filePath + ":" + linerNr );
803 | }, { validTest: validTest } ) );
804 | }
805 | return false;
806 | }
807 |
808 | return ret;
809 | };
810 |
811 | function done() {
812 | config.autorun = true;
813 |
814 | // Log the last module results
815 | if ( config.previousModule ) {
816 | runLoggingCallbacks( "moduleDone", QUnit, {
817 | name: config.previousModule,
818 | failed: config.moduleStats.bad,
819 | passed: config.moduleStats.all - config.moduleStats.bad,
820 | total: config.moduleStats.all
821 | });
822 | }
823 | delete config.previousModule;
824 |
825 | var i, key,
826 | banner = id( "qunit-banner" ),
827 | tests = id( "qunit-tests" ),
828 | runtime = +new Date() - config.started,
829 | passed = config.stats.all - config.stats.bad,
830 | html = [
831 | "Tests completed in ",
832 | runtime,
833 | " milliseconds.
",
834 | "",
835 | passed,
836 | " assertions of ",
837 | config.stats.all,
838 | " passed, ",
839 | config.stats.bad,
840 | " failed."
841 | ].join( "" );
842 |
843 | if ( banner ) {
844 | banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
845 | }
846 |
847 | if ( tests ) {
848 | id( "qunit-testresult" ).innerHTML = html;
849 | }
850 |
851 | if ( config.altertitle && defined.document && document.title ) {
852 | // show ✖ for good, ✔ for bad suite result in title
853 | // use escape sequences in case file gets loaded with non-utf-8-charset
854 | document.title = [
855 | ( config.stats.bad ? "\u2716" : "\u2714" ),
856 | document.title.replace( /^[\u2714\u2716] /i, "" )
857 | ].join( " " );
858 | }
859 |
860 | // clear own sessionStorage items if all tests passed
861 | if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
862 | // `key` & `i` initialized at top of scope
863 | for ( i = 0; i < sessionStorage.length; i++ ) {
864 | key = sessionStorage.key( i++ );
865 | if ( key.indexOf( "qunit-test-" ) === 0 ) {
866 | sessionStorage.removeItem( key );
867 | }
868 | }
869 | }
870 |
871 | // scroll back to top to show results
872 | if ( config.scrolltop && window.scrollTo ) {
873 | window.scrollTo(0, 0);
874 | }
875 |
876 | runLoggingCallbacks( "done", QUnit, {
877 | failed: config.stats.bad,
878 | passed: passed,
879 | total: config.stats.all,
880 | runtime: runtime
881 | });
882 | }
883 |
884 | /** @return Boolean: true if this test should be ran */
885 | function validTest( test ) {
886 | var include,
887 | filter = config.filter && config.filter.toLowerCase(),
888 | module = config.module && config.module.toLowerCase(),
889 | fullName = ( test.module + ": " + test.testName ).toLowerCase();
890 |
891 | // Internally-generated tests are always valid
892 | if ( test.callback && test.callback.validTest === validTest ) {
893 | delete test.callback.validTest;
894 | return true;
895 | }
896 |
897 | if ( config.testNumber.length > 0 ) {
898 | if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
899 | return false;
900 | }
901 | }
902 |
903 | if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
904 | return false;
905 | }
906 |
907 | if ( !filter ) {
908 | return true;
909 | }
910 |
911 | include = filter.charAt( 0 ) !== "!";
912 | if ( !include ) {
913 | filter = filter.slice( 1 );
914 | }
915 |
916 | // If the filter matches, we need to honour include
917 | if ( fullName.indexOf( filter ) !== -1 ) {
918 | return include;
919 | }
920 |
921 | // Otherwise, do the opposite
922 | return !include;
923 | }
924 |
925 | // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
926 | // Later Safari and IE10 are supposed to support error.stack as well
927 | // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
928 | function extractStacktrace( e, offset ) {
929 | offset = offset === undefined ? 3 : offset;
930 |
931 | var stack, include, i;
932 |
933 | if ( e.stacktrace ) {
934 | // Opera
935 | return e.stacktrace.split( "\n" )[ offset + 3 ];
936 | } else if ( e.stack ) {
937 | // Firefox, Chrome
938 | stack = e.stack.split( "\n" );
939 | if (/^error$/i.test( stack[0] ) ) {
940 | stack.shift();
941 | }
942 | if ( fileName ) {
943 | include = [];
944 | for ( i = offset; i < stack.length; i++ ) {
945 | if ( stack[ i ].indexOf( fileName ) !== -1 ) {
946 | break;
947 | }
948 | include.push( stack[ i ] );
949 | }
950 | if ( include.length ) {
951 | return include.join( "\n" );
952 | }
953 | }
954 | return stack[ offset ];
955 | } else if ( e.sourceURL ) {
956 | // Safari, PhantomJS
957 | // hopefully one day Safari provides actual stacktraces
958 | // exclude useless self-reference for generated Error objects
959 | if ( /qunit.js$/.test( e.sourceURL ) ) {
960 | return;
961 | }
962 | // for actual exceptions, this is useful
963 | return e.sourceURL + ":" + e.line;
964 | }
965 | }
966 | function sourceFromStacktrace( offset ) {
967 | try {
968 | throw new Error();
969 | } catch ( e ) {
970 | return extractStacktrace( e, offset );
971 | }
972 | }
973 |
974 | /**
975 | * Escape text for attribute or text content.
976 | */
977 | function escapeText( s ) {
978 | if ( !s ) {
979 | return "";
980 | }
981 | s = s + "";
982 | // Both single quotes and double quotes (for attributes)
983 | return s.replace( /['"<>&]/g, function( s ) {
984 | switch( s ) {
985 | case "'":
986 | return "'";
987 | case "\"":
988 | return """;
989 | case "<":
990 | return "<";
991 | case ">":
992 | return ">";
993 | case "&":
994 | return "&";
995 | }
996 | });
997 | }
998 |
999 | function synchronize( callback, last ) {
1000 | config.queue.push( callback );
1001 |
1002 | if ( config.autorun && !config.blocking ) {
1003 | process( last );
1004 | }
1005 | }
1006 |
1007 | function process( last ) {
1008 | function next() {
1009 | process( last );
1010 | }
1011 | var start = new Date().getTime();
1012 | config.depth = config.depth ? config.depth + 1 : 1;
1013 |
1014 | while ( config.queue.length && !config.blocking ) {
1015 | if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
1016 | config.queue.shift()();
1017 | } else {
1018 | setTimeout( next, 13 );
1019 | break;
1020 | }
1021 | }
1022 | config.depth--;
1023 | if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
1024 | done();
1025 | }
1026 | }
1027 |
1028 | function saveGlobal() {
1029 | config.pollution = [];
1030 |
1031 | if ( config.noglobals ) {
1032 | for ( var key in window ) {
1033 | if ( hasOwn.call( window, key ) ) {
1034 | // in Opera sometimes DOM element ids show up here, ignore them
1035 | if ( /^qunit-test-output/.test( key ) ) {
1036 | continue;
1037 | }
1038 | config.pollution.push( key );
1039 | }
1040 | }
1041 | }
1042 | }
1043 |
1044 | function checkPollution() {
1045 | var newGlobals,
1046 | deletedGlobals,
1047 | old = config.pollution;
1048 |
1049 | saveGlobal();
1050 |
1051 | newGlobals = diff( config.pollution, old );
1052 | if ( newGlobals.length > 0 ) {
1053 | QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
1054 | }
1055 |
1056 | deletedGlobals = diff( old, config.pollution );
1057 | if ( deletedGlobals.length > 0 ) {
1058 | QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
1059 | }
1060 | }
1061 |
1062 | // returns a new Array with the elements that are in a but not in b
1063 | function diff( a, b ) {
1064 | var i, j,
1065 | result = a.slice();
1066 |
1067 | for ( i = 0; i < result.length; i++ ) {
1068 | for ( j = 0; j < b.length; j++ ) {
1069 | if ( result[i] === b[j] ) {
1070 | result.splice( i, 1 );
1071 | i--;
1072 | break;
1073 | }
1074 | }
1075 | }
1076 | return result;
1077 | }
1078 |
1079 | function extend( a, b ) {
1080 | for ( var prop in b ) {
1081 | if ( hasOwn.call( b, prop ) ) {
1082 | // Avoid "Member not found" error in IE8 caused by messing with window.constructor
1083 | if ( !( prop === "constructor" && a === window ) ) {
1084 | if ( b[ prop ] === undefined ) {
1085 | delete a[ prop ];
1086 | } else {
1087 | a[ prop ] = b[ prop ];
1088 | }
1089 | }
1090 | }
1091 | }
1092 |
1093 | return a;
1094 | }
1095 |
1096 | /**
1097 | * @param {HTMLElement} elem
1098 | * @param {string} type
1099 | * @param {Function} fn
1100 | */
1101 | function addEvent( elem, type, fn ) {
1102 | if ( elem.addEventListener ) {
1103 |
1104 | // Standards-based browsers
1105 | elem.addEventListener( type, fn, false );
1106 | } else if ( elem.attachEvent ) {
1107 |
1108 | // support: IE <9
1109 | elem.attachEvent( "on" + type, fn );
1110 | } else {
1111 |
1112 | // Caller must ensure support for event listeners is present
1113 | throw new Error( "addEvent() was called in a context without event listener support" );
1114 | }
1115 | }
1116 |
1117 | /**
1118 | * @param {Array|NodeList} elems
1119 | * @param {string} type
1120 | * @param {Function} fn
1121 | */
1122 | function addEvents( elems, type, fn ) {
1123 | var i = elems.length;
1124 | while ( i-- ) {
1125 | addEvent( elems[i], type, fn );
1126 | }
1127 | }
1128 |
1129 | function hasClass( elem, name ) {
1130 | return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
1131 | }
1132 |
1133 | function addClass( elem, name ) {
1134 | if ( !hasClass( elem, name ) ) {
1135 | elem.className += (elem.className ? " " : "") + name;
1136 | }
1137 | }
1138 |
1139 | function removeClass( elem, name ) {
1140 | var set = " " + elem.className + " ";
1141 | // Class name may appear multiple times
1142 | while ( set.indexOf(" " + name + " ") > -1 ) {
1143 | set = set.replace(" " + name + " " , " ");
1144 | }
1145 | // If possible, trim it for prettiness, but not necessarily
1146 | elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
1147 | }
1148 |
1149 | function id( name ) {
1150 | return defined.document && document.getElementById && document.getElementById( name );
1151 | }
1152 |
1153 | function registerLoggingCallback( key ) {
1154 | return function( callback ) {
1155 | config[key].push( callback );
1156 | };
1157 | }
1158 |
1159 | // Supports deprecated method of completely overwriting logging callbacks
1160 | function runLoggingCallbacks( key, scope, args ) {
1161 | var i, callbacks;
1162 | if ( QUnit.hasOwnProperty( key ) ) {
1163 | QUnit[ key ].call(scope, args );
1164 | } else {
1165 | callbacks = config[ key ];
1166 | for ( i = 0; i < callbacks.length; i++ ) {
1167 | callbacks[ i ].call( scope, args );
1168 | }
1169 | }
1170 | }
1171 |
1172 | // from jquery.js
1173 | function inArray( elem, array ) {
1174 | if ( array.indexOf ) {
1175 | return array.indexOf( elem );
1176 | }
1177 |
1178 | for ( var i = 0, length = array.length; i < length; i++ ) {
1179 | if ( array[ i ] === elem ) {
1180 | return i;
1181 | }
1182 | }
1183 |
1184 | return -1;
1185 | }
1186 |
1187 | function Test( settings ) {
1188 | extend( this, settings );
1189 | this.assertions = [];
1190 | this.testNumber = ++Test.count;
1191 | }
1192 |
1193 | Test.count = 0;
1194 |
1195 | Test.prototype = {
1196 | init: function() {
1197 | var a, b, li,
1198 | tests = id( "qunit-tests" );
1199 |
1200 | if ( tests ) {
1201 | b = document.createElement( "strong" );
1202 | b.innerHTML = this.nameHtml;
1203 |
1204 | // `a` initialized at top of scope
1205 | a = document.createElement( "a" );
1206 | a.innerHTML = "Rerun";
1207 | a.href = QUnit.url({ testNumber: this.testNumber });
1208 |
1209 | li = document.createElement( "li" );
1210 | li.appendChild( b );
1211 | li.appendChild( a );
1212 | li.className = "running";
1213 | li.id = this.id = "qunit-test-output" + testId++;
1214 |
1215 | tests.appendChild( li );
1216 | }
1217 | },
1218 | setup: function() {
1219 | if (
1220 | // Emit moduleStart when we're switching from one module to another
1221 | this.module !== config.previousModule ||
1222 | // They could be equal (both undefined) but if the previousModule property doesn't
1223 | // yet exist it means this is the first test in a suite that isn't wrapped in a
1224 | // module, in which case we'll just emit a moduleStart event for 'undefined'.
1225 | // Without this, reporters can get testStart before moduleStart which is a problem.
1226 | !hasOwn.call( config, "previousModule" )
1227 | ) {
1228 | if ( hasOwn.call( config, "previousModule" ) ) {
1229 | runLoggingCallbacks( "moduleDone", QUnit, {
1230 | name: config.previousModule,
1231 | failed: config.moduleStats.bad,
1232 | passed: config.moduleStats.all - config.moduleStats.bad,
1233 | total: config.moduleStats.all
1234 | });
1235 | }
1236 | config.previousModule = this.module;
1237 | config.moduleStats = { all: 0, bad: 0 };
1238 | runLoggingCallbacks( "moduleStart", QUnit, {
1239 | name: this.module
1240 | });
1241 | }
1242 |
1243 | config.current = this;
1244 |
1245 | this.testEnvironment = extend({
1246 | setup: function() {},
1247 | teardown: function() {}
1248 | }, this.moduleTestEnvironment );
1249 |
1250 | this.started = +new Date();
1251 | runLoggingCallbacks( "testStart", QUnit, {
1252 | name: this.testName,
1253 | module: this.module
1254 | });
1255 |
1256 | /*jshint camelcase:false */
1257 |
1258 |
1259 | /**
1260 | * Expose the current test environment.
1261 | *
1262 | * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
1263 | */
1264 | QUnit.current_testEnvironment = this.testEnvironment;
1265 |
1266 | /*jshint camelcase:true */
1267 |
1268 | if ( !config.pollution ) {
1269 | saveGlobal();
1270 | }
1271 | if ( config.notrycatch ) {
1272 | this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
1273 | return;
1274 | }
1275 | try {
1276 | this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
1277 | } catch( e ) {
1278 | QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
1279 | }
1280 | },
1281 | run: function() {
1282 | config.current = this;
1283 |
1284 | var running = id( "qunit-testresult" );
1285 |
1286 | if ( running ) {
1287 | running.innerHTML = "Running:
" + this.nameHtml;
1288 | }
1289 |
1290 | if ( this.async ) {
1291 | QUnit.stop();
1292 | }
1293 |
1294 | this.callbackStarted = +new Date();
1295 |
1296 | if ( config.notrycatch ) {
1297 | this.callback.call( this.testEnvironment, QUnit.assert );
1298 | this.callbackRuntime = +new Date() - this.callbackStarted;
1299 | return;
1300 | }
1301 |
1302 | try {
1303 | this.callback.call( this.testEnvironment, QUnit.assert );
1304 | this.callbackRuntime = +new Date() - this.callbackStarted;
1305 | } catch( e ) {
1306 | this.callbackRuntime = +new Date() - this.callbackStarted;
1307 |
1308 | QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
1309 | // else next test will carry the responsibility
1310 | saveGlobal();
1311 |
1312 | // Restart the tests if they're blocking
1313 | if ( config.blocking ) {
1314 | QUnit.start();
1315 | }
1316 | }
1317 | },
1318 | teardown: function() {
1319 | config.current = this;
1320 | if ( config.notrycatch ) {
1321 | if ( typeof this.callbackRuntime === "undefined" ) {
1322 | this.callbackRuntime = +new Date() - this.callbackStarted;
1323 | }
1324 | this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
1325 | return;
1326 | } else {
1327 | try {
1328 | this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
1329 | } catch( e ) {
1330 | QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
1331 | }
1332 | }
1333 | checkPollution();
1334 | },
1335 | finish: function() {
1336 | config.current = this;
1337 | if ( config.requireExpects && this.expected === null ) {
1338 | QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
1339 | } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
1340 | QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
1341 | } else if ( this.expected === null && !this.assertions.length ) {
1342 | QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
1343 | }
1344 |
1345 | var i, assertion, a, b, time, li, ol,
1346 | test = this,
1347 | good = 0,
1348 | bad = 0,
1349 | tests = id( "qunit-tests" );
1350 |
1351 | this.runtime = +new Date() - this.started;
1352 | config.stats.all += this.assertions.length;
1353 | config.moduleStats.all += this.assertions.length;
1354 |
1355 | if ( tests ) {
1356 | ol = document.createElement( "ol" );
1357 | ol.className = "qunit-assert-list";
1358 |
1359 | for ( i = 0; i < this.assertions.length; i++ ) {
1360 | assertion = this.assertions[i];
1361 |
1362 | li = document.createElement( "li" );
1363 | li.className = assertion.result ? "pass" : "fail";
1364 | li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
1365 | ol.appendChild( li );
1366 |
1367 | if ( assertion.result ) {
1368 | good++;
1369 | } else {
1370 | bad++;
1371 | config.stats.bad++;
1372 | config.moduleStats.bad++;
1373 | }
1374 | }
1375 |
1376 | // store result when possible
1377 | if ( QUnit.config.reorder && defined.sessionStorage ) {
1378 | if ( bad ) {
1379 | sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
1380 | } else {
1381 | sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
1382 | }
1383 | }
1384 |
1385 | if ( bad === 0 ) {
1386 | addClass( ol, "qunit-collapsed" );
1387 | }
1388 |
1389 | // `b` initialized at top of scope
1390 | b = document.createElement( "strong" );
1391 | b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
1392 |
1393 | addEvent(b, "click", function() {
1394 | var next = b.parentNode.lastChild,
1395 | collapsed = hasClass( next, "qunit-collapsed" );
1396 | ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
1397 | });
1398 |
1399 | addEvent(b, "dblclick", function( e ) {
1400 | var target = e && e.target ? e.target : window.event.srcElement;
1401 | if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
1402 | target = target.parentNode;
1403 | }
1404 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
1405 | window.location = QUnit.url({ testNumber: test.testNumber });
1406 | }
1407 | });
1408 |
1409 | // `time` initialized at top of scope
1410 | time = document.createElement( "span" );
1411 | time.className = "runtime";
1412 | time.innerHTML = this.runtime + " ms";
1413 |
1414 | // `li` initialized at top of scope
1415 | li = id( this.id );
1416 | li.className = bad ? "fail" : "pass";
1417 | li.removeChild( li.firstChild );
1418 | a = li.firstChild;
1419 | li.appendChild( b );
1420 | li.appendChild( a );
1421 | li.appendChild( time );
1422 | li.appendChild( ol );
1423 |
1424 | } else {
1425 | for ( i = 0; i < this.assertions.length; i++ ) {
1426 | if ( !this.assertions[i].result ) {
1427 | bad++;
1428 | config.stats.bad++;
1429 | config.moduleStats.bad++;
1430 | }
1431 | }
1432 | }
1433 |
1434 | runLoggingCallbacks( "testDone", QUnit, {
1435 | name: this.testName,
1436 | module: this.module,
1437 | failed: bad,
1438 | passed: this.assertions.length - bad,
1439 | total: this.assertions.length,
1440 | runtime: this.runtime,
1441 | // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
1442 | duration: this.runtime
1443 | });
1444 |
1445 | QUnit.reset();
1446 |
1447 | config.current = undefined;
1448 | },
1449 |
1450 | queue: function() {
1451 | var bad,
1452 | test = this;
1453 |
1454 | synchronize(function() {
1455 | test.init();
1456 | });
1457 | function run() {
1458 | // each of these can by async
1459 | synchronize(function() {
1460 | test.setup();
1461 | });
1462 | synchronize(function() {
1463 | test.run();
1464 | });
1465 | synchronize(function() {
1466 | test.teardown();
1467 | });
1468 | synchronize(function() {
1469 | test.finish();
1470 | });
1471 | }
1472 |
1473 | // `bad` initialized at top of scope
1474 | // defer when previous test run passed, if storage is available
1475 | bad = QUnit.config.reorder && defined.sessionStorage &&
1476 | +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
1477 |
1478 | if ( bad ) {
1479 | run();
1480 | } else {
1481 | synchronize( run, true );
1482 | }
1483 | }
1484 | };
1485 |
1486 | // `assert` initialized at top of scope
1487 | // Assert helpers
1488 | // All of these must either call QUnit.push() or manually do:
1489 | // - runLoggingCallbacks( "log", .. );
1490 | // - config.current.assertions.push({ .. });
1491 | assert = QUnit.assert = {
1492 | /**
1493 | * Asserts rough true-ish result.
1494 | * @name ok
1495 | * @function
1496 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
1497 | */
1498 | ok: function( result, msg ) {
1499 | if ( !config.current ) {
1500 | throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
1501 | }
1502 | result = !!result;
1503 | msg = msg || ( result ? "okay" : "failed" );
1504 |
1505 | var source,
1506 | details = {
1507 | module: config.current.module,
1508 | name: config.current.testName,
1509 | result: result,
1510 | message: msg
1511 | };
1512 |
1513 | msg = "" + escapeText( msg ) + "";
1514 |
1515 | if ( !result ) {
1516 | source = sourceFromStacktrace( 2 );
1517 | if ( source ) {
1518 | details.source = source;
1519 | msg += "| Source: | " +
1520 | escapeText( source ) +
1521 | " |
|---|
";
1522 | }
1523 | }
1524 | runLoggingCallbacks( "log", QUnit, details );
1525 | config.current.assertions.push({
1526 | result: result,
1527 | message: msg
1528 | });
1529 | },
1530 |
1531 | /**
1532 | * Assert that the first two arguments are equal, with an optional message.
1533 | * Prints out both actual and expected values.
1534 | * @name equal
1535 | * @function
1536 | * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
1537 | */
1538 | equal: function( actual, expected, message ) {
1539 | /*jshint eqeqeq:false */
1540 | QUnit.push( expected == actual, actual, expected, message );
1541 | },
1542 |
1543 | /**
1544 | * @name notEqual
1545 | * @function
1546 | */
1547 | notEqual: function( actual, expected, message ) {
1548 | /*jshint eqeqeq:false */
1549 | QUnit.push( expected != actual, actual, expected, message );
1550 | },
1551 |
1552 | /**
1553 | * @name propEqual
1554 | * @function
1555 | */
1556 | propEqual: function( actual, expected, message ) {
1557 | actual = objectValues(actual);
1558 | expected = objectValues(expected);
1559 | QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
1560 | },
1561 |
1562 | /**
1563 | * @name notPropEqual
1564 | * @function
1565 | */
1566 | notPropEqual: function( actual, expected, message ) {
1567 | actual = objectValues(actual);
1568 | expected = objectValues(expected);
1569 | QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
1570 | },
1571 |
1572 | /**
1573 | * @name deepEqual
1574 | * @function
1575 | */
1576 | deepEqual: function( actual, expected, message ) {
1577 | QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
1578 | },
1579 |
1580 | /**
1581 | * @name notDeepEqual
1582 | * @function
1583 | */
1584 | notDeepEqual: function( actual, expected, message ) {
1585 | QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
1586 | },
1587 |
1588 | /**
1589 | * @name strictEqual
1590 | * @function
1591 | */
1592 | strictEqual: function( actual, expected, message ) {
1593 | QUnit.push( expected === actual, actual, expected, message );
1594 | },
1595 |
1596 | /**
1597 | * @name notStrictEqual
1598 | * @function
1599 | */
1600 | notStrictEqual: function( actual, expected, message ) {
1601 | QUnit.push( expected !== actual, actual, expected, message );
1602 | },
1603 |
1604 | "throws": function( block, expected, message ) {
1605 | var actual,
1606 | expectedOutput = expected,
1607 | ok = false;
1608 |
1609 | // 'expected' is optional
1610 | if ( !message && typeof expected === "string" ) {
1611 | message = expected;
1612 | expected = null;
1613 | }
1614 |
1615 | config.current.ignoreGlobalErrors = true;
1616 | try {
1617 | block.call( config.current.testEnvironment );
1618 | } catch (e) {
1619 | actual = e;
1620 | }
1621 | config.current.ignoreGlobalErrors = false;
1622 |
1623 | if ( actual ) {
1624 |
1625 | // we don't want to validate thrown error
1626 | if ( !expected ) {
1627 | ok = true;
1628 | expectedOutput = null;
1629 |
1630 | // expected is an Error object
1631 | } else if ( expected instanceof Error ) {
1632 | ok = actual instanceof Error &&
1633 | actual.name === expected.name &&
1634 | actual.message === expected.message;
1635 |
1636 | // expected is a regexp
1637 | } else if ( QUnit.objectType( expected ) === "regexp" ) {
1638 | ok = expected.test( errorString( actual ) );
1639 |
1640 | // expected is a string
1641 | } else if ( QUnit.objectType( expected ) === "string" ) {
1642 | ok = expected === errorString( actual );
1643 |
1644 | // expected is a constructor
1645 | } else if ( actual instanceof expected ) {
1646 | ok = true;
1647 |
1648 | // expected is a validation function which returns true is validation passed
1649 | } else if ( expected.call( {}, actual ) === true ) {
1650 | expectedOutput = null;
1651 | ok = true;
1652 | }
1653 |
1654 | QUnit.push( ok, actual, expectedOutput, message );
1655 | } else {
1656 | QUnit.pushFailure( message, null, "No exception was thrown." );
1657 | }
1658 | }
1659 | };
1660 |
1661 | /**
1662 | * @deprecated since 1.8.0
1663 | * Kept assertion helpers in root for backwards compatibility.
1664 | */
1665 | extend( QUnit.constructor.prototype, assert );
1666 |
1667 | /**
1668 | * @deprecated since 1.9.0
1669 | * Kept to avoid TypeErrors for undefined methods.
1670 | */
1671 | QUnit.constructor.prototype.raises = function() {
1672 | QUnit.push( false, false, false, "QUnit.raises has been deprecated since 2012 (fad3c1ea), use QUnit.throws instead" );
1673 | };
1674 |
1675 | /**
1676 | * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
1677 | * Kept to avoid TypeErrors for undefined methods.
1678 | */
1679 | QUnit.constructor.prototype.equals = function() {
1680 | QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
1681 | };
1682 | QUnit.constructor.prototype.same = function() {
1683 | QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
1684 | };
1685 |
1686 | // Test for equality any JavaScript type.
1687 | // Author: Philippe Rathé
1688 | QUnit.equiv = (function() {
1689 |
1690 | // Call the o related callback with the given arguments.
1691 | function bindCallbacks( o, callbacks, args ) {
1692 | var prop = QUnit.objectType( o );
1693 | if ( prop ) {
1694 | if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1695 | return callbacks[ prop ].apply( callbacks, args );
1696 | } else {
1697 | return callbacks[ prop ]; // or undefined
1698 | }
1699 | }
1700 | }
1701 |
1702 | // the real equiv function
1703 | var innerEquiv,
1704 | // stack to decide between skip/abort functions
1705 | callers = [],
1706 | // stack to avoiding loops from circular referencing
1707 | parents = [],
1708 | parentsB = [],
1709 |
1710 | getProto = Object.getPrototypeOf || function ( obj ) {
1711 | /*jshint camelcase:false */
1712 | return obj.__proto__;
1713 | },
1714 | callbacks = (function () {
1715 |
1716 | // for string, boolean, number and null
1717 | function useStrictEquality( b, a ) {
1718 | /*jshint eqeqeq:false */
1719 | if ( b instanceof a.constructor || a instanceof b.constructor ) {
1720 | // to catch short annotation VS 'new' annotation of a
1721 | // declaration
1722 | // e.g. var i = 1;
1723 | // var j = new Number(1);
1724 | return a == b;
1725 | } else {
1726 | return a === b;
1727 | }
1728 | }
1729 |
1730 | return {
1731 | "string": useStrictEquality,
1732 | "boolean": useStrictEquality,
1733 | "number": useStrictEquality,
1734 | "null": useStrictEquality,
1735 | "undefined": useStrictEquality,
1736 |
1737 | "nan": function( b ) {
1738 | return isNaN( b );
1739 | },
1740 |
1741 | "date": function( b, a ) {
1742 | return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1743 | },
1744 |
1745 | "regexp": function( b, a ) {
1746 | return QUnit.objectType( b ) === "regexp" &&
1747 | // the regex itself
1748 | a.source === b.source &&
1749 | // and its modifiers
1750 | a.global === b.global &&
1751 | // (gmi) ...
1752 | a.ignoreCase === b.ignoreCase &&
1753 | a.multiline === b.multiline &&
1754 | a.sticky === b.sticky;
1755 | },
1756 |
1757 | // - skip when the property is a method of an instance (OOP)
1758 | // - abort otherwise,
1759 | // initial === would have catch identical references anyway
1760 | "function": function() {
1761 | var caller = callers[callers.length - 1];
1762 | return caller !== Object && typeof caller !== "undefined";
1763 | },
1764 |
1765 | "array": function( b, a ) {
1766 | var i, j, len, loop, aCircular, bCircular;
1767 |
1768 | // b could be an object literal here
1769 | if ( QUnit.objectType( b ) !== "array" ) {
1770 | return false;
1771 | }
1772 |
1773 | len = a.length;
1774 | if ( len !== b.length ) {
1775 | // safe and faster
1776 | return false;
1777 | }
1778 |
1779 | // track reference to avoid circular references
1780 | parents.push( a );
1781 | parentsB.push( b );
1782 | for ( i = 0; i < len; i++ ) {
1783 | loop = false;
1784 | for ( j = 0; j < parents.length; j++ ) {
1785 | aCircular = parents[j] === a[i];
1786 | bCircular = parentsB[j] === b[i];
1787 | if ( aCircular || bCircular ) {
1788 | if ( a[i] === b[i] || aCircular && bCircular ) {
1789 | loop = true;
1790 | } else {
1791 | parents.pop();
1792 | parentsB.pop();
1793 | return false;
1794 | }
1795 | }
1796 | }
1797 | if ( !loop && !innerEquiv(a[i], b[i]) ) {
1798 | parents.pop();
1799 | parentsB.pop();
1800 | return false;
1801 | }
1802 | }
1803 | parents.pop();
1804 | parentsB.pop();
1805 | return true;
1806 | },
1807 |
1808 | "object": function( b, a ) {
1809 | /*jshint forin:false */
1810 | var i, j, loop, aCircular, bCircular,
1811 | // Default to true
1812 | eq = true,
1813 | aProperties = [],
1814 | bProperties = [];
1815 |
1816 | // comparing constructors is more strict than using
1817 | // instanceof
1818 | if ( a.constructor !== b.constructor ) {
1819 | // Allow objects with no prototype to be equivalent to
1820 | // objects with Object as their constructor.
1821 | if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
1822 | ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
1823 | return false;
1824 | }
1825 | }
1826 |
1827 | // stack constructor before traversing properties
1828 | callers.push( a.constructor );
1829 |
1830 | // track reference to avoid circular references
1831 | parents.push( a );
1832 | parentsB.push( b );
1833 |
1834 | // be strict: don't ensure hasOwnProperty and go deep
1835 | for ( i in a ) {
1836 | loop = false;
1837 | for ( j = 0; j < parents.length; j++ ) {
1838 | aCircular = parents[j] === a[i];
1839 | bCircular = parentsB[j] === b[i];
1840 | if ( aCircular || bCircular ) {
1841 | if ( a[i] === b[i] || aCircular && bCircular ) {
1842 | loop = true;
1843 | } else {
1844 | eq = false;
1845 | break;
1846 | }
1847 | }
1848 | }
1849 | aProperties.push(i);
1850 | if ( !loop && !innerEquiv(a[i], b[i]) ) {
1851 | eq = false;
1852 | break;
1853 | }
1854 | }
1855 |
1856 | parents.pop();
1857 | parentsB.pop();
1858 | callers.pop(); // unstack, we are done
1859 |
1860 | for ( i in b ) {
1861 | bProperties.push( i ); // collect b's properties
1862 | }
1863 |
1864 | // Ensures identical properties name
1865 | return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1866 | }
1867 | };
1868 | }());
1869 |
1870 | innerEquiv = function() { // can take multiple arguments
1871 | var args = [].slice.apply( arguments );
1872 | if ( args.length < 2 ) {
1873 | return true; // end transition
1874 | }
1875 |
1876 | return (function( a, b ) {
1877 | if ( a === b ) {
1878 | return true; // catch the most you can
1879 | } else if ( a === null || b === null || typeof a === "undefined" ||
1880 | typeof b === "undefined" ||
1881 | QUnit.objectType(a) !== QUnit.objectType(b) ) {
1882 | return false; // don't lose time with error prone cases
1883 | } else {
1884 | return bindCallbacks(a, callbacks, [ b, a ]);
1885 | }
1886 |
1887 | // apply transition with (1..n) arguments
1888 | }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
1889 | };
1890 |
1891 | return innerEquiv;
1892 | }());
1893 |
1894 | /**
1895 | * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1896 | * http://flesler.blogspot.com Licensed under BSD
1897 | * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1898 | *
1899 | * @projectDescription Advanced and extensible data dumping for Javascript.
1900 | * @version 1.0.0
1901 | * @author Ariel Flesler
1902 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1903 | */
1904 | QUnit.jsDump = (function() {
1905 | function quote( str ) {
1906 | return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
1907 | }
1908 | function literal( o ) {
1909 | return o + "";
1910 | }
1911 | function join( pre, arr, post ) {
1912 | var s = jsDump.separator(),
1913 | base = jsDump.indent(),
1914 | inner = jsDump.indent(1);
1915 | if ( arr.join ) {
1916 | arr = arr.join( "," + s + inner );
1917 | }
1918 | if ( !arr ) {
1919 | return pre + post;
1920 | }
1921 | return [ pre, inner + arr, base + post ].join(s);
1922 | }
1923 | function array( arr, stack ) {
1924 | var i = arr.length, ret = new Array(i);
1925 | this.up();
1926 | while ( i-- ) {
1927 | ret[i] = this.parse( arr[i] , undefined , stack);
1928 | }
1929 | this.down();
1930 | return join( "[", ret, "]" );
1931 | }
1932 |
1933 | var reName = /^function (\w+)/,
1934 | jsDump = {
1935 | // type is used mostly internally, you can fix a (custom)type in advance
1936 | parse: function( obj, type, stack ) {
1937 | stack = stack || [ ];
1938 | var inStack, res,
1939 | parser = this.parsers[ type || this.typeOf(obj) ];
1940 |
1941 | type = typeof parser;
1942 | inStack = inArray( obj, stack );
1943 |
1944 | if ( inStack !== -1 ) {
1945 | return "recursion(" + (inStack - stack.length) + ")";
1946 | }
1947 | if ( type === "function" ) {
1948 | stack.push( obj );
1949 | res = parser.call( this, obj, stack );
1950 | stack.pop();
1951 | return res;
1952 | }
1953 | return ( type === "string" ) ? parser : this.parsers.error;
1954 | },
1955 | typeOf: function( obj ) {
1956 | var type;
1957 | if ( obj === null ) {
1958 | type = "null";
1959 | } else if ( typeof obj === "undefined" ) {
1960 | type = "undefined";
1961 | } else if ( QUnit.is( "regexp", obj) ) {
1962 | type = "regexp";
1963 | } else if ( QUnit.is( "date", obj) ) {
1964 | type = "date";
1965 | } else if ( QUnit.is( "function", obj) ) {
1966 | type = "function";
1967 | } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
1968 | type = "window";
1969 | } else if ( obj.nodeType === 9 ) {
1970 | type = "document";
1971 | } else if ( obj.nodeType ) {
1972 | type = "node";
1973 | } else if (
1974 | // native arrays
1975 | toString.call( obj ) === "[object Array]" ||
1976 | // NodeList objects
1977 | ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
1978 | ) {
1979 | type = "array";
1980 | } else if ( obj.constructor === Error.prototype.constructor ) {
1981 | type = "error";
1982 | } else {
1983 | type = typeof obj;
1984 | }
1985 | return type;
1986 | },
1987 | separator: function() {
1988 | return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " ";
1989 | },
1990 | // extra can be a number, shortcut for increasing-calling-decreasing
1991 | indent: function( extra ) {
1992 | if ( !this.multiline ) {
1993 | return "";
1994 | }
1995 | var chr = this.indentChar;
1996 | if ( this.HTML ) {
1997 | chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
1998 | }
1999 | return new Array( this.depth + ( extra || 0 ) ).join(chr);
2000 | },
2001 | up: function( a ) {
2002 | this.depth += a || 1;
2003 | },
2004 | down: function( a ) {
2005 | this.depth -= a || 1;
2006 | },
2007 | setParser: function( name, parser ) {
2008 | this.parsers[name] = parser;
2009 | },
2010 | // The next 3 are exposed so you can use them
2011 | quote: quote,
2012 | literal: literal,
2013 | join: join,
2014 | //
2015 | depth: 1,
2016 | // This is the list of parsers, to modify them, use jsDump.setParser
2017 | parsers: {
2018 | window: "[Window]",
2019 | document: "[Document]",
2020 | error: function(error) {
2021 | return "Error(\"" + error.message + "\")";
2022 | },
2023 | unknown: "[Unknown]",
2024 | "null": "null",
2025 | "undefined": "undefined",
2026 | "function": function( fn ) {
2027 | var ret = "function",
2028 | // functions never have name in IE
2029 | name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
2030 |
2031 | if ( name ) {
2032 | ret += " " + name;
2033 | }
2034 | ret += "( ";
2035 |
2036 | ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
2037 | return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
2038 | },
2039 | array: array,
2040 | nodelist: array,
2041 | "arguments": array,
2042 | object: function( map, stack ) {
2043 | /*jshint forin:false */
2044 | var ret = [ ], keys, key, val, i;
2045 | QUnit.jsDump.up();
2046 | keys = [];
2047 | for ( key in map ) {
2048 | keys.push( key );
2049 | }
2050 | keys.sort();
2051 | for ( i = 0; i < keys.length; i++ ) {
2052 | key = keys[ i ];
2053 | val = map[ key ];
2054 | ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
2055 | }
2056 | QUnit.jsDump.down();
2057 | return join( "{", ret, "}" );
2058 | },
2059 | node: function( node ) {
2060 | var len, i, val,
2061 | open = QUnit.jsDump.HTML ? "<" : "<",
2062 | close = QUnit.jsDump.HTML ? ">" : ">",
2063 | tag = node.nodeName.toLowerCase(),
2064 | ret = open + tag,
2065 | attrs = node.attributes;
2066 |
2067 | if ( attrs ) {
2068 | for ( i = 0, len = attrs.length; i < len; i++ ) {
2069 | val = attrs[i].nodeValue;
2070 | // IE6 includes all attributes in .attributes, even ones not explicitly set.
2071 | // Those have values like undefined, null, 0, false, "" or "inherit".
2072 | if ( val && val !== "inherit" ) {
2073 | ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
2074 | }
2075 | }
2076 | }
2077 | ret += close;
2078 |
2079 | // Show content of TextNode or CDATASection
2080 | if ( node.nodeType === 3 || node.nodeType === 4 ) {
2081 | ret += node.nodeValue;
2082 | }
2083 |
2084 | return ret + open + "/" + tag + close;
2085 | },
2086 | // function calls it internally, it's the arguments part of the function
2087 | functionArgs: function( fn ) {
2088 | var args,
2089 | l = fn.length;
2090 |
2091 | if ( !l ) {
2092 | return "";
2093 | }
2094 |
2095 | args = new Array(l);
2096 | while ( l-- ) {
2097 | // 97 is 'a'
2098 | args[l] = String.fromCharCode(97+l);
2099 | }
2100 | return " " + args.join( ", " ) + " ";
2101 | },
2102 | // object calls it internally, the key part of an item in a map
2103 | key: quote,
2104 | // function calls it internally, it's the content of the function
2105 | functionCode: "[code]",
2106 | // node calls it internally, it's an html attribute value
2107 | attribute: quote,
2108 | string: quote,
2109 | date: quote,
2110 | regexp: literal,
2111 | number: literal,
2112 | "boolean": literal
2113 | },
2114 | // if true, entities are escaped ( <, >, \t, space and \n )
2115 | HTML: false,
2116 | // indentation unit
2117 | indentChar: " ",
2118 | // if true, items in a collection, are separated by a \n, else just a space.
2119 | multiline: true
2120 | };
2121 |
2122 | return jsDump;
2123 | }());
2124 |
2125 | /*
2126 | * Javascript Diff Algorithm
2127 | * By John Resig (http://ejohn.org/)
2128 | * Modified by Chu Alan "sprite"
2129 | *
2130 | * Released under the MIT license.
2131 | *
2132 | * More Info:
2133 | * http://ejohn.org/projects/javascript-diff-algorithm/
2134 | *
2135 | * Usage: QUnit.diff(expected, actual)
2136 | *
2137 | * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over"
2138 | */
2139 | QUnit.diff = (function() {
2140 | /*jshint eqeqeq:false, eqnull:true */
2141 | function diff( o, n ) {
2142 | var i,
2143 | ns = {},
2144 | os = {};
2145 |
2146 | for ( i = 0; i < n.length; i++ ) {
2147 | if ( !hasOwn.call( ns, n[i] ) ) {
2148 | ns[ n[i] ] = {
2149 | rows: [],
2150 | o: null
2151 | };
2152 | }
2153 | ns[ n[i] ].rows.push( i );
2154 | }
2155 |
2156 | for ( i = 0; i < o.length; i++ ) {
2157 | if ( !hasOwn.call( os, o[i] ) ) {
2158 | os[ o[i] ] = {
2159 | rows: [],
2160 | n: null
2161 | };
2162 | }
2163 | os[ o[i] ].rows.push( i );
2164 | }
2165 |
2166 | for ( i in ns ) {
2167 | if ( hasOwn.call( ns, i ) ) {
2168 | if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
2169 | n[ ns[i].rows[0] ] = {
2170 | text: n[ ns[i].rows[0] ],
2171 | row: os[i].rows[0]
2172 | };
2173 | o[ os[i].rows[0] ] = {
2174 | text: o[ os[i].rows[0] ],
2175 | row: ns[i].rows[0]
2176 | };
2177 | }
2178 | }
2179 | }
2180 |
2181 | for ( i = 0; i < n.length - 1; i++ ) {
2182 | if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
2183 | n[ i + 1 ] == o[ n[i].row + 1 ] ) {
2184 |
2185 | n[ i + 1 ] = {
2186 | text: n[ i + 1 ],
2187 | row: n[i].row + 1
2188 | };
2189 | o[ n[i].row + 1 ] = {
2190 | text: o[ n[i].row + 1 ],
2191 | row: i + 1
2192 | };
2193 | }
2194 | }
2195 |
2196 | for ( i = n.length - 1; i > 0; i-- ) {
2197 | if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
2198 | n[ i - 1 ] == o[ n[i].row - 1 ]) {
2199 |
2200 | n[ i - 1 ] = {
2201 | text: n[ i - 1 ],
2202 | row: n[i].row - 1
2203 | };
2204 | o[ n[i].row - 1 ] = {
2205 | text: o[ n[i].row - 1 ],
2206 | row: i - 1
2207 | };
2208 | }
2209 | }
2210 |
2211 | return {
2212 | o: o,
2213 | n: n
2214 | };
2215 | }
2216 |
2217 | return function( o, n ) {
2218 | o = o.replace( /\s+$/, "" );
2219 | n = n.replace( /\s+$/, "" );
2220 |
2221 | var i, pre,
2222 | str = "",
2223 | out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
2224 | oSpace = o.match(/\s+/g),
2225 | nSpace = n.match(/\s+/g);
2226 |
2227 | if ( oSpace == null ) {
2228 | oSpace = [ " " ];
2229 | }
2230 | else {
2231 | oSpace.push( " " );
2232 | }
2233 |
2234 | if ( nSpace == null ) {
2235 | nSpace = [ " " ];
2236 | }
2237 | else {
2238 | nSpace.push( " " );
2239 | }
2240 |
2241 | if ( out.n.length === 0 ) {
2242 | for ( i = 0; i < out.o.length; i++ ) {
2243 | str += "" + out.o[i] + oSpace[i] + "";
2244 | }
2245 | }
2246 | else {
2247 | if ( out.n[0].text == null ) {
2248 | for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
2249 | str += "" + out.o[n] + oSpace[n] + "";
2250 | }
2251 | }
2252 |
2253 | for ( i = 0; i < out.n.length; i++ ) {
2254 | if (out.n[i].text == null) {
2255 | str += "" + out.n[i] + nSpace[i] + "";
2256 | }
2257 | else {
2258 | // `pre` initialized at top of scope
2259 | pre = "";
2260 |
2261 | for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
2262 | pre += "" + out.o[n] + oSpace[n] + "";
2263 | }
2264 | str += " " + out.n[i].text + nSpace[i] + pre;
2265 | }
2266 | }
2267 | }
2268 |
2269 | return str;
2270 | };
2271 | }());
2272 |
2273 | // For browser, export only select globals
2274 | if ( typeof window !== "undefined" ) {
2275 | extend( window, QUnit.constructor.prototype );
2276 | window.QUnit = QUnit;
2277 | }
2278 |
2279 | // For CommonJS environments, export everything
2280 | if ( typeof module !== "undefined" && module.exports ) {
2281 | module.exports = QUnit;
2282 | }
2283 |
2284 |
2285 | // Get a reference to the global object, like window in browsers
2286 | }( (function() {
2287 | return this;
2288 | })() ));
--------------------------------------------------------------------------------