src/ztfy/myams/resources/js/ext/jquery-xmlrpc.js
changeset 127 cf00ea83daa7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ztfy/myams/resources/js/ext/jquery-xmlrpc.js	Mon Apr 20 15:55:01 2015 +0200
@@ -0,0 +1,415 @@
+/*jshint browser:true */
+/*global jQuery */
+(function($) {
+	"use strict";
+
+	var XmlRpcFault = function() {
+		Error.apply(this, arguments);
+	};
+	XmlRpcFault.prototype = new Error();
+	XmlRpcFault.prototype.type = 'XML-RPC fault';
+
+	var xmlrpc = $.xmlrpc = function(url, settings) {
+
+		if (arguments.length === 2) {
+			settings.url = url;
+		} else {
+			settings = url;
+			url = settings.url;
+		}
+
+		settings.dataType = 'xml json';
+		settings.type = 'POST';
+		settings.contentType = 'text/xml';
+		settings.converters = {'xml json': xmlrpc.parseDocument};
+
+		var xmlDoc = xmlrpc.document(settings.methodName, settings.params || []);
+
+		if ("XMLSerializer" in window) {
+			settings.data = new window.XMLSerializer().serializeToString(xmlDoc);
+		} else {
+			// IE does not have XMLSerializer
+			settings.data = xmlDoc.xml;
+		}
+
+		return $.ajax(settings);
+	};
+
+	/**
+	* Make an XML document node.
+	*/
+	xmlrpc.createXMLDocument = function () {
+
+		if (document.implementation && "createDocument" in document.implementation) {
+			// Most browsers support createDocument
+			return document.implementation.createDocument(null, null, null);
+
+		} else {
+			// IE uses ActiveXObject instead of the above.
+			var i, length, activeX = [
+				"MSXML6.DomDocument", "MSXML3.DomDocument",
+				"MSXML2.DomDocument", "MSXML.DomDocument", "Microsoft.XmlDom"
+			];
+			for (i = 0, length = activeX.length; i < length; i++) {
+				try {
+					return new ActiveXObject(activeX[i]);
+				} catch(_) {}
+			}
+		}
+	};
+
+	/**
+	* Make an XML-RPC document from a method name and a set of parameters
+	*/
+	xmlrpc.document = function(name, params) {
+		var doc = xmlrpc.createXMLDocument();
+
+
+		var $xml = function(name) {
+			return $(doc.createElement(name));
+		};
+
+		var $methodName = $xml('methodName').text(name);
+		var $params = $xml('params').append($.map(params, function(param) {
+			var $value = $xml('value').append(xmlrpc.toXmlRpc(param, $xml));
+			return $xml('param').append($value);
+		}));
+		var $methodCall = $xml('methodCall').append($methodName, $params);
+		doc.appendChild($methodCall.get(0));
+		return doc;
+	};
+
+	var _isInt = function(x) {
+		return (x === parseInt(x, 10)) && !isNaN(x);
+	};
+
+	/**
+	* Take a JavaScript value, and return an XML node representing the value
+	* in XML-RPC style. If the value is one of the `XmlRpcType`s, that type is
+	* used. Otherwise, a best guess is made as to its type. The best guess is
+	* good enough in the vast majority of cases.
+	*/
+	xmlrpc.toXmlRpc = function(item, $xml) {
+
+		if (item instanceof XmlRpcType) {
+			return item.toXmlRpc($xml);
+		}
+
+		var types = $.xmlrpc.types;
+		var type = $.type(item);
+
+		switch (type) {
+			case "undefined":
+			case "null":
+				return types.nil.encode(item, $xml);
+
+			case "date":
+				return types['datetime.iso8601'].encode(item, $xml);
+
+			case "object":
+				if (item instanceof ArrayBuffer) {
+					return types.base64.encode(item, $xml);
+				} else {
+					return types.struct.encode(item, $xml);
+				}
+				break;
+
+
+			case "number":
+				// Ints and Floats encode differently
+				if (_isInt(item)) {
+					return types['int'].encode(item, $xml);
+				} else {
+					return types['double'].encode(item, $xml);
+				}
+				break;
+
+			case "array":
+			case "boolean":
+			case "string":
+				return types[type].encode(item, $xml);
+
+			default:
+				throw new Error("Unknown type", item);
+		}
+	};
+
+	/**
+	* Take an XML-RPC document and decode it to an equivalent JavaScript
+	* representation.
+	*
+	* If the XML-RPC document represents a fault, then an equivalent
+	* XmlRpcFault will be thrown instead
+	*/
+	xmlrpc.parseDocument = function(doc) {
+		var $doc = $(doc);
+		var $response = $doc.children('methodresponse');
+
+		var $fault = $response.find('> fault');
+		if ($fault.length === 0) {
+			var $params = $response.find('> params > param > value > *');
+			var json = $params.toArray().map(xmlrpc.parseNode);
+			return json;
+		} else {
+			var fault = xmlrpc.parseNode($fault.find('> value > *').get(0));
+			var err = new XmlRpcFault(fault.faultString);
+			err.msg = err.message = fault.faultString;
+			err.type = err.code = fault.faultCode;
+			throw err;
+		}
+	};
+
+	/*
+	* Take an XML-RPC node, and return the JavaScript equivalent
+	*/
+	xmlrpc.parseNode = function(node) {
+
+		// Some XML-RPC services return empty <value /> elements. This is not
+		// legal XML-RPC, but we may as well handle it.
+		if (node === undefined) {
+			return null;
+		}
+		var nodename = node.nodeName.toLowerCase();
+		if (nodename in xmlrpc.types) {
+			return xmlrpc.types[nodename].decode(node);
+		} else {
+			throw new Error('Unknown type ' + nodename);
+		}
+	};
+
+	/*
+	* Take a <value> node, and return the JavaScript equivalent.
+	*/
+	xmlrpc.parseValue = function(value) {
+		var child = $(value).children()[0];
+		if (child) {
+			// Child nodes should be decoded.
+			return xmlrpc.parseNode(child);
+		} else {
+			// If no child nodes, the value is a plain text node.
+			return $(value).text();
+		}
+	};
+
+	var XmlRpcType = function() { };
+
+	$.xmlrpc.types = {};
+
+	/**
+	* Make a XML-RPC type. We use these to encode and decode values. You can
+	* also force a values type using this. See `$.xmlrpc.force()`
+	*/
+	xmlrpc.makeType = function(tagName, simple, encode, decode) {
+		var Type;
+
+		Type = function(value) {
+			this.value = value;
+		};
+		Type.prototype = new XmlRpcType();
+		Type.prototype.tagName = tagName;
+
+		if (simple) {
+			var simpleEncode = encode, simpleDecode = decode;
+			encode = function(value, $xml) {
+				var text = simpleEncode(value);
+				return $xml(Type.tagName).text(text);
+			};
+			decode = function(node) {
+				return simpleDecode($(node).text(), node);
+			};
+		}
+		Type.prototype.toXmlRpc = function($xml) {
+			return Type.encode(this.value, $xml);
+		};
+
+		Type.tagName = tagName;
+		Type.encode = encode;
+		Type.decode = decode;
+
+		xmlrpc.types[tagName.toLowerCase()] = Type;
+	};
+
+
+	// Number types
+	var _fromInt = function(value) { return '' + Math.floor(value); };
+	var _toInt = function(text, _) { return parseInt(text, 10); };
+
+	xmlrpc.makeType('int', true, _fromInt, _toInt);
+	xmlrpc.makeType('i4', true, _fromInt, _toInt);
+	xmlrpc.makeType('i8', true, _fromInt, _toInt);
+	xmlrpc.makeType('i16', true, _fromInt, _toInt);
+	xmlrpc.makeType('i32', true, _fromInt, _toInt);
+
+	xmlrpc.makeType('double', true, String, function(text) {
+		return parseFloat(text, 10);
+	});
+
+	// String type. Fairly simple
+	xmlrpc.makeType('string', true, String, String);
+
+	// Boolean type. True == '1', False == '0'
+	xmlrpc.makeType('boolean', true, function(value) {
+		return value ? '1' : '0';
+	}, function(text) {
+		return text === '1';
+	});
+
+	// Dates are a little trickier
+	var _pad = function(n) { return n<10 ? '0'+n : n; };
+
+	xmlrpc.makeType('dateTime.iso8601', true, function(d) {
+		return [
+			d.getUTCFullYear(), '-', _pad(d.getUTCMonth()+1), '-',
+			_pad(d.getUTCDate()), 'T', _pad(d.getUTCHours()), ':',
+			_pad(d.getUTCMinutes()), ':', _pad(d.getUTCSeconds()), 'Z'
+		].join('');
+	}, function(text) {
+		return new Date(text);
+	});
+
+	// Go between a base64 string and an ArrayBuffer
+	xmlrpc.binary = (function() {
+		var pad = '=';
+		var toChars = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
+			'abcdefghijklmnopqrstuvwxyz0123456789+/').split("");
+		var fromChars = toChars.reduce(function(acc, chr, i) {
+			acc[chr] = i;
+			return acc;
+		}, {});
+
+		/*
+		* In the following, three bytes are added together into a 24-bit
+		* number, which is then split up in to 4 6-bit numbers - or vice versa.
+		* That is why there is lots of shifting by multiples of 6 and 8, and
+		* the magic numbers 3 and 4.
+		*
+		* The modulo 64 is for converting to base 64, and the modulo 256 is for
+		* converting to 8-bit numbers.
+		*/
+		return {
+			toBase64: function(ab) {
+				var acc = [];
+
+				var int8View = new Uint8Array(ab);
+				var int8Index = 0, int24;
+				for (; int8Index < int8View.length; int8Index += 3) {
+
+					// Grab three bytes
+					int24 =
+						(int8View[int8Index + 0] << 16) +
+						(int8View[int8Index + 1] << 8) +
+						(int8View[int8Index + 2] << 0);
+
+					// Push four chars
+					acc.push(toChars[(int24 >> 18) % 64]);
+					acc.push(toChars[(int24 >> 12) % 64]);
+					acc.push(toChars[(int24 >> 6) % 64]);
+					acc.push(toChars[(int24 >> 0)% 64]);
+				}
+
+				// Set the last few characters to the padding character
+				var padChars = 3 - ((ab.byteLength % 3) || 3);
+				while (padChars--) {
+					acc[acc.length - padChars - 1] = pad;
+				}
+
+				return acc.join('');
+			},
+
+			fromBase64: function(base64) {
+				var base64Len = base64.length;
+
+				// Work out the length of the data, accommodating for padding
+				var abLen = (base64Len / 4) * 3;
+				if (base64.charAt(base64Len - 1) === pad) { abLen--; }
+				if (base64.charAt(base64Len - 2) === pad) { abLen--; }
+
+				// Make the ArrayBuffer, and an Int8Array to work with it
+				var ab = new ArrayBuffer(abLen);
+				var int8View = new Uint8Array(ab);
+
+				var base64Index = 0, int8Index = 0, int24;
+				for (; base64Index < base64Len; base64Index += 4, int8Index += 3) {
+
+					// Grab four chars
+					int24 =
+						(fromChars[base64[base64Index + 0]] << 18) +
+						(fromChars[base64[base64Index + 1]] << 12) +
+						(fromChars[base64[base64Index + 2]] << 6) +
+						(fromChars[base64[base64Index + 3]] << 0);
+
+					// Push three bytes
+					int8View[int8Index + 0] = (int24 >> 16) % 256;
+					int8View[int8Index + 1] = (int24 >> 8) % 256;
+					int8View[int8Index + 2] = (int24 >> 0) % 256;
+
+				}
+
+				return ab;
+			}
+		};
+	})();
+
+	xmlrpc.makeType('base64', true, function(ab) {
+		return xmlrpc.binary.toBase64(ab);
+	}, function(text) {
+		return xmlrpc.binary.fromBase64(text);
+	});
+
+	// Nil/null
+	xmlrpc.makeType('nil', false,
+		function(val, $xml) { return $xml('nil'); },
+		function(_) { return null; }
+	);
+
+	// Structs/Objects
+	xmlrpc.makeType('struct', false, function(value, $xml) {
+		var $struct = $xml('struct');
+
+		$.each(value, function(name, value) {
+			var $name = $xml('name').text(name);
+			var $value = $xml('value').append(xmlrpc.toXmlRpc(value, $xml));
+			$struct.append($xml('member').append($name, $value));
+		});
+
+		return $struct;
+
+	}, function(node) {
+		return $(node)
+			.find('> member')
+			.toArray()
+			.reduce(function(struct, el) {
+				var $el = $(el);
+				var key = $el.find('> name').text();
+				var value = xmlrpc.parseValue($el.find('> value'));
+
+				struct[key] = value;
+				return struct;
+			}, {});
+
+	});
+
+	// Arrays
+	xmlrpc.makeType('array', false, function(value, $xml) {
+		var $array = $xml('array');
+		var $data = $xml('data');
+		$.each(value, function(i, val) {
+			$data.append($xml('value').append(xmlrpc.toXmlRpc(val, $xml)));
+		});
+		$array.append($data);
+		return $array;
+	}, function(node) {
+		return $(node).find('> data > value').toArray()
+			.map(xmlrpc.parseValue);
+	});
+
+
+	/**
+	* Force a value to an XML-RPC type. All the usual XML-RPC types are
+	* supported
+	*/
+	xmlrpc.force = function(type, value) {
+		return new xmlrpc.types[type](value);
+	};
+
+})(jQuery);