├── docs
├── .gitignore
├── requirements.txt
├── index.rst
├── overview.rst
├── types.rst
├── make.bat
├── Makefile
├── conf.py
└── api.rst
├── .gitignore
├── bower.json
├── xmlrpc.jquery.json
├── tests
├── index.html
├── min.html
├── tests.binary.js
├── tests.request.js
├── tests.document.js
├── tests.encoding.js
├── tests.decoding.js
├── qunit.css
└── qunit.js
├── LICENCE
├── README.md
├── jquery.xmlrpc.min.js
└── jquery.xmlrpc.js
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | _build/
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx==1.1.3
2 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-xmlrpc",
3 | "version": "0.4.4",
4 | "main": ["jquery.xmlrpc.js", "jquery.xmlrpc.min.js"],
5 | "dependencies": {
6 | "jquery": ">1.10.2"
7 | },
8 | "devDependencies": {
9 | "jquery-mockjax": "~1.5.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Welcome to jQuery XML-RPC's documentation!
2 | ==========================================
3 |
4 | This is a small library that sits on top of jQuery for communicating with
5 | XML-RPC services - without worrying about the horrible bloat of XML-RPC. Using
6 | this library, you can pass JSON parameters to the library, and receive
7 | responses in JSON. Encoding the JSON document is handled for you, intelligently
8 | mapping types between the two languages.
9 |
10 | Contents:
11 |
12 | .. toctree::
13 | :maxdepth: 2
14 |
15 | overview
16 | types
17 | api
18 |
--------------------------------------------------------------------------------
/xmlrpc.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xmlrpc",
3 | "version": "0.4.4",
4 | "title": "XML-RPC over AJAX",
5 | "description": "Interact with remote XML-RPC services using AJAX",
6 | "author": {
7 | "name": "Tim Heap",
8 | "email": "heap.tim@gmail.com",
9 | "url": "http://timheap.me"
10 | },
11 | "licenses": [
12 | {"url": "http://unlicense.org/"}
13 | ],
14 | "dependencies": {
15 | "jquery": ">1.8.0"
16 | },
17 | "keywords": [
18 | "xmlrpc", "xml-rpc",
19 | "ajax",
20 | "xml",
21 | "remote-service"
22 | ],
23 | "homepage": "https://github.com/timheap/jquery-xmlrpc",
24 | "docs": "http://jquery-xml-rpc.readthedocs.org/"
25 | }
26 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | jQuery XML-RPC tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/min.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | jQuery XML-RPC minified tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/tests/tests.binary.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true jquery:true */
2 | /*globals deepEqual equal ok assert test module */
3 | (function() {
4 | "use strict";
5 | module("Binary");
6 |
7 | /**
8 | * Make an array buffer out of an array of ints
9 | */
10 | var makeArrayBuffer = function(uint8s) {
11 | var ab = new ArrayBuffer(uint8s.length);
12 | var uint8 = new Uint8Array(ab);
13 |
14 | uint8s.forEach(function(val, i) {
15 | uint8[i] = val;
16 | });
17 |
18 | return ab;
19 | };
20 |
21 | /**
22 | * A wrapper around `ok` that acts like `equal`, but works on ArrayBuffers
23 | * Call like:
24 | *
25 | * abEqual(testArrayBuffer, expectedArrayBuffer, message);
26 | */
27 | var abEqual = function(ab1, ab2) {
28 | var args = [].slice.call(arguments, 2);
29 |
30 | var allGood = (function() {
31 |
32 | var uint81 = new Uint8Array(ab1);
33 | var uint82 = new Uint8Array(ab2);
34 |
35 | if (uint81.length != uint82.length) return false;
36 |
37 | for (var i = 0; i < uint81.length; i++) {
38 | if (uint81[i] !== uint82[i]) return false;
39 | }
40 |
41 | return true;
42 | })();
43 |
44 | args.unshift(allGood);
45 |
46 | ok.apply(null, args);
47 | };
48 |
49 | /**
50 | * Naive range() function. Only supports forward steps
51 | */
52 | var range = function(start, stop) {
53 | var acc = [];
54 | for (var i = start; i < stop; i++) {
55 | acc.push(i);
56 | }
57 | return acc;
58 | };
59 |
60 | test("base64 encoding", function() {
61 | equal($.xmlrpc.binary.toBase64(makeArrayBuffer([1, 2, 3, 4, 5])), "AQIDBAU=",
62 | "ArrayBuffer encoded to base64 string");
63 | });
64 |
65 | test("base64 decoding", function() {
66 | abEqual($.xmlrpc.binary.fromBase64("AQIDBAU="), makeArrayBuffer([1, 2, 3, 4, 5]),
67 | "Base64 string decoded into ArrayBuffer");
68 | });
69 |
70 | test("base64 round trip", function() {
71 | var max = 20, low, high;
72 | for (low = 0; low < max; low++) {
73 | for (high = low + 1; high < max; high++) {
74 | var ab = makeArrayBuffer(range(low, high));
75 | abEqual(ab, $.xmlrpc.binary.fromBase64($.xmlrpc.binary.toBase64(ab)),
76 | "Range from " + low + " to " + high + " encodes and decodes");
77 | }
78 | }
79 | });
80 | })();
81 |
--------------------------------------------------------------------------------
/docs/overview.rst:
--------------------------------------------------------------------------------
1 | .. _installing:
2 |
3 | ==========
4 | Installing
5 | ==========
6 |
7 | Simply include the jQuery library, and this library in your page:
8 |
9 | .. code-block:: html
10 |
11 |
12 |
13 |
14 | This was built upon jQuery 1.8.1. It will probably work with old versions, and
15 | will probably continue to work with new versions.
16 |
17 | .. _using:
18 |
19 | =====
20 | Using
21 | =====
22 |
23 | The ``jQuery.xmlrpc`` function is the main work-horse of this library.
24 | Call it like so:
25 |
26 | .. code-block:: javascript
27 |
28 | $.xmlrpc({
29 | url: '/RPC2',
30 | methodName: 'foo',
31 | params: ['bar', 1, 4.6, true, [1, 2, 3], {name: 'value'}],
32 | success: function(response, status, jqXHR) { },
33 | error: function(jqXHR, status, error) { }
34 | });
35 |
36 | It takes all of the same arguments as ``jQuery.ajax``,
37 | so refer there for more documentation.
38 | The two new keys added are:
39 |
40 | ``methodName``
41 | This is method put in the ```` element from XML-RPC. It should be a
42 | string. The XML-RPC service you are communicating with will determine valid
43 | method names you can call.
44 |
45 | ``params``
46 | An array of parameters to send. Specify an empty array, or do not supply this
47 | key at all if you do not want to send any parameters.
48 |
49 | See the docs section on [Encoding and Decoding XML-RPC Documents][encoding] for
50 | more information.
51 |
52 | Getting data back
53 | -----------------
54 |
55 | When the XML-RPC call returns,
56 | the contents of the ```` element are parsed into JSON and
57 | supplied to the ``success`` callback of the AJAX call as the first parameter,
58 | much like a JSON request.
59 |
60 | Handling errors
61 | ---------------
62 |
63 | If any HTTP errors occur during transport,
64 | the normal jQuery AJAX error handling will be used.
65 | If the XML-RPC service successfully replies,
66 | but replies with a ```` response,
67 | an ``$.xmlrpc.XmlRpcFault`` is thrown.
68 | This error will be sent as the third parameter to the ``error`` callback
69 | of the AJAX call, as with other errors.
70 |
--------------------------------------------------------------------------------
/tests/tests.request.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true */
2 | /*globals $ deepEqual equal ok assert test module throws */
3 | (function() {
4 | "use strict";
5 | module("Request");
6 |
7 | /**
8 | * Serialize a Document to a string easily
9 | */
10 | var s = (function() {
11 | var serializer = new XMLSerializer();
12 | return function($node) {
13 | return serializer.serializeToString($node);
14 | };
15 | })();
16 |
17 | var d = (function() {
18 | var parser = new DOMParser();
19 | return function(xml) {
20 | return parser.parseFromString(xml, "text/xml");
21 | };
22 | })();
23 |
24 | $.mockjax({
25 | url: '/mock/xmlrpc/hello',
26 | contentType: 'text/xml',
27 | responseTime: 1,
28 | responseText: (
29 | '' +
30 | '' +
31 | '' +
32 | '4' +
33 | 'World!' +
34 | '' +
35 | ''
36 | )
37 | });
38 |
39 | $.mockjax({
40 | url: '/mock/xmlrpc/empty',
41 | contentType: 'text/xml',
42 | responseTime: 1,
43 | responseText: (
44 | '' +
45 | '' +
46 | '' +
47 | '' +
48 | ''
49 | )
50 | });
51 |
52 | asyncTest("Make a simple request", function() {
53 | expect(2);
54 | $.xmlrpc("/mock/xmlrpc/hello", {
55 | 'methodName': 'test-method',
56 | 'params': [1, "Hello"],
57 | 'success': function(data, status) {
58 | deepEqual(this.data,
59 | "test-method" +
60 | "1" +
61 | "Hello" +
62 | "",
63 | "Request body was encoded correctly!");
64 | deepEqual(data, [4, 'World!'],
65 | "Response body was decoded correctly!");
66 | start();
67 | },
68 | 'error': function() {
69 | ok(false, arguments);
70 | }
71 | });
72 | });
73 |
74 | asyncTest("Make a request with no params", function() {
75 | expect(2);
76 | $.xmlrpc("/mock/xmlrpc/empty", {
77 | 'methodName': 'test-method',
78 | 'success': function(data, request) {
79 | deepEqual(this.data,
80 | "test-method" +
81 | "",
82 | "Request body was encoded correctly!");
83 | deepEqual(data, [],
84 | "Response body was decoded correctly!");
85 | start();
86 | },
87 | 'error': function() {
88 | ok(false, arguments);
89 | }
90 | });
91 | });
92 |
93 | })();
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jQuery XML-RPC library
2 | ======================
3 |
4 | This is a small library that sits on top of jQuery for communicating with
5 | XML-RPC services - without worrying about the horrible bloat of XML-RPC. Using
6 | this library, you can pass JSON parameters to the library, and receive
7 | responses in JSON. Encoding the JSON document is handled for you, intelligently
8 | mapping types between the two languages.
9 |
10 | Installing
11 | ----------
12 |
13 | Simply include the jQuery library, and this library in your page:
14 |
15 | ```html
16 |
17 |
18 | ```
19 |
20 | This was built upon jQuery 1.8.1. It will probably work with old versions, and
21 | will probably continue to work with new versions.
22 |
23 | Using
24 | -----
25 |
26 | The `jQuery.xmlrpc` function is the main work-horse of this library. Call it
27 | like so:
28 |
29 | ```javascript
30 | $.xmlrpc({
31 | url: '/RPC2',
32 | methodName: 'foo',
33 | params: ['bar', 1, 4.6, true, [1, 2, 3], {name: 'value'}],
34 | success: function(response, status, jqXHR) { /* ... */ },
35 | error: function(jqXHR, status, error) { /* ... */ }
36 | });
37 | ```
38 |
39 | It takes all of the same arguments as `jQuery.ajax`, so refer there for more
40 | documentation. The two new keys added are:
41 |
42 | ### `methodName`
43 |
44 | This is method put in the `` element from XML-RPC. It should be a
45 | string. The XML-RPC service you are communicating with will determine valid
46 | method names you can call.
47 |
48 | ### `params`
49 |
50 | An array of parameters to send. Specify an empty array, or do not supply this
51 | key at all if you do not want to send any parameters.
52 |
53 | See the docs section on [Encoding and Decoding XML-RPC Documents][encoding] for
54 | more information.
55 |
56 | ### Getting data back
57 |
58 | When the XML-RPC call returns, the contents of the `` element are
59 | parsed into JSON and supplied to the `success` callback of the AJAX call as the
60 | first parameter, much like a JSON request.
61 |
62 | ### Handling errors
63 |
64 | If any HTTP errors occur during transport, the normal jQuery AJAX error
65 | handling will be used. If the XML-RPC service successfully replies, but replies
66 | with a `` response, an `$.xmlrpc.XmlRpcFault` is thrown. This error will
67 | be sent as the third parameter to the `error` callback of the AJAX call, as
68 | with other errors.
69 |
70 | Documentation
71 | -------------
72 |
73 | [The full documentation can be found on Read The Docs][docs].
74 |
75 | [docs]: http://jquery-xml-rpc.readthedocs.org/ "Documentation"
76 | [encoding]: http://jquery-xml-rpc.readthedocs.org/en/latest/types.html#encoding-and-decoding-xml-rpc-documents
77 | "Encoding and Decoding XML-RPC Documents"
78 |
--------------------------------------------------------------------------------
/docs/types.rst:
--------------------------------------------------------------------------------
1 | .. _types:
2 |
3 | =====
4 | Types
5 | =====
6 |
7 | JSON and XML-RPC are two unrelated markup languages, so converting between the
8 | types requires a small understanding of both languages. Luckily, most of the
9 | types have a direct mapping between the two languages.
10 |
11 | Encoding and Decoding XML-RPC Documents
12 | ---------------------------------------
13 |
14 | Use the following table to see how XML-RPC types are mapped to JavaScript
15 | types:
16 |
17 | +-------------------------+-----------------+
18 | | XML-RPC | JavaScript |
19 | +=========================+=================+
20 | | ```` | ``null`` |
21 | +-------------------------+-----------------+
22 | | ```` | ``Array`` |
23 | +-------------------------+-----------------+
24 | | ```` | ``Object`` |
25 | +-------------------------+-----------------+
26 | | ```` | ``String`` |
27 | +-------------------------+-----------------+
28 | | ```` | ``Boolean`` |
29 | +-------------------------+-----------------+
30 | | ```` | ``Number`` |
31 | +-------------------------+ |
32 | | ```` | |
33 | +-------------------------+ |
34 | | ```` | |
35 | +-------------------------+ |
36 | | ```` | |
37 | +-------------------------+-----------------+
38 | | ```` | ``Number`` |
39 | +-------------------------+-----------------+
40 | | ```` | ``Date`` |
41 | +-------------------------+-----------------+
42 | | ```` | ``ArrayBuffer`` |
43 | +-------------------------+-----------------+
44 |
45 | .. note:: JavaScript does not have separate types for integers and floats, it simply
46 | has ``Number``. As such, it is impossible to tell if ``4`` really means
47 | ``4`` or ``4``. If this is an issue for you, read on.
48 |
49 | Forcing types
50 | ~~~~~~~~~~~~~
51 |
52 | Some times, the automatic type guessing going from JSON to XML-RPC may not work
53 | for you. The most common source of this problem is in encoding numbers. The
54 | library may sometimes encode a ``Number`` as a ```` instead of a ````, as
55 | there is no reliable way of determining what was actually desired.
56 |
57 | To force a type, wrap the value in a call to ``$.xmlrpc.force``. The types are
58 | named after their XML-RPC equivalents, as mentioned in the above table.
59 |
60 |
61 | To force a floating point JavaScript ``Number``
62 | to be encoded as an ````
63 | and sent as a parameter, use the following:
64 |
65 | .. code-block:: javascript
66 |
67 | var forcedValue = $.xmlrpc.force('i8', 4.5)
68 |
69 | $.xmlrpc({
70 | url: '/RPC2',
71 | methodName: 'foo',
72 | params: [forcedValue]
73 | });
74 |
75 | Adding and Extending Types
76 | --------------------------
77 |
78 | You can add your own types to XML-RPC by adding a member to ``$.xmlrpc.types``,
79 | combined with the ``$.xmlrpc.makeType`` function. See
80 | :ref:`xmlrpc-makeType` for more information
81 |
--------------------------------------------------------------------------------
/tests/tests.document.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true jquery:true */
2 | /*globals deepEqual equal ok assert test module throws */
3 | (function() {
4 | "use strict";
5 | module("Document");
6 |
7 | /**
8 | * Serialize a Document to a string easily
9 | */
10 | var s = (function() {
11 | var serializer = new XMLSerializer();
12 | return function($node) {
13 | return serializer.serializeToString($node);
14 | };
15 | })();
16 |
17 | var d = (function() {
18 | var parser = new DOMParser();
19 | return function(xml) {
20 | return parser.parseFromString(xml, "text/xml");
21 | };
22 | })();
23 |
24 | test("Creating a document", function() {
25 | equal(s($.xmlrpc.document('method', [4, 'foo'])),
26 | '' +
27 | 'method' +
28 | '' +
29 | '4' +
30 | 'foo' +
31 | '' +
32 | '',
33 | 'Can create a simple document');
34 |
35 | equal(s($.xmlrpc.document('empty', [])),
36 | '' +
37 | 'empty' +
38 | '' +
39 | '',
40 | 'Can create an empty document');
41 | });
42 |
43 | test("Decoding a document", function() {
44 |
45 | deepEqual(
46 | $.xmlrpc.parseDocument(d(
47 | '' +
48 | '' +
49 | '' +
50 | '4' +
51 | 'Hello' +
52 | '' +
53 | '')),
54 | [4, "Hello"],
55 | "Can parse a simple response");
56 |
57 | deepEqual(
58 | $.xmlrpc.parseDocument(d(
59 | '' +
60 | '' +
61 | '' +
62 | '')),
63 | [],
64 | "Can parse an empty response");
65 |
66 | deepEqual(
67 | $.xmlrpc.parseDocument(d(
68 | '' +
69 | '' +
70 | '' +
71 | '4' +
72 | '' +
73 | '1' +
74 | '' +
75 | '2' +
76 | '' +
77 | '' +
78 | '' +
79 | '')),
80 | [4, [1, [2]]],
81 | "Can parse a complex response");
82 |
83 | });
84 |
85 | test("Handling errors", 5, function() {
86 | throws(
87 | function() {
88 | $.xmlrpc.parseDocument(d(
89 | '' +
90 | '' +
91 | '' +
92 | '' +
93 | '' +
94 | 'faultCode' +
95 | '4' +
96 | '' +
97 | '' +
98 | 'faultString' +
99 | 'Too many parameters.' +
100 | '' +
101 | '' +
102 | '' +
103 | ''
104 | ));
105 | },
106 | $.xmlrpc.XmlRpcFault,
107 | "Parsing a fault response throws an error");
108 |
109 | try {
110 | $.xmlrpc.parseDocument(d(
111 | '' +
112 | '' +
113 | '' +
114 | '' +
115 | '' +
116 | '' +
117 | 'faultCode' +
118 | '4' +
119 | '' +
120 | '' +
121 | 'faultString' +
122 | 'Error message' +
123 | '' +
124 | '' +
125 | '' +
126 | '' +
127 | ''
128 | ));
129 | ok(false, "Should have thrown an error");
130 | } catch (e) {
131 | equal(e.code, 4, "Error code is present");
132 | equal(e.type, 4, "Error type is present");
133 |
134 | equal(e.msg, "Error message", "Error message is present");
135 | equal(e.message, "Error message", "Error message is present");
136 | }
137 |
138 | });
139 |
140 | })();
141 |
--------------------------------------------------------------------------------
/tests/tests.encoding.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true jquery:true */
2 | /*globals deepEqual equal ok assert test module */
3 | (function() {
4 | "use strict";
5 | module("Encoding");
6 |
7 | /**
8 | * Serialize an Element to a string easily
9 | */
10 | var s = (function() {
11 | var serializer = new XMLSerializer();
12 | return function($node) {
13 | return serializer.serializeToString($node[0]);
14 | };
15 | })();
16 |
17 | /**
18 | * A wrapper around test functions that makes an $xml function for testing
19 | * with. Kinda like a Python decorator
20 | */
21 | var need$xml = function(fn) {
22 | return function() {
23 | var doc = document.implementation.createDocument(null, null, null);
24 | var $xml = function(name) {
25 | return $(doc.createElement(name));
26 | };
27 |
28 | var args = [].slice.call(arguments);
29 | args.unshift($xml);
30 |
31 | return fn.apply(this, args);
32 | };
33 | };
34 |
35 | test("JavaScript primitive value encoding", need$xml(function($xml) {
36 | var types = $.xmlrpc.types;
37 |
38 | deepEqual(types.boolean.encode(true, $xml), $xml('boolean').text('1'),
39 | "Boolean true encodes to 1");
40 |
41 | deepEqual(types.boolean.encode(false, $xml), $xml('boolean').text('0'),
42 | "Boolean true encodes to 0");
43 |
44 |
45 | deepEqual(types.int.encode(3, $xml), $xml('int').text('3'),
46 | "Integer 3 encodes to 3");
47 | deepEqual(types.i8.encode(4, $xml), $xml('i8').text('4'),
48 | "Integer 3 encodes to 4");
49 | deepEqual(types.double.encode(5.5, $xml), $xml('double').text('5.5'),
50 | "Double 5.5 encodes to 5.5");
51 |
52 | deepEqual(types.nil.encode(null, $xml), $xml('nil'),
53 | "Null encodes to ");
54 | deepEqual(types.nil.encode("hello", $xml), $xml('nil'),
55 | "Null encodes to when supplied a non-null value");
56 |
57 | deepEqual(types.string.encode("Hello, World!", $xml), $xml('string').text("Hello, World!"),
58 | "String encodes to ...");
59 | deepEqual(types.string.encode("", $xml), $xml('string').text(""),
60 | "Empty String encodes to ");
61 |
62 | var timestamp = 1350943077107;
63 | var datestring = "2012-10-22T21:57:57Z";
64 | var date = new Date();
65 | date.setTime(timestamp);
66 | deepEqual(types['datetime.iso8601'].encode(date, $xml), $xml('dateTime.iso8601').text(datestring),
67 | "Date encodes to ...");
68 |
69 | }));
70 |
71 | test("Array encoding", need$xml(function($xml) {
72 | var types = $.xmlrpc.types;
73 |
74 | equal(s(types.array.encode([4, "Hello"], $xml)),
75 | '4Hello',
76 | "Simple array encodes");
77 |
78 | // If not all browsers encode this to , this will fail.
79 | equal(s(types.array.encode([], $xml)),
80 | '',
81 | "Empty array encodes");
82 |
83 | equal(s(types.array.encode([1, [2]], $xml)),
84 | '' +
85 | '1' +
86 | '' +
87 | '2' +
88 | '' +
89 | '',
90 | "Array containing array encodes");
91 | }));
92 |
93 | test("Guessing types", need$xml(function($xml) {
94 | ok($.xmlrpc.toXmlRpc(4, $xml).is('int'),
95 | "Number 4 guessed to be ");
96 |
97 | ok($.xmlrpc.toXmlRpc(4.5, $xml).is('double'),
98 | "Number 4.5 guessed to be ");
99 |
100 | ok($.xmlrpc.toXmlRpc(true, $xml).is('boolean'),
101 | "Boolean guessed to be ");
102 |
103 | ok($.xmlrpc.toXmlRpc(null, $xml).is('nil'),
104 | "null guessed to be ");
105 |
106 | ok($.xmlrpc.toXmlRpc(undefined, $xml).is('nil'),
107 | "undefined guessed to be ");
108 |
109 | ok($.xmlrpc.toXmlRpc("Hello", $xml).is('string'),
110 | "String guessed to be ");
111 |
112 | ok($.xmlrpc.toXmlRpc(new Date(), $xml).is('dateTime\\.iso8601'),
113 | "Date guessed to be ");
114 |
115 | ok($.xmlrpc.toXmlRpc({foo: 'bar'}, $xml).is('struct'),
116 | "Object guessed to be ");
117 |
118 | ok($.xmlrpc.toXmlRpc([], $xml).is('array'),
119 | "Array guessed to be ");
120 |
121 | ok($.xmlrpc.toXmlRpc(new ArrayBuffer(), $xml).is('base64'),
122 | "ArrayBuffer guessed to be ");
123 | }));
124 |
125 | })();
126 |
--------------------------------------------------------------------------------
/jquery.xmlrpc.min.js:
--------------------------------------------------------------------------------
1 | !function(e){"use strict";var t=function(){Error.apply(this,arguments)};t.prototype=new Error,t.prototype.type="XML-RPC fault";var n=e.xmlrpc=function(t,i){2===arguments.length?i.url=t:(i=t,t=i.url),i.dataType="xml json",i.type="POST",i.contentType="text/xml",i.converters={"xml json":n.parseDocument};var r=n.document(i.methodName,i.params||[]);return i.data="XMLSerializer"in window?(new window.XMLSerializer).serializeToString(r):r.xml,e.ajax(i)};n.createXMLDocument=function(){if(document.implementation&&"createDocument"in document.implementation)return document.implementation.createDocument(null,null,null);var e,t,n=["MSXML6.DomDocument","MSXML3.DomDocument","MSXML2.DomDocument","MSXML.DomDocument","Microsoft.XmlDom"];for(e=0,t=n.length;t>e;e++)try{return new ActiveXObject(n[e])}catch(i){}},n.document=function(t,i){var r=n.createXMLDocument(),a=function(t){return e(r.createElement(t))},o=a("methodName").text(t),s=a("params").append(e.map(i,function(e){var t=a("value").append(n.toXmlRpc(e,a));return a("param").append(t)})),u=a("methodCall").append(o,s);return r.appendChild(u.get(0)),r};var i=function(e){return e===parseInt(e,10)&&!isNaN(e)};n.toXmlRpc=function(t,n){if(t instanceof r)return t.toXmlRpc(n);var a=e.xmlrpc.types,o=e.type(t);switch(o){case"undefined":case"null":return a.nil.encode(t,n);case"date":return a["datetime.iso8601"].encode(t,n);case"object":return t instanceof ArrayBuffer?a.base64.encode(t,n):a.struct.encode(t,n);case"number":return i(t)?a["int"].encode(t,n):a["double"].encode(t,n);case"array":case"boolean":case"string":return a[o].encode(t,n);default:throw new Error("Unknown type",t)}},n.parseDocument=function(i){var r=e(i),a=r.children("methodresponse"),o=a.find("> fault");if(0===o.length){var s=a.find("> params > param > value > *"),u=s.toArray().map(n.parseNode);return u}var l=n.parseNode(o.find("> value > *").get(0)),c=new t(l.faultString);throw c.msg=c.message=l.faultString,c.type=c.code=l.faultCode,c},n.parseNode=function(e){if(void 0===e)return null;var t=e.nodeName.toLowerCase();if(t in n.types)return n.types[t].decode(e);throw new Error("Unknown type "+t)},n.parseValue=function(t){var i=e(t).children()[0];return i?n.parseNode(i):e(t).text()};var r=function(){};e.xmlrpc.types={},n.makeType=function(t,i,a,o){var s;if(s=function(e){this.value=e},s.prototype=new r,s.prototype.tagName=t,i){var u=a,l=o;a=function(e,t){var n=u(e);return t(s.tagName).text(n)},o=function(t){return l(e(t).text(),t)}}s.prototype.toXmlRpc=function(e){return s.encode(this.value,e)},s.tagName=t,s.encode=a,s.decode=o,n.types[t.toLowerCase()]=s};var a=function(e){return""+Math.floor(e)},o=function(e){return parseInt(e,10)};n.makeType("int",!0,a,o),n.makeType("i4",!0,a,o),n.makeType("i8",!0,a,o),n.makeType("i16",!0,a,o),n.makeType("i32",!0,a,o),n.makeType("double",!0,String,function(e){return parseFloat(e,10)}),n.makeType("string",!0,String,String),n.makeType("boolean",!0,function(e){return e?"1":"0"},function(e){return"1"===e});var s=function(e){return 10>e?"0"+e:e};n.makeType("dateTime.iso8601",!0,function(e){return[e.getUTCFullYear(),"-",s(e.getUTCMonth()+1),"-",s(e.getUTCDate()),"T",s(e.getUTCHours()),":",s(e.getUTCMinutes()),":",s(e.getUTCSeconds()),"Z"].join("")},function(e){return new Date(e)}),n.binary=function(){var e="=",t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),n=t.reduce(function(e,t,n){return e[t]=n,e},{});return{toBase64:function(n){for(var i,r=[],a=new Uint8Array(n),o=0;o>18)%64]),r.push(t[(i>>12)%64]),r.push(t[(i>>6)%64]),r.push(t[(i>>0)%64]);for(var s=3-(n.byteLength%3||3);s--;)r[r.length-s-1]=e;return r.join("")},fromBase64:function(t){t=t.replace(/\s/g, '');var i=t.length,r=3*(i/4);t.charAt(i-1)===e&&r--,t.charAt(i-2)===e&&r--;for(var a,o=new ArrayBuffer(r),s=new Uint8Array(o),u=0,l=0;i>u;u+=4,l+=3)a=(n[t[u+0]]<<18)+(n[t[u+1]]<<12)+(n[t[u+2]]<<6)+(n[t[u+3]]<<0),s[l+0]=(a>>16)%256,s[l+1]=(a>>8)%256,s[l+2]=(a>>0)%256;return o}}}(),n.makeType("base64",!0,function(e){return n.binary.toBase64(e)},function(e){return n.binary.fromBase64(e)}),n.makeType("nil",!1,function(e,t){return t("nil")},function(){return null}),n.makeType("struct",!1,function(t,i){var r=i("struct");return e.each(t,function(e,t){var a=i("name").text(e),o=i("value").append(n.toXmlRpc(t,i));r.append(i("member").append(a,o))}),r},function(t){return e(t).find("> member").toArray().reduce(function(t,i){var r=e(i),a=r.find("> name").text(),o=n.parseValue(r.find("> value"));return t[a]=o,t},{})}),n.makeType("array",!1,function(t,i){var r=i("array"),a=i("data");return e.each(t,function(e,t){a.append(i("value").append(n.toXmlRpc(t,i)))}),r.append(a),r},function(t){return e(t).find("> data > value").toArray().map(n.parseValue)}),n.force=function(e,t){return new n.types[e](t)}}(jQuery);
2 |
--------------------------------------------------------------------------------
/tests/tests.decoding.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true jquery:true */
2 | /*globals deepEqual equal ok assert test module */
3 | (function() {
4 | "use strict";
5 | module("Decoding");
6 |
7 | /**
8 | * Generate an element from a string, and return just that element
9 | */
10 | function el(xml) {
11 | return $(xml)[0];
12 | }
13 |
14 | test("JavaScript primitive value decoding", function($xml) {
15 |
16 | equal($.xmlrpc.parseNode(el('1')), true,
17 | ' true node decodes');
18 |
19 | equal($.xmlrpc.parseNode(el('0')), false,
20 | ' false node decodes');
21 |
22 |
23 | equal($.xmlrpc.parseNode(el('4')), 4,
24 | ' node decodes');
25 | equal($.xmlrpc.parseNode(el('5')), 5,
26 | ' node decodes');
27 | equal($.xmlrpc.parseNode(el('6')), 6,
28 | ' node decodes');
29 | equal($.xmlrpc.parseNode(el('7')), 7,
30 | ' node decodes');
31 | equal($.xmlrpc.parseNode(el('8.9')), 8.9,
32 | ' node decodes');
33 |
34 | equal($.xmlrpc.parseNode(el('')), null,
35 | ' node decodes');
36 | equal($.xmlrpc.parseNode(el('Hello')), null,
37 | ' node decodes, even when not empty');
38 |
39 | equal($.xmlrpc.parseNode(el('Hello')), "Hello",
40 | ' node decodes');
41 |
42 | equal($.xmlrpc.parseNode(el('')), "",
43 | 'Empty node decodes');
44 |
45 | var timestamp = 1350943077000;
46 | var datestring = "2012-10-22T21:57:57Z";
47 | var dateNode = el('' + datestring + '');
48 | equal($.xmlrpc.parseNode(dateNode).getTime(), timestamp,
49 | " node decodes");
50 | });
51 |
52 | test("Array decoding", function($xml) {
53 | deepEqual($.xmlrpc.parseNode(el('4Hello')),
54 | [4, "Hello"],
55 | "Simple array decodes");
56 |
57 | deepEqual($.xmlrpc.parseNode(el('')),
58 | [],
59 | "Empty array decodes");
60 |
61 | deepEqual(
62 | $.xmlrpc.parseNode(el(
63 | '' +
64 | '1' +
65 | '' +
66 | '2' +
67 | '' +
68 | ''
69 | )),
70 | [1, [2]],
71 | "Array containing array encodes");
72 |
73 | // Childless value nodes should be treated like string nodes
74 | deepEqual(
75 | $.xmlrpc.parseNode(el(
76 | '' +
77 | 'String node' +
78 | '' +
79 | 'Raw value string' +
80 | ''
81 | )),
82 | ["String node", "", "Raw value string"],
83 | "Array containing childless nodes parses correctly");
84 | });
85 |
86 | test("Struct decoding", function($xml) {
87 | deepEqual(
88 | $.xmlrpc.parseNode(el(
89 | '' +
90 | '' +
91 | 'foo' +
92 | '4' +
93 | '' +
94 | '' +
95 | 'bar' +
96 | 'Hello' +
97 | '' +
98 | '')),
99 | {foo: 4, bar: "Hello"},
100 | "Simple decodes");
101 |
102 | deepEqual($.xmlrpc.parseNode(el('')), {},
103 | "Empty decodes");
104 |
105 | deepEqual(
106 | $.xmlrpc.parseNode(el(
107 | '' +
108 | '' +
109 | 'foo' +
110 | '4' +
111 | '' +
112 | '' +
113 | 'bar' +
114 | '' +
115 | '' +
116 | 'baz' +
117 | '5' +
118 | '' +
119 | '' +
120 | '' +
121 | '')),
122 | {foo: 4, bar: {baz: 5}},
123 | "struct containing struct decodes");
124 |
125 | deepEqual(
126 | $.xmlrpc.parseNode(el(
127 | '' +
128 | '' +
129 | 'stringNode' +
130 | 'String node' +
131 | '' +
132 | '' +
133 | 'emptyValue' +
134 | '' +
135 | '' +
136 | '' +
137 | 'rawStringValue' +
138 | 'Raw string value' +
139 | '' +
140 | '')),
141 | {
142 | stringNode: "String node",
143 | emptyValue: "",
144 | rawStringValue: "Raw string value"
145 | },
146 | "Struct with childless nodes parses correctly");
147 | });
148 |
149 | })();
150 |
--------------------------------------------------------------------------------
/tests/qunit.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.10.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | overflow: hidden;
71 | }
72 |
73 | #qunit-userAgent {
74 | padding: 0.5em 0 0.5em 2.5em;
75 | background-color: #2b81af;
76 | color: #fff;
77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78 | }
79 |
80 | #qunit-modulefilter-container {
81 | float: right;
82 | }
83 |
84 | /** Tests: Pass/Fail */
85 |
86 | #qunit-tests {
87 | list-style-position: inside;
88 | }
89 |
90 | #qunit-tests li {
91 | padding: 0.4em 0.5em 0.4em 2.5em;
92 | border-bottom: 1px solid #fff;
93 | list-style-position: inside;
94 | }
95 |
96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97 | display: none;
98 | }
99 |
100 | #qunit-tests li strong {
101 | cursor: pointer;
102 | }
103 |
104 | #qunit-tests li a {
105 | padding: 0.5em;
106 | color: #c2ccd1;
107 | text-decoration: none;
108 | }
109 | #qunit-tests li a:hover,
110 | #qunit-tests li a:focus {
111 | color: #000;
112 | }
113 |
114 | #qunit-tests ol {
115 | margin-top: 0.5em;
116 | padding: 0.5em;
117 |
118 | background-color: #fff;
119 |
120 | border-radius: 5px;
121 | -moz-border-radius: 5px;
122 | -webkit-border-radius: 5px;
123 | }
124 |
125 | #qunit-tests table {
126 | border-collapse: collapse;
127 | margin-top: .2em;
128 | }
129 |
130 | #qunit-tests th {
131 | text-align: right;
132 | vertical-align: top;
133 | padding: 0 .5em 0 0;
134 | }
135 |
136 | #qunit-tests td {
137 | vertical-align: top;
138 | }
139 |
140 | #qunit-tests pre {
141 | margin: 0;
142 | white-space: pre-wrap;
143 | word-wrap: break-word;
144 | }
145 |
146 | #qunit-tests del {
147 | background-color: #e0f2be;
148 | color: #374e0c;
149 | text-decoration: none;
150 | }
151 |
152 | #qunit-tests ins {
153 | background-color: #ffcaca;
154 | color: #500;
155 | text-decoration: none;
156 | }
157 |
158 | /*** Test Counts */
159 |
160 | #qunit-tests b.counts { color: black; }
161 | #qunit-tests b.passed { color: #5E740B; }
162 | #qunit-tests b.failed { color: #710909; }
163 |
164 | #qunit-tests li li {
165 | padding: 5px;
166 | background-color: #fff;
167 | border-bottom: none;
168 | list-style-position: inside;
169 | }
170 |
171 | /*** Passing Styles */
172 |
173 | #qunit-tests li li.pass {
174 | color: #3c510c;
175 | background-color: #fff;
176 | border-left: 10px solid #C6E746;
177 | }
178 |
179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
180 | #qunit-tests .pass .test-name { color: #366097; }
181 |
182 | #qunit-tests .pass .test-actual,
183 | #qunit-tests .pass .test-expected { color: #999999; }
184 |
185 | #qunit-banner.qunit-pass { background-color: #C6E746; }
186 |
187 | /*** Failing Styles */
188 |
189 | #qunit-tests li li.fail {
190 | color: #710909;
191 | background-color: #fff;
192 | border-left: 10px solid #EE5757;
193 | white-space: pre;
194 | }
195 |
196 | #qunit-tests > li:last-child {
197 | border-radius: 0 0 5px 5px;
198 | -moz-border-radius: 0 0 5px 5px;
199 | -webkit-border-bottom-right-radius: 5px;
200 | -webkit-border-bottom-left-radius: 5px;
201 | }
202 |
203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
204 | #qunit-tests .fail .test-name,
205 | #qunit-tests .fail .module-name { color: #000000; }
206 |
207 | #qunit-tests .fail .test-actual { color: #EE5757; }
208 | #qunit-tests .fail .test-expected { color: green; }
209 |
210 | #qunit-banner.qunit-fail { background-color: #EE5757; }
211 |
212 |
213 | /** Result */
214 |
215 | #qunit-testresult {
216 | padding: 0.5em 0.5em 0.5em 2.5em;
217 |
218 | color: #2b81af;
219 | background-color: #D2E0E6;
220 |
221 | border-bottom: 1px solid white;
222 | }
223 | #qunit-testresult .module-name {
224 | font-weight: bold;
225 | }
226 |
227 | /** Fixture */
228 |
229 | #qunit-fixture {
230 | position: absolute;
231 | top: -10000px;
232 | left: -10000px;
233 | width: 1000px;
234 | height: 1000px;
235 | }
236 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\jQueryXML-RPC.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\jQueryXML-RPC.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/jQueryXML-RPC.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/jQueryXML-RPC.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/jQueryXML-RPC"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/jQueryXML-RPC"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # jQuery XML-RPC documentation build configuration file, created by
4 | # sphinx-quickstart on Mon Oct 22 19:51:25 2012.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 | import sys, os
15 |
16 | # If extensions (or modules to document with autodoc) are in another directory,
17 | # add these directories to sys.path here. If the directory is relative to the
18 | # documentation root, use os.path.abspath to make it absolute, like shown here.
19 | #sys.path.insert(0, os.path.abspath('.'))
20 |
21 | # -- General configuration -----------------------------------------------------
22 |
23 | # If your documentation needs a minimal Sphinx version, state it here.
24 | #needs_sphinx = '1.0'
25 |
26 | # Add any Sphinx extension module names here, as strings. They can be extensions
27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
28 | extensions = []
29 |
30 | # Add any paths that contain templates here, relative to this directory.
31 | templates_path = ['_templates']
32 |
33 | # The suffix of source filenames.
34 | source_suffix = '.rst'
35 |
36 | # The encoding of source files.
37 | #source_encoding = 'utf-8-sig'
38 |
39 | # The master toctree document.
40 | master_doc = 'index'
41 |
42 | # General information about the project.
43 | project = u'jQuery XML-RPC'
44 | copyright = u'2012, Tim Heap'
45 |
46 | # The version info for the project you're documenting, acts as replacement for
47 | # |version| and |release|, also used in various other places throughout the
48 | # built documents.
49 | #
50 | # The short X.Y version.
51 | version = '0.1'
52 | # The full version, including alpha/beta/rc tags.
53 | release = '0.1.0'
54 |
55 | # The language for content autogenerated by Sphinx. Refer to documentation
56 | # for a list of supported languages.
57 | #language = None
58 |
59 | # There are two options for replacing |today|: either, you set today to some
60 | # non-false value, then it is used:
61 | #today = ''
62 | # Else, today_fmt is used as the format for a strftime call.
63 | #today_fmt = '%B %d, %Y'
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | exclude_patterns = ['_build']
68 |
69 | # The reST default role (used for this markup: `text`) to use for all documents.
70 | #default_role = None
71 |
72 | # If true, '()' will be appended to :func: etc. cross-reference text.
73 | #add_function_parentheses = True
74 |
75 | # If true, the current module name will be prepended to all description
76 | # unit titles (such as .. function::).
77 | #add_module_names = True
78 |
79 | # If true, sectionauthor and moduleauthor directives will be shown in the
80 | # output. They are ignored by default.
81 | #show_authors = False
82 |
83 | # The name of the Pygments (syntax highlighting) style to use.
84 | pygments_style = 'sphinx'
85 |
86 | # A list of ignored prefixes for module index sorting.
87 | #modindex_common_prefix = []
88 |
89 |
90 | # -- Options for HTML output ---------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = 'default'
95 |
96 | # Theme options are theme-specific and customize the look and feel of a theme
97 | # further. For a list of options available for each theme, see the
98 | # documentation.
99 | #html_theme_options = {}
100 |
101 | # Add any paths that contain custom themes here, relative to this directory.
102 | #html_theme_path = []
103 |
104 | # The name for this set of Sphinx documents. If None, it defaults to
105 | # " v documentation".
106 | #html_title = None
107 |
108 | # A shorter title for the navigation bar. Default is the same as html_title.
109 | #html_short_title = None
110 |
111 | # The name of an image file (relative to this directory) to place at the top
112 | # of the sidebar.
113 | #html_logo = None
114 |
115 | # The name of an image file (within the static path) to use as favicon of the
116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
117 | # pixels large.
118 | #html_favicon = None
119 |
120 | # Add any paths that contain custom static files (such as style sheets) here,
121 | # relative to this directory. They are copied after the builtin static files,
122 | # so a file named "default.css" will overwrite the builtin "default.css".
123 | html_static_path = ['_static']
124 |
125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
126 | # using the given strftime format.
127 | #html_last_updated_fmt = '%b %d, %Y'
128 |
129 | # If true, SmartyPants will be used to convert quotes and dashes to
130 | # typographically correct entities.
131 | #html_use_smartypants = True
132 |
133 | # Custom sidebar templates, maps document names to template names.
134 | #html_sidebars = {}
135 |
136 | # Additional templates that should be rendered to pages, maps page names to
137 | # template names.
138 | #html_additional_pages = {}
139 |
140 | # If false, no module index is generated.
141 | #html_domain_indices = True
142 |
143 | # If false, no index is generated.
144 | #html_use_index = True
145 |
146 | # If true, the index is split into individual pages for each letter.
147 | #html_split_index = False
148 |
149 | # If true, links to the reST sources are added to the pages.
150 | #html_show_sourcelink = True
151 |
152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
153 | #html_show_sphinx = True
154 |
155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
156 | #html_show_copyright = True
157 |
158 | # If true, an OpenSearch description file will be output, and all pages will
159 | # contain a tag referring to it. The value of this option must be the
160 | # base URL from which the finished HTML is served.
161 | #html_use_opensearch = ''
162 |
163 | # This is the file name suffix for HTML files (e.g. ".xhtml").
164 | #html_file_suffix = None
165 |
166 | # Output file base name for HTML help builder.
167 | htmlhelp_basename = 'jQueryXML-RPCdoc'
168 |
169 |
170 | # -- Options for LaTeX output --------------------------------------------------
171 |
172 | latex_elements = {
173 | # The paper size ('letterpaper' or 'a4paper').
174 | #'papersize': 'letterpaper',
175 |
176 | # The font size ('10pt', '11pt' or '12pt').
177 | #'pointsize': '10pt',
178 |
179 | # Additional stuff for the LaTeX preamble.
180 | #'preamble': '',
181 | }
182 |
183 | # Grouping the document tree into LaTeX files. List of tuples
184 | # (source start file, target name, title, author, documentclass [howto/manual]).
185 | latex_documents = [
186 | ('index', 'jQueryXML-RPC.tex', u'jQuery XML-RPC Documentation',
187 | u'Tim Heap', 'manual'),
188 | ]
189 |
190 | # The name of an image file (relative to this directory) to place at the top of
191 | # the title page.
192 | #latex_logo = None
193 |
194 | # For "manual" documents, if this is true, then toplevel headings are parts,
195 | # not chapters.
196 | #latex_use_parts = False
197 |
198 | # If true, show page references after internal links.
199 | #latex_show_pagerefs = False
200 |
201 | # If true, show URL addresses after external links.
202 | #latex_show_urls = False
203 |
204 | # Documents to append as an appendix to all manuals.
205 | #latex_appendices = []
206 |
207 | # If false, no module index is generated.
208 | #latex_domain_indices = True
209 |
210 |
211 | # -- Options for manual page output --------------------------------------------
212 |
213 | # One entry per manual page. List of tuples
214 | # (source start file, name, description, authors, manual section).
215 | man_pages = [
216 | ('index', 'jqueryxml-rpc', u'jQuery XML-RPC Documentation',
217 | [u'Tim Heap'], 1)
218 | ]
219 |
220 | # If true, show URL addresses after external links.
221 | #man_show_urls = False
222 |
223 |
224 | # -- Options for Texinfo output ------------------------------------------------
225 |
226 | # Grouping the document tree into Texinfo files. List of tuples
227 | # (source start file, target name, title, author,
228 | # dir menu entry, description, category)
229 | texinfo_documents = [
230 | ('index', 'jQueryXML-RPC', u'jQuery XML-RPC Documentation',
231 | u'Tim Heap', 'jQueryXML-RPC', 'One line description of project.',
232 | 'Miscellaneous'),
233 | ]
234 |
235 | # Documents to append as an appendix to all manuals.
236 | #texinfo_appendices = []
237 |
238 | # If false, no module index is generated.
239 | #texinfo_domain_indices = True
240 |
241 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
242 | #texinfo_show_urls = 'footnote'
243 |
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | .. _api:
2 |
3 | ===
4 | API
5 | ===
6 |
7 | The following API is exposed in case you need to extend this library.
8 | You should not have to use this API in everyday use of the library.
9 |
10 | .. _xmlrpc:
11 |
12 | $.xmlrpc()
13 | ----------
14 |
15 | Call a remote procedure.
16 | This is a small wrapper around `jQuery.ajax() `_
17 | so see the documentation for that for more information.
18 | It takes the following arguments:
19 |
20 | ``url``
21 | The URL of the service to call. Optional.
22 | If not specified, this is pulled from the options dict
23 |
24 | ``options``
25 | Options for the request.
26 | Most options are passed straight through to ``jQuery.ajax()``,
27 | with the exception of two keys.
28 |
29 | The ``methodName`` key must always be supplied, and must be a string.
30 | It is used as the ```` for the call
31 |
32 | The ``params`` key can be used to send parameters.
33 | This must be an array of values.
34 | Leave this blank, or supply and empty array to send no parameters.
35 |
36 | See :ref:`types` for more information on how JavaScript types are translated to XML-RPC types
37 |
38 | Getting data back
39 | ~~~~~~~~~~~~~~~~~
40 |
41 | When the XML-RPC call returns,
42 | the contents of the ```` element are parsed into JSON and
43 | supplied to the ``success`` callback of the AJAX call as the first parameter,
44 | much like a JSON request.
45 |
46 | Handling errors
47 | ~~~~~~~~~~~~~~~
48 |
49 | If any HTTP errors occur during transport,
50 | the normal jQuery AJAX error handling will be used.
51 | If the XML-RPC service successfully replies,
52 | but replies with a ```` response,
53 | an ``$.xmlrpc.XmlRpcFault`` is thrown.
54 | This error will be sent as the third parameter to the ``error`` callback
55 | of the AJAX call, as with other errors.
56 |
57 | .. _xmlrpc-document:
58 |
59 | $.xmlrpc.document()
60 | -------------------
61 |
62 | Make an XML-RPC document from a method name and a set of parameters.
63 | It takes the following arguments:
64 |
65 | ``methodName``
66 | This is method put in the ```` element from XML-RPC. It should be a
67 | string. The XML-RPC service you are communicating with will determine valid
68 | method names you can call.
69 |
70 | ``params``
71 | An array of parameters to send.
72 | Specify an empty array if you do not want to send any parameters.
73 |
74 | Example
75 | ~~~~~~~
76 |
77 | The JavaScript call:
78 |
79 | .. code-block:: javascript
80 | :linenos:
81 |
82 | $.xmlrpc.document('foo', ['bar, true, [1, 2, 3]]);
83 |
84 | produces the following XML document (with out the whitespace):
85 |
86 | .. code-block:: xml
87 |
88 |
89 | foo
90 |
91 |
92 | bar
93 |
94 |
95 | 1
96 |
97 |
98 |
99 | 1
100 | 2
101 | 3
102 |
103 |
104 |
105 |
106 |
107 | .. _xmlrpc-toXmlRpc:
108 |
109 | $.xmlrpc.toXmlRpc()
110 | -------------------
111 |
112 | Take a value, and encode it as an XML-RPC node.
113 | Because the XML nodes must be created by the XML documents own ``createElement``,
114 | this can not be used outside of a call to ``$.xmlrpc.document``.
115 | It takes the following arguments:
116 |
117 | ``value``
118 | The value to encode
119 |
120 | ``$xml``
121 | A helper function to create an XML node on the document.
122 | It is then returned, wrapped by ``jQuery``.
123 |
124 | .. _xmlrpc-parseDocument:
125 |
126 | $.xmlrpc.parseDocument()
127 | ------------------------
128 |
129 | Parse an XML-RPC document, and return its contents.
130 | If the document represents an XML-RPC fault,
131 | an ``$.xmlrpc.XmlRpcFault`` is thrown.
132 | It takes the following arguments:
133 |
134 | Example
135 | ~~~~~~~
136 |
137 | The following XML document:
138 |
139 | .. code-block:: xml
140 |
141 |
142 |
143 |
144 |
145 | foo
146 |
147 |
148 | 3
149 |
150 |
151 |
152 |
153 | foo
154 | 1
155 |
156 |
157 | bar
158 | 2
159 |
160 |
161 |
162 |
163 |
164 |
165 | parsed by:
166 |
167 | .. code-block:: javascript
168 |
169 | $.xmlrpc.parseDocument(doc);
170 |
171 | would result in the JSON document:
172 |
173 | .. code-block:: javascript
174 |
175 | [
176 | 'foo',
177 | 3,
178 | {
179 | foo: 1,
180 | bar: 2
181 | }
182 | ]
183 |
184 | .. _xmlrpc-parseNode:
185 |
186 | $.xmlrpc.parseNode()
187 | --------------------
188 |
189 | Take a single XML element, and return the JSON equivalent of it.
190 | It takes one argument:
191 |
192 | ``node``
193 | The XML node to decode.
194 | It should be be one of the types registered with
195 | :ref:`xmlrpc-makeType`.
196 | If the type can not be found, and error is thrown.
197 |
198 | Example
199 | ~~~~~~~
200 |
201 | The XML element:
202 |
203 | .. code-block:: xml
204 |
205 |
206 |
207 | foo
208 | 1
209 |
210 |
211 | bar
212 | 2
213 |
214 |
215 |
216 | would be parsed by calling:
217 |
218 | .. code-block:: javascript
219 |
220 | $.xmlrpc.parseNode(node)
221 |
222 | resulting in the JSON:
223 |
224 | .. code-block:: javascript
225 |
226 | {
227 | foo: 1,
228 | bar: 2
229 | }
230 |
231 | .. _xmlrpc-makeType:
232 |
233 | $.xmlrpc.makeType()
234 | -------------------
235 |
236 | Add a XML-RPC type to the library.
237 | The library will then know how to decode elements of this type when they are returned.
238 | It takes the following arguments:
239 |
240 | ``tag``
241 | The name of the XML-RPC element this represents.
242 | Example: ``'boolean'``
243 |
244 | ``simple``
245 | If the element is a simple type or not.
246 | All standard elements except ```` and ```` are simple types.
247 | The encoding a decoding functions of simple types are simplified,
248 | as they just deal with the text content of the elements.
249 |
250 | ``encode``
251 | Take a JavaScript value, and encode it to an XML-RPC element.
252 | Receives the value to be encoded,
253 | and a helper function used to create XML nodes on the correct document -
254 | This helper MUST be used to create XML nodes for child elements.
255 |
256 | Simple types need only return the text of the node,
257 | creating the node is handled for you.
258 |
259 | ``decode``
260 | Take an XML element, and decode it to a JavaScript representation.
261 |
262 | Simple types receive the text of the node instead of the node itself.
263 |
264 | Example
265 | ~~~~~~~
266 |
267 | A simple boolean node:
268 |
269 | .. code-block:: javascript
270 |
271 | // Boolean type. True == '1', False == '0'
272 | $.xmlrpc.makeType('boolean', true, function(value) {
273 | return value ? '1' : '0';
274 | }, function(text) {
275 | return text == '1';
276 | });
277 |
278 | A complex, custom element:
279 |
280 | .. code-block:: javascript
281 |
282 | /**
283 | * Convert
284 | * {foo: 1, bar: "hello"}
285 | * into
286 | * 1hello
287 | * Note the call to `$.xmlrpc.toXmlRpc`` to recursively encode the `bar` element.
288 | */
289 | $.xmlrpc.makeType('custom', false, function(value, $xml) {
290 | return $xml('custom').append([
291 | $xml('foo').text($.xmlrpc.toXmlRpc(value.foo, $xml)),
292 | $xml('bar').text($.xmlrpc.toXmlRpc(value.foo, $xml))
293 | ]);
294 | }, function(node) {
295 | return {
296 | foo: parseInt($(node).find('> foo').text()),
297 | bar: fromXmlRpc($(node).find('> bar > *').get(0)),
298 | }
299 | });
300 |
301 | .. _xmlrpc-force:
302 |
303 | $.xmlrpc.force()
304 | ----------------
305 |
306 | Force a value to be encoded as a certain type in XML-RPC.
307 | It takes the following arguments:
308 |
309 | ``type``
310 | The type to force the value to. One of the XML-RPC types named in the
311 | [types documentation][types], or one of the custom types added with
312 | ``$.xmlrpc.makeType``.
313 |
314 | ``value``
315 | Any value that will be encoded as the type.
316 |
317 | Example
318 | ~~~~~~~
319 |
320 | Force a float to be encoded as an i8, to send as a parameter:
321 |
322 | .. code-block:: javascript
323 |
324 | var forcedValue = $.xmlrpc.force('i8', 4.5)
325 |
326 | $.xmlrpc({
327 | url: '/RPC2',
328 | methodName: 'foo',
329 | params: [forcedValue]
330 | });
331 |
--------------------------------------------------------------------------------
/jquery.xmlrpc.js:
--------------------------------------------------------------------------------
1 | /*jshint browser:true */
2 | /*global jQuery */
3 | (function($) {
4 | "use strict";
5 |
6 | var XmlRpcFault = function() {
7 | Error.apply(this, arguments);
8 | };
9 | XmlRpcFault.prototype = new Error();
10 | XmlRpcFault.prototype.type = 'XML-RPC fault';
11 |
12 | var xmlrpc = $.xmlrpc = function(url, settings) {
13 |
14 | if (arguments.length === 2) {
15 | settings.url = url;
16 | } else {
17 | settings = url;
18 | url = settings.url;
19 | }
20 |
21 | settings.dataType = 'xml json';
22 | settings.type = 'POST';
23 | settings.contentType = 'text/xml';
24 | settings.converters = {'xml json': xmlrpc.parseDocument};
25 |
26 | var xmlDoc = xmlrpc.document(settings.methodName, settings.params || []);
27 |
28 | if ("XMLSerializer" in window) {
29 | settings.data = new window.XMLSerializer().serializeToString(xmlDoc);
30 | } else {
31 | // IE does not have XMLSerializer
32 | settings.data = xmlDoc.xml;
33 | }
34 |
35 | return $.ajax(settings);
36 | };
37 |
38 | /**
39 | * Make an XML document node.
40 | */
41 | xmlrpc.createXMLDocument = function () {
42 |
43 | if (document.implementation && "createDocument" in document.implementation) {
44 | // Most browsers support createDocument
45 | return document.implementation.createDocument(null, null, null);
46 |
47 | } else {
48 | // IE uses ActiveXObject instead of the above.
49 | var i, length, activeX = [
50 | "MSXML6.DomDocument", "MSXML3.DomDocument",
51 | "MSXML2.DomDocument", "MSXML.DomDocument", "Microsoft.XmlDom"
52 | ];
53 | for (i = 0, length = activeX.length; i < length; i++) {
54 | try {
55 | return new ActiveXObject(activeX[i]);
56 | } catch(_) {}
57 | }
58 | }
59 | };
60 |
61 | /**
62 | * Make an XML-RPC document from a method name and a set of parameters
63 | */
64 | xmlrpc.document = function(name, params) {
65 | var doc = xmlrpc.createXMLDocument();
66 |
67 |
68 | var $xml = function(name) {
69 | return $(doc.createElement(name));
70 | };
71 |
72 | var $methodName = $xml('methodName').text(name);
73 | var $params = $xml('params').append($.map(params, function(param) {
74 | var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
75 | return $xml('param').append($value);
76 | }));
77 | var $methodCall = $xml('methodCall').append($methodName, $params);
78 | doc.appendChild($methodCall.get(0));
79 | return doc;
80 | };
81 |
82 | var _isInt = function(x) {
83 | return (x === parseInt(x, 10)) && !isNaN(x);
84 | };
85 |
86 | /**
87 | * Take a JavaScript value, and return an XML node representing the value
88 | * in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
89 | * used. Otherwise, a best guess is made as to its type. The best guess is
90 | * good enough in the vast majority of cases.
91 | */
92 | xmlrpc.toXmlRpc = function(item, $xml) {
93 |
94 | if (item instanceof XmlRpcType) {
95 | return item.toXmlRpc($xml);
96 | }
97 |
98 | var types = $.xmlrpc.types;
99 | var type = $.type(item);
100 |
101 | switch (type) {
102 | case "undefined":
103 | case "null":
104 | return types.nil.encode(item, $xml);
105 |
106 | case "date":
107 | return types['datetime.iso8601'].encode(item, $xml);
108 |
109 | case "object":
110 | if (item instanceof ArrayBuffer) {
111 | return types.base64.encode(item, $xml);
112 | } else {
113 | return types.struct.encode(item, $xml);
114 | }
115 | break;
116 |
117 |
118 | case "number":
119 | // Ints and Floats encode differently
120 | if (_isInt(item)) {
121 | return types['int'].encode(item, $xml);
122 | } else {
123 | return types['double'].encode(item, $xml);
124 | }
125 | break;
126 |
127 | case "array":
128 | case "boolean":
129 | case "string":
130 | return types[type].encode(item, $xml);
131 |
132 | default:
133 | throw new Error("Unknown type", item);
134 | }
135 | };
136 |
137 | /**
138 | * Take an XML-RPC document and decode it to an equivalent JavaScript
139 | * representation.
140 | *
141 | * If the XML-RPC document represents a fault, then an equivalent
142 | * XmlRpcFault will be thrown instead
143 | */
144 | xmlrpc.parseDocument = function(doc) {
145 | var $doc = $(doc);
146 | var $response = $doc.children('methodresponse');
147 |
148 | var $fault = $response.find('> fault');
149 | if ($fault.length === 0) {
150 | var $params = $response.find('> params > param > value > *');
151 | var json = $params.toArray().map(xmlrpc.parseNode);
152 | return json;
153 | } else {
154 | var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
155 | var err = new XmlRpcFault(fault.faultString);
156 | err.msg = err.message = fault.faultString;
157 | err.type = err.code = fault.faultCode;
158 | throw err;
159 | }
160 | };
161 |
162 | /*
163 | * Take an XML-RPC node, and return the JavaScript equivalent
164 | */
165 | xmlrpc.parseNode = function(node) {
166 |
167 | // Some XML-RPC services return empty elements. This is not
168 | // legal XML-RPC, but we may as well handle it.
169 | if (node === undefined) {
170 | return null;
171 | }
172 | var nodename = node.nodeName.toLowerCase();
173 | if (nodename in xmlrpc.types) {
174 | return xmlrpc.types[nodename].decode(node);
175 | } else {
176 | throw new Error('Unknown type ' + nodename);
177 | }
178 | };
179 |
180 | /*
181 | * Take a node, and return the JavaScript equivalent.
182 | */
183 | xmlrpc.parseValue = function(value) {
184 | var child = $(value).children()[0];
185 | if (child) {
186 | // Child nodes should be decoded.
187 | return xmlrpc.parseNode(child);
188 | } else {
189 | // If no child nodes, the value is a plain text node.
190 | return $(value).text();
191 | }
192 | };
193 |
194 | var XmlRpcType = function() { };
195 |
196 | $.xmlrpc.types = {};
197 |
198 | /**
199 | * Make a XML-RPC type. We use these to encode and decode values. You can
200 | * also force a values type using this. See `$.xmlrpc.force()`
201 | */
202 | xmlrpc.makeType = function(tagName, simple, encode, decode) {
203 | var Type;
204 |
205 | Type = function(value) {
206 | this.value = value;
207 | };
208 | Type.prototype = new XmlRpcType();
209 | Type.prototype.tagName = tagName;
210 |
211 | if (simple) {
212 | var simpleEncode = encode, simpleDecode = decode;
213 | encode = function(value, $xml) {
214 | var text = simpleEncode(value);
215 | return $xml(Type.tagName).text(text);
216 | };
217 | decode = function(node) {
218 | return simpleDecode($(node).text(), node);
219 | };
220 | }
221 | Type.prototype.toXmlRpc = function($xml) {
222 | return Type.encode(this.value, $xml);
223 | };
224 |
225 | Type.tagName = tagName;
226 | Type.encode = encode;
227 | Type.decode = decode;
228 |
229 | xmlrpc.types[tagName.toLowerCase()] = Type;
230 | };
231 |
232 |
233 | // Number types
234 | var _fromInt = function(value) { return '' + Math.floor(value); };
235 | var _toInt = function(text, _) { return parseInt(text, 10); };
236 |
237 | xmlrpc.makeType('int', true, _fromInt, _toInt);
238 | xmlrpc.makeType('i4', true, _fromInt, _toInt);
239 | xmlrpc.makeType('i8', true, _fromInt, _toInt);
240 | xmlrpc.makeType('i16', true, _fromInt, _toInt);
241 | xmlrpc.makeType('i32', true, _fromInt, _toInt);
242 |
243 | xmlrpc.makeType('double', true, String, function(text) {
244 | return parseFloat(text, 10);
245 | });
246 |
247 | // String type. Fairly simple
248 | xmlrpc.makeType('string', true, String, String);
249 |
250 | // Boolean type. True == '1', False == '0'
251 | xmlrpc.makeType('boolean', true, function(value) {
252 | return value ? '1' : '0';
253 | }, function(text) {
254 | return text === '1';
255 | });
256 |
257 | // Dates are a little trickier
258 | var _pad = function(n) { return n<10 ? '0'+n : n; };
259 |
260 | xmlrpc.makeType('dateTime.iso8601', true, function(d) {
261 | return [
262 | d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
263 | _pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
264 | _pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
265 | ].join('');
266 | }, function(text) {
267 | return new Date(text);
268 | });
269 |
270 | // Go between a base64 string and an ArrayBuffer
271 | xmlrpc.binary = (function() {
272 | var pad = '=';
273 | var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
274 | 'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
275 | var fromChars = toChars.reduce(function(acc, chr, i) {
276 | acc[chr] = i;
277 | return acc;
278 | }, {});
279 |
280 | /*
281 | * In the following, three bytes are added together into a 24-bit
282 | * number, which is then split up in to 4 6-bit numbers - or vice versa.
283 | * That is why there is lots of shifting by multiples of 6 and 8, and
284 | * the magic numbers 3 and 4.
285 | *
286 | * The modulo 64 is for converting to base 64, and the modulo 256 is for
287 | * converting to 8-bit numbers.
288 | */
289 | return {
290 | toBase64: function(ab) {
291 | var acc = [];
292 |
293 | var int8View = new Uint8Array(ab);
294 | var int8Index = 0, int24;
295 | for (; int8Index < int8View.length; int8Index += 3) {
296 |
297 | // Grab three bytes
298 | int24 =
299 | (int8View[int8Index + 0] << 16) +
300 | (int8View[int8Index + 1] << 8) +
301 | (int8View[int8Index + 2] << 0);
302 |
303 | // Push four chars
304 | acc.push(toChars[(int24 >> 18) % 64]);
305 | acc.push(toChars[(int24 >> 12) % 64]);
306 | acc.push(toChars[(int24 >> 6) % 64]);
307 | acc.push(toChars[(int24 >> 0)% 64]);
308 | }
309 |
310 | // Set the last few characters to the padding character
311 | var padChars = 3 - ((ab.byteLength % 3) || 3);
312 | while (padChars--) {
313 | acc[acc.length - padChars - 1] = pad;
314 | }
315 |
316 | return acc.join('');
317 | },
318 |
319 | fromBase64: function(base64) {
320 | var base64Len = base64.length;
321 |
322 | // Work out the length of the data, accommodating for padding
323 | var abLen = (base64Len / 4) * 3;
324 | if (base64.charAt(base64Len - 1) === pad) { abLen--; }
325 | if (base64.charAt(base64Len - 2) === pad) { abLen--; }
326 |
327 | // Make the ArrayBuffer, and an Int8Array to work with it
328 | var ab = new ArrayBuffer(abLen);
329 | var int8View = new Uint8Array(ab);
330 |
331 | var base64Index = 0, int8Index = 0, int24;
332 | for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {
333 |
334 | // Grab four chars
335 | int24 =
336 | (fromChars[base64[base64Index + 0]] << 18) +
337 | (fromChars[base64[base64Index + 1]] << 12) +
338 | (fromChars[base64[base64Index + 2]] << 6) +
339 | (fromChars[base64[base64Index + 3]] << 0);
340 |
341 | // Push three bytes
342 | int8View[int8Index + 0] = (int24 >> 16) % 256;
343 | int8View[int8Index + 1] = (int24 >> 8) % 256;
344 | int8View[int8Index + 2] = (int24 >> 0) % 256;
345 |
346 | }
347 |
348 | return ab;
349 | }
350 | };
351 | })();
352 |
353 | xmlrpc.makeType('base64', true, function(ab) {
354 | return xmlrpc.binary.toBase64(ab);
355 | }, function(text) {
356 | return xmlrpc.binary.fromBase64(text);
357 | });
358 |
359 | // Nil/null
360 | xmlrpc.makeType('nil', false,
361 | function(val, $xml) { return $xml('nil'); },
362 | function(_) { return null; }
363 | );
364 |
365 | // Structs/Objects
366 | xmlrpc.makeType('struct', false, function(value, $xml) {
367 | var $struct = $xml('struct');
368 |
369 | $.each(value, function(name, value) {
370 | var $name = $xml('name').text(name);
371 | var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
372 | $struct.append($xml('member').append($name, $value));
373 | });
374 |
375 | return $struct;
376 |
377 | }, function(node) {
378 | return $(node)
379 | .find('> member')
380 | .toArray()
381 | .reduce(function(struct, el) {
382 | var $el = $(el);
383 | var key = $el.find('> name').text();
384 | var value = xmlrpc.parseValue($el.find('> value'));
385 |
386 | struct[key] = value;
387 | return struct;
388 | }, {});
389 |
390 | });
391 |
392 | // Arrays
393 | xmlrpc.makeType('array', false, function(value, $xml) {
394 | var $array = $xml('array');
395 | var $data = $xml('data');
396 | $.each(value, function(i, val) {
397 | $data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
398 | });
399 | $array.append($data);
400 | return $array;
401 | }, function(node) {
402 | return $(node).find('> data > value').toArray()
403 | .map(xmlrpc.parseValue);
404 | });
405 |
406 |
407 | /**
408 | * Force a value to an XML-RPC type. All the usual XML-RPC types are
409 | * supported
410 | */
411 | xmlrpc.force = function(type, value) {
412 | return new xmlrpc.types[type](value);
413 | };
414 |
415 | })(jQuery);
416 |
--------------------------------------------------------------------------------
/tests/qunit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.10.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | (function( window ) {
12 |
13 | var QUnit,
14 | config,
15 | onErrorFnPrev,
16 | testId = 0,
17 | fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
18 | toString = Object.prototype.toString,
19 | hasOwn = Object.prototype.hasOwnProperty,
20 | // Keep a local reference to Date (GH-283)
21 | Date = window.Date,
22 | defined = {
23 | setTimeout: typeof window.setTimeout !== "undefined",
24 | sessionStorage: (function() {
25 | var x = "qunit-test-string";
26 | try {
27 | sessionStorage.setItem( x, x );
28 | sessionStorage.removeItem( x );
29 | return true;
30 | } catch( e ) {
31 | return false;
32 | }
33 | }())
34 | };
35 |
36 | function Test( settings ) {
37 | extend( this, settings );
38 | this.assertions = [];
39 | this.testNumber = ++Test.count;
40 | }
41 |
42 | Test.count = 0;
43 |
44 | Test.prototype = {
45 | init: function() {
46 | var a, b, li,
47 | tests = id( "qunit-tests" );
48 |
49 | if ( tests ) {
50 | b = document.createElement( "strong" );
51 | b.innerHTML = this.name;
52 |
53 | // `a` initialized at top of scope
54 | a = document.createElement( "a" );
55 | a.innerHTML = "Rerun";
56 | a.href = QUnit.url({ testNumber: this.testNumber });
57 |
58 | li = document.createElement( "li" );
59 | li.appendChild( b );
60 | li.appendChild( a );
61 | li.className = "running";
62 | li.id = this.id = "qunit-test-output" + testId++;
63 |
64 | tests.appendChild( li );
65 | }
66 | },
67 | setup: function() {
68 | if ( this.module !== config.previousModule ) {
69 | if ( config.previousModule ) {
70 | runLoggingCallbacks( "moduleDone", QUnit, {
71 | name: config.previousModule,
72 | failed: config.moduleStats.bad,
73 | passed: config.moduleStats.all - config.moduleStats.bad,
74 | total: config.moduleStats.all
75 | });
76 | }
77 | config.previousModule = this.module;
78 | config.moduleStats = { all: 0, bad: 0 };
79 | runLoggingCallbacks( "moduleStart", QUnit, {
80 | name: this.module
81 | });
82 | } else if ( config.autorun ) {
83 | runLoggingCallbacks( "moduleStart", QUnit, {
84 | name: this.module
85 | });
86 | }
87 |
88 | config.current = this;
89 |
90 | this.testEnvironment = extend({
91 | setup: function() {},
92 | teardown: function() {}
93 | }, this.moduleTestEnvironment );
94 |
95 | runLoggingCallbacks( "testStart", QUnit, {
96 | name: this.testName,
97 | module: this.module
98 | });
99 |
100 | // allow utility functions to access the current test environment
101 | // TODO why??
102 | QUnit.current_testEnvironment = this.testEnvironment;
103 |
104 | if ( !config.pollution ) {
105 | saveGlobal();
106 | }
107 | if ( config.notrycatch ) {
108 | this.testEnvironment.setup.call( this.testEnvironment );
109 | return;
110 | }
111 | try {
112 | this.testEnvironment.setup.call( this.testEnvironment );
113 | } catch( e ) {
114 | QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
115 | }
116 | },
117 | run: function() {
118 | config.current = this;
119 |
120 | var running = id( "qunit-testresult" );
121 |
122 | if ( running ) {
123 | running.innerHTML = "Running:
" + this.name;
124 | }
125 |
126 | if ( this.async ) {
127 | QUnit.stop();
128 | }
129 |
130 | if ( config.notrycatch ) {
131 | this.callback.call( this.testEnvironment, QUnit.assert );
132 | return;
133 | }
134 |
135 | try {
136 | this.callback.call( this.testEnvironment, QUnit.assert );
137 | } catch( e ) {
138 | QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
139 | // else next test will carry the responsibility
140 | saveGlobal();
141 |
142 | // Restart the tests if they're blocking
143 | if ( config.blocking ) {
144 | QUnit.start();
145 | }
146 | }
147 | },
148 | teardown: function() {
149 | config.current = this;
150 | if ( config.notrycatch ) {
151 | this.testEnvironment.teardown.call( this.testEnvironment );
152 | return;
153 | } else {
154 | try {
155 | this.testEnvironment.teardown.call( this.testEnvironment );
156 | } catch( e ) {
157 | QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
158 | }
159 | }
160 | checkPollution();
161 | },
162 | finish: function() {
163 | config.current = this;
164 | if ( config.requireExpects && this.expected == null ) {
165 | QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
166 | } else if ( this.expected != null && this.expected != this.assertions.length ) {
167 | QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
168 | } else if ( this.expected == null && !this.assertions.length ) {
169 | QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
170 | }
171 |
172 | var assertion, a, b, i, li, ol,
173 | test = this,
174 | good = 0,
175 | bad = 0,
176 | tests = id( "qunit-tests" );
177 |
178 | config.stats.all += this.assertions.length;
179 | config.moduleStats.all += this.assertions.length;
180 |
181 | if ( tests ) {
182 | ol = document.createElement( "ol" );
183 |
184 | for ( i = 0; i < this.assertions.length; i++ ) {
185 | assertion = this.assertions[i];
186 |
187 | li = document.createElement( "li" );
188 | li.className = assertion.result ? "pass" : "fail";
189 | li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
190 | ol.appendChild( li );
191 |
192 | if ( assertion.result ) {
193 | good++;
194 | } else {
195 | bad++;
196 | config.stats.bad++;
197 | config.moduleStats.bad++;
198 | }
199 | }
200 |
201 | // store result when possible
202 | if ( QUnit.config.reorder && defined.sessionStorage ) {
203 | if ( bad ) {
204 | sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
205 | } else {
206 | sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
207 | }
208 | }
209 |
210 | if ( bad === 0 ) {
211 | ol.style.display = "none";
212 | }
213 |
214 | // `b` initialized at top of scope
215 | b = document.createElement( "strong" );
216 | b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
217 |
218 | addEvent(b, "click", function() {
219 | var next = b.nextSibling.nextSibling,
220 | display = next.style.display;
221 | next.style.display = display === "none" ? "block" : "none";
222 | });
223 |
224 | addEvent(b, "dblclick", function( e ) {
225 | var target = e && e.target ? e.target : window.event.srcElement;
226 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
227 | target = target.parentNode;
228 | }
229 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
230 | window.location = QUnit.url({ testNumber: test.testNumber });
231 | }
232 | });
233 |
234 | // `li` initialized at top of scope
235 | li = id( this.id );
236 | li.className = bad ? "fail" : "pass";
237 | li.removeChild( li.firstChild );
238 | a = li.firstChild;
239 | li.appendChild( b );
240 | li.appendChild ( a );
241 | li.appendChild( ol );
242 |
243 | } else {
244 | for ( i = 0; i < this.assertions.length; i++ ) {
245 | if ( !this.assertions[i].result ) {
246 | bad++;
247 | config.stats.bad++;
248 | config.moduleStats.bad++;
249 | }
250 | }
251 | }
252 |
253 | runLoggingCallbacks( "testDone", QUnit, {
254 | name: this.testName,
255 | module: this.module,
256 | failed: bad,
257 | passed: this.assertions.length - bad,
258 | total: this.assertions.length
259 | });
260 |
261 | QUnit.reset();
262 |
263 | config.current = undefined;
264 | },
265 |
266 | queue: function() {
267 | var bad,
268 | test = this;
269 |
270 | synchronize(function() {
271 | test.init();
272 | });
273 | function run() {
274 | // each of these can by async
275 | synchronize(function() {
276 | test.setup();
277 | });
278 | synchronize(function() {
279 | test.run();
280 | });
281 | synchronize(function() {
282 | test.teardown();
283 | });
284 | synchronize(function() {
285 | test.finish();
286 | });
287 | }
288 |
289 | // `bad` initialized at top of scope
290 | // defer when previous test run passed, if storage is available
291 | bad = QUnit.config.reorder && defined.sessionStorage &&
292 | +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
293 |
294 | if ( bad ) {
295 | run();
296 | } else {
297 | synchronize( run, true );
298 | }
299 | }
300 | };
301 |
302 | // Root QUnit object.
303 | // `QUnit` initialized at top of scope
304 | QUnit = {
305 |
306 | // call on start of module test to prepend name to all tests
307 | module: function( name, testEnvironment ) {
308 | config.currentModule = name;
309 | config.currentModuleTestEnvironment = testEnvironment;
310 | config.modules[name] = true;
311 | },
312 |
313 | asyncTest: function( testName, expected, callback ) {
314 | if ( arguments.length === 2 ) {
315 | callback = expected;
316 | expected = null;
317 | }
318 |
319 | QUnit.test( testName, expected, callback, true );
320 | },
321 |
322 | test: function( testName, expected, callback, async ) {
323 | var test,
324 | name = "" + escapeInnerText( testName ) + "";
325 |
326 | if ( arguments.length === 2 ) {
327 | callback = expected;
328 | expected = null;
329 | }
330 |
331 | if ( config.currentModule ) {
332 | name = "" + config.currentModule + ": " + name;
333 | }
334 |
335 | test = new Test({
336 | name: name,
337 | testName: testName,
338 | expected: expected,
339 | async: async,
340 | callback: callback,
341 | module: config.currentModule,
342 | moduleTestEnvironment: config.currentModuleTestEnvironment,
343 | stack: sourceFromStacktrace( 2 )
344 | });
345 |
346 | if ( !validTest( test ) ) {
347 | return;
348 | }
349 |
350 | test.queue();
351 | },
352 |
353 | // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
354 | expect: function( asserts ) {
355 | if (arguments.length === 1) {
356 | config.current.expected = asserts;
357 | } else {
358 | return config.current.expected;
359 | }
360 | },
361 |
362 | start: function( count ) {
363 | config.semaphore -= count || 1;
364 | // don't start until equal number of stop-calls
365 | if ( config.semaphore > 0 ) {
366 | return;
367 | }
368 | // ignore if start is called more often then stop
369 | if ( config.semaphore < 0 ) {
370 | config.semaphore = 0;
371 | }
372 | // A slight delay, to avoid any current callbacks
373 | if ( defined.setTimeout ) {
374 | window.setTimeout(function() {
375 | if ( config.semaphore > 0 ) {
376 | return;
377 | }
378 | if ( config.timeout ) {
379 | clearTimeout( config.timeout );
380 | }
381 |
382 | config.blocking = false;
383 | process( true );
384 | }, 13);
385 | } else {
386 | config.blocking = false;
387 | process( true );
388 | }
389 | },
390 |
391 | stop: function( count ) {
392 | config.semaphore += count || 1;
393 | config.blocking = true;
394 |
395 | if ( config.testTimeout && defined.setTimeout ) {
396 | clearTimeout( config.timeout );
397 | config.timeout = window.setTimeout(function() {
398 | QUnit.ok( false, "Test timed out" );
399 | config.semaphore = 1;
400 | QUnit.start();
401 | }, config.testTimeout );
402 | }
403 | }
404 | };
405 |
406 | // Asssert helpers
407 | // All of these must call either QUnit.push() or manually do:
408 | // - runLoggingCallbacks( "log", .. );
409 | // - config.current.assertions.push({ .. });
410 | QUnit.assert = {
411 | /**
412 | * Asserts rough true-ish result.
413 | * @name ok
414 | * @function
415 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
416 | */
417 | ok: function( result, msg ) {
418 | if ( !config.current ) {
419 | throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
420 | }
421 | result = !!result;
422 |
423 | var source,
424 | details = {
425 | module: config.current.module,
426 | name: config.current.testName,
427 | result: result,
428 | message: msg
429 | };
430 |
431 | msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
432 | msg = "" + msg + "";
433 |
434 | if ( !result ) {
435 | source = sourceFromStacktrace( 2 );
436 | if ( source ) {
437 | details.source = source;
438 | msg += "| Source: | " + escapeInnerText( source ) + " |
|---|
";
439 | }
440 | }
441 | runLoggingCallbacks( "log", QUnit, details );
442 | config.current.assertions.push({
443 | result: result,
444 | message: msg
445 | });
446 | },
447 |
448 | /**
449 | * Assert that the first two arguments are equal, with an optional message.
450 | * Prints out both actual and expected values.
451 | * @name equal
452 | * @function
453 | * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
454 | */
455 | equal: function( actual, expected, message ) {
456 | QUnit.push( expected == actual, actual, expected, message );
457 | },
458 |
459 | /**
460 | * @name notEqual
461 | * @function
462 | */
463 | notEqual: function( actual, expected, message ) {
464 | QUnit.push( expected != actual, actual, expected, message );
465 | },
466 |
467 | /**
468 | * @name deepEqual
469 | * @function
470 | */
471 | deepEqual: function( actual, expected, message ) {
472 | QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
473 | },
474 |
475 | /**
476 | * @name notDeepEqual
477 | * @function
478 | */
479 | notDeepEqual: function( actual, expected, message ) {
480 | QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
481 | },
482 |
483 | /**
484 | * @name strictEqual
485 | * @function
486 | */
487 | strictEqual: function( actual, expected, message ) {
488 | QUnit.push( expected === actual, actual, expected, message );
489 | },
490 |
491 | /**
492 | * @name notStrictEqual
493 | * @function
494 | */
495 | notStrictEqual: function( actual, expected, message ) {
496 | QUnit.push( expected !== actual, actual, expected, message );
497 | },
498 |
499 | throws: function( block, expected, message ) {
500 | var actual,
501 | ok = false;
502 |
503 | // 'expected' is optional
504 | if ( typeof expected === "string" ) {
505 | message = expected;
506 | expected = null;
507 | }
508 |
509 | config.current.ignoreGlobalErrors = true;
510 | try {
511 | block.call( config.current.testEnvironment );
512 | } catch (e) {
513 | actual = e;
514 | }
515 | config.current.ignoreGlobalErrors = false;
516 |
517 | if ( actual ) {
518 | // we don't want to validate thrown error
519 | if ( !expected ) {
520 | ok = true;
521 | // expected is a regexp
522 | } else if ( QUnit.objectType( expected ) === "regexp" ) {
523 | ok = expected.test( actual );
524 | // expected is a constructor
525 | } else if ( actual instanceof expected ) {
526 | ok = true;
527 | // expected is a validation function which returns true is validation passed
528 | } else if ( expected.call( {}, actual ) === true ) {
529 | ok = true;
530 | }
531 |
532 | QUnit.push( ok, actual, null, message );
533 | } else {
534 | QUnit.pushFailure( message, null, 'No exception was thrown.' );
535 | }
536 | }
537 | };
538 |
539 | /**
540 | * @deprecate since 1.8.0
541 | * Kept assertion helpers in root for backwards compatibility
542 | */
543 | extend( QUnit, QUnit.assert );
544 |
545 | /**
546 | * @deprecated since 1.9.0
547 | * Kept global "raises()" for backwards compatibility
548 | */
549 | QUnit.raises = QUnit.assert.throws;
550 |
551 | /**
552 | * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
553 | * Kept to avoid TypeErrors for undefined methods.
554 | */
555 | QUnit.equals = function() {
556 | QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
557 | };
558 | QUnit.same = function() {
559 | QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
560 | };
561 |
562 | // We want access to the constructor's prototype
563 | (function() {
564 | function F() {}
565 | F.prototype = QUnit;
566 | QUnit = new F();
567 | // Make F QUnit's constructor so that we can add to the prototype later
568 | QUnit.constructor = F;
569 | }());
570 |
571 | /**
572 | * Config object: Maintain internal state
573 | * Later exposed as QUnit.config
574 | * `config` initialized at top of scope
575 | */
576 | config = {
577 | // The queue of tests to run
578 | queue: [],
579 |
580 | // block until document ready
581 | blocking: true,
582 |
583 | // when enabled, show only failing tests
584 | // gets persisted through sessionStorage and can be changed in UI via checkbox
585 | hidepassed: false,
586 |
587 | // by default, run previously failed tests first
588 | // very useful in combination with "Hide passed tests" checked
589 | reorder: true,
590 |
591 | // by default, modify document.title when suite is done
592 | altertitle: true,
593 |
594 | // when enabled, all tests must call expect()
595 | requireExpects: false,
596 |
597 | // add checkboxes that are persisted in the query-string
598 | // when enabled, the id is set to `true` as a `QUnit.config` property
599 | urlConfig: [
600 | {
601 | id: "noglobals",
602 | label: "Check for Globals",
603 | tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
604 | },
605 | {
606 | id: "notrycatch",
607 | label: "No try-catch",
608 | tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
609 | }
610 | ],
611 |
612 | // Set of all modules.
613 | modules: {},
614 |
615 | // logging callback queues
616 | begin: [],
617 | done: [],
618 | log: [],
619 | testStart: [],
620 | testDone: [],
621 | moduleStart: [],
622 | moduleDone: []
623 | };
624 |
625 | // Initialize more QUnit.config and QUnit.urlParams
626 | (function() {
627 | var i,
628 | location = window.location || { search: "", protocol: "file:" },
629 | params = location.search.slice( 1 ).split( "&" ),
630 | length = params.length,
631 | urlParams = {},
632 | current;
633 |
634 | if ( params[ 0 ] ) {
635 | for ( i = 0; i < length; i++ ) {
636 | current = params[ i ].split( "=" );
637 | current[ 0 ] = decodeURIComponent( current[ 0 ] );
638 | // allow just a key to turn on a flag, e.g., test.html?noglobals
639 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
640 | urlParams[ current[ 0 ] ] = current[ 1 ];
641 | }
642 | }
643 |
644 | QUnit.urlParams = urlParams;
645 |
646 | // String search anywhere in moduleName+testName
647 | config.filter = urlParams.filter;
648 |
649 | // Exact match of the module name
650 | config.module = urlParams.module;
651 |
652 | config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
653 |
654 | // Figure out if we're running the tests from a server or not
655 | QUnit.isLocal = location.protocol === "file:";
656 | }());
657 |
658 | // Export global variables, unless an 'exports' object exists,
659 | // in that case we assume we're in CommonJS (dealt with on the bottom of the script)
660 | if ( typeof exports === "undefined" ) {
661 | extend( window, QUnit );
662 |
663 | // Expose QUnit object
664 | window.QUnit = QUnit;
665 | }
666 |
667 | // Extend QUnit object,
668 | // these after set here because they should not be exposed as global functions
669 | extend( QUnit, {
670 | config: config,
671 |
672 | // Initialize the configuration options
673 | init: function() {
674 | extend( config, {
675 | stats: { all: 0, bad: 0 },
676 | moduleStats: { all: 0, bad: 0 },
677 | started: +new Date(),
678 | updateRate: 1000,
679 | blocking: false,
680 | autostart: true,
681 | autorun: false,
682 | filter: "",
683 | queue: [],
684 | semaphore: 0
685 | });
686 |
687 | var tests, banner, result,
688 | qunit = id( "qunit" );
689 |
690 | if ( qunit ) {
691 | qunit.innerHTML =
692 | "" +
693 | "" +
694 | "" +
695 | "" +
696 | "
";
697 | }
698 |
699 | tests = id( "qunit-tests" );
700 | banner = id( "qunit-banner" );
701 | result = id( "qunit-testresult" );
702 |
703 | if ( tests ) {
704 | tests.innerHTML = "";
705 | }
706 |
707 | if ( banner ) {
708 | banner.className = "";
709 | }
710 |
711 | if ( result ) {
712 | result.parentNode.removeChild( result );
713 | }
714 |
715 | if ( tests ) {
716 | result = document.createElement( "p" );
717 | result.id = "qunit-testresult";
718 | result.className = "result";
719 | tests.parentNode.insertBefore( result, tests );
720 | result.innerHTML = "Running...
";
721 | }
722 | },
723 |
724 | // Resets the test setup. Useful for tests that modify the DOM.
725 | reset: function() {
726 | var fixture = id( "qunit-fixture" );
727 | if ( fixture ) {
728 | fixture.innerHTML = config.fixture;
729 | }
730 | },
731 |
732 | // Trigger an event on an element.
733 | // @example triggerEvent( document.body, "click" );
734 | triggerEvent: function( elem, type, event ) {
735 | if ( document.createEvent ) {
736 | event = document.createEvent( "MouseEvents" );
737 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
738 | 0, 0, 0, 0, 0, false, false, false, false, 0, null);
739 |
740 | elem.dispatchEvent( event );
741 | } else if ( elem.fireEvent ) {
742 | elem.fireEvent( "on" + type );
743 | }
744 | },
745 |
746 | // Safe object type checking
747 | is: function( type, obj ) {
748 | return QUnit.objectType( obj ) == type;
749 | },
750 |
751 | objectType: function( obj ) {
752 | if ( typeof obj === "undefined" ) {
753 | return "undefined";
754 | // consider: typeof null === object
755 | }
756 | if ( obj === null ) {
757 | return "null";
758 | }
759 |
760 | var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
761 |
762 | switch ( type ) {
763 | case "Number":
764 | if ( isNaN(obj) ) {
765 | return "nan";
766 | }
767 | return "number";
768 | case "String":
769 | case "Boolean":
770 | case "Array":
771 | case "Date":
772 | case "RegExp":
773 | case "Function":
774 | return type.toLowerCase();
775 | }
776 | if ( typeof obj === "object" ) {
777 | return "object";
778 | }
779 | return undefined;
780 | },
781 |
782 | push: function( result, actual, expected, message ) {
783 | if ( !config.current ) {
784 | throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
785 | }
786 |
787 | var output, source,
788 | details = {
789 | module: config.current.module,
790 | name: config.current.testName,
791 | result: result,
792 | message: message,
793 | actual: actual,
794 | expected: expected
795 | };
796 |
797 | message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
798 | message = "" + message + "";
799 | output = message;
800 |
801 | if ( !result ) {
802 | expected = escapeInnerText( QUnit.jsDump.parse(expected) );
803 | actual = escapeInnerText( QUnit.jsDump.parse(actual) );
804 | output += "| Expected: | " + expected + " |
";
805 |
806 | if ( actual != expected ) {
807 | output += "| Result: | " + actual + " |
";
808 | output += "| Diff: | " + QUnit.diff( expected, actual ) + " |
";
809 | }
810 |
811 | source = sourceFromStacktrace();
812 |
813 | if ( source ) {
814 | details.source = source;
815 | output += "| Source: | " + escapeInnerText( source ) + " |
";
816 | }
817 |
818 | output += "
";
819 | }
820 |
821 | runLoggingCallbacks( "log", QUnit, details );
822 |
823 | config.current.assertions.push({
824 | result: !!result,
825 | message: output
826 | });
827 | },
828 |
829 | pushFailure: function( message, source, actual ) {
830 | if ( !config.current ) {
831 | throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
832 | }
833 |
834 | var output,
835 | details = {
836 | module: config.current.module,
837 | name: config.current.testName,
838 | result: false,
839 | message: message
840 | };
841 |
842 | message = escapeInnerText( message ) || "error";
843 | message = "" + message + "";
844 | output = message;
845 |
846 | output += "";
847 |
848 | if ( actual ) {
849 | output += "| Result: | " + escapeInnerText( actual ) + " |
";
850 | }
851 |
852 | if ( source ) {
853 | details.source = source;
854 | output += "| Source: | " + escapeInnerText( source ) + " |
";
855 | }
856 |
857 | output += "
";
858 |
859 | runLoggingCallbacks( "log", QUnit, details );
860 |
861 | config.current.assertions.push({
862 | result: false,
863 | message: output
864 | });
865 | },
866 |
867 | url: function( params ) {
868 | params = extend( extend( {}, QUnit.urlParams ), params );
869 | var key,
870 | querystring = "?";
871 |
872 | for ( key in params ) {
873 | if ( !hasOwn.call( params, key ) ) {
874 | continue;
875 | }
876 | querystring += encodeURIComponent( key ) + "=" +
877 | encodeURIComponent( params[ key ] ) + "&";
878 | }
879 | return window.location.pathname + querystring.slice( 0, -1 );
880 | },
881 |
882 | extend: extend,
883 | id: id,
884 | addEvent: addEvent
885 | // load, equiv, jsDump, diff: Attached later
886 | });
887 |
888 | /**
889 | * @deprecated: Created for backwards compatibility with test runner that set the hook function
890 | * into QUnit.{hook}, instead of invoking it and passing the hook function.
891 | * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
892 | * Doing this allows us to tell if the following methods have been overwritten on the actual
893 | * QUnit object.
894 | */
895 | extend( QUnit.constructor.prototype, {
896 |
897 | // Logging callbacks; all receive a single argument with the listed properties
898 | // run test/logs.html for any related changes
899 | begin: registerLoggingCallback( "begin" ),
900 |
901 | // done: { failed, passed, total, runtime }
902 | done: registerLoggingCallback( "done" ),
903 |
904 | // log: { result, actual, expected, message }
905 | log: registerLoggingCallback( "log" ),
906 |
907 | // testStart: { name }
908 | testStart: registerLoggingCallback( "testStart" ),
909 |
910 | // testDone: { name, failed, passed, total }
911 | testDone: registerLoggingCallback( "testDone" ),
912 |
913 | // moduleStart: { name }
914 | moduleStart: registerLoggingCallback( "moduleStart" ),
915 |
916 | // moduleDone: { name, failed, passed, total }
917 | moduleDone: registerLoggingCallback( "moduleDone" )
918 | });
919 |
920 | if ( typeof document === "undefined" || document.readyState === "complete" ) {
921 | config.autorun = true;
922 | }
923 |
924 | QUnit.load = function() {
925 | runLoggingCallbacks( "begin", QUnit, {} );
926 |
927 | // Initialize the config, saving the execution queue
928 | var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter,
929 | numModules = 0,
930 | moduleFilterHtml = "",
931 | urlConfigHtml = "",
932 | oldconfig = extend( {}, config );
933 |
934 | QUnit.init();
935 | extend(config, oldconfig);
936 |
937 | config.blocking = false;
938 |
939 | len = config.urlConfig.length;
940 |
941 | for ( i = 0; i < len; i++ ) {
942 | val = config.urlConfig[i];
943 | if ( typeof val === "string" ) {
944 | val = {
945 | id: val,
946 | label: val,
947 | tooltip: "[no tooltip available]"
948 | };
949 | }
950 | config[ val.id ] = QUnit.urlParams[ val.id ];
951 | urlConfigHtml += "";
952 | }
953 |
954 | moduleFilterHtml += "";
962 |
963 | // `userAgent` initialized at top of scope
964 | userAgent = id( "qunit-userAgent" );
965 | if ( userAgent ) {
966 | userAgent.innerHTML = navigator.userAgent;
967 | }
968 |
969 | // `banner` initialized at top of scope
970 | banner = id( "qunit-header" );
971 | if ( banner ) {
972 | banner.innerHTML = "" + banner.innerHTML + " ";
973 | }
974 |
975 | // `toolbar` initialized at top of scope
976 | toolbar = id( "qunit-testrunner-toolbar" );
977 | if ( toolbar ) {
978 | // `filter` initialized at top of scope
979 | filter = document.createElement( "input" );
980 | filter.type = "checkbox";
981 | filter.id = "qunit-filter-pass";
982 |
983 | addEvent( filter, "click", function() {
984 | var tmp,
985 | ol = document.getElementById( "qunit-tests" );
986 |
987 | if ( filter.checked ) {
988 | ol.className = ol.className + " hidepass";
989 | } else {
990 | tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
991 | ol.className = tmp.replace( / hidepass /, " " );
992 | }
993 | if ( defined.sessionStorage ) {
994 | if (filter.checked) {
995 | sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
996 | } else {
997 | sessionStorage.removeItem( "qunit-filter-passed-tests" );
998 | }
999 | }
1000 | });
1001 |
1002 | if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
1003 | filter.checked = true;
1004 | // `ol` initialized at top of scope
1005 | ol = document.getElementById( "qunit-tests" );
1006 | ol.className = ol.className + " hidepass";
1007 | }
1008 | toolbar.appendChild( filter );
1009 |
1010 | // `label` initialized at top of scope
1011 | label = document.createElement( "label" );
1012 | label.setAttribute( "for", "qunit-filter-pass" );
1013 | label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
1014 | label.innerHTML = "Hide passed tests";
1015 | toolbar.appendChild( label );
1016 |
1017 | urlConfigCheckboxes = document.createElement( 'span' );
1018 | urlConfigCheckboxes.innerHTML = urlConfigHtml;
1019 | addEvent( urlConfigCheckboxes, "change", function( event ) {
1020 | var params = {};
1021 | params[ event.target.name ] = event.target.checked ? true : undefined;
1022 | window.location = QUnit.url( params );
1023 | });
1024 | toolbar.appendChild( urlConfigCheckboxes );
1025 |
1026 | if (numModules > 1) {
1027 | moduleFilter = document.createElement( 'span' );
1028 | moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' );
1029 | moduleFilter.innerHTML = moduleFilterHtml;
1030 | addEvent( moduleFilter, "change", function() {
1031 | var selectBox = moduleFilter.getElementsByTagName("select")[0],
1032 | selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
1033 |
1034 | window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } );
1035 | });
1036 | toolbar.appendChild(moduleFilter);
1037 | }
1038 | }
1039 |
1040 | // `main` initialized at top of scope
1041 | main = id( "qunit-fixture" );
1042 | if ( main ) {
1043 | config.fixture = main.innerHTML;
1044 | }
1045 |
1046 | if ( config.autostart ) {
1047 | QUnit.start();
1048 | }
1049 | };
1050 |
1051 | addEvent( window, "load", QUnit.load );
1052 |
1053 | // `onErrorFnPrev` initialized at top of scope
1054 | // Preserve other handlers
1055 | onErrorFnPrev = window.onerror;
1056 |
1057 | // Cover uncaught exceptions
1058 | // Returning true will surpress the default browser handler,
1059 | // returning false will let it run.
1060 | window.onerror = function ( error, filePath, linerNr ) {
1061 | var ret = false;
1062 | if ( onErrorFnPrev ) {
1063 | ret = onErrorFnPrev( error, filePath, linerNr );
1064 | }
1065 |
1066 | // Treat return value as window.onerror itself does,
1067 | // Only do our handling if not surpressed.
1068 | if ( ret !== true ) {
1069 | if ( QUnit.config.current ) {
1070 | if ( QUnit.config.current.ignoreGlobalErrors ) {
1071 | return true;
1072 | }
1073 | QUnit.pushFailure( error, filePath + ":" + linerNr );
1074 | } else {
1075 | QUnit.test( "global failure", extend( function() {
1076 | QUnit.pushFailure( error, filePath + ":" + linerNr );
1077 | }, { validTest: validTest } ) );
1078 | }
1079 | return false;
1080 | }
1081 |
1082 | return ret;
1083 | };
1084 |
1085 | function done() {
1086 | config.autorun = true;
1087 |
1088 | // Log the last module results
1089 | if ( config.currentModule ) {
1090 | runLoggingCallbacks( "moduleDone", QUnit, {
1091 | name: config.currentModule,
1092 | failed: config.moduleStats.bad,
1093 | passed: config.moduleStats.all - config.moduleStats.bad,
1094 | total: config.moduleStats.all
1095 | });
1096 | }
1097 |
1098 | var i, key,
1099 | banner = id( "qunit-banner" ),
1100 | tests = id( "qunit-tests" ),
1101 | runtime = +new Date() - config.started,
1102 | passed = config.stats.all - config.stats.bad,
1103 | html = [
1104 | "Tests completed in ",
1105 | runtime,
1106 | " milliseconds.
",
1107 | "",
1108 | passed,
1109 | " tests of ",
1110 | config.stats.all,
1111 | " passed, ",
1112 | config.stats.bad,
1113 | " failed."
1114 | ].join( "" );
1115 |
1116 | if ( banner ) {
1117 | banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
1118 | }
1119 |
1120 | if ( tests ) {
1121 | id( "qunit-testresult" ).innerHTML = html;
1122 | }
1123 |
1124 | if ( config.altertitle && typeof document !== "undefined" && document.title ) {
1125 | // show ✖ for good, ✔ for bad suite result in title
1126 | // use escape sequences in case file gets loaded with non-utf-8-charset
1127 | document.title = [
1128 | ( config.stats.bad ? "\u2716" : "\u2714" ),
1129 | document.title.replace( /^[\u2714\u2716] /i, "" )
1130 | ].join( " " );
1131 | }
1132 |
1133 | // clear own sessionStorage items if all tests passed
1134 | if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
1135 | // `key` & `i` initialized at top of scope
1136 | for ( i = 0; i < sessionStorage.length; i++ ) {
1137 | key = sessionStorage.key( i++ );
1138 | if ( key.indexOf( "qunit-test-" ) === 0 ) {
1139 | sessionStorage.removeItem( key );
1140 | }
1141 | }
1142 | }
1143 |
1144 | // scroll back to top to show results
1145 | if ( window.scrollTo ) {
1146 | window.scrollTo(0, 0);
1147 | }
1148 |
1149 | runLoggingCallbacks( "done", QUnit, {
1150 | failed: config.stats.bad,
1151 | passed: passed,
1152 | total: config.stats.all,
1153 | runtime: runtime
1154 | });
1155 | }
1156 |
1157 | /** @return Boolean: true if this test should be ran */
1158 | function validTest( test ) {
1159 | var include,
1160 | filter = config.filter && config.filter.toLowerCase(),
1161 | module = config.module && config.module.toLowerCase(),
1162 | fullName = (test.module + ": " + test.testName).toLowerCase();
1163 |
1164 | // Internally-generated tests are always valid
1165 | if ( test.callback && test.callback.validTest === validTest ) {
1166 | delete test.callback.validTest;
1167 | return true;
1168 | }
1169 |
1170 | if ( config.testNumber ) {
1171 | return test.testNumber === config.testNumber;
1172 | }
1173 |
1174 | if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
1175 | return false;
1176 | }
1177 |
1178 | if ( !filter ) {
1179 | return true;
1180 | }
1181 |
1182 | include = filter.charAt( 0 ) !== "!";
1183 | if ( !include ) {
1184 | filter = filter.slice( 1 );
1185 | }
1186 |
1187 | // If the filter matches, we need to honour include
1188 | if ( fullName.indexOf( filter ) !== -1 ) {
1189 | return include;
1190 | }
1191 |
1192 | // Otherwise, do the opposite
1193 | return !include;
1194 | }
1195 |
1196 | // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
1197 | // Later Safari and IE10 are supposed to support error.stack as well
1198 | // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
1199 | function extractStacktrace( e, offset ) {
1200 | offset = offset === undefined ? 3 : offset;
1201 |
1202 | var stack, include, i, regex;
1203 |
1204 | if ( e.stacktrace ) {
1205 | // Opera
1206 | return e.stacktrace.split( "\n" )[ offset + 3 ];
1207 | } else if ( e.stack ) {
1208 | // Firefox, Chrome
1209 | stack = e.stack.split( "\n" );
1210 | if (/^error$/i.test( stack[0] ) ) {
1211 | stack.shift();
1212 | }
1213 | if ( fileName ) {
1214 | include = [];
1215 | for ( i = offset; i < stack.length; i++ ) {
1216 | if ( stack[ i ].indexOf( fileName ) != -1 ) {
1217 | break;
1218 | }
1219 | include.push( stack[ i ] );
1220 | }
1221 | if ( include.length ) {
1222 | return include.join( "\n" );
1223 | }
1224 | }
1225 | return stack[ offset ];
1226 | } else if ( e.sourceURL ) {
1227 | // Safari, PhantomJS
1228 | // hopefully one day Safari provides actual stacktraces
1229 | // exclude useless self-reference for generated Error objects
1230 | if ( /qunit.js$/.test( e.sourceURL ) ) {
1231 | return;
1232 | }
1233 | // for actual exceptions, this is useful
1234 | return e.sourceURL + ":" + e.line;
1235 | }
1236 | }
1237 | function sourceFromStacktrace( offset ) {
1238 | try {
1239 | throw new Error();
1240 | } catch ( e ) {
1241 | return extractStacktrace( e, offset );
1242 | }
1243 | }
1244 |
1245 | function escapeInnerText( s ) {
1246 | if ( !s ) {
1247 | return "";
1248 | }
1249 | s = s + "";
1250 | return s.replace( /[\&<>]/g, function( s ) {
1251 | switch( s ) {
1252 | case "&": return "&";
1253 | case "<": return "<";
1254 | case ">": return ">";
1255 | default: return s;
1256 | }
1257 | });
1258 | }
1259 |
1260 | function synchronize( callback, last ) {
1261 | config.queue.push( callback );
1262 |
1263 | if ( config.autorun && !config.blocking ) {
1264 | process( last );
1265 | }
1266 | }
1267 |
1268 | function process( last ) {
1269 | function next() {
1270 | process( last );
1271 | }
1272 | var start = new Date().getTime();
1273 | config.depth = config.depth ? config.depth + 1 : 1;
1274 |
1275 | while ( config.queue.length && !config.blocking ) {
1276 | if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
1277 | config.queue.shift()();
1278 | } else {
1279 | window.setTimeout( next, 13 );
1280 | break;
1281 | }
1282 | }
1283 | config.depth--;
1284 | if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
1285 | done();
1286 | }
1287 | }
1288 |
1289 | function saveGlobal() {
1290 | config.pollution = [];
1291 |
1292 | if ( config.noglobals ) {
1293 | for ( var key in window ) {
1294 | // in Opera sometimes DOM element ids show up here, ignore them
1295 | if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
1296 | continue;
1297 | }
1298 | config.pollution.push( key );
1299 | }
1300 | }
1301 | }
1302 |
1303 | function checkPollution( name ) {
1304 | var newGlobals,
1305 | deletedGlobals,
1306 | old = config.pollution;
1307 |
1308 | saveGlobal();
1309 |
1310 | newGlobals = diff( config.pollution, old );
1311 | if ( newGlobals.length > 0 ) {
1312 | QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
1313 | }
1314 |
1315 | deletedGlobals = diff( old, config.pollution );
1316 | if ( deletedGlobals.length > 0 ) {
1317 | QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
1318 | }
1319 | }
1320 |
1321 | // returns a new Array with the elements that are in a but not in b
1322 | function diff( a, b ) {
1323 | var i, j,
1324 | result = a.slice();
1325 |
1326 | for ( i = 0; i < result.length; i++ ) {
1327 | for ( j = 0; j < b.length; j++ ) {
1328 | if ( result[i] === b[j] ) {
1329 | result.splice( i, 1 );
1330 | i--;
1331 | break;
1332 | }
1333 | }
1334 | }
1335 | return result;
1336 | }
1337 |
1338 | function extend( a, b ) {
1339 | for ( var prop in b ) {
1340 | if ( b[ prop ] === undefined ) {
1341 | delete a[ prop ];
1342 |
1343 | // Avoid "Member not found" error in IE8 caused by setting window.constructor
1344 | } else if ( prop !== "constructor" || a !== window ) {
1345 | a[ prop ] = b[ prop ];
1346 | }
1347 | }
1348 |
1349 | return a;
1350 | }
1351 |
1352 | function addEvent( elem, type, fn ) {
1353 | if ( elem.addEventListener ) {
1354 | elem.addEventListener( type, fn, false );
1355 | } else if ( elem.attachEvent ) {
1356 | elem.attachEvent( "on" + type, fn );
1357 | } else {
1358 | fn();
1359 | }
1360 | }
1361 |
1362 | function id( name ) {
1363 | return !!( typeof document !== "undefined" && document && document.getElementById ) &&
1364 | document.getElementById( name );
1365 | }
1366 |
1367 | function registerLoggingCallback( key ) {
1368 | return function( callback ) {
1369 | config[key].push( callback );
1370 | };
1371 | }
1372 |
1373 | // Supports deprecated method of completely overwriting logging callbacks
1374 | function runLoggingCallbacks( key, scope, args ) {
1375 | //debugger;
1376 | var i, callbacks;
1377 | if ( QUnit.hasOwnProperty( key ) ) {
1378 | QUnit[ key ].call(scope, args );
1379 | } else {
1380 | callbacks = config[ key ];
1381 | for ( i = 0; i < callbacks.length; i++ ) {
1382 | callbacks[ i ].call( scope, args );
1383 | }
1384 | }
1385 | }
1386 |
1387 | // Test for equality any JavaScript type.
1388 | // Author: Philippe Rathé
1389 | QUnit.equiv = (function() {
1390 |
1391 | // Call the o related callback with the given arguments.
1392 | function bindCallbacks( o, callbacks, args ) {
1393 | var prop = QUnit.objectType( o );
1394 | if ( prop ) {
1395 | if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1396 | return callbacks[ prop ].apply( callbacks, args );
1397 | } else {
1398 | return callbacks[ prop ]; // or undefined
1399 | }
1400 | }
1401 | }
1402 |
1403 | // the real equiv function
1404 | var innerEquiv,
1405 | // stack to decide between skip/abort functions
1406 | callers = [],
1407 | // stack to avoiding loops from circular referencing
1408 | parents = [],
1409 |
1410 | getProto = Object.getPrototypeOf || function ( obj ) {
1411 | return obj.__proto__;
1412 | },
1413 | callbacks = (function () {
1414 |
1415 | // for string, boolean, number and null
1416 | function useStrictEquality( b, a ) {
1417 | if ( b instanceof a.constructor || a instanceof b.constructor ) {
1418 | // to catch short annotaion VS 'new' annotation of a
1419 | // declaration
1420 | // e.g. var i = 1;
1421 | // var j = new Number(1);
1422 | return a == b;
1423 | } else {
1424 | return a === b;
1425 | }
1426 | }
1427 |
1428 | return {
1429 | "string": useStrictEquality,
1430 | "boolean": useStrictEquality,
1431 | "number": useStrictEquality,
1432 | "null": useStrictEquality,
1433 | "undefined": useStrictEquality,
1434 |
1435 | "nan": function( b ) {
1436 | return isNaN( b );
1437 | },
1438 |
1439 | "date": function( b, a ) {
1440 | return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1441 | },
1442 |
1443 | "regexp": function( b, a ) {
1444 | return QUnit.objectType( b ) === "regexp" &&
1445 | // the regex itself
1446 | a.source === b.source &&
1447 | // and its modifers
1448 | a.global === b.global &&
1449 | // (gmi) ...
1450 | a.ignoreCase === b.ignoreCase &&
1451 | a.multiline === b.multiline &&
1452 | a.sticky === b.sticky;
1453 | },
1454 |
1455 | // - skip when the property is a method of an instance (OOP)
1456 | // - abort otherwise,
1457 | // initial === would have catch identical references anyway
1458 | "function": function() {
1459 | var caller = callers[callers.length - 1];
1460 | return caller !== Object && typeof caller !== "undefined";
1461 | },
1462 |
1463 | "array": function( b, a ) {
1464 | var i, j, len, loop;
1465 |
1466 | // b could be an object literal here
1467 | if ( QUnit.objectType( b ) !== "array" ) {
1468 | return false;
1469 | }
1470 |
1471 | len = a.length;
1472 | if ( len !== b.length ) {
1473 | // safe and faster
1474 | return false;
1475 | }
1476 |
1477 | // track reference to avoid circular references
1478 | parents.push( a );
1479 | for ( i = 0; i < len; i++ ) {
1480 | loop = false;
1481 | for ( j = 0; j < parents.length; j++ ) {
1482 | if ( parents[j] === a[i] ) {
1483 | loop = true;// dont rewalk array
1484 | }
1485 | }
1486 | if ( !loop && !innerEquiv(a[i], b[i]) ) {
1487 | parents.pop();
1488 | return false;
1489 | }
1490 | }
1491 | parents.pop();
1492 | return true;
1493 | },
1494 |
1495 | "object": function( b, a ) {
1496 | var i, j, loop,
1497 | // Default to true
1498 | eq = true,
1499 | aProperties = [],
1500 | bProperties = [];
1501 |
1502 | // comparing constructors is more strict than using
1503 | // instanceof
1504 | if ( a.constructor !== b.constructor ) {
1505 | // Allow objects with no prototype to be equivalent to
1506 | // objects with Object as their constructor.
1507 | if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
1508 | ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
1509 | return false;
1510 | }
1511 | }
1512 |
1513 | // stack constructor before traversing properties
1514 | callers.push( a.constructor );
1515 | // track reference to avoid circular references
1516 | parents.push( a );
1517 |
1518 | for ( i in a ) { // be strict: don't ensures hasOwnProperty
1519 | // and go deep
1520 | loop = false;
1521 | for ( j = 0; j < parents.length; j++ ) {
1522 | if ( parents[j] === a[i] ) {
1523 | // don't go down the same path twice
1524 | loop = true;
1525 | }
1526 | }
1527 | aProperties.push(i); // collect a's properties
1528 |
1529 | if (!loop && !innerEquiv( a[i], b[i] ) ) {
1530 | eq = false;
1531 | break;
1532 | }
1533 | }
1534 |
1535 | callers.pop(); // unstack, we are done
1536 | parents.pop();
1537 |
1538 | for ( i in b ) {
1539 | bProperties.push( i ); // collect b's properties
1540 | }
1541 |
1542 | // Ensures identical properties name
1543 | return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1544 | }
1545 | };
1546 | }());
1547 |
1548 | innerEquiv = function() { // can take multiple arguments
1549 | var args = [].slice.apply( arguments );
1550 | if ( args.length < 2 ) {
1551 | return true; // end transition
1552 | }
1553 |
1554 | return (function( a, b ) {
1555 | if ( a === b ) {
1556 | return true; // catch the most you can
1557 | } else if ( a === null || b === null || typeof a === "undefined" ||
1558 | typeof b === "undefined" ||
1559 | QUnit.objectType(a) !== QUnit.objectType(b) ) {
1560 | return false; // don't lose time with error prone cases
1561 | } else {
1562 | return bindCallbacks(a, callbacks, [ b, a ]);
1563 | }
1564 |
1565 | // apply transition with (1..n) arguments
1566 | }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
1567 | };
1568 |
1569 | return innerEquiv;
1570 | }());
1571 |
1572 | /**
1573 | * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1574 | * http://flesler.blogspot.com Licensed under BSD
1575 | * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1576 | *
1577 | * @projectDescription Advanced and extensible data dumping for Javascript.
1578 | * @version 1.0.0
1579 | * @author Ariel Flesler
1580 | * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1581 | */
1582 | QUnit.jsDump = (function() {
1583 | function quote( str ) {
1584 | return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
1585 | }
1586 | function literal( o ) {
1587 | return o + "";
1588 | }
1589 | function join( pre, arr, post ) {
1590 | var s = jsDump.separator(),
1591 | base = jsDump.indent(),
1592 | inner = jsDump.indent(1);
1593 | if ( arr.join ) {
1594 | arr = arr.join( "," + s + inner );
1595 | }
1596 | if ( !arr ) {
1597 | return pre + post;
1598 | }
1599 | return [ pre, inner + arr, base + post ].join(s);
1600 | }
1601 | function array( arr, stack ) {
1602 | var i = arr.length, ret = new Array(i);
1603 | this.up();
1604 | while ( i-- ) {
1605 | ret[i] = this.parse( arr[i] , undefined , stack);
1606 | }
1607 | this.down();
1608 | return join( "[", ret, "]" );
1609 | }
1610 |
1611 | var reName = /^function (\w+)/,
1612 | jsDump = {
1613 | parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
1614 | stack = stack || [ ];
1615 | var inStack, res,
1616 | parser = this.parsers[ type || this.typeOf(obj) ];
1617 |
1618 | type = typeof parser;
1619 | inStack = inArray( obj, stack );
1620 |
1621 | if ( inStack != -1 ) {
1622 | return "recursion(" + (inStack - stack.length) + ")";
1623 | }
1624 | //else
1625 | if ( type == "function" ) {
1626 | stack.push( obj );
1627 | res = parser.call( this, obj, stack );
1628 | stack.pop();
1629 | return res;
1630 | }
1631 | // else
1632 | return ( type == "string" ) ? parser : this.parsers.error;
1633 | },
1634 | typeOf: function( obj ) {
1635 | var type;
1636 | if ( obj === null ) {
1637 | type = "null";
1638 | } else if ( typeof obj === "undefined" ) {
1639 | type = "undefined";
1640 | } else if ( QUnit.is( "regexp", obj) ) {
1641 | type = "regexp";
1642 | } else if ( QUnit.is( "date", obj) ) {
1643 | type = "date";
1644 | } else if ( QUnit.is( "function", obj) ) {
1645 | type = "function";
1646 | } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
1647 | type = "window";
1648 | } else if ( obj.nodeType === 9 ) {
1649 | type = "document";
1650 | } else if ( obj.nodeType ) {
1651 | type = "node";
1652 | } else if (
1653 | // native arrays
1654 | toString.call( obj ) === "[object Array]" ||
1655 | // NodeList objects
1656 | ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
1657 | ) {
1658 | type = "array";
1659 | } else {
1660 | type = typeof obj;
1661 | }
1662 | return type;
1663 | },
1664 | separator: function() {
1665 | return this.multiline ? this.HTML ? "
" : "\n" : this.HTML ? " " : " ";
1666 | },
1667 | indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
1668 | if ( !this.multiline ) {
1669 | return "";
1670 | }
1671 | var chr = this.indentChar;
1672 | if ( this.HTML ) {
1673 | chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
1674 | }
1675 | return new Array( this._depth_ + (extra||0) ).join(chr);
1676 | },
1677 | up: function( a ) {
1678 | this._depth_ += a || 1;
1679 | },
1680 | down: function( a ) {
1681 | this._depth_ -= a || 1;
1682 | },
1683 | setParser: function( name, parser ) {
1684 | this.parsers[name] = parser;
1685 | },
1686 | // The next 3 are exposed so you can use them
1687 | quote: quote,
1688 | literal: literal,
1689 | join: join,
1690 | //
1691 | _depth_: 1,
1692 | // This is the list of parsers, to modify them, use jsDump.setParser
1693 | parsers: {
1694 | window: "[Window]",
1695 | document: "[Document]",
1696 | error: "[ERROR]", //when no parser is found, shouldn"t happen
1697 | unknown: "[Unknown]",
1698 | "null": "null",
1699 | "undefined": "undefined",
1700 | "function": function( fn ) {
1701 | var ret = "function",
1702 | name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
1703 |
1704 | if ( name ) {
1705 | ret += " " + name;
1706 | }
1707 | ret += "( ";
1708 |
1709 | ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
1710 | return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
1711 | },
1712 | array: array,
1713 | nodelist: array,
1714 | "arguments": array,
1715 | object: function( map, stack ) {
1716 | var ret = [ ], keys, key, val, i;
1717 | QUnit.jsDump.up();
1718 | if ( Object.keys ) {
1719 | keys = Object.keys( map );
1720 | } else {
1721 | keys = [];
1722 | for ( key in map ) {
1723 | keys.push( key );
1724 | }
1725 | }
1726 | keys.sort();
1727 | for ( i = 0; i < keys.length; i++ ) {
1728 | key = keys[ i ];
1729 | val = map[ key ];
1730 | ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
1731 | }
1732 | QUnit.jsDump.down();
1733 | return join( "{", ret, "}" );
1734 | },
1735 | node: function( node ) {
1736 | var a, val,
1737 | open = QUnit.jsDump.HTML ? "<" : "<",
1738 | close = QUnit.jsDump.HTML ? ">" : ">",
1739 | tag = node.nodeName.toLowerCase(),
1740 | ret = open + tag;
1741 |
1742 | for ( a in QUnit.jsDump.DOMAttrs ) {
1743 | val = node[ QUnit.jsDump.DOMAttrs[a] ];
1744 | if ( val ) {
1745 | ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
1746 | }
1747 | }
1748 | return ret + close + open + "/" + tag + close;
1749 | },
1750 | functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
1751 | var args,
1752 | l = fn.length;
1753 |
1754 | if ( !l ) {
1755 | return "";
1756 | }
1757 |
1758 | args = new Array(l);
1759 | while ( l-- ) {
1760 | args[l] = String.fromCharCode(97+l);//97 is 'a'
1761 | }
1762 | return " " + args.join( ", " ) + " ";
1763 | },
1764 | key: quote, //object calls it internally, the key part of an item in a map
1765 | functionCode: "[code]", //function calls it internally, it's the content of the function
1766 | attribute: quote, //node calls it internally, it's an html attribute value
1767 | string: quote,
1768 | date: quote,
1769 | regexp: literal, //regex
1770 | number: literal,
1771 | "boolean": literal
1772 | },
1773 | DOMAttrs: {
1774 | //attributes to dump from nodes, name=>realName
1775 | id: "id",
1776 | name: "name",
1777 | "class": "className"
1778 | },
1779 | HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
1780 | indentChar: " ",//indentation unit
1781 | multiline: true //if true, items in a collection, are separated by a \n, else just a space.
1782 | };
1783 |
1784 | return jsDump;
1785 | }());
1786 |
1787 | // from Sizzle.js
1788 | function getText( elems ) {
1789 | var i, elem,
1790 | ret = "";
1791 |
1792 | for ( i = 0; elems[i]; i++ ) {
1793 | elem = elems[i];
1794 |
1795 | // Get the text from text nodes and CDATA nodes
1796 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
1797 | ret += elem.nodeValue;
1798 |
1799 | // Traverse everything else, except comment nodes
1800 | } else if ( elem.nodeType !== 8 ) {
1801 | ret += getText( elem.childNodes );
1802 | }
1803 | }
1804 |
1805 | return ret;
1806 | }
1807 |
1808 | // from jquery.js
1809 | function inArray( elem, array ) {
1810 | if ( array.indexOf ) {
1811 | return array.indexOf( elem );
1812 | }
1813 |
1814 | for ( var i = 0, length = array.length; i < length; i++ ) {
1815 | if ( array[ i ] === elem ) {
1816 | return i;
1817 | }
1818 | }
1819 |
1820 | return -1;
1821 | }
1822 |
1823 | /*
1824 | * Javascript Diff Algorithm
1825 | * By John Resig (http://ejohn.org/)
1826 | * Modified by Chu Alan "sprite"
1827 | *
1828 | * Released under the MIT license.
1829 | *
1830 | * More Info:
1831 | * http://ejohn.org/projects/javascript-diff-algorithm/
1832 | *
1833 | * Usage: QUnit.diff(expected, actual)
1834 | *
1835 | * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over"
1836 | */
1837 | QUnit.diff = (function() {
1838 | function diff( o, n ) {
1839 | var i,
1840 | ns = {},
1841 | os = {};
1842 |
1843 | for ( i = 0; i < n.length; i++ ) {
1844 | if ( ns[ n[i] ] == null ) {
1845 | ns[ n[i] ] = {
1846 | rows: [],
1847 | o: null
1848 | };
1849 | }
1850 | ns[ n[i] ].rows.push( i );
1851 | }
1852 |
1853 | for ( i = 0; i < o.length; i++ ) {
1854 | if ( os[ o[i] ] == null ) {
1855 | os[ o[i] ] = {
1856 | rows: [],
1857 | n: null
1858 | };
1859 | }
1860 | os[ o[i] ].rows.push( i );
1861 | }
1862 |
1863 | for ( i in ns ) {
1864 | if ( !hasOwn.call( ns, i ) ) {
1865 | continue;
1866 | }
1867 | if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
1868 | n[ ns[i].rows[0] ] = {
1869 | text: n[ ns[i].rows[0] ],
1870 | row: os[i].rows[0]
1871 | };
1872 | o[ os[i].rows[0] ] = {
1873 | text: o[ os[i].rows[0] ],
1874 | row: ns[i].rows[0]
1875 | };
1876 | }
1877 | }
1878 |
1879 | for ( i = 0; i < n.length - 1; i++ ) {
1880 | if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
1881 | n[ i + 1 ] == o[ n[i].row + 1 ] ) {
1882 |
1883 | n[ i + 1 ] = {
1884 | text: n[ i + 1 ],
1885 | row: n[i].row + 1
1886 | };
1887 | o[ n[i].row + 1 ] = {
1888 | text: o[ n[i].row + 1 ],
1889 | row: i + 1
1890 | };
1891 | }
1892 | }
1893 |
1894 | for ( i = n.length - 1; i > 0; i-- ) {
1895 | if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
1896 | n[ i - 1 ] == o[ n[i].row - 1 ]) {
1897 |
1898 | n[ i - 1 ] = {
1899 | text: n[ i - 1 ],
1900 | row: n[i].row - 1
1901 | };
1902 | o[ n[i].row - 1 ] = {
1903 | text: o[ n[i].row - 1 ],
1904 | row: i - 1
1905 | };
1906 | }
1907 | }
1908 |
1909 | return {
1910 | o: o,
1911 | n: n
1912 | };
1913 | }
1914 |
1915 | return function( o, n ) {
1916 | o = o.replace( /\s+$/, "" );
1917 | n = n.replace( /\s+$/, "" );
1918 |
1919 | var i, pre,
1920 | str = "",
1921 | out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
1922 | oSpace = o.match(/\s+/g),
1923 | nSpace = n.match(/\s+/g);
1924 |
1925 | if ( oSpace == null ) {
1926 | oSpace = [ " " ];
1927 | }
1928 | else {
1929 | oSpace.push( " " );
1930 | }
1931 |
1932 | if ( nSpace == null ) {
1933 | nSpace = [ " " ];
1934 | }
1935 | else {
1936 | nSpace.push( " " );
1937 | }
1938 |
1939 | if ( out.n.length === 0 ) {
1940 | for ( i = 0; i < out.o.length; i++ ) {
1941 | str += "" + out.o[i] + oSpace[i] + "";
1942 | }
1943 | }
1944 | else {
1945 | if ( out.n[0].text == null ) {
1946 | for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
1947 | str += "" + out.o[n] + oSpace[n] + "";
1948 | }
1949 | }
1950 |
1951 | for ( i = 0; i < out.n.length; i++ ) {
1952 | if (out.n[i].text == null) {
1953 | str += "" + out.n[i] + nSpace[i] + "";
1954 | }
1955 | else {
1956 | // `pre` initialized at top of scope
1957 | pre = "";
1958 |
1959 | for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
1960 | pre += "" + out.o[n] + oSpace[n] + "";
1961 | }
1962 | str += " " + out.n[i].text + nSpace[i] + pre;
1963 | }
1964 | }
1965 | }
1966 |
1967 | return str;
1968 | };
1969 | }());
1970 |
1971 | // for CommonJS enviroments, export everything
1972 | if ( typeof exports !== "undefined" ) {
1973 | extend(exports, QUnit);
1974 | }
1975 |
1976 | // get at whatever the global object is, like window in browsers
1977 | }( (function() {return this;}.call()) ));
1978 |
--------------------------------------------------------------------------------