"
10 | ],
11 | "description": "innerHTML polyfill for SVGElement",
12 | "main": "innersvg.js",
13 | "moduleType": [
14 | "globals"
15 | ],
16 | "keywords": [
17 | "innerHTML",
18 | "polyfill",
19 | "SVG",
20 | "SVGElement"
21 | ],
22 | "license": "Apache License, version 2",
23 | "ignore": [
24 | "**/.*",
25 | "node_modules",
26 | "bower_components",
27 | "test",
28 | "tests"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | innersvg-polyfill
2 | =================
3 |
4 | mirror for https://code.google.com/p/innersvg/ + some files to make the package available in [npm](https://www.npmjs.com/package/innersvg-polyfill) and bower.
5 |
6 |
7 | Introduction
8 | ============
9 |
10 | This JavaScript library provides the innerHTML property on all SVGElements.
11 |
12 | innerHTML in a SVG document works in Chrome 6+, Safari 5+, Firefox 4+ and IE9+.
13 |
14 | innerHTML in a HTML5 document works in Chrome 7+, Firefox 4+ and IE9+.
15 |
16 | Doesn't work in Opera since the SVGElement interface is not exposed.
17 |
18 | Sample Code
19 | ===========
20 |
21 |
22 |
23 | document.getElementId("foo").innerHTML = "";
24 |
25 |
26 | License
27 | =======
28 |
29 | Apache License, Version 2.0
30 |
--------------------------------------------------------------------------------
/demos/innersvg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTML5 Demo Page for innersvg
5 |
6 |
7 |
8 |
9 |
10 | You should see a green circle with the word PASS.
11 |
12 |
13 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "innersvg-polyfill",
3 | "version": "0.0.5",
4 | "description": "innerHTML polyfill for SVGElement",
5 | "main": "innersvg.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/dnozay/innersvg-polyfill.git"
15 | },
16 | "keywords": [
17 | "innerHTML",
18 | "polyfill",
19 | "SVG",
20 | "SVGElement"
21 | ],
22 | "author": "Jeff Schiller (https://github.com/codedread)",
23 | "maintainer": "Damien Nozay (https://github.com/dnozay)",
24 | "license": "Apache License, version 2",
25 | "bugs": {
26 | "url": "https://github.com/dnozay/innersvg-polyfill/issues"
27 | },
28 | "homepage": "https://github.com/dnozay/innersvg-polyfill"
29 | }
30 |
--------------------------------------------------------------------------------
/test/qunit.css:
--------------------------------------------------------------------------------
1 |
2 | ol#qunit-tests {
3 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
4 | margin:0;
5 | padding:0;
6 | list-style-position:inside;
7 |
8 | font-size: smaller;
9 | }
10 | ol#qunit-tests li{
11 | padding:0.4em 0.5em 0.4em 2.5em;
12 | border-bottom:1px solid #fff;
13 | font-size:small;
14 | list-style-position:inside;
15 | }
16 | ol#qunit-tests li ol{
17 | box-shadow: inset 0px 2px 13px #999;
18 | -moz-box-shadow: inset 0px 2px 13px #999;
19 | -webkit-box-shadow: inset 0px 2px 13px #999;
20 | margin-top:0.5em;
21 | margin-left:0;
22 | padding:0.5em;
23 | background-color:#fff;
24 | border-radius:15px;
25 | -moz-border-radius: 15px;
26 | -webkit-border-radius: 15px;
27 | }
28 | ol#qunit-tests li li{
29 | border-bottom:none;
30 | margin:0.5em;
31 | background-color:#fff;
32 | list-style-position: inside;
33 | padding:0.4em 0.5em 0.4em 0.5em;
34 | }
35 |
36 | ol#qunit-tests li li.pass{
37 | border-left:26px solid #C6E746;
38 | background-color:#fff;
39 | color:#5E740B;
40 | }
41 | ol#qunit-tests li li.fail{
42 | border-left:26px solid #EE5757;
43 | background-color:#fff;
44 | color:#710909;
45 | }
46 | ol#qunit-tests li.pass{
47 | background-color:#D2E0E6;
48 | color:#528CE0;
49 | }
50 | ol#qunit-tests li.fail{
51 | background-color:#EE5757;
52 | color:#000;
53 | }
54 | ol#qunit-tests li strong {
55 | cursor:pointer;
56 | }
57 | h1#qunit-header{
58 | background-color:#0d3349;
59 | margin:0;
60 | padding:0.5em 0 0.5em 1em;
61 | color:#fff;
62 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
63 | border-top-right-radius:15px;
64 | border-top-left-radius:15px;
65 | -moz-border-radius-topright:15px;
66 | -moz-border-radius-topleft:15px;
67 | -webkit-border-top-right-radius:15px;
68 | -webkit-border-top-left-radius:15px;
69 | text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
70 | }
71 | h2#qunit-banner{
72 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
73 | height:5px;
74 | margin:0;
75 | padding:0;
76 | }
77 | h2#qunit-banner.qunit-pass{
78 | background-color:#C6E746;
79 | }
80 | h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
81 | background-color:#EE5757;
82 | }
83 | #qunit-testrunner-toolbar {
84 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
85 | padding:0;
86 | /*width:80%;*/
87 | padding:0em 0 0.5em 2em;
88 | font-size: small;
89 | }
90 | h2#qunit-userAgent {
91 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
92 | background-color:#2b81af;
93 | margin:0;
94 | padding:0;
95 | color:#fff;
96 | font-size: small;
97 | padding:0.5em 0 0.5em 2.5em;
98 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
99 | }
100 | p#qunit-testresult{
101 | font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
102 | margin:0;
103 | font-size: small;
104 | color:#2b81af;
105 | border-bottom-right-radius:15px;
106 | border-bottom-left-radius:15px;
107 | -moz-border-radius-bottomright:15px;
108 | -moz-border-radius-bottomleft:15px;
109 | -webkit-border-bottom-right-radius:15px;
110 | -webkit-border-bottom-left-radius:15px;
111 | background-color:#D2E0E6;
112 | padding:0.5em 0.5em 0.5em 2.5em;
113 | }
114 | strong b.fail{
115 | color:#710909;
116 | }
117 | strong b.pass{
118 | color:#5E740B;
119 | }
120 |
--------------------------------------------------------------------------------
/innersvg.js:
--------------------------------------------------------------------------------
1 | /**
2 | * innerHTML property for SVGElement
3 | * Copyright(c) 2010, Jeff Schiller
4 | *
5 | * Licensed under the Apache License, Version 2
6 | *
7 | * Works in a SVG document in Chrome 6+, Safari 5+, Firefox 4+ and IE9+.
8 | * Works in a HTML5 document in Chrome 7+, Firefox 4+ and IE9+.
9 | * Does not work in Opera since it doesn't support the SVGElement interface yet.
10 | *
11 | * I haven't decided on the best name for this property - thus the duplication.
12 | */
13 |
14 | (function() {
15 | var serializeXML = function(node, output) {
16 | var nodeType = node.nodeType;
17 | if (nodeType == 3) { // TEXT nodes.
18 | // Replace special XML characters with their entities.
19 | output.push(node.textContent.replace(/&/, '&').replace(/, '<').replace('>', '>'));
20 | } else if (nodeType == 1) { // ELEMENT nodes.
21 | // Serialize Element nodes.
22 | output.push('<', node.tagName);
23 | if (node.hasAttributes()) {
24 | var attrMap = node.attributes;
25 | for (var i = 0, len = attrMap.length; i < len; ++i) {
26 | var attrNode = attrMap.item(i);
27 | output.push(' ', attrNode.name, '=\'', attrNode.value, '\'');
28 | }
29 | }
30 | if (node.hasChildNodes()) {
31 | output.push('>');
32 | var childNodes = node.childNodes;
33 | for (var i = 0, len = childNodes.length; i < len; ++i) {
34 | serializeXML(childNodes.item(i), output);
35 | }
36 | output.push('', node.tagName, '>');
37 | } else {
38 | output.push('/>');
39 | }
40 | } else if (nodeType == 8) {
41 | // TODO(codedread): Replace special characters with XML entities?
42 | output.push('');
43 | } else {
44 | // TODO: Handle CDATA nodes.
45 | // TODO: Handle ENTITY nodes.
46 | // TODO: Handle DOCUMENT nodes.
47 | throw 'Error serializing XML. Unhandled node of type: ' + nodeType;
48 | }
49 | }
50 | // The innerHTML DOM property for SVGElement.
51 | if (!('innerHTML' in SVGElement.prototype)) {
52 | Object.defineProperty(SVGElement.prototype, 'innerHTML', {
53 | get: function() {
54 | var output = [];
55 | var childNode = this.firstChild;
56 | while (childNode) {
57 | serializeXML(childNode, output);
58 | childNode = childNode.nextSibling;
59 | }
60 | return output.join('');
61 | },
62 | set: function(markupText) {
63 | // Wipe out the current contents of the element.
64 | while (this.firstChild) {
65 | this.removeChild(this.firstChild);
66 | }
67 |
68 | try {
69 | // Parse the markup into valid nodes.
70 | var dXML = new DOMParser();
71 | dXML.async = false;
72 | // Wrap the markup into a SVG node to ensure parsing works.
73 | var sXML = '';
74 | var svgDocElement = dXML.parseFromString(sXML, 'text/xml').documentElement;
75 |
76 | // Now take each node, import it and append to this element.
77 | var childNode = svgDocElement.firstChild;
78 | while(childNode) {
79 | this.appendChild(this.ownerDocument.importNode(childNode, true));
80 | childNode = childNode.nextSibling;
81 | }
82 | } catch(e) {
83 | throw new Error('Error parsing XML string');
84 | };
85 | }
86 | });
87 | }
88 |
89 | // The innerSVG DOM property for SVGElement.
90 | if (!('innerSVG' in SVGElement.prototype)) {
91 | Object.defineProperty(SVGElement.prototype, 'innerSVG', {
92 | get: function() {
93 | return this.innerHTML;
94 | },
95 | set: function(markupText) {
96 | this.innerHTML = markupText;
97 | }
98 | });
99 | }
100 |
101 | })();
102 |
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Automated tests for innersvg
5 |
6 |
7 |
8 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/test/qunit.js:
--------------------------------------------------------------------------------
1 | /*
2 | * QUnit - A JavaScript Unit Testing Framework
3 | *
4 | * http://docs.jquery.com/QUnit
5 | *
6 | * Copyright (c) 2009 John Resig, Jörn Zaefferer
7 | * Dual licensed under the MIT (MIT-LICENSE.txt)
8 | * and GPL (GPL-LICENSE.txt) licenses.
9 | */
10 |
11 | (function(window) {
12 |
13 | var QUnit = {
14 |
15 | // Initialize the configuration options
16 | init: function() {
17 | config = {
18 | stats: { all: 0, bad: 0 },
19 | moduleStats: { all: 0, bad: 0 },
20 | started: +new Date,
21 | blocking: false,
22 | autorun: false,
23 | assertions: [],
24 | filters: [],
25 | queue: []
26 | };
27 |
28 | var tests = id("qunit-tests"),
29 | banner = id("qunit-banner"),
30 | result = id("qunit-testresult");
31 |
32 | if ( tests ) {
33 | tests.innerHTML = "";
34 | }
35 |
36 | if ( banner ) {
37 | banner.className = "";
38 | }
39 |
40 | if ( result ) {
41 | result.parentNode.removeChild( result );
42 | }
43 | },
44 |
45 | // call on start of module test to prepend name to all tests
46 | module: function(name, testEnvironment) {
47 | config.currentModule = name;
48 |
49 | synchronize(function() {
50 | if ( config.currentModule ) {
51 | QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
52 | }
53 |
54 | config.currentModule = name;
55 | config.moduleTestEnvironment = testEnvironment;
56 | config.moduleStats = { all: 0, bad: 0 };
57 |
58 | QUnit.moduleStart( name, testEnvironment );
59 | });
60 | },
61 |
62 | asyncTest: function(testName, expected, callback) {
63 | if ( arguments.length === 2 ) {
64 | callback = expected;
65 | expected = 0;
66 | }
67 |
68 | QUnit.test(testName, expected, callback, true);
69 | },
70 |
71 | test: function(testName, expected, callback, async) {
72 | var name = testName, testEnvironment, testEnvironmentArg;
73 |
74 | if ( arguments.length === 2 ) {
75 | callback = expected;
76 | expected = null;
77 | }
78 | // is 2nd argument a testEnvironment?
79 | if ( expected && typeof expected === 'object') {
80 | testEnvironmentArg = expected;
81 | expected = null;
82 | }
83 |
84 | if ( config.currentModule ) {
85 | name = config.currentModule + " module: " + name;
86 | }
87 |
88 | if ( !validTest(name) ) {
89 | return;
90 | }
91 |
92 | synchronize(function() {
93 | QUnit.testStart( testName );
94 |
95 | testEnvironment = extend({
96 | setup: function() {},
97 | teardown: function() {}
98 | }, config.moduleTestEnvironment);
99 | if (testEnvironmentArg) {
100 | extend(testEnvironment,testEnvironmentArg);
101 | }
102 |
103 | // allow utility functions to access the current test environment
104 | QUnit.current_testEnvironment = testEnvironment;
105 |
106 | config.assertions = [];
107 | config.expected = expected;
108 |
109 | try {
110 | if ( !config.pollution ) {
111 | saveGlobal();
112 | }
113 |
114 | testEnvironment.setup.call(testEnvironment);
115 | } catch(e) {
116 | QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
117 | }
118 |
119 | if ( async ) {
120 | QUnit.stop();
121 | }
122 |
123 | try {
124 | callback.call(testEnvironment);
125 | } catch(e) {
126 | fail("Test " + name + " died, exception and test follows", e, callback);
127 | QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
128 | // else next test will carry the responsibility
129 | saveGlobal();
130 |
131 | // Restart the tests if they're blocking
132 | if ( config.blocking ) {
133 | start();
134 | }
135 | }
136 | });
137 |
138 | synchronize(function() {
139 | try {
140 | checkPollution();
141 | testEnvironment.teardown.call(testEnvironment);
142 | } catch(e) {
143 | QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
144 | }
145 |
146 | try {
147 | QUnit.reset();
148 | } catch(e) {
149 | fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
150 | }
151 |
152 | if ( config.expected && config.expected != config.assertions.length ) {
153 | QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
154 | }
155 |
156 | var good = 0, bad = 0,
157 | tests = id("qunit-tests");
158 |
159 | config.stats.all += config.assertions.length;
160 | config.moduleStats.all += config.assertions.length;
161 |
162 | if ( tests ) {
163 | var ol = document.createElement("ol");
164 | ol.style.display = "none";
165 |
166 | for ( var i = 0; i < config.assertions.length; i++ ) {
167 | var assertion = config.assertions[i];
168 |
169 | var li = document.createElement("li");
170 | li.className = assertion.result ? "pass" : "fail";
171 | li.appendChild(document.createTextNode(assertion.message || "(no message)"));
172 | ol.appendChild( li );
173 |
174 | if ( assertion.result ) {
175 | good++;
176 | } else {
177 | bad++;
178 | config.stats.bad++;
179 | config.moduleStats.bad++;
180 | }
181 | }
182 |
183 | var b = document.createElement("strong");
184 | b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")";
185 |
186 | addEvent(b, "click", function() {
187 | var next = b.nextSibling, display = next.style.display;
188 | next.style.display = display === "none" ? "block" : "none";
189 | });
190 |
191 | addEvent(b, "dblclick", function(e) {
192 | var target = e && e.target ? e.target : window.event.srcElement;
193 | if ( target.nodeName.toLowerCase() === "strong" ) {
194 | var text = "", node = target.firstChild;
195 |
196 | while ( node.nodeType === 3 ) {
197 | text += node.nodeValue;
198 | node = node.nextSibling;
199 | }
200 |
201 | text = text.replace(/(^\s*|\s*$)/g, "");
202 |
203 | if ( window.location ) {
204 | window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
205 | }
206 | }
207 | });
208 |
209 | var li = document.createElement("li");
210 | li.className = bad ? "fail" : "pass";
211 | li.appendChild( b );
212 | li.appendChild( ol );
213 | tests.appendChild( li );
214 |
215 | if ( bad ) {
216 | var toolbar = id("qunit-testrunner-toolbar");
217 | if ( toolbar ) {
218 | toolbar.style.display = "block";
219 | id("qunit-filter-pass").disabled = null;
220 | id("qunit-filter-missing").disabled = null;
221 | }
222 | }
223 |
224 | } else {
225 | for ( var i = 0; i < config.assertions.length; i++ ) {
226 | if ( !config.assertions[i].result ) {
227 | bad++;
228 | config.stats.bad++;
229 | config.moduleStats.bad++;
230 | }
231 | }
232 | }
233 |
234 | QUnit.testDone( testName, bad, config.assertions.length );
235 |
236 | if ( !window.setTimeout && !config.queue.length ) {
237 | done();
238 | }
239 | });
240 |
241 | if ( window.setTimeout && !config.doneTimer ) {
242 | config.doneTimer = window.setTimeout(function(){
243 | if ( !config.queue.length ) {
244 | done();
245 | } else {
246 | synchronize( done );
247 | }
248 | }, 13);
249 | }
250 | },
251 |
252 | /**
253 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
254 | */
255 | expect: function(asserts) {
256 | config.expected = asserts;
257 | },
258 |
259 | /**
260 | * Asserts true.
261 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
262 | */
263 | ok: function(a, msg) {
264 | QUnit.log(a, msg);
265 |
266 | config.assertions.push({
267 | result: !!a,
268 | message: msg
269 | });
270 | },
271 |
272 | /**
273 | * Checks that the first two arguments are equal, with an optional message.
274 | * Prints out both actual and expected values.
275 | *
276 | * Prefered to ok( actual == expected, message )
277 | *
278 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
279 | *
280 | * @param Object actual
281 | * @param Object expected
282 | * @param String message (optional)
283 | */
284 | equal: function(actual, expected, message) {
285 | push(expected == actual, actual, expected, message);
286 | },
287 |
288 | notEqual: function(actual, expected, message) {
289 | push(expected != actual, actual, expected, message);
290 | },
291 |
292 | deepEqual: function(a, b, message) {
293 | push(QUnit.equiv(a, b), a, b, message);
294 | },
295 |
296 | notDeepEqual: function(a, b, message) {
297 | push(!QUnit.equiv(a, b), a, b, message);
298 | },
299 |
300 | strictEqual: function(actual, expected, message) {
301 | push(expected === actual, actual, expected, message);
302 | },
303 |
304 | notStrictEqual: function(actual, expected, message) {
305 | push(expected !== actual, actual, expected, message);
306 | },
307 |
308 | start: function() {
309 | // A slight delay, to avoid any current callbacks
310 | if ( window.setTimeout ) {
311 | window.setTimeout(function() {
312 | if ( config.timeout ) {
313 | clearTimeout(config.timeout);
314 | }
315 |
316 | config.blocking = false;
317 | process();
318 | }, 13);
319 | } else {
320 | config.blocking = false;
321 | process();
322 | }
323 | },
324 |
325 | stop: function(timeout) {
326 | config.blocking = true;
327 |
328 | if ( timeout && window.setTimeout ) {
329 | config.timeout = window.setTimeout(function() {
330 | QUnit.ok( false, "Test timed out" );
331 | QUnit.start();
332 | }, timeout);
333 | }
334 | },
335 |
336 | /**
337 | * Resets the test setup. Useful for tests that modify the DOM.
338 | */
339 | reset: function() {
340 | if ( window.jQuery ) {
341 | jQuery("#main").html( config.fixture );
342 | jQuery.event.global = {};
343 | jQuery.ajaxSettings = extend({}, config.ajaxSettings);
344 | }
345 | },
346 |
347 | /**
348 | * Trigger an event on an element.
349 | *
350 | * @example triggerEvent( document.body, "click" );
351 | *
352 | * @param DOMElement elem
353 | * @param String type
354 | */
355 | triggerEvent: function( elem, type, event ) {
356 | if ( document.createEvent ) {
357 | event = document.createEvent("MouseEvents");
358 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
359 | 0, 0, 0, 0, 0, false, false, false, false, 0, null);
360 | elem.dispatchEvent( event );
361 |
362 | } else if ( elem.fireEvent ) {
363 | elem.fireEvent("on"+type);
364 | }
365 | },
366 |
367 | // Safe object type checking
368 | is: function( type, obj ) {
369 | return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
370 | },
371 |
372 | // Logging callbacks
373 | done: function(failures, total) {},
374 | log: function(result, message) {},
375 | testStart: function(name) {},
376 | testDone: function(name, failures, total) {},
377 | moduleStart: function(name, testEnvironment) {},
378 | moduleDone: function(name, failures, total) {}
379 | };
380 |
381 | // Backwards compatibility, deprecated
382 | QUnit.equals = QUnit.equal;
383 | QUnit.same = QUnit.deepEqual;
384 |
385 | // Maintain internal state
386 | var config = {
387 | // The queue of tests to run
388 | queue: [],
389 |
390 | // block until document ready
391 | blocking: true
392 | };
393 |
394 | // Load paramaters
395 | (function() {
396 | var location = window.location || { search: "", protocol: "file:" },
397 | GETParams = location.search.slice(1).split('&');
398 |
399 | for ( var i = 0; i < GETParams.length; i++ ) {
400 | GETParams[i] = decodeURIComponent( GETParams[i] );
401 | if ( GETParams[i] === "noglobals" ) {
402 | GETParams.splice( i, 1 );
403 | i--;
404 | config.noglobals = true;
405 | } else if ( GETParams[i].search('=') > -1 ) {
406 | GETParams.splice( i, 1 );
407 | i--;
408 | }
409 | }
410 |
411 | // restrict modules/tests by get parameters
412 | config.filters = GETParams;
413 |
414 | // Figure out if we're running the tests from a server or not
415 | QUnit.isLocal = !!(location.protocol === 'file:');
416 | })();
417 |
418 | // Expose the API as global variables, unless an 'exports'
419 | // object exists, in that case we assume we're in CommonJS
420 | if ( typeof exports === "undefined" || typeof require === "undefined" ) {
421 | extend(window, QUnit);
422 | window.QUnit = QUnit;
423 | } else {
424 | extend(exports, QUnit);
425 | exports.QUnit = QUnit;
426 | }
427 |
428 | if ( typeof document === "undefined" || document.readyState === "complete" ) {
429 | config.autorun = true;
430 | }
431 |
432 | addEvent(window, "load", function() {
433 | // Initialize the config, saving the execution queue
434 | var oldconfig = extend({}, config);
435 | QUnit.init();
436 | extend(config, oldconfig);
437 |
438 | config.blocking = false;
439 |
440 | var userAgent = id("qunit-userAgent");
441 | if ( userAgent ) {
442 | userAgent.innerHTML = navigator.userAgent;
443 | }
444 |
445 | var toolbar = id("qunit-testrunner-toolbar");
446 | if ( toolbar ) {
447 | toolbar.style.display = "none";
448 |
449 | var filter = document.createElement("input");
450 | filter.type = "checkbox";
451 | filter.id = "qunit-filter-pass";
452 | filter.disabled = true;
453 | addEvent( filter, "click", function() {
454 | var li = document.getElementsByTagName("li");
455 | for ( var i = 0; i < li.length; i++ ) {
456 | if ( li[i].className.indexOf("pass") > -1 ) {
457 | li[i].style.display = filter.checked ? "none" : "";
458 | }
459 | }
460 | });
461 | toolbar.appendChild( filter );
462 |
463 | var label = document.createElement("label");
464 | label.setAttribute("for", "qunit-filter-pass");
465 | label.innerHTML = "Hide passed tests";
466 | toolbar.appendChild( label );
467 |
468 | var missing = document.createElement("input");
469 | missing.type = "checkbox";
470 | missing.id = "qunit-filter-missing";
471 | missing.disabled = true;
472 | addEvent( missing, "click", function() {
473 | var li = document.getElementsByTagName("li");
474 | for ( var i = 0; i < li.length; i++ ) {
475 | if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
476 | li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
477 | }
478 | }
479 | });
480 | toolbar.appendChild( missing );
481 |
482 | label = document.createElement("label");
483 | label.setAttribute("for", "qunit-filter-missing");
484 | label.innerHTML = "Hide missing tests (untested code is broken code)";
485 | toolbar.appendChild( label );
486 | }
487 |
488 | var main = id('main');
489 | if ( main ) {
490 | config.fixture = main.innerHTML;
491 | }
492 |
493 | if ( window.jQuery ) {
494 | config.ajaxSettings = window.jQuery.ajaxSettings;
495 | }
496 |
497 | QUnit.start();
498 | });
499 |
500 | function done() {
501 | if ( config.doneTimer && window.clearTimeout ) {
502 | window.clearTimeout( config.doneTimer );
503 | config.doneTimer = null;
504 | }
505 |
506 | if ( config.queue.length ) {
507 | config.doneTimer = window.setTimeout(function(){
508 | if ( !config.queue.length ) {
509 | done();
510 | } else {
511 | synchronize( done );
512 | }
513 | }, 13);
514 |
515 | return;
516 | }
517 |
518 | config.autorun = true;
519 |
520 | // Log the last module results
521 | if ( config.currentModule ) {
522 | QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
523 | }
524 |
525 | var banner = id("qunit-banner"),
526 | tests = id("qunit-tests"),
527 | html = ['Tests completed in ',
528 | +new Date - config.started, ' milliseconds.
',
529 | '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join('');
530 |
531 | if ( banner ) {
532 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
533 | }
534 |
535 | if ( tests ) {
536 | var result = id("qunit-testresult");
537 |
538 | if ( !result ) {
539 | result = document.createElement("p");
540 | result.id = "qunit-testresult";
541 | result.className = "result";
542 | tests.parentNode.insertBefore( result, tests.nextSibling );
543 | }
544 |
545 | result.innerHTML = html;
546 | }
547 |
548 | QUnit.done( config.stats.bad, config.stats.all );
549 | }
550 |
551 | function validTest( name ) {
552 | var i = config.filters.length,
553 | run = false;
554 |
555 | if ( !i ) {
556 | return true;
557 | }
558 |
559 | while ( i-- ) {
560 | var filter = config.filters[i],
561 | not = filter.charAt(0) == '!';
562 |
563 | if ( not ) {
564 | filter = filter.slice(1);
565 | }
566 |
567 | if ( name.indexOf(filter) !== -1 ) {
568 | return !not;
569 | }
570 |
571 | if ( not ) {
572 | run = true;
573 | }
574 | }
575 |
576 | return run;
577 | }
578 |
579 | function push(result, actual, expected, message) {
580 | message = message || (result ? "okay" : "failed");
581 | QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
582 | }
583 |
584 | function synchronize( callback ) {
585 | config.queue.push( callback );
586 |
587 | if ( config.autorun && !config.blocking ) {
588 | process();
589 | }
590 | }
591 |
592 | function process() {
593 | while ( config.queue.length && !config.blocking ) {
594 | config.queue.shift()();
595 | }
596 | }
597 |
598 | function saveGlobal() {
599 | config.pollution = [];
600 |
601 | if ( config.noglobals ) {
602 | for ( var key in window ) {
603 | config.pollution.push( key );
604 | }
605 | }
606 | }
607 |
608 | function checkPollution( name ) {
609 | var old = config.pollution;
610 | saveGlobal();
611 |
612 | var newGlobals = diff( old, config.pollution );
613 | if ( newGlobals.length > 0 ) {
614 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
615 | config.expected++;
616 | }
617 |
618 | var deletedGlobals = diff( config.pollution, old );
619 | if ( deletedGlobals.length > 0 ) {
620 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
621 | config.expected++;
622 | }
623 | }
624 |
625 | // returns a new Array with the elements that are in a but not in b
626 | function diff( a, b ) {
627 | var result = a.slice();
628 | for ( var i = 0; i < result.length; i++ ) {
629 | for ( var j = 0; j < b.length; j++ ) {
630 | if ( result[i] === b[j] ) {
631 | result.splice(i, 1);
632 | i--;
633 | break;
634 | }
635 | }
636 | }
637 | return result;
638 | }
639 |
640 | function fail(message, exception, callback) {
641 | if ( typeof console !== "undefined" && console.error && console.warn ) {
642 | console.error(message);
643 | console.error(exception);
644 | console.warn(callback.toString());
645 |
646 | } else if ( window.opera && opera.postError ) {
647 | opera.postError(message, exception, callback.toString);
648 | }
649 | }
650 |
651 | function extend(a, b) {
652 | for ( var prop in b ) {
653 | a[prop] = b[prop];
654 | }
655 |
656 | return a;
657 | }
658 |
659 | function addEvent(elem, type, fn) {
660 | if ( elem.addEventListener ) {
661 | elem.addEventListener( type, fn, false );
662 | } else if ( elem.attachEvent ) {
663 | elem.attachEvent( "on" + type, fn );
664 | } else {
665 | fn();
666 | }
667 | }
668 |
669 | function id(name) {
670 | return !!(typeof document !== "undefined" && document && document.getElementById) &&
671 | document.getElementById( name );
672 | }
673 |
674 | // Test for equality any JavaScript type.
675 | // Discussions and reference: http://philrathe.com/articles/equiv
676 | // Test suites: http://philrathe.com/tests/equiv
677 | // Author: Philippe Rathé
678 | QUnit.equiv = function () {
679 |
680 | var innerEquiv; // the real equiv function
681 | var callers = []; // stack to decide between skip/abort functions
682 |
683 |
684 | // Determine what is o.
685 | function hoozit(o) {
686 | if (QUnit.is("String", o)) {
687 | return "string";
688 |
689 | } else if (QUnit.is("Boolean", o)) {
690 | return "boolean";
691 |
692 | } else if (QUnit.is("Number", o)) {
693 |
694 | if (isNaN(o)) {
695 | return "nan";
696 | } else {
697 | return "number";
698 | }
699 |
700 | } else if (typeof o === "undefined") {
701 | return "undefined";
702 |
703 | // consider: typeof null === object
704 | } else if (o === null) {
705 | return "null";
706 |
707 | // consider: typeof [] === object
708 | } else if (QUnit.is( "Array", o)) {
709 | return "array";
710 |
711 | // consider: typeof new Date() === object
712 | } else if (QUnit.is( "Date", o)) {
713 | return "date";
714 |
715 | // consider: /./ instanceof Object;
716 | // /./ instanceof RegExp;
717 | // typeof /./ === "function"; // => false in IE and Opera,
718 | // true in FF and Safari
719 | } else if (QUnit.is( "RegExp", o)) {
720 | return "regexp";
721 |
722 | } else if (typeof o === "object") {
723 | return "object";
724 |
725 | } else if (QUnit.is( "Function", o)) {
726 | return "function";
727 | } else {
728 | return undefined;
729 | }
730 | }
731 |
732 | // Call the o related callback with the given arguments.
733 | function bindCallbacks(o, callbacks, args) {
734 | var prop = hoozit(o);
735 | if (prop) {
736 | if (hoozit(callbacks[prop]) === "function") {
737 | return callbacks[prop].apply(callbacks, args);
738 | } else {
739 | return callbacks[prop]; // or undefined
740 | }
741 | }
742 | }
743 |
744 | var callbacks = function () {
745 |
746 | // for string, boolean, number and null
747 | function useStrictEquality(b, a) {
748 | if (b instanceof a.constructor || a instanceof b.constructor) {
749 | // to catch short annotaion VS 'new' annotation of a declaration
750 | // e.g. var i = 1;
751 | // var j = new Number(1);
752 | return a == b;
753 | } else {
754 | return a === b;
755 | }
756 | }
757 |
758 | return {
759 | "string": useStrictEquality,
760 | "boolean": useStrictEquality,
761 | "number": useStrictEquality,
762 | "null": useStrictEquality,
763 | "undefined": useStrictEquality,
764 |
765 | "nan": function (b) {
766 | return isNaN(b);
767 | },
768 |
769 | "date": function (b, a) {
770 | return hoozit(b) === "date" && a.valueOf() === b.valueOf();
771 | },
772 |
773 | "regexp": function (b, a) {
774 | return hoozit(b) === "regexp" &&
775 | a.source === b.source && // the regex itself
776 | a.global === b.global && // and its modifers (gmi) ...
777 | a.ignoreCase === b.ignoreCase &&
778 | a.multiline === b.multiline;
779 | },
780 |
781 | // - skip when the property is a method of an instance (OOP)
782 | // - abort otherwise,
783 | // initial === would have catch identical references anyway
784 | "function": function () {
785 | var caller = callers[callers.length - 1];
786 | return caller !== Object &&
787 | typeof caller !== "undefined";
788 | },
789 |
790 | "array": function (b, a) {
791 | var i;
792 | var len;
793 |
794 | // b could be an object literal here
795 | if ( ! (hoozit(b) === "array")) {
796 | return false;
797 | }
798 |
799 | len = a.length;
800 | if (len !== b.length) { // safe and faster
801 | return false;
802 | }
803 | for (i = 0; i < len; i++) {
804 | if ( ! innerEquiv(a[i], b[i])) {
805 | return false;
806 | }
807 | }
808 | return true;
809 | },
810 |
811 | "object": function (b, a) {
812 | var i;
813 | var eq = true; // unless we can proove it
814 | var aProperties = [], bProperties = []; // collection of strings
815 |
816 | // comparing constructors is more strict than using instanceof
817 | if ( a.constructor !== b.constructor) {
818 | return false;
819 | }
820 |
821 | // stack constructor before traversing properties
822 | callers.push(a.constructor);
823 |
824 | for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
825 |
826 | aProperties.push(i); // collect a's properties
827 |
828 | if ( ! innerEquiv(a[i], b[i])) {
829 | eq = false;
830 | }
831 | }
832 |
833 | callers.pop(); // unstack, we are done
834 |
835 | for (i in b) {
836 | bProperties.push(i); // collect b's properties
837 | }
838 |
839 | // Ensures identical properties name
840 | return eq && innerEquiv(aProperties.sort(), bProperties.sort());
841 | }
842 | };
843 | }();
844 |
845 | innerEquiv = function () { // can take multiple arguments
846 | var args = Array.prototype.slice.apply(arguments);
847 | if (args.length < 2) {
848 | return true; // end transition
849 | }
850 |
851 | return (function (a, b) {
852 | if (a === b) {
853 | return true; // catch the most you can
854 | } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
855 | return false; // don't lose time with error prone cases
856 | } else {
857 | return bindCallbacks(a, callbacks, [b, a]);
858 | }
859 |
860 | // apply transition with (1..n) arguments
861 | })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
862 | };
863 |
864 | return innerEquiv;
865 |
866 | }();
867 |
868 | /**
869 | * jsDump
870 | * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
871 | * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
872 | * Date: 5/15/2008
873 | * @projectDescription Advanced and extensible data dumping for Javascript.
874 | * @version 1.0.0
875 | * @author Ariel Flesler
876 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
877 | */
878 | QUnit.jsDump = (function() {
879 | function quote( str ) {
880 | return '"' + str.toString().replace(/"/g, '\\"') + '"';
881 | };
882 | function literal( o ) {
883 | return o + '';
884 | };
885 | function join( pre, arr, post ) {
886 | var s = jsDump.separator(),
887 | base = jsDump.indent(),
888 | inner = jsDump.indent(1);
889 | if ( arr.join )
890 | arr = arr.join( ',' + s + inner );
891 | if ( !arr )
892 | return pre + post;
893 | return [ pre, inner + arr, base + post ].join(s);
894 | };
895 | function array( arr ) {
896 | var i = arr.length, ret = Array(i);
897 | this.up();
898 | while ( i-- )
899 | ret[i] = this.parse( arr[i] );
900 | this.down();
901 | return join( '[', ret, ']' );
902 | };
903 |
904 | var reName = /^function (\w+)/;
905 |
906 | var jsDump = {
907 | parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
908 | var parser = this.parsers[ type || this.typeOf(obj) ];
909 | type = typeof parser;
910 |
911 | return type == 'function' ? parser.call( this, obj ) :
912 | type == 'string' ? parser :
913 | this.parsers.error;
914 | },
915 | typeOf:function( obj ) {
916 | var type;
917 | if ( obj === null ) {
918 | type = "null";
919 | } else if (typeof obj === "undefined") {
920 | type = "undefined";
921 | } else if (QUnit.is("RegExp", obj)) {
922 | type = "regexp";
923 | } else if (QUnit.is("Date", obj)) {
924 | type = "date";
925 | } else if (QUnit.is("Function", obj)) {
926 | type = "function";
927 | } else if (QUnit.is("Array", obj)) {
928 | type = "array";
929 | } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) {
930 | type = "window";
931 | } else if (QUnit.is("HTMLDocument", obj)) {
932 | type = "document";
933 | } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) {
934 | type = "nodelist";
935 | } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) {
936 | type = "node";
937 | } else {
938 | type = typeof obj;
939 | }
940 | return type;
941 | },
942 | separator:function() {
943 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' ';
944 | },
945 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
946 | if ( !this.multiline )
947 | return '';
948 | var chr = this.indentChar;
949 | if ( this.HTML )
950 | chr = chr.replace(/\t/g,' ').replace(/ /g,' ');
951 | return Array( this._depth_ + (extra||0) ).join(chr);
952 | },
953 | up:function( a ) {
954 | this._depth_ += a || 1;
955 | },
956 | down:function( a ) {
957 | this._depth_ -= a || 1;
958 | },
959 | setParser:function( name, parser ) {
960 | this.parsers[name] = parser;
961 | },
962 | // The next 3 are exposed so you can use them
963 | quote:quote,
964 | literal:literal,
965 | join:join,
966 | //
967 | _depth_: 1,
968 | // This is the list of parsers, to modify them, use jsDump.setParser
969 | parsers:{
970 | window: '[Window]',
971 | document: '[Document]',
972 | error:'[ERROR]', //when no parser is found, shouldn't happen
973 | unknown: '[Unknown]',
974 | 'null':'null',
975 | undefined:'undefined',
976 | 'function':function( fn ) {
977 | var ret = 'function',
978 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
979 | if ( name )
980 | ret += ' ' + name;
981 | ret += '(';
982 |
983 | ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
984 | return join( ret, this.parse(fn,'functionCode'), '}' );
985 | },
986 | array: array,
987 | nodelist: array,
988 | arguments: array,
989 | object:function( map ) {
990 | var ret = [ ];
991 | this.up();
992 | for ( var key in map )
993 | ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
994 | this.down();
995 | return join( '{', ret, '}' );
996 | },
997 | node:function( node ) {
998 | var open = this.HTML ? '<' : '<',
999 | close = this.HTML ? '>' : '>';
1000 |
1001 | var tag = node.nodeName.toLowerCase(),
1002 | ret = open + tag;
1003 |
1004 | for ( var a in this.DOMAttrs ) {
1005 | var val = node[this.DOMAttrs[a]];
1006 | if ( val )
1007 | ret += ' ' + a + '=' + this.parse( val, 'attribute' );
1008 | }
1009 | return ret + close + open + '/' + tag + close;
1010 | },
1011 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
1012 | var l = fn.length;
1013 | if ( !l ) return '';
1014 |
1015 | var args = Array(l);
1016 | while ( l-- )
1017 | args[l] = String.fromCharCode(97+l);//97 is 'a'
1018 | return ' ' + args.join(', ') + ' ';
1019 | },
1020 | key:quote, //object calls it internally, the key part of an item in a map
1021 | functionCode:'[code]', //function calls it internally, it's the content of the function
1022 | attribute:quote, //node calls it internally, it's an html attribute value
1023 | string:quote,
1024 | date:quote,
1025 | regexp:literal, //regex
1026 | number:literal,
1027 | 'boolean':literal
1028 | },
1029 | DOMAttrs:{//attributes to dump from nodes, name=>realName
1030 | id:'id',
1031 | name:'name',
1032 | 'class':'className'
1033 | },
1034 | HTML:true,//if true, entities are escaped ( <, >, \t, space and \n )
1035 | indentChar:' ',//indentation unit
1036 | multiline:true //if true, items in a collection, are separated by a \n, else just a space.
1037 | };
1038 |
1039 | return jsDump;
1040 | })();
1041 |
1042 | })(this);
1043 |
--------------------------------------------------------------------------------