1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
input[type="text"] { width: 300px; }
.muted {color: #CCCCCC; font-size: 10px;}
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/centrifugal/centrifuge-js@master/dist/centrifuge.min.js"></script>
<script type="text/javascript">
// helper functions to work with escaping html.
const tagsToReplace = {'&': '&', '<': '<', '>': '>'};
function replaceTag(tag) {return tagsToReplace[tag] || tag;}
function safeTagsReplace(str) {return str.replace(/[&<>]/g, replaceTag);}
const channel = "chat:index";
// Manually keep last seen stream position.
let streamPosition = null;
window.addEventListener('load', function() {
const input = document.getElementById('input');
const container = document.getElementById('messages');
const centrifuge = new Centrifuge('ws://localhost:8000/connection/websocket');
centrifuge.on('connect', function(ctx){
drawText('Connected with client ID ' + ctx.client + ' over ' + ctx.transport + ' with data: ' + JSON.stringify(ctx.data));
input.removeAttribute("disabled");
});
centrifuge.on('disconnect', function(ctx){
drawText('Disconnected: ' + ctx.reason + (ctx.reconnect?", will try to reconnect":", won't try to reconnect"));
input.setAttribute("disabled", 'true');
});
const sub = centrifuge.subscribe(channel, handleMessage)
.on("unsubscribe", handleUnsubscribe)
.on("subscribe", handleSubscribe)
.on("error", handleSubscribeError);
centrifuge.connect();
async function restoreMissedPublications() {
let recovered = false;
while (true) {
if (recovered) {
break;
}
let resp;
try {
const limit = 10;
drawText("Since offset " + streamPosition.offset + " with limit " + limit)
resp = await sub.history({since: streamPosition, limit: limit});
} catch (e) {
return false;
}
const pubs = resp.publications;
if (pubs && pubs.length > 0) {
for (let i in pubs) {
if (!pubs.hasOwnProperty(i)) {
continue
}
handleMessage(pubs[i]);
if (pubs[i].offset === resp.offset) {
recovered = true;
break;
}
}
} else {
recovered = true;
break;
}
}
return recovered;
}
async function handleSubscribe(ctx) {
drawText('Subscribed on channel ' + ctx.channel + ' (resubscribed: ' + ctx.isResubscribe + ')');
if (streamPosition == null) {
streamPosition = ctx.streamPosition;
} else {
drawText("Start missed publications recovery")
const ok = await restoreMissedPublications();
if (!ok) {
drawText("Oops, seems like we can't recover");
centrifuge.disconnect();
return;
}
drawText("Successfully recovered")
}
}
function handleSubscribeError(err) {
drawText('Error subscribing on channel ' + err.channel + ': ' + err.message);
}
function handleMessage(message) {
if (message.offset !== streamPosition.offset + 1) {
return;
}
drawMessage(message)
streamPosition.offset = message.offset;
}
function drawMessage(message) {
let clientID;
if (message.info){
clientID = message.info.client;
} else {
clientID = null;
}
const inputText = message.data["input"].toString();
const text = safeTagsReplace(inputText) + ' <span class="muted">from ' + clientID + '</span>';
drawText(text);
}
function handleUnsubscribe(sub) {
drawText('Unsubscribed from channel ' + sub.channel);
}
function drawText(text) {
let e = document.createElement('li');
e.innerHTML = [(new Date()).toString(), ' ' + text].join(':');
container.insertBefore(e, container.firstChild);
}
document.getElementById('form').addEventListener('submit', function(event) {
event.preventDefault();
sub.publish({"input": input.value}).then(function() {
drawText("Successfully published to channel");
}, function(err) {
drawText("Publish error: " + JSON.stringify(err));
});
input.value = '';
});
});
</script>
</head>
<body>
<form id="form">
<label for="input"></label><input type="text" id="input" autocomplete="off" />
<input type="submit" id="submit" value="ยป">
</form>
<ul id="messages"></ul>
</body>
</html>
|