diff --git a/examples/browser/browserTest.js b/examples/browser/browserTest.js
new file mode 100644
index 000000000..418ac0614
--- /dev/null
+++ b/examples/browser/browserTest.js
@@ -0,0 +1,10 @@
+"use strict";
+console.log("Loading browser sdk");
+
+matrixcs.request(request);
+
+var client = matrixcs.createClient("http://matrix.org");
+client.publicRooms(function (err, data) {
+ console.log("data %s", JSON.stringify(data));
+ console.error("err %s", JSON.stringify(err));
+});
diff --git a/examples/browser/index.html b/examples/browser/index.html
new file mode 100644
index 000000000..5ca6e221e
--- /dev/null
+++ b/examples/browser/index.html
@@ -0,0 +1,13 @@
+
+
+Test
+
+
+
+
+
+ Sanity Testing (check the console) : This example is here to make sure that
+ the SDK works inside a browser. It simply does a GET /publicRooms on
+ matrix.org
+
+
diff --git a/examples/browser/lib/browser-request.js b/examples/browser/lib/browser-request.js
new file mode 100644
index 000000000..ed0cc5d4c
--- /dev/null
+++ b/examples/browser/lib/browser-request.js
@@ -0,0 +1,498 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.request = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 299) && body.error) {
+ // The body is a Couch JSON object indicating the error.
+ er = new Error('CouchDB error: ' + (body.error.reason || body.error.error))
+ for (var key in body)
+ er[key] = body[key]
+ return callback(er, resp, body);
+ }
+
+ return callback(er, resp, body);
+ }
+}
+
+//
+// Utility
+//
+
+function noop() {}
+
+function getLogger() {
+ var logger = {}
+ , levels = ['trace', 'debug', 'info', 'warn', 'error']
+ , level, i
+
+ for(i = 0; i < levels.length; i++) {
+ level = levels[i]
+
+ logger[level] = noop
+ if(typeof console !== 'undefined' && console && console[level])
+ logger[level] = formatted(console, level)
+ }
+
+ return logger
+}
+
+function formatted(obj, method) {
+ return formatted_logger
+
+ function formatted_logger(str, context) {
+ if(typeof context === 'object')
+ str += ' ' + JSON.stringify(context)
+
+ return obj[method].call(obj, str)
+ }
+}
+
+// Return whether a URL is a cross-domain request.
+function is_crossDomain(url) {
+ var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/
+
+ // jQuery #8138, IE may throw an exception when accessing
+ // a field from window.location if document.domain has been set
+ var ajaxLocation
+ try { ajaxLocation = location.href }
+ catch (e) {
+ // Use the href attribute of an A element since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+ }
+
+ var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || []
+ , parts = rurl.exec(url.toLowerCase() )
+
+ var result = !!(
+ parts &&
+ ( parts[1] != ajaxLocParts[1]
+ || parts[2] != ajaxLocParts[2]
+ || (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443))
+ )
+ )
+
+ //console.debug('is_crossDomain('+url+') -> ' + result)
+ return result
+}
+
+// MIT License from http://phpjs.org/functions/base64_encode:358
+function b64_enc (data) {
+ // Encodes string using MIME base64 algorithm
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = [];
+
+ if (!data) {
+ return data;
+ }
+
+ // assume utf8 data
+ // data = this.utf8_encode(data+'');
+
+ do { // pack three octets into four hexets
+ o1 = data.charCodeAt(i++);
+ o2 = data.charCodeAt(i++);
+ o3 = data.charCodeAt(i++);
+
+ bits = o1<<16 | o2<<8 | o3;
+
+ h1 = bits>>18 & 0x3f;
+ h2 = bits>>12 & 0x3f;
+ h3 = bits>>6 & 0x3f;
+ h4 = bits & 0x3f;
+
+ // use hexets to index into b64, and append result to encoded string
+ tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
+ } while (i < data.length);
+
+ enc = tmp_arr.join('');
+
+ switch (data.length % 3) {
+ case 1:
+ enc = enc.slice(0, -2) + '==';
+ break;
+ case 2:
+ enc = enc.slice(0, -1) + '=';
+ break;
+ }
+
+ return enc;
+}
+ return request;
+//UMD FOOTER START
+}));
+//UMD FOOTER END
+
+},{}]},{},[1])(1)
+});
\ No newline at end of file
diff --git a/examples/browser/lib/matrix.js b/examples/browser/lib/matrix.js
new file mode 100644
index 000000000..d04a39593
--- /dev/null
+++ b/examples/browser/lib/matrix.js
@@ -0,0 +1,605 @@
+"use strict";
+
+/*
+TODO:
+- CS: complete register function (doing stages)
+- Internal: rate limiting
+- Identity server: linkEmail, authEmail, bindEmail, lookup3pid
+- uploadContent (?)
+*/
+
+// wrap in a closure for browsers
+var init = function(exports){
+ // expose the underlying request object so different environments can use
+ // different request libs (e.g. request or browser-request)
+ var request;
+ exports.request = function(r) {
+ request = r;
+ };
+
+ // entry point
+ function MatrixClient(credentials) {
+ if (typeof credentials === "string") {
+ credentials = {
+ "baseUrl": credentials
+ };
+ }
+ var requiredKeys = [
+ "baseUrl"
+ ];
+ for (var i=0; i 0) {
+ // these are thumbnailing params so they probably want the
+ // thumbnailing API...
+ prefix = "/_matrix/media/v1/thumbnail/";
+ }
+
+ var fragmentOffset = serverAndMediaId.indexOf("#"),
+ fragment = "";
+ if (fragmentOffset >= 0) {
+ fragment = serverAndMediaId.substr(fragmentOffset);
+ serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
+ }
+ return this.credentials.baseUrl + prefix + serverAndMediaId +
+ (Object.keys(params).length === 0 ? "" :
+ ("?" + encodeParams(params))) + fragment;
+ },
+
+ getIdenticonUri: function(identiconString, width, height) {
+ if (!identiconString) {
+ return;
+ }
+ if (!width) { width = 96; }
+ if (!height) { height = 96; }
+ var params = {
+ width: width,
+ height: height
+ };
+
+ var path = encodeUri("/_matrix/media/v1/identicon/$ident", {
+ $ident: identiconString
+ });
+ return this.credentials.baseUrl + path +
+ (Object.keys(params).length === 0 ? "" :
+ ("?" + encodeParams(params)));
+ },
+
+ /**
+ * Get the content repository url with query parameters.
+ * @returns An object with a 'base', 'path' and 'params' for base URL,
+ * path and query parameters respectively.
+ */
+ getContentUri: function() {
+ var params = {
+ access_token: this.credentials.accessToken
+ };
+ return {
+ base: this.credentials.baseUrl,
+ path: "/_matrix/media/v1/upload",
+ params: params
+ };
+ },
+
+ // Internals
+ // =========
+
+ _doAuthedRequest: function(callback, method, path, params, data) {
+ if (!params) { params = {}; }
+ params.access_token = this.credentials.accessToken;
+ return this._doRequest(callback, method, path, params, data);
+ },
+
+ _doRequest: function(callback, method, path, params, data) {
+ var fullUri = this.credentials.baseUrl + CLIENT_PREFIX + path;
+ if (!params) { params = {}; }
+ return this._request(callback, method, fullUri, params, data);
+ },
+
+ _request: function(callback, method, uri, params, data) {
+ console.log(" => %s", uri);
+ console.log(" %s", JSON.stringify(data));
+ if (callback !== undefined && !isFunction(callback)) {
+ throw Error("Expected callback to be a function");
+ }
+
+ return request(
+ {
+ uri: uri,
+ method: method,
+ withCredentials: false,
+ qs: params,
+ body: data,
+ json: true,
+ headers: HEADERS
+ },
+ requestCallback(callback)
+ );
+ }
+ };
+
+ var encodeUri = function(pathTemplate, variables) {
+ for (var key in variables) {
+ if (!variables.hasOwnProperty(key)) { continue; }
+ pathTemplate = pathTemplate.replace(
+ key, encodeURIComponent(variables[key])
+ );
+ }
+ return pathTemplate;
+ };
+
+ // avoiding deps on jquery and co
+ var encodeParams = function(params) {
+ var qs = "";
+ for (var key in params) {
+ if (!params.hasOwnProperty(key)) { continue; }
+ qs += "&" + encodeURIComponent(key) + "=" +
+ encodeURIComponent(params[key]);
+ }
+ return qs.substring(1);
+ };
+
+ var requestCallback = function(userDefinedCallback) {
+ if (!userDefinedCallback) {
+ return undefined;
+ }
+ return function(err, response, body) {
+ if (err) {
+ return userDefinedCallback(err);
+ }
+ if (response.statusCode >= 400) {
+ return userDefinedCallback(body);
+ }
+ else {
+ userDefinedCallback(null, body);
+ }
+ };
+ };
+
+ var isFunction = function(value) {
+ return Object.prototype.toString.call(value) == "[object Function]";
+ };
+};
+
+if (typeof exports === 'undefined') {
+ init(this['matrixcs']={}); // this assigns to "window" on browsers
+}
+else {
+ init(exports);
+}
\ No newline at end of file
diff --git a/examples/node/app.js b/examples/node/app.js
new file mode 100644
index 000000000..7b1d7e14d
--- /dev/null
+++ b/examples/node/app.js
@@ -0,0 +1,11 @@
+"use strict";
+console.log("Loading node sdk");
+
+var matrix = require("./lib/matrix");
+matrix.request(require("request"));
+
+var client = matrix.createClient("http://matrix.org");
+client.publicRooms(function (err, data) {
+ console.log("data %s", JSON.stringify(data));
+ console.error("err %s", JSON.stringify(err));
+});
diff --git a/examples/node/lib/matrix.js b/examples/node/lib/matrix.js
new file mode 100644
index 000000000..d04a39593
--- /dev/null
+++ b/examples/node/lib/matrix.js
@@ -0,0 +1,605 @@
+"use strict";
+
+/*
+TODO:
+- CS: complete register function (doing stages)
+- Internal: rate limiting
+- Identity server: linkEmail, authEmail, bindEmail, lookup3pid
+- uploadContent (?)
+*/
+
+// wrap in a closure for browsers
+var init = function(exports){
+ // expose the underlying request object so different environments can use
+ // different request libs (e.g. request or browser-request)
+ var request;
+ exports.request = function(r) {
+ request = r;
+ };
+
+ // entry point
+ function MatrixClient(credentials) {
+ if (typeof credentials === "string") {
+ credentials = {
+ "baseUrl": credentials
+ };
+ }
+ var requiredKeys = [
+ "baseUrl"
+ ];
+ for (var i=0; i 0) {
+ // these are thumbnailing params so they probably want the
+ // thumbnailing API...
+ prefix = "/_matrix/media/v1/thumbnail/";
+ }
+
+ var fragmentOffset = serverAndMediaId.indexOf("#"),
+ fragment = "";
+ if (fragmentOffset >= 0) {
+ fragment = serverAndMediaId.substr(fragmentOffset);
+ serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
+ }
+ return this.credentials.baseUrl + prefix + serverAndMediaId +
+ (Object.keys(params).length === 0 ? "" :
+ ("?" + encodeParams(params))) + fragment;
+ },
+
+ getIdenticonUri: function(identiconString, width, height) {
+ if (!identiconString) {
+ return;
+ }
+ if (!width) { width = 96; }
+ if (!height) { height = 96; }
+ var params = {
+ width: width,
+ height: height
+ };
+
+ var path = encodeUri("/_matrix/media/v1/identicon/$ident", {
+ $ident: identiconString
+ });
+ return this.credentials.baseUrl + path +
+ (Object.keys(params).length === 0 ? "" :
+ ("?" + encodeParams(params)));
+ },
+
+ /**
+ * Get the content repository url with query parameters.
+ * @returns An object with a 'base', 'path' and 'params' for base URL,
+ * path and query parameters respectively.
+ */
+ getContentUri: function() {
+ var params = {
+ access_token: this.credentials.accessToken
+ };
+ return {
+ base: this.credentials.baseUrl,
+ path: "/_matrix/media/v1/upload",
+ params: params
+ };
+ },
+
+ // Internals
+ // =========
+
+ _doAuthedRequest: function(callback, method, path, params, data) {
+ if (!params) { params = {}; }
+ params.access_token = this.credentials.accessToken;
+ return this._doRequest(callback, method, path, params, data);
+ },
+
+ _doRequest: function(callback, method, path, params, data) {
+ var fullUri = this.credentials.baseUrl + CLIENT_PREFIX + path;
+ if (!params) { params = {}; }
+ return this._request(callback, method, fullUri, params, data);
+ },
+
+ _request: function(callback, method, uri, params, data) {
+ console.log(" => %s", uri);
+ console.log(" %s", JSON.stringify(data));
+ if (callback !== undefined && !isFunction(callback)) {
+ throw Error("Expected callback to be a function");
+ }
+
+ return request(
+ {
+ uri: uri,
+ method: method,
+ withCredentials: false,
+ qs: params,
+ body: data,
+ json: true,
+ headers: HEADERS
+ },
+ requestCallback(callback)
+ );
+ }
+ };
+
+ var encodeUri = function(pathTemplate, variables) {
+ for (var key in variables) {
+ if (!variables.hasOwnProperty(key)) { continue; }
+ pathTemplate = pathTemplate.replace(
+ key, encodeURIComponent(variables[key])
+ );
+ }
+ return pathTemplate;
+ };
+
+ // avoiding deps on jquery and co
+ var encodeParams = function(params) {
+ var qs = "";
+ for (var key in params) {
+ if (!params.hasOwnProperty(key)) { continue; }
+ qs += "&" + encodeURIComponent(key) + "=" +
+ encodeURIComponent(params[key]);
+ }
+ return qs.substring(1);
+ };
+
+ var requestCallback = function(userDefinedCallback) {
+ if (!userDefinedCallback) {
+ return undefined;
+ }
+ return function(err, response, body) {
+ if (err) {
+ return userDefinedCallback(err);
+ }
+ if (response.statusCode >= 400) {
+ return userDefinedCallback(body);
+ }
+ else {
+ userDefinedCallback(null, body);
+ }
+ };
+ };
+
+ var isFunction = function(value) {
+ return Object.prototype.toString.call(value) == "[object Function]";
+ };
+};
+
+if (typeof exports === 'undefined') {
+ init(this['matrixcs']={}); // this assigns to "window" on browsers
+}
+else {
+ init(exports);
+}
\ No newline at end of file