mirror of
https://github.com/ENSL/ensl_gathers.git
synced 2024-11-10 15:21:56 +00:00
Implemented basic chat
This commit is contained in:
parent
00339aefaf
commit
4a21328691
14 changed files with 722 additions and 84 deletions
|
@ -23,6 +23,7 @@ npm start
|
||||||
- Admin tools
|
- Admin tools
|
||||||
- Add a backend datastore to persist gather data and messages
|
- Add a backend datastore to persist gather data and messages
|
||||||
- Add sounds
|
- Add sounds
|
||||||
|
- Add WebRTC for internal voice comms
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var chatController = require(path.join(__dirname, "../lib/chat/controller"));
|
||||||
|
|
||||||
module.exports = function (io) {
|
module.exports = function (io) {
|
||||||
var root = io.of("/");
|
var root = io.of("/");
|
||||||
var authorised = io.of("/authorised");
|
var authorised = io.of("/authorised");
|
||||||
|
@ -11,7 +15,8 @@ module.exports = function (io) {
|
||||||
username: "Chris (" + socket.id.slice(0,5) + ")",
|
username: "Chris (" + socket.id.slice(0,5) + ")",
|
||||||
steamId: "11111111",
|
steamId: "11111111",
|
||||||
email: "cablanchard@gmail.com",
|
email: "cablanchard@gmail.com",
|
||||||
bans: []
|
bans: [],
|
||||||
|
avatar: "http://www.ensl.org/local/avatars/6359.jpg"
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -27,6 +32,9 @@ module.exports = function (io) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Activate chat controller on root namespace
|
||||||
|
chatController(root);
|
||||||
|
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
refreshGatherers();
|
refreshGatherers();
|
||||||
|
|
||||||
|
@ -37,7 +45,3 @@ module.exports = function (io) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// socket.on('my other event', function (data) {
|
|
||||||
// console.log(data);
|
|
||||||
// });
|
|
17
lib/chat/Author.js
Normal file
17
lib/chat/Author.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Method which caches a single instance for each author
|
||||||
|
|
||||||
|
var Authors = (function () {
|
||||||
|
var authors = {};
|
||||||
|
|
||||||
|
return function (author) {
|
||||||
|
if (authors[author.id]) {
|
||||||
|
return authors[author.id]
|
||||||
|
} else {
|
||||||
|
return authors[author.id] = author;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
module.exports = Authors;
|
33
lib/chat/Chatroom.js
Normal file
33
lib/chat/Chatroom.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Message = require("./message");
|
||||||
|
|
||||||
|
function Chatroom (o) {
|
||||||
|
if (!(this instanceof Chatroom)) {
|
||||||
|
return new Chatroom(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
Chatroom.prototype.createMessage = function (options, callback) {
|
||||||
|
var message = new Message({
|
||||||
|
author: options.author,
|
||||||
|
content: options.content
|
||||||
|
})
|
||||||
|
this.messages.push(message);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
return callback(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Chatroom.prototype.retrieveMessages = function (n) {
|
||||||
|
return this.messages
|
||||||
|
.slice(this.messages.length - n, this.messages.length)
|
||||||
|
.map(function (message) {
|
||||||
|
return message.toJson();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Chatroom;
|
22
lib/chat/Message.js
Normal file
22
lib/chat/Message.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Author = require("./Author");
|
||||||
|
|
||||||
|
function Message (o) {
|
||||||
|
this.author = Author(o.author);
|
||||||
|
this.content = o.content;
|
||||||
|
this.createdAt = new Date();
|
||||||
|
};
|
||||||
|
|
||||||
|
Message.prototype.toJson = function () {
|
||||||
|
return {
|
||||||
|
author: {
|
||||||
|
username: this.author.username,
|
||||||
|
avatar: this.author.avatar
|
||||||
|
},
|
||||||
|
content: this.content,
|
||||||
|
createdAt: this.createdAt
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Message;
|
39
lib/chat/controller.js
Normal file
39
lib/chat/controller.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chatroom Controller
|
||||||
|
*
|
||||||
|
* Server API
|
||||||
|
* message:new - New message needs to be displayed
|
||||||
|
* message:refresh - Reload all messages
|
||||||
|
*
|
||||||
|
* Client API
|
||||||
|
* message:new - New message has been created
|
||||||
|
* message:refresh - Retrieve most recent messages
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var chatroom = require("./chatroom")();
|
||||||
|
|
||||||
|
module.exports = function (namespace) {
|
||||||
|
|
||||||
|
var broadcastUpdate = function (message) {
|
||||||
|
namespace.emit("message:new", message.toJson());
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace.on('connection', function (socket) {
|
||||||
|
|
||||||
|
socket.on('message:new', function (data) {
|
||||||
|
chatroom.createMessage({
|
||||||
|
author: socket._user,
|
||||||
|
content: data.content
|
||||||
|
}, broadcastUpdate);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('message:refresh', function () {
|
||||||
|
socket.emit("message:refresh", {
|
||||||
|
chatHistory: chatroom.retrieveMessages(20)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,48 +1,191 @@
|
||||||
var GatherCounter = React.createClass({
|
var GatherCounter = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<a href="#"><i className="fa fa-users fa-fw"></i> Gatherers ({this.props.count})</a>
|
<a href="#">
|
||||||
</li>
|
<i className="fa fa-users fa-fw"></i> Gatherers
|
||||||
);
|
<span className="badge add-left"> {this.props.count} </span>
|
||||||
}
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var Gatherer = React.createClass({
|
var Gatherer = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<a href="#">{this.props.gatherer.username}</a>
|
<a href="#">{this.props.gatherer.username}</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var GathererMenu = React.createClass({
|
var GathererMenu = React.createClass({
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
socket.on('gatherCount', this.updateGatherers);
|
socket.on('gatherCount', this.updateGatherers);
|
||||||
},
|
},
|
||||||
updateGatherers: function (data) {
|
updateGatherers: function (data) {
|
||||||
this.setProps({
|
this.setProps({
|
||||||
count: data.count,
|
count: data.count,
|
||||||
gatherers: data.gatherers
|
gatherers: data.gatherers
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var gatherers = this.props.gatherers.map(function (gatherer) {
|
var gatherers = this.props.gatherers.map(function (gatherer) {
|
||||||
return (
|
return (
|
||||||
<Gatherer gatherer={gatherer} />
|
<Gatherer gatherer={gatherer} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<ul className="nav" id="side-menu">
|
<ul className="nav" id="side-menu">
|
||||||
<GatherCounter {...this.props} />
|
<GatherCounter {...this.props} />
|
||||||
{gatherers}
|
{gatherers}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Chatroom = React.createClass({
|
||||||
|
componentDidMount: function () {
|
||||||
|
var self = this;
|
||||||
|
var TIMER_INTERVAL = 60000; // Every minute
|
||||||
|
|
||||||
|
socket.on("message:new", function (data) {
|
||||||
|
var history = self.props.history;
|
||||||
|
history.push(data);
|
||||||
|
self.setProps({
|
||||||
|
history: history
|
||||||
|
});
|
||||||
|
self.scrollToBottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Message History Retrieved
|
||||||
|
socket.on("message:refresh", function (data) {
|
||||||
|
self.setProps({
|
||||||
|
history: data.chatHistory
|
||||||
|
});
|
||||||
|
self.scrollToBottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.emit("message:refresh", {});
|
||||||
|
|
||||||
|
self.timer = setInterval(function () {
|
||||||
|
self.refs.messages.refreshTime();
|
||||||
|
}, TIMER_INTERVAL);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUnmount: function () {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
},
|
||||||
|
sendMessage: function (message) {
|
||||||
|
socket.emit("newMessage", {message: message});
|
||||||
|
},
|
||||||
|
scrollToBottom: function () {
|
||||||
|
var node = React.findDOMNode(this.refs.messageContainer);
|
||||||
|
console.log(node)
|
||||||
|
node.scrollTop = node.scrollHeight;
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
var messages = this.props.history.map(function (message) {
|
||||||
|
return (
|
||||||
|
<ChatMessage
|
||||||
|
avatar={message.author.avatar}
|
||||||
|
username={message.author.username}
|
||||||
|
content={message.content}
|
||||||
|
ref="messages"
|
||||||
|
createdAt={message.createdAt} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div className="panel panel-default">
|
||||||
|
<div className="panel-heading">Gather Chat</div>
|
||||||
|
<div className="panel-body">
|
||||||
|
<ul className="chat" id="chatmessages" ref="messageContainer">
|
||||||
|
{messages}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="panel-footer">
|
||||||
|
<MessageBar />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var ChatMessage = React.createClass({
|
||||||
|
getInitialState: function () {
|
||||||
|
return {
|
||||||
|
timeAgo: $.timeago(this.props.createdAt)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshTime: function () {
|
||||||
|
var self = this;
|
||||||
|
self.setState({
|
||||||
|
timeAgo: $.timeago(self.props.createdAt)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
return (
|
||||||
|
<li className="left clearfix">
|
||||||
|
<span className="chat-img pull-left">
|
||||||
|
<img
|
||||||
|
src={this.props.avatar}
|
||||||
|
alt="User Avatar"
|
||||||
|
height="40"
|
||||||
|
className="img-circle" />
|
||||||
|
</span>
|
||||||
|
<div className="chat-body clearfix">
|
||||||
|
<div className="header">
|
||||||
|
<strong className="primary-font">{this.props.username}</strong>
|
||||||
|
<small className="pull-right text-muted">
|
||||||
|
<i className="fa fa-clock-o fa-fw"></i> {this.state.timeAgo}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<p>{this.props.content}</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var MessageBar = React.createClass({
|
||||||
|
sendMessage: function (content) {
|
||||||
|
socket.emit("message:new", {
|
||||||
|
content: content
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSubmit: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var content = React.findDOMNode(this.refs.content).value.trim();
|
||||||
|
if (!content) return;
|
||||||
|
React.findDOMNode(this.refs.content).value = '';
|
||||||
|
this.sendMessage(content);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
return (
|
||||||
|
<form onSubmit={this.handleSubmit} >
|
||||||
|
<div className="input-group">
|
||||||
|
<input
|
||||||
|
id="btn-input"
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
ref="content"
|
||||||
|
placeholder="Be polite please..." />
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-primary"
|
||||||
|
id="btn-chat"
|
||||||
|
value="Send" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
React.render(<GathererMenu count={0} gatherers={[]} />, document.getElementById('side-menu'));
|
React.render(<GathererMenu count={0} gatherers={[]} />, document.getElementById('side-menu'));
|
||||||
|
React.render(<Chatroom history={[]}/>, document.getElementById('chatroom'));
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "NODE_ENV=test mocha spec/",
|
"test": "NODE_ENV=test mocha spec/",
|
||||||
"start": "node index.js",
|
"start": "node index.js",
|
||||||
"compile:dev:react": "node_modules/react-tools/bin/jsx --watch -x jsx lib/react/ public/js/",
|
"compile:dev:react": "node_modules/react-tools/bin/jsx --watch --source-map-inline -x jsx lib/react/ public/js/",
|
||||||
"compile:react": "node_modules/react-tools/bin/jsx -x jsx lib/react/ public/js/",
|
"compile:react": "node_modules/react-tools/bin/jsx -x jsx lib/react/ public/js/",
|
||||||
"dev": "nodemon index.js"
|
"dev": "nodemon index.js"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#chatroom {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-left {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatmessages {
|
||||||
|
max-height: 70%;
|
||||||
|
min-height: 70%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
219
public/js/app.js
219
public/js/app.js
File diff suppressed because one or more lines are too long
221
public/js/timeago.jquery.js
Normal file
221
public/js/timeago.jquery.js
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
/**
|
||||||
|
* Timeago is a jQuery plugin that makes it easy to support automatically
|
||||||
|
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
|
||||||
|
*
|
||||||
|
* @name timeago
|
||||||
|
* @version 1.4.1
|
||||||
|
* @requires jQuery v1.2.3+
|
||||||
|
* @author Ryan McGeary
|
||||||
|
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*
|
||||||
|
* For usage and examples, visit:
|
||||||
|
* http://timeago.yarp.com/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function (factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(['jquery'], factory);
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
factory(jQuery);
|
||||||
|
}
|
||||||
|
}(function ($) {
|
||||||
|
$.timeago = function(timestamp) {
|
||||||
|
if (timestamp instanceof Date) {
|
||||||
|
return inWords(timestamp);
|
||||||
|
} else if (typeof timestamp === "string") {
|
||||||
|
return inWords($.timeago.parse(timestamp));
|
||||||
|
} else if (typeof timestamp === "number") {
|
||||||
|
return inWords(new Date(timestamp));
|
||||||
|
} else {
|
||||||
|
return inWords($.timeago.datetime(timestamp));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var $t = $.timeago;
|
||||||
|
|
||||||
|
$.extend($.timeago, {
|
||||||
|
settings: {
|
||||||
|
refreshMillis: 60000,
|
||||||
|
allowPast: true,
|
||||||
|
allowFuture: false,
|
||||||
|
localeTitle: false,
|
||||||
|
cutoff: 0,
|
||||||
|
strings: {
|
||||||
|
prefixAgo: null,
|
||||||
|
prefixFromNow: null,
|
||||||
|
suffixAgo: "ago",
|
||||||
|
suffixFromNow: "from now",
|
||||||
|
inPast: 'any moment now',
|
||||||
|
seconds: "less than a minute",
|
||||||
|
minute: "about a minute",
|
||||||
|
minutes: "%d minutes",
|
||||||
|
hour: "about an hour",
|
||||||
|
hours: "about %d hours",
|
||||||
|
day: "a day",
|
||||||
|
days: "%d days",
|
||||||
|
month: "about a month",
|
||||||
|
months: "%d months",
|
||||||
|
year: "about a year",
|
||||||
|
years: "%d years",
|
||||||
|
wordSeparator: " ",
|
||||||
|
numbers: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
inWords: function(distanceMillis) {
|
||||||
|
if(!this.settings.allowPast && ! this.settings.allowFuture) {
|
||||||
|
throw 'timeago allowPast and allowFuture settings can not both be set to false.';
|
||||||
|
}
|
||||||
|
|
||||||
|
var $l = this.settings.strings;
|
||||||
|
var prefix = $l.prefixAgo;
|
||||||
|
var suffix = $l.suffixAgo;
|
||||||
|
if (this.settings.allowFuture) {
|
||||||
|
if (distanceMillis < 0) {
|
||||||
|
prefix = $l.prefixFromNow;
|
||||||
|
suffix = $l.suffixFromNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.settings.allowPast && distanceMillis >= 0) {
|
||||||
|
return this.settings.strings.inPast;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seconds = Math.abs(distanceMillis) / 1000;
|
||||||
|
var minutes = seconds / 60;
|
||||||
|
var hours = minutes / 60;
|
||||||
|
var days = hours / 24;
|
||||||
|
var years = days / 365;
|
||||||
|
|
||||||
|
function substitute(stringOrFunction, number) {
|
||||||
|
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
|
||||||
|
var value = ($l.numbers && $l.numbers[number]) || number;
|
||||||
|
return string.replace(/%d/i, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
|
||||||
|
seconds < 90 && substitute($l.minute, 1) ||
|
||||||
|
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
|
||||||
|
minutes < 90 && substitute($l.hour, 1) ||
|
||||||
|
hours < 24 && substitute($l.hours, Math.round(hours)) ||
|
||||||
|
hours < 42 && substitute($l.day, 1) ||
|
||||||
|
days < 30 && substitute($l.days, Math.round(days)) ||
|
||||||
|
days < 45 && substitute($l.month, 1) ||
|
||||||
|
days < 365 && substitute($l.months, Math.round(days / 30)) ||
|
||||||
|
years < 1.5 && substitute($l.year, 1) ||
|
||||||
|
substitute($l.years, Math.round(years));
|
||||||
|
|
||||||
|
var separator = $l.wordSeparator || "";
|
||||||
|
if ($l.wordSeparator === undefined) { separator = " "; }
|
||||||
|
return $.trim([prefix, words, suffix].join(separator));
|
||||||
|
},
|
||||||
|
|
||||||
|
parse: function(iso8601) {
|
||||||
|
var s = $.trim(iso8601);
|
||||||
|
s = s.replace(/\.\d+/,""); // remove milliseconds
|
||||||
|
s = s.replace(/-/,"/").replace(/-/,"/");
|
||||||
|
s = s.replace(/T/," ").replace(/Z/," UTC");
|
||||||
|
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
|
||||||
|
s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
|
||||||
|
return new Date(s);
|
||||||
|
},
|
||||||
|
datetime: function(elem) {
|
||||||
|
var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
|
||||||
|
return $t.parse(iso8601);
|
||||||
|
},
|
||||||
|
isTime: function(elem) {
|
||||||
|
// jQuery's `is()` doesn't play well with HTML5 in IE
|
||||||
|
return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// functions that can be called via $(el).timeago('action')
|
||||||
|
// init is default when no action is given
|
||||||
|
// functions are called with context of a single element
|
||||||
|
var functions = {
|
||||||
|
init: function(){
|
||||||
|
var refresh_el = $.proxy(refresh, this);
|
||||||
|
refresh_el();
|
||||||
|
var $s = $t.settings;
|
||||||
|
if ($s.refreshMillis > 0) {
|
||||||
|
this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update: function(time){
|
||||||
|
var parsedTime = $t.parse(time);
|
||||||
|
$(this).data('timeago', { datetime: parsedTime });
|
||||||
|
if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
|
||||||
|
refresh.apply(this);
|
||||||
|
},
|
||||||
|
updateFromDOM: function(){
|
||||||
|
$(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
|
||||||
|
refresh.apply(this);
|
||||||
|
},
|
||||||
|
dispose: function () {
|
||||||
|
if (this._timeagoInterval) {
|
||||||
|
window.clearInterval(this._timeagoInterval);
|
||||||
|
this._timeagoInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.timeago = function(action, options) {
|
||||||
|
var fn = action ? functions[action] : functions.init;
|
||||||
|
if(!fn){
|
||||||
|
throw new Error("Unknown function name '"+ action +"' for timeago");
|
||||||
|
}
|
||||||
|
// each over objects here and call the requested function
|
||||||
|
this.each(function(){
|
||||||
|
fn.call(this, options);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
//check if it's still visible
|
||||||
|
if(!$.contains(document.documentElement,this)){
|
||||||
|
//stop if it has been removed
|
||||||
|
$(this).timeago("dispose");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = prepareData(this);
|
||||||
|
var $s = $t.settings;
|
||||||
|
|
||||||
|
if (!isNaN(data.datetime)) {
|
||||||
|
if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
|
||||||
|
$(this).text(inWords(data.datetime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareData(element) {
|
||||||
|
element = $(element);
|
||||||
|
if (!element.data("timeago")) {
|
||||||
|
element.data("timeago", { datetime: $t.datetime(element) });
|
||||||
|
var text = $.trim(element.text());
|
||||||
|
if ($t.settings.localeTitle) {
|
||||||
|
element.attr("title", element.data('timeago').datetime.toLocaleString());
|
||||||
|
} else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
|
||||||
|
element.attr("title", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return element.data("timeago");
|
||||||
|
}
|
||||||
|
|
||||||
|
function inWords(date) {
|
||||||
|
return $t.inWords(distance(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
function distance(date) {
|
||||||
|
return (new Date().getTime() - date.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix for IE6 suckage
|
||||||
|
document.createElement("abbr");
|
||||||
|
document.createElement("time");
|
||||||
|
}));
|
|
@ -8,8 +8,7 @@
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<h3 class="page-header">Current Gather</h3>
|
<h3 class="page-header">Current Gather</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6" id="chatroom">
|
||||||
<h3 class="page-header">Chat</h3>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<link rel="stylesheet" href="/css/app.css">
|
<link rel="stylesheet" href="/css/app.css">
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
||||||
|
<script src="/js/timeago.jquery.js"></script>
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||||||
<script src="/js/socket.io-1.3.5.js"></script>
|
<script src="/js/socket.io-1.3.5.js"></script>
|
||||||
<script src="/js/theme.js"></script>
|
<script src="/js/theme.js"></script>
|
||||||
|
|
Loading…
Reference in a new issue