diff --git a/jsfiddles/example_app/demo.css b/jsfiddles/example_app/demo.css
new file mode 100644
index 0000000000..4c1e157cc8
--- /dev/null
+++ b/jsfiddles/example_app/demo.css
@@ -0,0 +1,43 @@
+.roomListDashboard, .roomContents, .sendMessageForm {
+ visibility: hidden;
+}
+
+.roomList {
+ background-color: #909090;
+}
+
+.messageWrapper {
+ background-color: #EEEEEE;
+ height: 400px;
+ overflow: scroll;
+}
+
+.membersWrapper {
+ background-color: #EEEEEE;
+ height: 200px;
+ width: 50%;
+ overflow: scroll;
+}
+
+.textEntry {
+ width: 100%
+}
+
+p {
+ font-family: monospace;
+}
+
+table
+{
+ border-spacing:5px;
+}
+
+th,td
+{
+ padding:5px;
+}
+
+.roomList tr:not(:first-child):hover {
+ background-color: orange;
+ cursor: pointer;
+}
diff --git a/jsfiddles/example_app/demo.html b/jsfiddles/example_app/demo.html
new file mode 100644
index 0000000000..0af946f6bc
--- /dev/null
+++ b/jsfiddles/example_app/demo.html
@@ -0,0 +1,56 @@
+<div class="signUp">
+ <p>Matrix example application: Requires a local home server running at http://localhost:8080</p>
+ <form class="registrationForm">
+ <p>No account? Register:</p>
+ <input type="text" id="userReg" placeholder="Username"></input>
+ <input type="password" id="passwordReg" placeholder="Password"></input>
+ <input type="button" class="register" value="Register"></input>
+ </form>
+ <form class="loginForm">
+ <p>Got an account? Login:</p>
+ <input type="text" id="userLogin" placeholder="Username"></input>
+ <input type="password" id="passwordLogin" placeholder="Password"></input>
+ <input type="button" class="login" value="Login"></input>
+ </form>
+</div>
+
+<div class="roomListDashboard">
+ <form class="createRoomForm">
+ <input type="text" id="roomAlias" placeholder="Room alias"></input>
+ <input type="button" class="createRoom" value="Create Room"></input>
+ </form>
+ <table id="rooms" class="roomList">
+ <tbody>
+ <tr>
+ <th>Room</th>
+ <th>My state</th>
+ <th>Latest message</th>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+<div class="roomContents">
+ <p id="roomName">Select a room</p>
+ <div class="messageWrapper">
+ <table id="messages">
+ <tbody>
+ </tbody>
+ </table>
+ </div>
+ <form class="sendMessageForm">
+ <input type="text" class="textEntry" id="body" placeholder="Enter text here..." onkeydown="javascript:if (event.keyCode == 13) document.getElementById('sendMsg').focus()"></input>
+ <input type="button" class="sendMessage" id="sendMsg" value="Send"></input>
+ </form>
+</div>
+
+<div>
+ <p>Member list:</p>
+ <div class="membersWrapper">
+ <table id="members">
+ <tbody>
+ </tbody>
+ </table>
+ </div>
+</div>
+
diff --git a/jsfiddles/example_app/demo.js b/jsfiddles/example_app/demo.js
new file mode 100644
index 0000000000..295597f0f7
--- /dev/null
+++ b/jsfiddles/example_app/demo.js
@@ -0,0 +1,303 @@
+var accountInfo = {};
+
+var eventStreamInfo = {
+ from: "END"
+};
+
+var roomInfo = [];
+var memberInfo = [];
+var viewingRoomId;
+
+// ************** Event Streaming **************
+var longpollEventStream = function() {
+ var url = "http://localhost:8080/matrix/client/api/v1/events?access_token=$token&from=$from";
+ url = url.replace("$token", accountInfo.access_token);
+ url = url.replace("$from", eventStreamInfo.from);
+
+ $.getJSON(url, function(data) {
+ eventStreamInfo.from = data.end;
+
+ var hasNewLatestMessage = false;
+ var updatedMemberList = false;
+ var i=0;
+ var j=0;
+ for (i=0; i<data.chunk.length; ++i) {
+ if (data.chunk[i].type === "m.room.message") {
+ console.log("Got new message: " + JSON.stringify(data.chunk[i]));
+ if (viewingRoomId === data.chunk[i].room_id) {
+ addMessage(data.chunk[i]);
+ }
+
+ for (j=0; j<roomInfo.length; ++j) {
+ if (roomInfo[j].room_id === data.chunk[i].room_id) {
+ roomInfo[j].latest_message = data.chunk[i].content.body;
+ hasNewLatestMessage = true;
+ }
+ }
+ }
+ else if (data.chunk[i].type === "m.room.member") {
+ if (viewingRoomId === data.chunk[i].room_id) {
+ console.log("Got new member: " + JSON.stringify(data.chunk[i]));
+ for (j=0; j<memberInfo.length; ++j) {
+ if (memberInfo[j].target_user_id === data.chunk[i].target_user_id) {
+ memberInfo[j] = data.chunk[i];
+ updatedMemberList = true;
+ break;
+ }
+ }
+ if (!updatedMemberList) {
+ memberInfo.push(data.chunk[i]);
+ updatedMemberList = true;
+ }
+ }
+ if (data.chunk[i].target_user_id === accountInfo.user_id) {
+ getCurrentRoomList(); // update our join/invite list
+ }
+ }
+ else {
+ console.log("Discarding: " + JSON.stringify(data.chunk[i]));
+ }
+ }
+
+ if (hasNewLatestMessage) {
+ setRooms(roomInfo);
+ }
+ if (updatedMemberList) {
+ $("#members").empty();
+ for (i=0; i<memberInfo.length; ++i) {
+ addMember(memberInfo[i]);
+ }
+ }
+ longpollEventStream();
+ }).fail(function(err) {
+ setTimeout(longpollEventStream, 5000);
+ });
+};
+
+// ************** Registration and Login **************
+var onLoggedIn = function(data) {
+ accountInfo = data;
+ longpollEventStream();
+ getCurrentRoomList();
+ $(".roomListDashboard").css({visibility: "visible"});
+ $(".roomContents").css({visibility: "visible"});
+ $(".signUp").css({display: "none"});
+};
+
+$('.login').live('click', function() {
+ var user = $("#userLogin").val();
+ var password = $("#passwordLogin").val();
+ $.ajax({
+ url: "http://localhost:8080/matrix/client/api/v1/login",
+ type: "POST",
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
+ dataType: "json",
+ success: function(data) {
+ onLoggedIn(data);
+ },
+ error: function(err) {
+ alert("Unable to login: is the home server running?");
+ }
+ });
+});
+
+$('.register').live('click', function() {
+ var user = $("#userReg").val();
+ var password = $("#passwordReg").val();
+ $.ajax({
+ url: "http://localhost:8080/matrix/client/api/v1/register",
+ type: "POST",
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify({ user_id: user, password: password }),
+ dataType: "json",
+ success: function(data) {
+ onLoggedIn(data);
+ },
+ error: function(err) {
+ var msg = "Is the home server running?";
+ var errJson = $.parseJSON(err.responseText);
+ if (errJson !== null) {
+ msg = errJson.error;
+ }
+ alert("Unable to register: "+msg);
+ }
+ });
+});
+
+// ************** Creating a room ******************
+$('.createRoom').live('click', function() {
+ var roomAlias = $("#roomAlias").val();
+ var data = {};
+ if (roomAlias.length > 0) {
+ data.room_alias_name = roomAlias;
+ }
+ $.ajax({
+ url: "http://localhost:8080/matrix/client/api/v1/rooms?access_token="+accountInfo.access_token,
+ type: "POST",
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify(data),
+ dataType: "json",
+ success: function(response) {
+ $("#roomAlias").val("");
+ response.membership = "join"; // you are automatically joined into every room you make.
+ response.latest_message = "";
+
+ roomInfo.push(response);
+ setRooms(roomInfo);
+ },
+ error: function(err) {
+ alert(JSON.stringify($.parseJSON(err.responseText)));
+ }
+ });
+});
+
+// ************** Getting current state **************
+var getCurrentRoomList = function() {
+ var url = "http://localhost:8080/matrix/client/api/v1/im/sync?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=1";
+ $.getJSON(url, function(data) {
+ for (var i=0; i<data.length; ++i) {
+ if ("messages" in data[i]) {
+ data[i].latest_message = data[i].messages.chunk[0].content.body;
+ }
+ }
+ roomInfo = data;
+ setRooms(roomInfo);
+ }).fail(function(err) {
+ alert(JSON.stringify($.parseJSON(err.responseText)));
+ });
+};
+
+var loadRoomContent = function(roomId) {
+ console.log("loadRoomContent " + roomId);
+ viewingRoomId = roomId;
+ $("#roomName").text("Room: "+roomId);
+ $(".sendMessageForm").css({visibility: "visible"});
+ getMessages(roomId);
+ getMemberList(roomId);
+};
+
+var getMessages = function(roomId) {
+ $("#messages").empty();
+ var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + roomId + "/messages/list?access_token=" + accountInfo.access_token + "&from=END&to=START&limit=10";
+ $.getJSON(url, function(data) {
+ for (var i=data.chunk.length-1; i>=0; --i) {
+ addMessage(data.chunk[i]);
+ }
+ });
+};
+
+var getMemberList = function(roomId) {
+ $("#members").empty();
+ memberInfo = [];
+ var url = "http://localhost:8080/matrix/client/api/v1/rooms/" + roomId + "/members/list?access_token=" + accountInfo.access_token;
+ $.getJSON(url, function(data) {
+ for (var i=0; i<data.chunk.length; ++i) {
+ memberInfo.push(data.chunk[i]);
+ addMember(data.chunk[i]);
+ }
+ });
+};
+
+// ************** Sending messages **************
+$('.sendMessage').live('click', function() {
+ if (viewingRoomId === undefined) {
+ alert("There is no room to send a message to!");
+ return;
+ }
+ var body = $("#body").val();
+ sendMessage(viewingRoomId, body);
+});
+
+var sendMessage = function(roomId, body) {
+ var msgId = $.now();
+
+ var url = "http://localhost:8080/matrix/client/api/v1/rooms/$roomid/messages/$user/$msgid?access_token=$token";
+ url = url.replace("$token", accountInfo.access_token);
+ url = url.replace("$roomid", encodeURIComponent(roomId));
+ url = url.replace("$user", encodeURIComponent(accountInfo.user_id));
+ url = url.replace("$msgid", msgId);
+
+ var data = {
+ msgtype: "m.text",
+ body: body
+ };
+
+ $.ajax({
+ url: url,
+ type: "PUT",
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify(data),
+ dataType: "json",
+ success: function(data) {
+ $("#body").val("");
+ },
+ error: function(err) {
+ alert(JSON.stringify($.parseJSON(err.responseText)));
+ }
+ });
+};
+
+// ************** Navigation and DOM manipulation **************
+var setRooms = function(roomList) {
+ // wipe existing entries
+ $("#rooms").find("tr:gt(0)").remove();
+
+ var rows = "";
+ for (var i=0; i<roomList.length; ++i) {
+ row = "<tr>" +
+ "<td>"+roomList[i].room_id+"</td>" +
+ "<td>"+roomList[i].membership+"</td>" +
+ "<td>"+roomList[i].latest_message+"</td>" +
+ "</tr>";
+ rows += row;
+ }
+
+ $("#rooms").append(rows);
+
+ $('#rooms').find("tr").click(function(){
+ var roomId = $(this).find('td:eq(0)').text();
+ var membership = $(this).find('td:eq(1)').text();
+ if (membership !== "join") {
+ console.log("Joining room " + roomId);
+ var url = "http://localhost:8080/matrix/client/api/v1/rooms/$roomid/members/$user/state?access_token=$token";
+ url = url.replace("$token", accountInfo.access_token);
+ url = url.replace("$roomid", encodeURIComponent(roomId));
+ url = url.replace("$user", encodeURIComponent(accountInfo.user_id));
+ $.ajax({
+ url: url,
+ type: "PUT",
+ contentType: "application/json; charset=utf-8",
+ data: JSON.stringify({membership: "join"}),
+ dataType: "json",
+ success: function(data) {
+ loadRoomContent(roomId);
+ getCurrentRoomList();
+ },
+ error: function(err) {
+ alert(JSON.stringify($.parseJSON(err.responseText)));
+ }
+ });
+ }
+ else {
+ loadRoomContent(roomId);
+ }
+ });
+};
+
+var addMessage = function(data) {
+ var row = "<tr>" +
+ "<td>"+data.user_id+"</td>" +
+ "<td>"+data.content.body+"</td>" +
+ "</tr>";
+ $("#messages").append(row);
+};
+
+var addMember = function(data) {
+ var row = "<tr>" +
+ "<td>"+data.target_user_id+"</td>" +
+ "<td>"+data.content.membership+"</td>" +
+ "</tr>";
+ $("#members").append(row);
+};
+
|