Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
289 lines (275 sloc) 13.3 KB
@inherits ServiceStack.Razor.ViewPage
@{
ViewBag.Title = "ServiceStack Chat (IE)";
var channels = Request.QueryString["channels"] ?? "home";
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewBag.Title</title>
<link href="/img/favicon.png" rel="icon"> <link href="/default.css" rel="stylesheet" />
<script src="/ie/jquery-1.11.2.min.js"></script>
<script src="/ie/console.min.js"></script>
<script src="/ie/es5-shim.min.js"></script>
<script src="/ie/eventsource.min.js"></script>
<script src="/js/ss-utils.js"></script>
<style>
body {
background-image: url(@AppSettings.Get("background","/img/bg.jpg"))
}
</style>
</head>
<body>
<div id="top">
<a href="https://github.com/ServiceStackApps/LiveDemos">
<img src="https://raw.githubusercontent.com/ServiceStack/Assets/master/img/artwork/logo-32-inverted.png" style="height:28px; padding: 10px 0 0 0"/>
</a>
<div id="social">
<div id="welcome"></div>
@if (!IsAuthenticated)
{
<a href="/auth/twitter" class="twitter"></a>
<a href="/auth/facebook" class="facebook"></a>
<a href="/auth/github" class="github"></a>
}
</div>
<ul id="channels" style="margin: 0 0 0 30px">
<li style="background: none; padding: 0 0 0 5px;">
<button onclick="var chan = prompt('Join another Channel:','ChannelName'); if (chan) location.href = '?channels=@channels,'+chan.replace(/\s+/g,'');">+</button>
</li>
<li style="background: none;padding: 0;">
<span style="font-size: 13px; color: #ccc" onclick="$('#logs [data-channel]').html('')">clear</span>
</li>
</ul>
</div>
<div id="announce"></div>
<div id="tv"></div>
<div id="right">
<div id="users"></div>
<div id="examples" data-click="sendCommand">
<span style="position:absolute; top: 2px; right:7px" data-click="toggleExamples">hide</span>
<h4><a href="https://github.com/ServiceStackApps/Chat#global-event-handlers">Example Commands</a></h4>
<div>/cmd.announce This is your captain speaking ...</div>
<div>/cmd.toggle$#channels</div>
<h4><a href="https://github.com/ServiceStackApps/Chat#modifying-css-via-jquery">CSS</a></h4>
<div>/css.background-image url(http://bit.ly/1oQqhtm)</div>
<div>/css.background-image url(http://bit.ly/1yIJOBH)</div>
<div>@@me /css.background #eceff1</div>
<div>/css.background$#top #673ab7</div>
<div>/css.background$#bottom #0091ea</div>
<div>/css.background$#right #fffde7</div>
<div>/css.color$#welcome #ff0</div>
<div>/css.visibility$img,a hidden</div>
<div>/css.visibility$img,a visible</div>
<h4><a href="https://github.com/ServiceStackApps/Chat#receivers">Receivers</a></h4>
<div>/tv.watch http://youtu.be/518XP8prwZo</div>
<div>/tv.watch https://servicestack.net/img/logo-220.png</div>
<div>@@me /tv.off</div>
<div>/document.title New Window Title</div>
<div>/cmd.addReceiver window</div>
<div>/window.location http://google.com</div>
<div>/cmd.removeReceiver window</div>
<h4><a href="https://github.com/ServiceStackApps/Chat#jquery-events">Triggers</a></h4>
<div>/trigger.customEvent arg</div>
<div>/cmd.stopListening</div>
<div data-click="startListening">startListening</div>
</div>
</div>
<div id="logs"></div>
<div id="bottom">
<input type="text" id="txtMsg" onfocus="this.value = this.value;" />
<button id="btnSend">Send</button>
</div>
<script>
var channels = "@channels".split(',');
$("#channels").prepend($.map(channels, function (c) { return '<li data-click="changeChannel:' + c + '" data-channel="' + c + '">' + c + '</li>'; }));
$("#logs").append($.map(channels, function (c) { return '<div class="channel" data-channel="' + c + '"></div>'; }));
var selectedChannel = function () { return $("#channels .selected").html(); };
var source = new EventSource('/event-stream?channels=@channels&ss-id=@(base.GetSession().Id)&t=' + new Date().getTime()); //disable cache
source.addEventListener('error', function (e) {
console.log(e);
addEntry({ msg: "ERROR!", cls: "error" });
}, false);
var $txtMsg = $("#txtMsg").focus(), usersMap = {}, activeSub = null;
function refreshUsers() {
$.getJSON("/event-subscribers?channels=@channels", function (users) {
usersMap = {};
$.map(users, function (user) { usersMap[user.userId] = user; });
$("#users").html($.map(channels, function (c) {
var usersInChannel = $.grep($.map(usersMap,function(u){return u;}), function (u) { return u.channels.split(',').indexOf(c) >= 0; });
return '<div class="channel" data-channel="' + c + '">'
+ $.map(usersInChannel, function (u) { return createUser(u); }).join('')
+ '</div>';
}).join(''));
$.ss.handlers["changeChannel"](selectedChannel());
});
}
$.ss.eventReceivers = { "document": document };
$(source).handleServerEvents({
handlers: {
onConnect: function (u) {
console.log(u);
activeSub = u;
$("#welcome").html('<span>Welcome, ' + u.displayName + '</span>' + '<img src="' + u.profileUrl + '"/>');
$("#logs .channel").html("");
addEntry({ msg: "CONNECTED!", cls: "open" });
$.getJSON("/chathistory?channels=@channels", function (r) {
$.map(r.results, $.ss.handlers["chat"]);
});
refreshUsers();
},
onHeartbeat: function (msg, e) { if (console) console.log("onHeartbeat", msg, e); },
onJoin: refreshUsers,
onLeave: refreshUsers,
chat: function (m, e) {
addEntry({ id: m.id, userId: m.fromUserId, userName: m.fromName, msg: m.message, cls: m.private ? ' private' : '', channel: m.channel || e.channel });
},
stopListening: function () { $.ss.eventSource.close(); }
},
receivers: {
tv: {
watch: function (id) {
if (id.indexOf('youtube.com') >= 0) {
var qs = $.ss.queryString(id);
$("#tv").html(templates.youtube.replace("{id}", qs["v"])).show();
}
else if (id.indexOf('youtu.be') >= 0) {
var v = $.ss.splitOnLast(id, '/')[1];
$("#tv").html(templates.youtube.replace("{id}", v)).show();
} else {
$("#tv").html(templates.generic.replace("{id}", id)).show();
}
},
off: function () {
$("#tv").hide().html("");
}
}
}
});
var templates = {
youtube: '<iframe width="640" height="360" src="//www.youtube.com/embed/{id}?autoplay=1" frameborder="0" allowfullscreen></iframe>',
generic: '<iframe width="640" height="360" src="{id}" frameborder="0"></iframe>'
};
$(document).bindHandlers({
announce: function (msg) {
$("#announce").html(msg).fadeIn('fast');
setTimeout(function () { $("#announce").fadeOut('slow'); }, 2000);
},
toggle: function () {
$(this).toggle();
},
sendCommand: function () {
$("#txtMsg").val($(this).html()).focus();
},
privateMsg: function () {
$txtMsg.val("@@" + this.innerHTML + " ").focus();
},
removeReceiver: function (name) {
delete $.ss.eventReceivers[name];
},
addReceiver: function (name) {
$.ss.eventReceivers[name] = window[name];
},
toggleExamples: function () {
var willHide = this.innerHTML == "hide";
$(this).html(willHide ? "show" : "hide").parent().css({ height: willHide ? '25px' : 'auto' });
},
changeChannel: function (channel) {
$("[data-channel]").removeClass('selected');
$("[data-channel=" + channel + "]").addClass('selected');
$("#txtMsg").focus();
},
startListening: function() { $.ss.reconnectServerEvents(); }
}).on('customEvent', function (e, msg, msgEvent) {
addEntry({ msg: "[event " + e.type + " message: " + msg + "]", cls: "event", channel: msgEvent.channel });
});
$.ss.handlers["changeChannel"](channels[channels.length-1]);
function addEntry(o) {
console.log("addEntry", o);
var id = "u_" + o.userId + "";
var skipUser = $("#log .msg:last-child b").hasClass(id);
var inactive = $(createUser($.extend(o, { displayName: o.userName }))).html();
var user = o.userId && !skipUser ? "<b class='user " + id + "'>" + ($("#users ." + id).html() || inactive) + "</b>" : "<b class=" + id + ">&nbsp;</b>";
var time = "<i>" + $.ss.tfmt12(new Date()) + "</i>";
var highlight = o.msg.indexOf(activeSub.displayName.replace(" ", "")) >= 0 ? "highlight " : "";
var filter = o.channel ? "=" + o.channel : "";
$("#logs [data-channel" + filter + "]").append("<div id='m_" + (o.id || "0") + "' class='msg " + highlight + o.cls + "'>" +
user + time + "<div>" + o.msg + "</div>" + "</div>").scrollTop(1E10);
}
function createUser(user) {
return "<div class='user u_" + user.userId + "'><img src='" + (user.profileUrl || "/img/no-profile64.png") + "'/><span data-id='" +
user.userId + "' data-click='privateMsg'>" + user.displayName + "</span></div>";
}
var msgHistory = [], historyIndex = -1;
function postMsg() {
var msg = $txtMsg.val(), parts, to = null;
msgHistory.push(msg);
if (msg[0] == "@@") {
parts = $.ss.splitOnFirst(msg, " ");
var toName = parts[0].substring(1);
if (toName == "me") {
to = activeSub.userId;
} else {
var matches = $.grep($("#users .user span"),
function (x) { return x.innerHTML.replace(" ", "").toLowerCase() === toName.toLowerCase(); });
to = matches.length > 0 ? matches[0].getAttribute("data-id") : null;
}
msg = parts[1];
}
if (!msg || !activeSub) return;
var onError = function (e) {
if (e.responseJSON && e.responseJSON.responseStatus)
$.ss.handlers["announce"](e.responseJSON.responseStatus.message);
};
var channel = selectedChannel();
if (msg[0] == "/") {
parts = $.ss.splitOnFirst(msg, " ");
$.post("/channels/" + channel + "/raw", { from: activeSub.id, toUserId: to, message: parts[1], selector: parts[0].substring(1) }, function () { }).fail(onError);
} else {
$.post("/channels/" + channel + "/chat", { from: activeSub.id, toUserId: to, message: msg, selector: "cmd.chat" }, function () { }).fail(onError);
}
$txtMsg.val("");
}
$("#btnSend").click(postMsg);
$txtMsg.keydown(function (e) {
var keycode = (e.keyCode ? e.keyCode : e.which);
if ($.ss.getSelection()) {
if (keycode == '9' || keycode == '13' || keycode == '32' || keycode == '39') {
this.value += " ";
if (this.setSelectionRange) this.setSelectionRange(this.value.length, this.value.length);
return false;
}
}
if (keycode == '13') { //enter
postMsg();
return false;
} else if (keycode == '38') { //up arrow
historyIndex = Math.min(++historyIndex, msgHistory.length);
$txtMsg.val(msgHistory[msgHistory.length - 1 - historyIndex]);
return false;
}
else if (keycode == '40') { //down arrow
historyIndex = Math.max(--historyIndex, -1);
$txtMsg.val(msgHistory[msgHistory.length - 1 - historyIndex]);
} else {
historyIndex = -1;
}
}).keyup(function (e) {
if (!$.ss.getSelection() && this.value[0] == '@@' && this.value.indexOf(' ') < 0) {
var partialVal = this.value.substring(1);
var matchingNames = $.grep($("#users .user span")
.map(function () { return this.innerHTML.replace(" ", ""); }), function (x) {
return x.substring(0, partialVal.length).toLowerCase() === partialVal.toLowerCase()
&& x.toLowerCase() != activeSub.displayName.toLowerCase();
});
if (matchingNames.length > 0) {
this.value += matchingNames[0].substring(partialVal.length);
if (this.setSelectionRange) this.setSelectionRange(partialVal.length + 1, this.value.length);
return false;
}
}
});
</script>
</body>
</html>