Set up ESLint, make it happy

This commit is contained in:
Ken-Håvard Lieng 2015-06-02 00:09:28 +02:00
parent c325168a20
commit 6a1d55c968
26 changed files with 132 additions and 110 deletions

File diff suppressed because one or more lines are too long

22
client/.eslintrc Normal file
View File

@ -0,0 +1,22 @@
{
"ecmaFeatures": {
"jsx": true
},
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint",
"plugins": [
"react"
],
"rules": {
"quotes": [2, "single"],
"strict": [2, "never"],
"eol-last": [0],
"new-cap": [2, { "capIsNew": false }],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2
}
}

View File

@ -8,6 +8,7 @@ var autoprefixer = require('gulp-autoprefixer');
var uglify = require('gulp-uglify'); var uglify = require('gulp-uglify');
var gzip = require('gulp-gzip'); var gzip = require('gulp-gzip');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var eslint = require('gulp-eslint');
var browserify = require('browserify'); var browserify = require('browserify');
var source = require('vinyl-source-stream'); var source = require('vinyl-source-stream');
var streamify = require('gulp-streamify'); var streamify = require('gulp-streamify');
@ -74,6 +75,13 @@ function js(watch) {
return rebundle(); return rebundle();
} }
gulp.task('lint', function() {
return gulp.src('src/js/**/*.{js,jsx}')
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failOnError());
});
gulp.task('fonts', function() { gulp.task('fonts', function() {
return gulp.src('src/font/*') return gulp.src('src/font/*')
.pipe(gulp.dest('dist/font')); .pipe(gulp.dest('dist/font'));
@ -116,7 +124,8 @@ gulp.task('watch', ['default'], function() {
gulp.watch('dist/**/*.{html,css,js}', ['gzip:watch', 'bindata:watch']) gulp.watch('dist/**/*.{html,css,js}', ['gzip:watch', 'bindata:watch'])
gulp.watch('src/*.html', ['html']); gulp.watch('src/*.html', ['html']);
gulp.watch('src/css/*.css', ['css']); gulp.watch('src/css/*.css', ['css']);
gulp.watch('src/js/**/*.{js,jsx}', ['lint']);
return js(true); return js(true);
}); });
gulp.task('default', ['html', 'css', 'js', 'fonts', 'config', 'gzip', 'bindata']); gulp.task('default', ['html', 'css', 'js', 'lint', 'fonts', 'config', 'gzip', 'bindata']);

View File

@ -4,22 +4,26 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"devDependencies": { "devDependencies": {
"yargs": "~1.3.3", "babel-eslint": "^3.1.9",
"babelify": "6.1.0",
"browserify": "10.2.0",
"eslint": "^0.22.1",
"eslint-plugin-react": "^2.4.0",
"gulp": "~3.8.10",
"gulp-autoprefixer": "2.2.0",
"gulp-concat": "~2.5.2",
"gulp-eslint": "^0.12.0",
"gulp-gzip": "0.0.8",
"gulp-if": "~1.2.5",
"gulp-minify-css": "1.1.1",
"gulp-minify-html": "1.0.2",
"gulp-streamify": "0.0.5",
"gulp-uglify": "1.2.0",
"reactify": "^1.1.1",
"strictify": "~0.2.0", "strictify": "~0.2.0",
"vinyl-source-stream": "~1.0.0", "vinyl-source-stream": "~1.0.0",
"gulp-if": "~1.2.5",
"gulp": "~3.8.10",
"gulp-uglify": "1.2.0",
"gulp-minify-css": "1.1.1",
"gulp-streamify": "0.0.5",
"gulp-minify-html": "1.0.2",
"watchify": "3.2.1", "watchify": "3.2.1",
"browserify": "10.2.0", "yargs": "~1.3.3"
"gulp-autoprefixer": "2.2.0",
"gulp-gzip": "0.0.8",
"babelify": "6.1.0",
"gulp-concat": "~2.5.2",
"reactify": "^1.1.1"
}, },
"dependencies": { "dependencies": {
"lodash": "3.8.0", "lodash": "3.8.0",

View File

@ -1,5 +1,4 @@
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var socket = require('../socket'); var socket = require('../socket');

View File

@ -1,5 +1,4 @@
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var socket = require('../socket'); var socket = require('../socket');

View File

@ -1,7 +1,5 @@
var Reflux = require('reflux'); var Reflux = require('reflux');
var routeActions = require('./route');
var tabActions = Reflux.createActions([ var tabActions = Reflux.createActions([
'select' 'select'
]); ]);

View File

@ -43,7 +43,7 @@ messageActions.command.listen(function(line, channel, server) {
break; break;
case 'topic': case 'topic':
var topic = channelStore.getTopic(server, channel); let topic = channelStore.getTopic(server, channel);
if (topic) { if (topic) {
messageActions.add({ messageActions.add({
server: server, server: server,
@ -72,8 +72,8 @@ messageActions.command.listen(function(line, channel, server) {
case 'msg': case 'msg':
if (params.length > 2) { if (params.length > 2) {
var dest = params[1]; let dest = params[1];
var message = params.slice(2).join(' '); let message = params.slice(2).join(' ');
messageActions.send(message, dest, server); messageActions.send(message, dest, server);
} }
@ -81,7 +81,7 @@ messageActions.command.listen(function(line, channel, server) {
case 'say': case 'say':
if (params.length > 1) { if (params.length > 1) {
var message = params.slice(1).join(' '); let message = params.slice(1).join(' ');
messageActions.send(message, channel, server); messageActions.send(message, channel, server);
} }

View File

@ -70,9 +70,9 @@ var ChatTitle = React.createClass({
<span className="chat-topic" dangerouslySetInnerHTML={{ __html: topic }}></span> <span className="chat-topic" dangerouslySetInnerHTML={{ __html: topic }}></span>
</div> </div>
<i className="icon-search" title="Search" onClick={searchActions.toggle}></i> <i className="icon-search" title="Search" onClick={searchActions.toggle}></i>
<i <i
className="icon-logout button-leave" className="icon-logout button-leave"
title={leaveTitle} title={leaveTitle}
onClick={this.handleLeaveClick}></i> onClick={this.handleLeaveClick}></i>
</div> </div>
<div className="userlist-bar"> <div className="userlist-bar">

View File

@ -35,7 +35,7 @@ var Connect = React.createClass({
serverActions.connect(address, nick, opts); serverActions.connect(address, nick, opts);
if (channels.length > 0) { if (channels.length > 0) {
channelActions.join(channels, address); channelActions.join(channels, address);
} }
} }
}, },

View File

@ -1,8 +1,6 @@
var React = require('react'); var React = require('react');
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var Infinite = require('react-infinite'); var Infinite = require('react-infinite');
var Autolinker = require('autolinker');
var MessageHeader = require('./MessageHeader.jsx'); var MessageHeader = require('./MessageHeader.jsx');
var MessageLine = require('./MessageLine.jsx'); var MessageLine = require('./MessageLine.jsx');
@ -66,7 +64,7 @@ var MessageBox = React.createClass({
var lines = []; var lines = [];
this.state.messages.forEach((message, j) => { this.state.messages.forEach((message, j) => {
var key = message.server + dest + j; var key = message.server + dest + j;
lines.push(<MessageHeader key={key} message={message} />); lines.push(<MessageHeader key={key} message={message} />);

View File

@ -1,5 +1,4 @@
var React = require('react'); var React = require('react');
var Reflux = require('reflux');
var Autolinker = require('autolinker'); var Autolinker = require('autolinker');
var util = require('../util'); var util = require('../util');

View File

@ -61,10 +61,10 @@ var MessageInput = React.createClass({
render() { render() {
return ( return (
<div className="message-input-wrap"> <div className="message-input-wrap">
<input <input
ref="input" ref="input"
className="message-input" className="message-input"
type="text" type="text"
value={this.state.history || this.state.value} value={this.state.history || this.state.value}
onKeyDown={this.handleKey} onKeyDown={this.handleKey}
onChange={this.handleChange} /> onChange={this.handleChange} />

View File

@ -1,6 +1,5 @@
var React = require('react'); var React = require('react');
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var util = require('../util'); var util = require('../util');
var searchStore = require('../stores/search'); var searchStore = require('../stores/search');
@ -42,9 +41,9 @@ var Search = React.createClass({
return ( return (
<div className="search" style={style}> <div className="search" style={style}>
<input <input
ref="input" ref="input"
className="search-input" className="search-input"
type="text" type="text"
onChange={this.handleChange} /> onChange={this.handleChange} />
<div className="search-results">{results}</div> <div className="search-results">{results}</div>

View File

@ -1,6 +1,5 @@
var React = require('react'); var React = require('react');
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var TabListItem = require('./TabListItem.jsx'); var TabListItem = require('./TabListItem.jsx');
var channelStore = require('../stores/channel'); var channelStore = require('../stores/channel');
@ -30,9 +29,9 @@ var TabList = React.createClass({
this.state.channels.forEach((server, address) => { this.state.channels.forEach((server, address) => {
tabs.push( tabs.push(
<TabListItem <TabListItem
key={address} key={address}
server={address} server={address}
channel={null} channel={null}
name={this.state.servers.getIn([address, 'name'])}> name={this.state.servers.getIn([address, 'name'])}>
</TabListItem> </TabListItem>
@ -41,8 +40,8 @@ var TabList = React.createClass({
server.forEach((channel, name) => { server.forEach((channel, name) => {
tabs.push( tabs.push(
<TabListItem <TabListItem
key={address + name} key={address + name}
server={address} server={address}
channel={name} channel={name}
name={name}> name={name}>
</TabListItem> </TabListItem>
@ -52,9 +51,9 @@ var TabList = React.createClass({
if (this.state.privateChats.has(address)) { if (this.state.privateChats.has(address)) {
this.state.privateChats.get(address).forEach(nick => { this.state.privateChats.get(address).forEach(nick => {
tabs.push( tabs.push(
<TabListItem <TabListItem
key={address + nick} key={address + nick}
server={address} server={address}
channel={nick} channel={nick}
name={nick}> name={nick}>
</TabListItem> </TabListItem>

View File

@ -1,6 +1,5 @@
var React = require('react'); var React = require('react');
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var Infinite = require('react-infinite'); var Infinite = require('react-infinite');
var UserListItem = require('./UserListItem.jsx'); var UserListItem = require('./UserListItem.jsx');
@ -77,7 +76,7 @@ var UserList = React.createClass({
<div>{users}</div> <div>{users}</div>
</div> </div>
); );
} }
} }
}); });

View File

@ -7,8 +7,8 @@ var PureMixin = require('../mixins/pure');
var UserListItem = React.createClass({ var UserListItem = React.createClass({
mixins: [PureMixin], mixins: [PureMixin],
handleClick() { handleClick() {
var server = selectedTabStore.getServer(); var server = selectedTabStore.getServer();
privateChatActions.open(server, this.props.user.nick); privateChatActions.open(server, this.props.user.nick);

View File

@ -6,9 +6,12 @@ var channelActions = require('./actions/channel');
var messageActions = require('./actions/message'); var messageActions = require('./actions/message');
var serverActions = require('./actions/server'); var serverActions = require('./actions/server');
var routeActions = require('./actions/route'); var routeActions = require('./actions/route');
var tabActions = require('./actions/tab');
var searchActions = require('./actions/search'); var searchActions = require('./actions/search');
function withReason(message, reason) {
return message + (reason ? ' (' + reason + ')' : '');
}
socket.on('join', function(data) { socket.on('join', function(data) {
channelActions.addUser(data.user, data.server, data.channels[0]); channelActions.addUser(data.user, data.server, data.channels[0]);
messageActions.inform(data.user + ' joined the channel', data.server, data.channels[0]); messageActions.inform(data.user + ' joined the channel', data.server, data.channels[0]);
@ -90,10 +93,6 @@ socket.on('search', function(data) {
searchActions.searchDone(data.results); searchActions.searchDone(data.results);
}); });
serverActions.connect.listen(function(server, nick, opts) { serverActions.connect.listen(function(server) {
messageActions.inform('Connecting...', server); messageActions.inform('Connecting...', server);
}); });
function withReason(message, reason) {
return message + (reason ? ' (' + reason + ')' : '');
}

View File

@ -12,6 +12,6 @@ module.exports = {
} }
return !shallowEqual(this.props, nextProps) || return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState); !shallowEqual(this.state, nextState);
} }
}; };

View File

@ -3,7 +3,7 @@ var EventEmitter = require('events').EventEmitter;
class Socket extends EventEmitter { class Socket extends EventEmitter {
constructor() { constructor() {
super(); super();
this.ws = new WebSocket('ws://' + window.location.host + '/ws'); this.ws = new WebSocket('ws://' + window.location.host + '/ws');
this.ws.onopen = () => this.emit('connect'); this.ws.onopen = () => this.emit('connect');
@ -12,7 +12,7 @@ class Socket extends EventEmitter {
var msg = JSON.parse(e.data); var msg = JSON.parse(e.data);
this.emit(msg.type, msg.response); this.emit(msg.type, msg.response);
} };
} }
send(type, data) { send(type, data) {

View File

@ -14,6 +14,18 @@ var User = Immutable.Record({
mode: '' mode: ''
}); });
function updateRenderName(user) {
var name = user.nick;
if (user.mode.indexOf('o') !== -1) {
name = '@' + name;
} else if (user.mode.indexOf('v') !== -1) {
name = '+' + name;
}
return user.set('renderName', name);
}
function createUser(nick, mode) { function createUser(nick, mode) {
return updateRenderName(new User({ return updateRenderName(new User({
nick: nick, nick: nick,
@ -38,18 +50,6 @@ function loadUser(nick) {
return createUser(nick, mode); return createUser(nick, mode);
} }
function updateRenderName(user) {
var name = user.nick;
if (user.mode.indexOf('o') !== -1) {
name = '@' + name;
} else if (user.mode.indexOf('v') !== -1) {
name = '+' + name;
}
return user.set('renderName', name);
}
function compareUsers(a, b) { function compareUsers(a, b) {
a = a.renderName.toLowerCase(); a = a.renderName.toLowerCase();
b = b.renderName.toLowerCase(); b = b.renderName.toLowerCase();
@ -142,10 +142,10 @@ var channelStore = Reflux.createStore({
var i = channels.getIn([mode.server, mode.channel, 'users']).findIndex(u => u.nick === mode.user); var i = channels.getIn([mode.server, mode.channel, 'users']).findIndex(u => u.nick === mode.user);
channels = channels.updateIn([mode.server, mode.channel, 'users', i], user => { channels = channels.updateIn([mode.server, mode.channel, 'users', i], user => {
_.each(mode.remove, function(mode) { _.each(mode.remove, remove => {
user = user.set('mode', user.mode.replace(mode, '')); user = user.set('mode', user.mode.replace(remove, ''));
}); });
user = user.set('mode', user.mode + mode.add); user = user.set('mode', user.mode + mode.add);
return updateRenderName(user); return updateRenderName(user);

View File

@ -1,5 +1,4 @@
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var actions = require('../actions/inputHistory'); var actions = require('../actions/inputHistory');

View File

@ -95,7 +95,7 @@ var messageStore = Reflux.createStore({
type: 'info' type: 'info'
}, channel || server); }, channel || server);
} }
this.trigger(messages); this.trigger(messages);
}, },

View File

@ -1,5 +1,4 @@
var Reflux = require('reflux'); var Reflux = require('reflux');
var _ = require('lodash');
var util = require('../util'); var util = require('../util');
var messageStore = require('./message'); var messageStore = require('./message');
@ -13,14 +12,14 @@ var prev;
function updateCharWidth() { function updateCharWidth() {
window.charWidth = util.stringWidth(' ', '16px Droid Sans Mono'); window.charWidth = util.stringWidth(' ', '16px Droid Sans Mono');
window.messageIndent = 6 * charWidth; window.messageIndent = 6 * window.charWidth;
} }
function wrap() { function wrap() {
var next = messageStore.getMessages(tab.server, tab.channel || tab.server); var next = messageStore.getMessages(tab.server, tab.channel || tab.server);
if (next !== prev) { if (next !== prev) {
prev = next; prev = next;
messages = util.wrapMessages(next, width, charWidth, messageIndent); messages = util.wrapMessages(next, width, window.charWidth, window.messageIndent);
return true; return true;
} }
return false; return false;
@ -45,7 +44,7 @@ var messageLineStore = Reflux.createStore({
setWrapWidth(w) { setWrapWidth(w) {
width = w; width = w;
messages = util.wrapMessages(messages, width, charWidth, messageIndent); messages = util.wrapMessages(messages, width, window.charWidth, window.messageIndent);
this.trigger(messages); this.trigger(messages);
}, },
@ -57,7 +56,7 @@ var messageLineStore = Reflux.createStore({
selectedTabChanged(selectedTab) { selectedTabChanged(selectedTab) {
tab = selectedTab; tab = selectedTab;
if (wrap()) { if (wrap()) {
this.trigger(messages); this.trigger(messages);
} }

View File

@ -51,10 +51,10 @@ var selectedTabStore = Reflux.createStore({
}, },
select(server, channel = null) { select(server, channel = null) {
selectedTab = new Tab({ selectedTab = new Tab({
server, server,
channel, channel,
name: channel || serverStore.getName(server) name: channel || serverStore.getName(server)
}); });
history.push(selectedTab); history.push(selectedTab);
@ -63,14 +63,14 @@ var selectedTabStore = Reflux.createStore({
}, },
part(channels, server) { part(channels, server) {
if (server === selectedTab.server && if (server === selectedTab.server &&
channels.indexOf(selectedTab.channel) !== -1) { channels.indexOf(selectedTab.channel) !== -1) {
if (!selectPrevTab()) { if (!selectPrevTab()) {
selectedTab = selectedTab selectedTab = selectedTab
.set('channel', null) .set('channel', null)
.set('name', serverStore.getName(server)); .set('name', serverStore.getName(server));
} }
this.trigger(selectedTab); this.trigger(selectedTab);
} }
}, },
@ -83,7 +83,7 @@ var selectedTabStore = Reflux.createStore({
.set('channel', null) .set('channel', null)
.set('name', serverStore.getName(server)); .set('name', serverStore.getName(server));
} }
this.trigger(selectedTab); this.trigger(selectedTab);
} }
}, },
@ -112,7 +112,7 @@ var selectedTabStore = Reflux.createStore({
}, },
loadChannels(channels) { loadChannels(channels) {
_.each(channels, (channel) => { _.each(channels, (channel) => {
if (channel.server === selectedTab.server && if (channel.server === selectedTab.server &&
channel.name !== selectedTab.channel && channel.name !== selectedTab.channel &&
channel.name.indexOf(selectedTab.channel) !== -1) { channel.name.indexOf(selectedTab.channel) !== -1) {
@ -156,23 +156,23 @@ var selectedTabStore = Reflux.createStore({
getState getState
}); });
selectedTabStore.listen(selectedTab => { selectedTabStore.listen(tab => {
var channel = selectedTab.channel; var channel = tab.channel;
if (selectedTab.server) { if (tab.server) {
if (channel) { if (channel) {
while (channel[0] === '#') { while (channel[0] === '#') {
channel = channel.slice(1); channel = channel.slice(1);
} }
routeActions.navigate('/' + selectedTab.server + '/' + channel); routeActions.navigate('/' + tab.server + '/' + channel);
} else { } else {
routeActions.navigate('/' + selectedTab.server); routeActions.navigate('/' + tab.server);
} }
} else if (serverStore.getState().size === 0) { } else if (serverStore.getState().size === 0) {
routeActions.navigate('connect'); routeActions.navigate('connect');
} }
localStorage.selectedTab = JSON.stringify(selectedTab); localStorage.selectedTab = JSON.stringify(tab);
}); });
module.exports = selectedTabStore; module.exports = selectedTabStore;

View File

@ -2,15 +2,15 @@ var _ = require('lodash');
exports.UUID = function() { exports.UUID = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16); return v.toString(16);
}); });
}; };
exports.timestamp = function(date) { exports.timestamp = function(date) {
date = date || new Date(); date = date || new Date();
var h = _.padLeft(date.getHours(), 2, '0') var h = _.padLeft(date.getHours(), 2, '0');
var m = _.padLeft(date.getMinutes(), 2, '0'); var m = _.padLeft(date.getMinutes(), 2, '0');
return h + ':' + m; return h + ':' + m;
@ -85,7 +85,7 @@ exports.wrapMessages = function(messages, width, charWidth, indent = 0) {
var canvas = document.createElement('canvas'); var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
exports.stringWidth = function(str, font) { exports.stringWidth = function(str, font) {
ctx.font = font; ctx.font = font;
return ctx.measureText(str).width; return ctx.measureText(str).width;
}; };
@ -102,7 +102,7 @@ exports.scrollbarWidth = function() {
var inner = document.createElement('div'); var inner = document.createElement('div');
inner.style.width = '100%'; inner.style.width = '100%';
outer.appendChild(inner); outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth; var widthWithScroll = inner.offsetWidth;