commit 62ba2b8585ce270d8486788a9fe7129427626cc9 Author: Jeremy Kauffman Date: Mon Mar 14 18:05:24 2016 -0400 initial commit (broken) diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 000000000..2eceeddae Binary files /dev/null and b/favicon.ico differ diff --git a/font/FontAwesome.otf b/font/FontAwesome.otf new file mode 100644 index 000000000..f7936cc1e Binary files /dev/null and b/font/FontAwesome.otf differ diff --git a/font/fontawesome-webfont.eot b/font/fontawesome-webfont.eot new file mode 100644 index 000000000..33b2bb800 Binary files /dev/null and b/font/fontawesome-webfont.eot differ diff --git a/font/fontawesome-webfont.svg b/font/fontawesome-webfont.svg new file mode 100644 index 000000000..1ee89d436 --- /dev/null +++ b/font/fontawesome-webfont.svg @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/font/fontawesome-webfont.ttf b/font/fontawesome-webfont.ttf new file mode 100644 index 000000000..ed9372f8e Binary files /dev/null and b/font/fontawesome-webfont.ttf differ diff --git a/font/fontawesome-webfont.woff b/font/fontawesome-webfont.woff new file mode 100644 index 000000000..8b280b98f Binary files /dev/null and b/font/fontawesome-webfont.woff differ diff --git a/font/fontawesome-webfont.woff2 b/font/fontawesome-webfont.woff2 new file mode 100644 index 000000000..3311d5851 Binary files /dev/null and b/font/fontawesome-webfont.woff2 differ diff --git a/img/fav/android-chrome-144x144.png b/img/fav/android-chrome-144x144.png new file mode 100644 index 000000000..c76cef27d Binary files /dev/null and b/img/fav/android-chrome-144x144.png differ diff --git a/img/fav/android-chrome-192x192.png b/img/fav/android-chrome-192x192.png new file mode 100644 index 000000000..0cd9fd50f Binary files /dev/null and b/img/fav/android-chrome-192x192.png differ diff --git a/img/fav/android-chrome-36x36.png b/img/fav/android-chrome-36x36.png new file mode 100644 index 000000000..4488cb565 Binary files /dev/null and b/img/fav/android-chrome-36x36.png differ diff --git a/img/fav/android-chrome-48x48.png b/img/fav/android-chrome-48x48.png new file mode 100644 index 000000000..2694bad95 Binary files /dev/null and b/img/fav/android-chrome-48x48.png differ diff --git a/img/fav/android-chrome-72x72.png b/img/fav/android-chrome-72x72.png new file mode 100644 index 000000000..747d58c7b Binary files /dev/null and b/img/fav/android-chrome-72x72.png differ diff --git a/img/fav/android-chrome-96x96.png b/img/fav/android-chrome-96x96.png new file mode 100644 index 000000000..653551197 Binary files /dev/null and b/img/fav/android-chrome-96x96.png differ diff --git a/img/fav/apple-touch-icon-114x114.png b/img/fav/apple-touch-icon-114x114.png new file mode 100644 index 000000000..a4764bdbc Binary files /dev/null and b/img/fav/apple-touch-icon-114x114.png differ diff --git a/img/fav/apple-touch-icon-120x120.png b/img/fav/apple-touch-icon-120x120.png new file mode 100644 index 000000000..6bab9700a Binary files /dev/null and b/img/fav/apple-touch-icon-120x120.png differ diff --git a/img/fav/apple-touch-icon-120x120_1.png b/img/fav/apple-touch-icon-120x120_1.png new file mode 100644 index 000000000..6bab9700a Binary files /dev/null and b/img/fav/apple-touch-icon-120x120_1.png differ diff --git a/img/fav/apple-touch-icon-144x144.png b/img/fav/apple-touch-icon-144x144.png new file mode 100644 index 000000000..cd8672ae9 Binary files /dev/null and b/img/fav/apple-touch-icon-144x144.png differ diff --git a/img/fav/apple-touch-icon-152x152.png b/img/fav/apple-touch-icon-152x152.png new file mode 100644 index 000000000..17f18254a Binary files /dev/null and b/img/fav/apple-touch-icon-152x152.png differ diff --git a/img/fav/apple-touch-icon-180x180.png b/img/fav/apple-touch-icon-180x180.png new file mode 100644 index 000000000..83dd10fe7 Binary files /dev/null and b/img/fav/apple-touch-icon-180x180.png differ diff --git a/img/fav/apple-touch-icon-57x57.png b/img/fav/apple-touch-icon-57x57.png new file mode 100644 index 000000000..e177deb8d Binary files /dev/null and b/img/fav/apple-touch-icon-57x57.png differ diff --git a/img/fav/apple-touch-icon-60x60.png b/img/fav/apple-touch-icon-60x60.png new file mode 100644 index 000000000..a4d02c5f2 Binary files /dev/null and b/img/fav/apple-touch-icon-60x60.png differ diff --git a/img/fav/apple-touch-icon-72x72.png b/img/fav/apple-touch-icon-72x72.png new file mode 100644 index 000000000..b0deac832 Binary files /dev/null and b/img/fav/apple-touch-icon-72x72.png differ diff --git a/img/fav/apple-touch-icon-76x76.png b/img/fav/apple-touch-icon-76x76.png new file mode 100644 index 000000000..90ce5a1a9 Binary files /dev/null and b/img/fav/apple-touch-icon-76x76.png differ diff --git a/img/fav/apple-touch-icon-76x76_1.png b/img/fav/apple-touch-icon-76x76_1.png new file mode 100644 index 000000000..90ce5a1a9 Binary files /dev/null and b/img/fav/apple-touch-icon-76x76_1.png differ diff --git a/img/fav/apple-touch-icon-precomposed.png b/img/fav/apple-touch-icon-precomposed.png new file mode 100644 index 000000000..7e0bf0f3e Binary files /dev/null and b/img/fav/apple-touch-icon-precomposed.png differ diff --git a/img/fav/apple-touch-icon.png b/img/fav/apple-touch-icon.png new file mode 100644 index 000000000..83dd10fe7 Binary files /dev/null and b/img/fav/apple-touch-icon.png differ diff --git a/img/fav/browserconfig.xml b/img/fav/browserconfig.xml new file mode 100644 index 000000000..65380f387 --- /dev/null +++ b/img/fav/browserconfig.xml @@ -0,0 +1,12 @@ + + + + + + + + + #da532c + + + diff --git a/img/fav/favicon-16x16.png b/img/fav/favicon-16x16.png new file mode 100644 index 000000000..cf45959d1 Binary files /dev/null and b/img/fav/favicon-16x16.png differ diff --git a/img/fav/favicon-194x194.png b/img/fav/favicon-194x194.png new file mode 100644 index 000000000..522fa6cf4 Binary files /dev/null and b/img/fav/favicon-194x194.png differ diff --git a/img/fav/favicon-32x32.png b/img/fav/favicon-32x32.png new file mode 100644 index 000000000..1dc37a45f Binary files /dev/null and b/img/fav/favicon-32x32.png differ diff --git a/img/fav/favicon-96x96.png b/img/fav/favicon-96x96.png new file mode 100644 index 000000000..6b46aa5aa Binary files /dev/null and b/img/fav/favicon-96x96.png differ diff --git a/img/fav/manifest.json b/img/fav/manifest.json new file mode 100644 index 000000000..371ee6518 --- /dev/null +++ b/img/fav/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "My app", + "icons": [ + { + "src": "\/android-chrome-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-chrome-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-chrome-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-chrome-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-chrome-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/img/fav/mstile-144x144.png b/img/fav/mstile-144x144.png new file mode 100644 index 000000000..e1626e4b3 Binary files /dev/null and b/img/fav/mstile-144x144.png differ diff --git a/img/fav/mstile-150x150.png b/img/fav/mstile-150x150.png new file mode 100644 index 000000000..4a8a8ab8e Binary files /dev/null and b/img/fav/mstile-150x150.png differ diff --git a/img/fav/mstile-310x150.png b/img/fav/mstile-310x150.png new file mode 100644 index 000000000..92acf3d05 Binary files /dev/null and b/img/fav/mstile-310x150.png differ diff --git a/img/fav/mstile-310x310.png b/img/fav/mstile-310x310.png new file mode 100644 index 000000000..14e3c1193 Binary files /dev/null and b/img/fav/mstile-310x310.png differ diff --git a/img/fav/mstile-70x70.png b/img/fav/mstile-70x70.png new file mode 100644 index 000000000..83f1a7e38 Binary files /dev/null and b/img/fav/mstile-70x70.png differ diff --git a/img/lbry-bg.png b/img/lbry-bg.png new file mode 100644 index 000000000..b1a4b1901 Binary files /dev/null and b/img/lbry-bg.png differ diff --git a/img/lbry-dark-1600x528.png b/img/lbry-dark-1600x528.png new file mode 100644 index 000000000..cc9837e36 Binary files /dev/null and b/img/lbry-dark-1600x528.png differ diff --git a/img/lbry-white-485x160.png b/img/lbry-white-485x160.png new file mode 100644 index 000000000..d8da148a8 Binary files /dev/null and b/img/lbry-white-485x160.png differ diff --git a/img/lbry-white-909x300.png b/img/lbry-white-909x300.png new file mode 100644 index 000000000..1428e1052 Binary files /dev/null and b/img/lbry-white-909x300.png differ diff --git a/index.html b/index.html new file mode 100644 index 000000000..911debb6a --- /dev/null +++ b/index.html @@ -0,0 +1,28 @@ + + + + + LBRY + + + + + + + + + + + + + + +
+ + + + + + + + diff --git a/js/gui.js b/js/gui.js new file mode 100644 index 000000000..943a9855f --- /dev/null +++ b/js/gui.js @@ -0,0 +1,337 @@ +//component/icon.js + +var Icon = React.createClass({ + render: function() { + var className = 'icon ' + this.props.icon; + return + } +}); + +//component/link.js + +var Link = React.createClass({ + render: function() { + console.log(this.props); + var href = this.props.href ? this.props.href : 'javascript:;', + icon = this.props.icon ? : '', + className = this.props.button ? 'button-block button-' + this.props.button : 'button-text'; + return ( + + {this.props.icon ? icon : '' } + {this.props.label} + + ); + } +}); + + +//component/splash.js +var splashStyle = { + color: 'white', + backgroundImage: 'url(' + lbry.imagePath('lbry-bg.png') + ')', + backgroundSize: 'cover', + minHeight: '100vh', + minWidth: '100vw', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center' +}, splashMessageStyle = { + marginTop: '24px' +}; + +var SplashScreen = React.createClass({ + propTypes: { + message: React.PropTypes.string, + }, + render: function() { + var imgSrc = lbry.imagePath('lbry-white-485x160.png'); + return ( +
+ LBRY +
+

+ {this.props.message} + +

+
+
+ ); + } +}); + +//component/credit-amount.js +var creditAmountStyle = { + color: '#216C2A', + fontWeight: 'bold', + fontSize: '0.8em' +}, estimateStyle = { + marginLeft : '5px', + color: '#aaa', +} + +var CreditAmount = React.createClass({ + propTypes: { + amount: React.PropTypes.number, + }, + render: function() { + var formattedAmount = lbry.formatCredits(this.props.amount); + return ( + + {formattedAmount} + { this.props.isEstimate ? (est) : null } + + ); + } +}); + + +//component/header.js +var logoStyle = { + padding: '48px 12px', + textAlign: 'center', + maxHeight: '80px', +}, + balanceStyle = { + float: 'right', + marginTop: '3px' +}, + imgStyle = { //@TODO: remove this, img should be properly scaled once size is settled + height: '80px' +}; + +var Header = React.createClass({ + render: function() { + var balance = lbry.getBalance(); + return ( +
+ + + +
+ +
+
+ ); + } +}); + +//component/discover.js + +var searchInputStyle = { + width: '400px', + display: 'block', + marginBottom: '48px', + marginLeft: 'auto', + marginRight: 'auto' +}, + fetchResultsStyle = { + color: '#888', + textAlign: 'center', + fontSize: '1.2em' +}; + +var SearchActive = React.createClass({ + render: function() { + return ( +
+ Looking up the Dewey Decimals + +
+ ); + } +}); + +var searchNoResultsStyle = { + textAlign: 'center' +}, searchNoResultsMessageStyle = { + fontStyle: 'italic', + marginRight: '5px' +}; + +var SearchNoResults = React.createClass({ + render: function() { + return ( +
+ No one has checked anything in for {this.props.query} yet. + +
+ ); + } +}); + +var SearchResults = React.createClass({ + render: function() { + var rows = []; + console.log('results'); + this.props.results.forEach(function(result) { + rows.push( + + ); + }); + console.log(this.props.results); + console.log(rows); + console.log('done'); + return ( +
{rows}
+ ); + } +}); + +var searchRowImgStyle = { + maxHeight: '200px', + display: 'block', + marginLeft: 'auto', + marginRight: 'auto', + float: 'left' +}, + searchRowCostStyle = { + float: 'right', + marginLeft: '20px', + marginTop: '5px', + display: 'inline-block' +}, + searchRowNameStyle = { + fontSize: '0.9em', + color: '#666', + marginBottom: '24px', + clear: 'both' +}, + searchRowDescriptionStyle = { + color : '#444', + marginBottom: '24px', + fontSize: '0.9em' +}; + + +var SearchResultRow = React.createClass({ + render: function() { + var uri = 'lbry://' + this.props.name; + return ( +
+
+ Photo for {this.props.title} +
+
+ + + +

{this.props.title}

+
{uri}
+

{this.props.description}

+
+ + +
+
+
+ ); + } +}); + + +var discoverMainStyle = { + color: '#333' +}; + +var Discover = React.createClass({ + userTypingTimer: null, + + getInitialState: function() { + return { + results: [], + searching: false, + query: '' + }; + }, + + search: function() { + if (this.state.query) + { + console.log('search'); + lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query)); + } + else + { + this.setState({ + searching: false, + results: [] + }); + } + }, + + searchCallback: function(originalQuery, results) { + if (this.state.searching) //could have canceled while results were pending, in which case nothing to do + { + this.setState({ + results: results, + searching: this.state.query != originalQuery, //multiple searches can be out, we're only done if we receive one we actually care about + }); + } + }, + + onQueryChange: function(event) { + if (this.userTypingTimer) + { + clearTimeout(this.userTypingTimer); + } + + //@TODO: Switch to React.js timing + this.userTypingTimer = setTimeout(this.search, 800); // 800ms delay, tweak for faster/slower + + this.setState({ + searching: event.target.value.length > 0, + query: event.target.value + }); + }, + + render: function() { + console.log(this.state); + return ( +
+
+ { this.state.searching ? : null } + { !this.state.searching && this.state.results.length ? : null } + { !this.state.searching && !this.state.results.length && this.state.query ? : null } +
+ ); + } +}); + +//component/home.js + +var homeStyles = { + width: '800px', + marginLeft: 'auto', + marginRight: 'auto', +}; + +var Home = React.createClass({ + render: function() { + return ( +
+
+ +
+ ); + } +}); + +//main.js +var init = function() { + var canvas = document.getElementById('canvas'); + + ReactDOM.render( + , + canvas + ); + + lbry.connect(function() { + ReactDOM.render(, canvas); + }) +}; + +init(); + diff --git a/js/lbry.js b/js/lbry.js new file mode 100644 index 000000000..efcf8376e --- /dev/null +++ b/js/lbry.js @@ -0,0 +1,64 @@ +var lbry = { + isConnected: false, + rootPath: '../../web/', + colors: { + primary: '#155B4A' + } +}; + +//core +lbry.connect = function(callback) +{ + //dummy connect function - one of the first things we should do is dump people to get help if it can't connect + setTimeout(function() { + lbry.isConnected = true; + callback(); + }, 1500); +} + +lbry.getBalance = function() +{ + var method = "get_balance"; + var request = new XmlRpcRequest("http://localhost:7080/", method); + var amount = Number(request.send().parseXML()); + return amount; +} + +lbry.search = function(query, callback) +{ + //simulate searching via setTimeout with + setTimeout(function() { + var method = "search_nametrie" + var request = new XmlRpcRequest("http://localhost:7080/", method); + request.addParam(query); + var results = request.send().parseXML(); + + callback(results); + }, 300); +} + +//utilities +lbry.formatCredits = function(amount, precision) +{ + return amount.toFixed(precision || 1); +} + +lbry.loadJs = function(src, type, onload) +{ + var lbryScriptTag = document.getElementById('lbry'), + newScriptTag = document.createElement('script'), + type = type || 'text/javascript'; + + newScriptTag.src = src; + newScriptTag.type = type; + if (onload) + { + newScript.onload = onload; + } + lbryScriptTag.parentNode.insertBefore(newScriptTag, lbryScriptTag); +} + +lbry.imagePath = function(file) +{ + return lbry.rootPath + '/img/' + file; +} diff --git a/js/mimic.js b/js/mimic.js new file mode 100644 index 000000000..a76a7fe41 --- /dev/null +++ b/js/mimic.js @@ -0,0 +1,647 @@ +/*! + * Mimic (XML-RPC Client for JavaScript) v2.3 + * Copyright (C) 2005-2013 Carlos Eduardo Goncalves (cadu.goncalves@gmail.com) + * + * Mimic is dual licensed under the MIT (http://opensource.org/licenses/mit-license.php) + * and GPLv3 (http://opensource.org/licenses/gpl-3.0.html) licenses. + */ + +/** + * XmlRpc helper. + */ +function XmlRpc() { + +}; + +/** + *

+ * XML-RPC document prolog. + *

+ */ +XmlRpc.PROLOG = "\n"; + +/** + *

+ * XML-RPC methodCall node template. + *

+ */ +XmlRpc.REQUEST = "\n${METHOD}\n\n${DATA}\n"; + +/** + *

+ * XML-RPC param node template. + *

+ */ +XmlRpc.PARAM = "\n\n${DATA}\n\n"; + +/** + *

+ * XML-RPC array node template. + *

+ */ +XmlRpc.ARRAY = "\n\n${DATA}\n\n"; + +/** + *

+ * XML-RPC struct node template. + *

+ */ +XmlRpc.STRUCT = "\n${DATA}\n"; + +/** + *

+ * XML-RPC member node template. + *

+ */ +XmlRpc.MEMBER = "\n${DATA}\n"; + +/** + *

+ * XML-RPC name node template. + *

+ */ +XmlRpc.NAME = "${DATA}\n"; + +/** + *

+ * XML-RPC value node template. + *

+ */ +XmlRpc.VALUE = "\n${DATA}\n"; + +/** + *

+ * XML-RPC scalar node template (int, i4, double, string, boolean, base64, + * dateTime.iso8601). + *

+ */ +XmlRpc.SCALAR = "<${TYPE}>${DATA}\n"; + +/** + *

+ * Get the tag name used to represent a JavaScript object in the XMLRPC + * protocol. + *

+ * + * @param data + * A JavaScript object. + * @return String with XMLRPC object type. + */ +XmlRpc.getDataTag = function(data) { + try { + // Vars + var tag = typeof data; + + switch (tag.toLowerCase()) { + case "number": + tag = (Math.round(data) == data) ? "int" : "double"; + break; + case "object": + if (data.constructor == Base64) { + tag = "base64"; + } else if (data.constructor == String) { + tag = "string"; + } else if (data.constructor == Boolean) { + tag = "boolean"; + } else if (data.constructor == Array) { + tag = "array"; + } else if (data.constructor == Date) { + tag = "dateTime.iso8601"; + } else if (data.constructor == Number) { + tag = (Math.round(data) == data) ? "int" : "double"; + } else { + tag = "struct"; + } + break; + } + return tag; + } catch (e) { + return null; + } +}; + +/** + *

+ * Get JavaScript object type represented by XMLRPC protocol tag. + *

+ * + * @param tag + * A XMLRPC tag name. + * @return A JavaScript object. + */ +XmlRpc.getTagData = function(tag) { + // Vars + var data = null; + + switch (tag) { + case "struct": + data = new Object(); + break; + case "array": + data = new Array(); + break; + case "datetime.iso8601": + data = new Date(); + break; + case "boolean": + data = new Boolean(); + break; + case "int": + case "i4": + case "double": + data = new Number(); + break; + case "string": + data = new String(); + break; + case "base64": + data = new Base64(); + break; + } + return data; +}; + +/** + * XmlRpcRequest. + * + * @param url + * Server url. + * @param method + * Server side method do call. + */ +function XmlRpcRequest(url, method) { + this.serviceUrl = url; + this.methodName = method; + this.crossDomain = false; + this.withCredentials = false; + this.params = []; + this.headers = {}; +}; + +/** + *

+ * Add a new request parameter. + *

+ * + * @param data + * New parameter value. + */ +XmlRpcRequest.prototype.addParam = function(data) { + // Vars + var type = typeof data; + + switch (type.toLowerCase()) { + case "function": + return; + case "object": + if (!data.constructor.name){ + return; + } + } + this.params.push(data); +}; + +/** + *

+ * Clear all request parameters. + *

+ * + * @param data + * New parameter value. + */ +XmlRpcRequest.prototype.clearParams = function() { + this.params.splice(0, this.params.length); +}; + +/** + *

+ * Define HTTP header value. + *

+ * + * @param name + * Header name. + * @param data + * Header value. Use to clear the header. + */ +XmlRpcRequest.prototype.setHeader = function(name, value) { + if(value) { + this.headers[name] = value; + } else { + delete this.headers[name]; + } +}; + +/** + *

+ * Execute a synchronous XML-RPC request. + *

+ * + * @return XmlRpcResponse object. + */ +XmlRpcRequest.prototype.send = function() { + // Vars + var xml_params = "", + i = 0, + xml_call, xhr; + // XMLRPC + for (i = 0; i < this.params.length; i++) { + xml_params += XmlRpc.PARAM.replace("${DATA}", this.marshal(this.params[i])); + } + xml_call = XmlRpc.REQUEST.replace("${METHOD}", this.methodName); + xml_call = XmlRpc.PROLOG + xml_call.replace("${DATA}", xml_params); + // XHR + xhr = Builder.buildXHR(this.crossDomain); + xhr.open("POST", this.serviceUrl, false); + // HTTP headers + for(i in this.headers) { + if (this.headers.hasOwnProperty(i)) { + xhr.setRequestHeader(i, this.headers[i]); + } + } + // HTTP credentials + if(this.withCredentials && "withCredentials" in xhr) { + xhr.withCredentials = true; + } + xhr.send(Builder.buildDOM(xml_call)); + return new XmlRpcResponse(xhr.responseXML); +}; + +/** + *

+ * Marshal request parameters. + *

+ * + * @param data + * A request parameter. + * @return String with XML-RPC element notation. + */ +XmlRpcRequest.prototype.marshal = function(data) { + // Vars + var type = XmlRpc.getDataTag(data), + scalar_type = XmlRpc.SCALAR.replace(/\$\{TYPE\}/g, type), + xml = "", + value, i, member; + + switch (type) { + case "struct": + member = ""; + for (i in data) { + value = ""; + value += XmlRpc.NAME.replace("${DATA}", i); + value += XmlRpc.VALUE.replace("${DATA}", this.marshal(data[i])); + member += XmlRpc.MEMBER.replace("${DATA}", value); + } + xml = XmlRpc.STRUCT.replace("${DATA}", member); + break; + case "array": + value = ""; + for (i = 0; i < data.length; i++) { + value += XmlRpc.VALUE.replace("${DATA}", this.marshal(data[i])); + } + xml = XmlRpc.ARRAY.replace("${DATA}", value); + break; + case "dateTime.iso8601": + xml = scalar_type.replace("${DATA}", data.toIso8601()); + break; + case "boolean": + xml = scalar_type.replace("${DATA}", (data == true) ? 1 : 0); + break; + case "base64": + xml = scalar_type.replace("${DATA}", data.encode()); + break; + default: + xml = scalar_type.replace("${DATA}", data); + break; + } + return xml; +}; + +/** + * XmlRpcResponse. + * + * @param xml + * Response XML document. + */ +function XmlRpcResponse(xml) { + this.xmlData = xml; +}; + +/** + *

+ * Indicate if response is a fault. + *

+ * + * @return Boolean flag indicating fault status. + */ +XmlRpcResponse.prototype.isFault = function() { + return this.faultValue; +}; + +/** + *

+ * Parse XML response to JavaScript. + *

+ * + * @return JavaScript object parsed from XML-RPC document. + */ +XmlRpcResponse.prototype.parseXML = function() { + // Vars + var i, nodesLength; + + nodesLength = this.xmlData.childNodes.length; + this.faultValue = undefined; + this.currentIsName = false; + this.propertyName = ""; + this.params = []; + for (i = 0; i < nodesLength; i++) { + this.unmarshal(this.xmlData.childNodes[i], 0); + } + return this.params[0]; +}; + +/** + *

+ * Unmarshal response parameters. + *

+ * + * @param node + * Current document node under processing. + * @param parent + * Current node' parent node. + */ +XmlRpcResponse.prototype.unmarshal = function(node, parent) { + // Vars + var obj, tag, i, nodesLength; + + if (node.nodeType == 1) { + obj = null; + tag = node.tagName.toLowerCase(); + switch (tag) { + case "fault": + this.faultValue = true; + break; + case "name": + this.currentIsName = true; + break; + default: + obj = XmlRpc.getTagData(tag); + break; + } + if (obj != null) { + this.params.push(obj); + if (tag == "struct" || tag == "array") { + if (this.params.length > 1) { + switch (XmlRpc.getDataTag(this.params[parent])) { + case "struct": + this.params[parent][this.propertyName] = this.params[this.params.length - 1]; + break; + case "array": + this.params[parent].push(this.params[this.params.length - 1]); + break; + } + } + parent = this.params.length - 1; + } + } + nodesLength = node.childNodes.length; + for (i = 0; i < nodesLength; i++) { + this.unmarshal(node.childNodes[i], parent); + } + } + if ((node.nodeType == 3) && (/[^\t\n\r ]/.test(node.nodeValue))) { + if (this.currentIsName == true) { + this.propertyName = node.nodeValue; + this.currentIsName = false; + } else { + switch (XmlRpc.getDataTag(this.params[this.params.length - 1])) { + case "dateTime.iso8601": + this.params[this.params.length - 1] = Date.fromIso8601(node.nodeValue); + break; + case "boolean": + this.params[this.params.length - 1] = (node.nodeValue == "1") ? true : false; + break; + case "int": + case "double": + this.params[this.params.length - 1] = new Number(node.nodeValue); + break; + case "string": + this.params[this.params.length - 1] = new String(node.nodeValue); + break; + case "base64": + this.params[this.params.length - 1] = new Base64(node.nodeValue); + break; + } + if (this.params.length > 1) { + switch (XmlRpc.getDataTag(this.params[parent])) { + case "struct": + this.params[parent][this.propertyName] = this.params[this.params.length - 1]; + break; + case "array": + this.params[parent].push(this.params[this.params.length - 1]); + break; + } + } + } + } +}; + +/** + * Builder helper for W3C / ActiveX objects + */ +function Builder() { + +}; + +/** + *

+ * Build a valid XMLHttpRequest object + *

+ * + * @param cors + * Define if returned implementation must provide CORS (Cross-Origin Resource Sharing) support. + * @return XMLHttpRequest object. + */ +Builder.buildXHR = function(cors) { + if(cors) { + return (typeof XDomainRequest != "undefined") ? new XDomainRequest() : new XMLHttpRequest(); + } else { + return (typeof XMLHttpRequest != "undefined") ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); + } +}; + +/** + *

+ * Build a valid XML document from string markup. + *

+ * + * @param xml + * Document markup. + * @return XMLDocument object. + */ +Builder.buildDOM = function(xml) { + // Vars + var parser, names, i; + + if (typeof DOMParser != "undefined") { + parser = new DOMParser(); + return parser.parseFromString(xml, "text/xml"); + } else { + names = [ "Microsoft.XMLDOM", "MSXML2.DOMDocument", "MSXML.DOMDocument" ]; + for (i = 0; i < names.length; i++) { + try { + parser = new ActiveXObject(names[i]); + parser.loadXML(xml); + return parser; + } catch (e) { + /* Ignore */ + } + } + } + return null; +}; + +/** + * Date extensions. + */ + +/** + *

+ * Convert a GMT date to ISO8601. + *

+ * + * @return String with an ISO8601 date. + */ +Date.prototype.toIso8601 = function() { + // Vars + var year = this.getYear(), + month = this.getMonth() + 1, + day = this.getDate(), + time = this.toTimeString().substr(0, 8); + + // Normalization + if (year < 1900) { + year += 1900; + } + if (month < 10) { + month = "0" + month; + } + if (day < 10) { + day = "0" + day; + } + + return year + month + day + "T" + time; +}; + +/** + *

+ * Convert ISO8601 date to GMT. + *

+ * + * @param value + * ISO8601 date. + * @return GMT date. + */ +Date.fromIso8601 = function(value) { + // Vars + var year = value.substr(0, 4), + month = value.substr(4, 2), + day = value.substr(6, 2), + hour = value.substr(9, 2), + minute = value.substr(12, 2), + sec = value.substr(15, 2); + + return new Date(year, month - 1, day, hour, minute, sec, 0); +}; + +/** + * Base64 implementation. + */ +function Base64(value) { + this.bytes = value; +}; + +/** + *

+ * Base64 characters map. + *

+ */ +Base64.CHAR_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +/** + *

+ * Encode the object bytes using base64 algorithm. + *

+ * + * @return Encoded string. + */ +Base64.prototype.encode = function() { + if (typeof btoa == "function") { + return btoa(this.bytes); + } else { + // Vars + var _byte = [], + _char = [], + _result = [], + j = 0, i = 0; + + for (i = 0; i < this.bytes.length; i += 3) { + _byte[0] = this.bytes.charCodeAt(i); + _byte[1] = this.bytes.charCodeAt(i + 1); + _byte[2] = this.bytes.charCodeAt(i + 2); + _char[0] = _byte[0] >> 2; + _char[1] = ((_byte[0] & 3) << 4) | (_byte[1] >> 4); + _char[2] = ((_byte[1] & 15) << 2) | (_byte[2] >> 6); + _char[3] = _byte[2] & 63; + if (isNaN(_byte[1])) { + _char[2] = _char[3] = 64; + } else if (isNaN(_byte[2])) { + _char[3] = 64; + } + _result[j++] = Base64.CHAR_MAP.charAt(_char[0]) + + Base64.CHAR_MAP.charAt(_char[1]) + + Base64.CHAR_MAP.charAt(_char[2]) + + Base64.CHAR_MAP.charAt(_char[3]); + } + return _result.join(""); + } +}; + +/** + *

+ * Decode the object bytes using base64 algorithm. + *

+ * + * @return Decoded string. + */ +Base64.prototype.decode = function() { + if (typeof atob == "function") { + return atob(this.bytes); + } else { + // Vars + var _byte = [], + _char = [], + _result = [], + j = 0, i = 0; + + while ((this.bytes.length % 4) != 0) { + this.bytes += "="; + } + for (i = 0; i < this.bytes.length; i += 4) { + _char[0] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(i)); + _char[1] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(i + 1)); + _char[2] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(i + 2)); + _char[3] = Base64.CHAR_MAP.indexOf(this.bytes.charAt(i + 3)); + _byte[0] = (_char[0] << 2) | (_char[1] >> 4); + _byte[1] = ((_char[1] & 15) << 4) | (_char[2] >> 2); + _byte[2] = ((_char[2] & 3) << 6) | _char[3]; + _result[j++] = String.fromCharCode(_byte[0]); + if (_char[2] != 64) { + _result[j++] = String.fromCharCode(_byte[1]); + } + if (_char[3] != 64) { + _result[j++] = String.fromCharCode(_byte[2]); + } + } + return _result.join(""); + } +};