Merge pull request #1683 from thelounge/astorije/split-index
Split index.html into components
This commit is contained in:
commit
489bb8e395
@ -9,23 +9,23 @@
|
||||
<link rel="preload" as="script" href="js/bundle.js">
|
||||
<link rel="stylesheet" href="css/bootstrap.css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link id="theme" rel="stylesheet" href="{{ theme }}">
|
||||
<link id="theme" rel="stylesheet" href="<%- theme %>">
|
||||
<style id="user-specified-css"></style>
|
||||
|
||||
<title>The Lounge</title>
|
||||
|
||||
<link rel="shortcut icon" href="img/favicon.png" data-other="img/favicon-notification.png" data-toggled="false" id="favicon">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="img/apple-touch-icon-120x120.png">
|
||||
<link rel="mask-icon" href="img/logo.svg" color="#455164">
|
||||
<link rel="mask-icon" href="img/logo.svg" color="<%- themeColor %>">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#455164">
|
||||
<meta name="theme-color" content="<%- themeColor %>">
|
||||
|
||||
</head>
|
||||
<body class="signed-out {{#if public}}public{{/if}}" data-transports="{{tojson transports}}">
|
||||
<body class="signed-out<%- public ? " public" : "" %>" data-transports="<%- JSON.stringify(transports) %>">
|
||||
|
||||
<div id="wrap">
|
||||
<div id="viewport">
|
||||
@ -79,805 +79,10 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="sign-in" class="window">
|
||||
<form class="container" method="post" action="">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1 class="title">Sign in to The Lounge</h1>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<label>
|
||||
Username
|
||||
<input class="input" name="user">
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<label>
|
||||
Password
|
||||
<input class="input" type="password" name="password">
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-12 error" style="display: none;">
|
||||
Authentication failed.
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<button type="submit" class="btn">
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="connect" class="window">
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<form class="container" method="post" action="">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1 class="title">
|
||||
{{#if public}}The Lounge - {{/if}}
|
||||
Connect
|
||||
{{#unless displayNetwork}}
|
||||
{{#if lockNetwork}}
|
||||
to {{defaults.name}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</h1>
|
||||
</div>
|
||||
{{#if displayNetwork}}
|
||||
<div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Network settings</h2>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:name">Name</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:name" name="name" value="{{defaults.name}}">
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:host">Server</label>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-8">
|
||||
<input class="input" id="connect:host" name="host" value="{{defaults.host}}" aria-label="Server address" {{#if lockNetwork}}disabled{{/if}}>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-4">
|
||||
<div class="port">
|
||||
<input class="input" type="number" min="1" max="65535" name="port" value="{{defaults.port}}" aria-label="Server port" {{#if lockNetwork}}disabled{{/if}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:password">Password</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:password" type="password" name="password" value="{{defaults.password}}">
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3">
|
||||
<label class="tls">
|
||||
<input type="checkbox" name="tls" {{#if defaults.tls}}checked{{/if}} {{#if lockNetwork}}disabled{{/if}}>
|
||||
Enable TLS/SSL
|
||||
</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="col-sm-12">
|
||||
<h2>User preferences</h2>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:nick">Nick</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input nick" id="connect:nick" name="nick" value="{{defaults.nick}}">
|
||||
</div>
|
||||
{{#unless useHexIp}}
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:username">Username</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input username" id="connect:username" name="username" value="{{defaults.username}}">
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:realname">Real name</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:realname" name="realname" value="{{defaults.realname}}">
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:channels">Channels</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:channels" name="join" value="{{defaults.join}}">
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3">
|
||||
<button type="submit" class="btn">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="settings" class="window" data-type="settings">
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1 class="title">Settings</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>Messages</h2>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="motd">
|
||||
Show <abbr title="Message Of The Day">MOTD</abbr>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="showSeconds">
|
||||
Show seconds in timestamp
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>
|
||||
Status messages
|
||||
<span class="tooltipped tooltipped-n tooltipped-no-delay" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes">
|
||||
<button class="extra-help" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes"></button>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="shown">
|
||||
Show all status messages individually
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="condensed">
|
||||
Condense status messages together
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="hidden">
|
||||
Hide all status messages
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Visual Aids</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="coloredNicks">
|
||||
Enable colored nicknames
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="autocomplete">
|
||||
Enable autocomplete
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Theme</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="theme-select" class="sr-only">Theme</label>
|
||||
<select id="theme-select" name="theme" class="input">
|
||||
{{#each themes}}
|
||||
<option value="{{name}}">
|
||||
{{displayName}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
{{#if prefetch}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Link previews</h2>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="thumbnails">
|
||||
Auto-expand images
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="links">
|
||||
Auto-expand websites
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#unless public}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Push Notifications</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn" id="pushNotifications" disabled data-text-alternate="Unsubscribe from push notifications">Subscribe to push notifications</button>
|
||||
<div class="error" id="pushNotificationsHttps">
|
||||
<strong>Warning</strong>:
|
||||
Push notifications are only supported over HTTPS connections.
|
||||
</div>
|
||||
<div class="error" id="pushNotificationsUnsupported">
|
||||
<strong>Warning</strong>:
|
||||
<span>Push notifications are not supported by your browser.</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Browser Notifications</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input id="desktopNotifications" type="checkbox" name="desktopNotifications">
|
||||
Enable browser notifications<br>
|
||||
<div class="error" id="warnUnsupportedDesktopNotifications">
|
||||
<strong>Warning</strong>:
|
||||
Notifications are not supported by your browser.
|
||||
</div>
|
||||
<div class="error" id="warnBlockedDesktopNotifications">
|
||||
<strong>Warning</strong>:
|
||||
Notifications are blocked by your browser.
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="notification">
|
||||
Enable notification sound
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="opt">
|
||||
<button id="play">Play sound</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="notifyAllMessages">
|
||||
Enable notification for all messages
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<label for="highlights" class="sr-only">Custom highlights (comma-separated keywords)</label>
|
||||
<input type="text" id="highlights" name="highlights" class="input" placeholder="Custom highlights (comma-separated keywords)">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{#unless public}}
|
||||
{{#unless ldap.enable}}
|
||||
<div id="change-password">
|
||||
<form action="" method="post">
|
||||
<div class="col-sm-12">
|
||||
<h2>Change password</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="old_password_input" class="sr-only">Enter current password</label>
|
||||
<input type="password" id="old_password_input" name="old_password" class="input" placeholder="Enter current password">
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="new_password_input" class="sr-only">Enter desired new password</label>
|
||||
<input type="password" id="new_password_input" name="new_password" class="input" placeholder="Enter desired new password">
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="verify_password_input" class="sr-only">Repeat new password</label>
|
||||
<input type="password" id="verify_password_input" name="verify_password" class="input" placeholder="Repeat new password">
|
||||
</div>
|
||||
<div class="col-sm-12 feedback"></div>
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn">Change password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Custom Stylesheet</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<textarea class="input" name="userStyles" id="user-specified-css-input" placeholder="/* You can override any style with CSS here */"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#unless public}}
|
||||
<div class="session-list">
|
||||
<h2>Sessions</h2>
|
||||
|
||||
<h3>Current session</h3>
|
||||
<div id="session-current"></div>
|
||||
|
||||
<h3>Other sessions</h3>
|
||||
<div id="session-list"></div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="help" class="window">
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1 class="title">Help</h1>
|
||||
|
||||
<h2>Keyboard Shortcuts</h2>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>↑</kbd> / <kbd>↓</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Switch to the previous/next window in the channel list</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>K</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Mark any text typed after this shortcut to be colored. After
|
||||
hitting this shortcut, enter an integer in the range
|
||||
<code>0—15</code> to select the desired color, or use the
|
||||
autocompletion menu to choose a color name (see below).
|
||||
</p>
|
||||
<p>
|
||||
Background color can be specified by putting a comma and
|
||||
another integer in the range <code>0—15</code> after the
|
||||
foreground color number (autocompletion works too).
|
||||
</p>
|
||||
<p>
|
||||
A color reference can be found
|
||||
<a href="https://modern.ircdocs.horse/formatting.html#colors" target="_blank" rel="noopener">here</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>B</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as bold.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>U</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as underlined.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>I</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as italics.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>O</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Mark all text typed after this shortcut to be reset to its
|
||||
original formatting.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Autocompletion</h2>
|
||||
|
||||
<p>
|
||||
To auto-complete nicknames, channels, commands, and emoji, type one of the characters below to open
|
||||
a suggestion list. Use the <kbd>↑</kbd> and <kbd>↓</kbd> keys to highlight an item, and insert it by
|
||||
pressing <kbd>Tab</kbd> or <kbd>Enter</kbd> (or by clicking the desired item).
|
||||
</p>
|
||||
<p>
|
||||
Autocompletion can be disabled in settings.
|
||||
</p>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>@</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Nickname</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>#</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Channel</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Commands (see list of commands below)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>:</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Emoji (note: requires two search characters, to avoid conflicting with common emoticons like <code>:)</code>)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Commands</h2>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/away [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark yourself as away with an optional message.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/back</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Remove your away status (set with <code>/away</code>).</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/ban nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Ban (<code>+b</code>) a user from the current channel.
|
||||
This can be a nickname or a hostmask.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/banlist</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Load the banlist for the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/collapse</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Collapse all previews in the current channel (opposite of
|
||||
<code>/expand</code>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/connect host [port]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Connect to a new IRC network. If <code>port</code> starts with
|
||||
a <code>+</code> sign, the connection will be made secure
|
||||
using TLS.
|
||||
</p>
|
||||
<p>Alias: <code>/server</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/ctcp target cmd [args]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Send a <abbr title="Client-to-client protocol">CTCP</abbr>
|
||||
request. Read more about this on
|
||||
<a href="https://en.wikipedia.org/wiki/Client-to-client_protocol" target="_blank" rel="noopener">the dedicated Wikipedia article</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/deop nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Remove op (<code>-o</code>) from one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/devoice nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Remove voice (<code>-v</code>) from one or several users in
|
||||
the current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/disconnect [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Disconnect from the current network with an
|
||||
optionally-provided message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/expand</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Expand all previews in the current channel (opposite of
|
||||
<code>/collapse</code>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/invite nick [channel]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Invite a user to the specified channel. If
|
||||
<code>channel</code> is ommitted, user will be invited to the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/join channel</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Join a channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/kick nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Kick a user from the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/list</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Retrieve a list of available channels on this network.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/me message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Send an action message to the current channel. The Lounge will
|
||||
display it inline, as if the message was posted in the third
|
||||
person.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/mode flags [args]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Set the given flags to the current channel if the active
|
||||
window is a channel, another user if the active window is a
|
||||
private message window, or yourself if the current window is a
|
||||
server window.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/msg channel message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a message to the specified channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/nick newnick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Change your nickname on the current network.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/notice channel message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Sends a notice message to the specified channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/op nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Give op (<code>+o</code>) to one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/part [channel]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Close the specified channel or private message window, or the
|
||||
current channel if <code>channel</code> is ommitted.
|
||||
</p>
|
||||
<p>Aliases: <code>/close</code>, <code>/leave</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/rejoin</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Leave and immediately rejoin the current channel. Useful to
|
||||
quickly get op from ChanServ in an empty channel, for example.
|
||||
</p>
|
||||
<p>Alias: <code>/cycle</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/query nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a private message to the specified user.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/quit [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Disconnect from the current network with an optional message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/raw message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a raw message to the current IRC network.</p>
|
||||
<p>Aliases: <code>/quote</code>, <code>/send</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/slap nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Slap someone in the current channel with a trout!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/topic newtopic</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Set the topic in the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/unban nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Unban (<code>-b</code>) a user from the current channel.
|
||||
This can be a nickname or a hostmask.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/voice nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Give voice (<code>+v</code>) to one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/whois nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Retrieve information about the given user on the current
|
||||
network.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>About The Lounge</h2>
|
||||
|
||||
<p class="about">
|
||||
{{#if gitCommit}}
|
||||
The Lounge is running from source
|
||||
(<a href="https://github.com/thelounge/lounge/tree/{{ gitCommit }}" target="_blank" rel="noopener"><code>{{ gitCommit }}</code></a>).<br>
|
||||
{{else}}
|
||||
The Lounge is in version <strong>{{version}}</strong>
|
||||
(<a href="https://github.com/thelounge/lounge/releases/tag/v{{ version }}" target="_blank" rel="noopener">See release notes</a>).<br>
|
||||
{{/if}}
|
||||
|
||||
<a href="https://thelounge.github.io/" target="_blank" rel="noopener">Website</a><br>
|
||||
<a href="https://thelounge.github.io/docs/" target="_blank" rel="noopener">Documentation</a><br>
|
||||
<a href="https://github.com/thelounge/lounge/issues/new" target="_blank" rel="noopener">Report a bug</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sign-in" class="window"></div>
|
||||
<div id="connect" class="window"></div>
|
||||
<div id="settings" class="window" data-type="settings"></div>
|
||||
<div id="help" class="window"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -15,7 +15,6 @@ const templates = require("../views");
|
||||
const socket = require("./socket");
|
||||
require("./socket-events");
|
||||
const storage = require("./localStorage");
|
||||
require("./options");
|
||||
const utils = require("./utils");
|
||||
require("./autocompletion");
|
||||
require("./webpush");
|
||||
@ -28,7 +27,6 @@ $(function() {
|
||||
|
||||
$(document.body).data("app-name", document.title);
|
||||
|
||||
var windows = $("#windows");
|
||||
var viewport = $("#viewport");
|
||||
var sidebarSlide = slideoutMenu(viewport[0], sidebar[0]);
|
||||
var contextMenuContainer = $("#context-menu-container");
|
||||
@ -485,18 +483,6 @@ $(function() {
|
||||
container.html(templates.user_filtered({matches: result})).show();
|
||||
});
|
||||
|
||||
var forms = $("#sign-in, #connect, #change-password");
|
||||
|
||||
windows.on("show", "#sign-in", function() {
|
||||
$(this).find("input").each(function() {
|
||||
var self = $(this);
|
||||
if (self.val() === "") {
|
||||
self.focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ($("body").hasClass("public") && (window.location.hash === "#connect" || window.location.hash === "")) {
|
||||
$("#connect").one("show", function() {
|
||||
var params = URI(document.location.search);
|
||||
@ -523,56 +509,6 @@ $(function() {
|
||||
});
|
||||
}
|
||||
|
||||
forms.on("submit", "form", function(e) {
|
||||
e.preventDefault();
|
||||
var event = "auth";
|
||||
var form = $(this);
|
||||
form.find(".btn").attr("disabled", true);
|
||||
|
||||
if (form.closest(".window").attr("id") === "connect") {
|
||||
event = "conn";
|
||||
} else if (form.closest("div").attr("id") === "change-password") {
|
||||
event = "change-password";
|
||||
}
|
||||
|
||||
var values = {};
|
||||
$.each(form.serializeArray(), function(i, obj) {
|
||||
if (obj.value !== "") {
|
||||
values[obj.name] = obj.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (values.user) {
|
||||
storage.set("user", values.user);
|
||||
}
|
||||
|
||||
socket.emit(
|
||||
event, values
|
||||
);
|
||||
});
|
||||
|
||||
forms.on("focusin", ".nick", function() {
|
||||
// Need to set the first "lastvalue", so it can be used in the below function
|
||||
var nick = $(this);
|
||||
nick.data("lastvalue", nick.val());
|
||||
});
|
||||
|
||||
forms.on("input", ".nick", function() {
|
||||
var nick = $(this).val();
|
||||
var usernameInput = forms.find(".username");
|
||||
|
||||
// Because this gets called /after/ it has already changed, we need use the previous value
|
||||
var lastValue = $(this).data("lastvalue");
|
||||
|
||||
// They were the same before the change, so update the username field
|
||||
if (usernameInput.val() === lastValue) {
|
||||
usernameInput.val(nick);
|
||||
}
|
||||
|
||||
// Store the "previous" value, for next time
|
||||
$(this).data("lastvalue", nick);
|
||||
});
|
||||
|
||||
$(document).on("visibilitychange focus click", () => {
|
||||
if (sidebar.find(".highlight").length === 0) {
|
||||
utils.toggleNotificationMarkers(false);
|
||||
|
@ -3,7 +3,6 @@
|
||||
const $ = require("jquery");
|
||||
require("jquery-textcomplete");
|
||||
const escapeRegExp = require("lodash/escapeRegExp");
|
||||
const settings = $("#settings");
|
||||
const userStyles = $("#user-specified-css");
|
||||
const storage = require("./localStorage");
|
||||
const tz = require("./libs/handlebars/tz");
|
||||
@ -35,6 +34,11 @@ for (const key in options) {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply custom CSS on page load
|
||||
if (typeof userOptions.userStyles === "string" && !/[?&]nocss/.test(window.location.search)) {
|
||||
userStyles.html(userOptions.userStyles);
|
||||
}
|
||||
|
||||
userOptions = null;
|
||||
|
||||
module.exports = options;
|
||||
@ -43,123 +47,126 @@ module.exports.shouldOpenMessagePreview = function(type) {
|
||||
return (options.links && type === "link") || (options.thumbnails && type === "image");
|
||||
};
|
||||
|
||||
for (var i in options) {
|
||||
if (i === "userStyles") {
|
||||
if (!/[?&]nocss/.test(window.location.search)) {
|
||||
$(document.head).find("#user-specified-css").html(options[i]);
|
||||
module.exports.initialize = () => {
|
||||
module.exports.initialize = null;
|
||||
|
||||
const settings = $("#settings");
|
||||
|
||||
for (var i in options) {
|
||||
if (i === "userStyles") {
|
||||
settings.find("#user-specified-css-input").val(options[i]);
|
||||
} else if (i === "highlights") {
|
||||
settings.find("input[name=" + i + "]").val(options[i]);
|
||||
} else if (i === "statusMessages") {
|
||||
settings.find(`input[name=${i}][value=${options[i]}]`)
|
||||
.prop("checked", true);
|
||||
} else if (i === "theme") {
|
||||
$("#theme").attr("href", "themes/" + options[i] + ".css");
|
||||
settings.find("select[name=" + i + "]").val(options[i]);
|
||||
} else if (options[i]) {
|
||||
settings.find("input[name=" + i + "]").prop("checked", true);
|
||||
}
|
||||
settings.find("#user-specified-css-input").val(options[i]);
|
||||
} else if (i === "highlights") {
|
||||
settings.find("input[name=" + i + "]").val(options[i]);
|
||||
} else if (i === "statusMessages") {
|
||||
settings.find(`input[name=${i}][value=${options[i]}]`)
|
||||
.prop("checked", true);
|
||||
} else if (i === "theme") {
|
||||
$("#theme").attr("href", "themes/" + options[i] + ".css");
|
||||
settings.find("select[name=" + i + "]").val(options[i]);
|
||||
} else if (options[i]) {
|
||||
settings.find("input[name=" + i + "]").prop("checked", true);
|
||||
}
|
||||
}
|
||||
|
||||
settings.on("change", "input, select, textarea", function() {
|
||||
const self = $(this);
|
||||
const type = self.attr("type");
|
||||
const name = self.attr("name");
|
||||
settings.on("change", "input, select, textarea", function() {
|
||||
const self = $(this);
|
||||
const type = self.attr("type");
|
||||
const name = self.attr("name");
|
||||
|
||||
if (type === "password") {
|
||||
return;
|
||||
} else if (type === "radio") {
|
||||
if (self.prop("checked")) {
|
||||
if (type === "password") {
|
||||
return;
|
||||
} else if (type === "radio") {
|
||||
if (self.prop("checked")) {
|
||||
options[name] = self.val();
|
||||
}
|
||||
} else if (type === "checkbox") {
|
||||
options[name] = self.prop("checked");
|
||||
} else {
|
||||
options[name] = self.val();
|
||||
}
|
||||
} else if (type === "checkbox") {
|
||||
options[name] = self.prop("checked");
|
||||
|
||||
storage.set("settings", JSON.stringify(options));
|
||||
|
||||
if (name === "motd") {
|
||||
chat.toggleClass("hide-" + name, !self.prop("checked"));
|
||||
} else if (name === "statusMessages") {
|
||||
chat.toggleClass("hide-status-messages", options[name] === "hidden");
|
||||
chat.toggleClass("condensed-status-messages", options[name] === "condensed");
|
||||
} else if (name === "coloredNicks") {
|
||||
chat.toggleClass("colored-nicks", self.prop("checked"));
|
||||
} else if (name === "theme") {
|
||||
$("#theme").attr("href", "themes/" + options[name] + ".css");
|
||||
} else if (name === "userStyles") {
|
||||
userStyles.html(options[name]);
|
||||
} else if (name === "highlights") {
|
||||
var highlightString = options[name];
|
||||
options.highlights = highlightString.split(",").map(function(h) {
|
||||
return h.trim();
|
||||
}).filter(function(h) {
|
||||
// Ensure we don't have empty string in the list of highlights
|
||||
// otherwise, users get notifications for everything
|
||||
return h !== "";
|
||||
});
|
||||
// Construct regex with wordboundary for every highlight item
|
||||
const highlightsTokens = options.highlights.map(function(h) {
|
||||
return escapeRegExp(h);
|
||||
});
|
||||
if (highlightsTokens && highlightsTokens.length) {
|
||||
module.exports.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i");
|
||||
} else {
|
||||
module.exports.highlightsRE = null;
|
||||
}
|
||||
} else if (name === "showSeconds") {
|
||||
chat.find(".msg > .time").each(function() {
|
||||
$(this).text(tz($(this).parent().data("time")));
|
||||
});
|
||||
chat.toggleClass("show-seconds", self.prop("checked"));
|
||||
} else if (name === "autocomplete") {
|
||||
if (self.prop("checked")) {
|
||||
$("#input").trigger("autocomplete:on");
|
||||
} else {
|
||||
$("#input").textcomplete("destroy");
|
||||
}
|
||||
}
|
||||
}).find("input")
|
||||
.trigger("change");
|
||||
|
||||
$("#desktopNotifications").on("change", function() {
|
||||
if ($(this).prop("checked") && Notification.permission !== "granted") {
|
||||
Notification.requestPermission(updateDesktopNotificationStatus);
|
||||
}
|
||||
});
|
||||
|
||||
// Updates the checkbox and warning in settings when the Settings page is
|
||||
// opened or when the checkbox state is changed.
|
||||
// When notifications are not supported, this is never called (because
|
||||
// checkbox state can not be changed).
|
||||
var updateDesktopNotificationStatus = function() {
|
||||
if (Notification.permission === "denied") {
|
||||
desktopNotificationsCheckbox.attr("disabled", true);
|
||||
desktopNotificationsCheckbox.attr("checked", false);
|
||||
warningBlocked.show();
|
||||
} else {
|
||||
if (Notification.permission === "default" && desktopNotificationsCheckbox.prop("checked")) {
|
||||
desktopNotificationsCheckbox.attr("checked", false);
|
||||
}
|
||||
desktopNotificationsCheckbox.attr("disabled", false);
|
||||
warningBlocked.hide();
|
||||
}
|
||||
};
|
||||
|
||||
// If browser does not support notifications, override existing settings and
|
||||
// display proper message in settings.
|
||||
var desktopNotificationsCheckbox = $("#desktopNotifications");
|
||||
var warningUnsupported = $("#warnUnsupportedDesktopNotifications");
|
||||
var warningBlocked = $("#warnBlockedDesktopNotifications");
|
||||
warningBlocked.hide();
|
||||
if (("Notification" in window)) {
|
||||
warningUnsupported.hide();
|
||||
windows.on("show", "#settings", updateDesktopNotificationStatus);
|
||||
} else {
|
||||
options[name] = self.val();
|
||||
}
|
||||
|
||||
storage.set("settings", JSON.stringify(options));
|
||||
|
||||
if (name === "motd") {
|
||||
chat.toggleClass("hide-" + name, !self.prop("checked"));
|
||||
} else if (name === "statusMessages") {
|
||||
chat.toggleClass("hide-status-messages", options[name] === "hidden");
|
||||
chat.toggleClass("condensed-status-messages", options[name] === "condensed");
|
||||
} else if (name === "coloredNicks") {
|
||||
chat.toggleClass("colored-nicks", self.prop("checked"));
|
||||
} else if (name === "theme") {
|
||||
$("#theme").attr("href", "themes/" + options[name] + ".css");
|
||||
} else if (name === "userStyles") {
|
||||
userStyles.html(options[name]);
|
||||
} else if (name === "highlights") {
|
||||
var highlightString = options[name];
|
||||
options.highlights = highlightString.split(",").map(function(h) {
|
||||
return h.trim();
|
||||
}).filter(function(h) {
|
||||
// Ensure we don't have empty string in the list of highlights
|
||||
// otherwise, users get notifications for everything
|
||||
return h !== "";
|
||||
});
|
||||
// Construct regex with wordboundary for every highlight item
|
||||
const highlightsTokens = options.highlights.map(function(h) {
|
||||
return escapeRegExp(h);
|
||||
});
|
||||
if (highlightsTokens && highlightsTokens.length) {
|
||||
module.exports.highlightsRE = new RegExp("\\b(?:" + highlightsTokens.join("|") + ")\\b", "i");
|
||||
} else {
|
||||
module.exports.highlightsRE = null;
|
||||
}
|
||||
} else if (name === "showSeconds") {
|
||||
chat.find(".msg > .time").each(function() {
|
||||
$(this).text(tz($(this).parent().data("time")));
|
||||
});
|
||||
chat.toggleClass("show-seconds", self.prop("checked"));
|
||||
} else if (name === "autocomplete") {
|
||||
if (self.prop("checked")) {
|
||||
$("#input").trigger("autocomplete:on");
|
||||
} else {
|
||||
$("#input").textcomplete("destroy");
|
||||
}
|
||||
}
|
||||
}).find("input")
|
||||
.trigger("change");
|
||||
|
||||
$("#desktopNotifications").on("change", function() {
|
||||
if ($(this).prop("checked") && Notification.permission !== "granted") {
|
||||
Notification.requestPermission(updateDesktopNotificationStatus);
|
||||
}
|
||||
});
|
||||
|
||||
// Updates the checkbox and warning in settings when the Settings page is
|
||||
// opened or when the checkbox state is changed.
|
||||
// When notifications are not supported, this is never called (because
|
||||
// checkbox state can not be changed).
|
||||
var updateDesktopNotificationStatus = function() {
|
||||
if (Notification.permission === "denied") {
|
||||
options.desktopNotifications = false;
|
||||
desktopNotificationsCheckbox.attr("disabled", true);
|
||||
desktopNotificationsCheckbox.attr("checked", false);
|
||||
warningBlocked.show();
|
||||
} else {
|
||||
if (Notification.permission === "default" && desktopNotificationsCheckbox.prop("checked")) {
|
||||
desktopNotificationsCheckbox.attr("checked", false);
|
||||
}
|
||||
desktopNotificationsCheckbox.attr("disabled", false);
|
||||
warningBlocked.hide();
|
||||
}
|
||||
};
|
||||
|
||||
// If browser does not support notifications, override existing settings and
|
||||
// display proper message in settings.
|
||||
var desktopNotificationsCheckbox = $("#desktopNotifications");
|
||||
var warningUnsupported = $("#warnUnsupportedDesktopNotifications");
|
||||
var warningBlocked = $("#warnBlockedDesktopNotifications");
|
||||
warningBlocked.hide();
|
||||
if (("Notification" in window)) {
|
||||
warningUnsupported.hide();
|
||||
windows.on("show", "#settings", updateDesktopNotificationStatus);
|
||||
} else {
|
||||
options.desktopNotifications = false;
|
||||
desktopNotificationsCheckbox.attr("disabled", true);
|
||||
desktopNotificationsCheckbox.attr("checked", false);
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ const $ = require("jquery");
|
||||
const socket = require("../socket");
|
||||
const storage = require("../localStorage");
|
||||
const utils = require("../utils");
|
||||
const templates = require("../../views");
|
||||
|
||||
const login = $("#sign-in").html(templates.windows.sign_in());
|
||||
|
||||
socket.on("auth", function(data) {
|
||||
// If we reconnected and serverHash differs, that means the server restarted
|
||||
@ -17,12 +20,28 @@ socket.on("auth", function(data) {
|
||||
|
||||
utils.serverHash = data.serverHash;
|
||||
|
||||
const login = $("#sign-in");
|
||||
let token;
|
||||
const user = storage.get("user");
|
||||
|
||||
login.find(".btn").prop("disabled", false);
|
||||
|
||||
login.find("form").on("submit", function() {
|
||||
const form = $(this);
|
||||
|
||||
form.find(".btn").attr("disabled", true);
|
||||
|
||||
const values = {};
|
||||
$.each(form.serializeArray(), function(i, obj) {
|
||||
values[obj.name] = obj.value;
|
||||
});
|
||||
|
||||
storage.set("user", values.user);
|
||||
|
||||
socket.emit("auth", values);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!data.success) {
|
||||
if (login.length === 0) {
|
||||
socket.disconnect();
|
||||
|
68
client/js/socket-events/configuration.js
Normal file
68
client/js/socket-events/configuration.js
Normal file
@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
const $ = require("jquery");
|
||||
const socket = require("../socket");
|
||||
const templates = require("../../views");
|
||||
const options = require("../options");
|
||||
const webpush = require("../webpush");
|
||||
|
||||
socket.on("configuration", function(data) {
|
||||
if (!options.initialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#settings").html(templates.windows.settings(data));
|
||||
$("#connect").html(templates.windows.connect(data));
|
||||
$("#help").html(templates.windows.help(data));
|
||||
|
||||
$("#play").on("click", () => {
|
||||
const pop = new Audio();
|
||||
pop.src = "audio/pop.ogg";
|
||||
pop.play();
|
||||
});
|
||||
|
||||
options.initialize();
|
||||
webpush.initialize();
|
||||
|
||||
const forms = $("#connect form, #change-password form");
|
||||
|
||||
forms.on("submit", function() {
|
||||
const form = $(this);
|
||||
const event = form.data("event");
|
||||
|
||||
form.find(".btn").attr("disabled", true);
|
||||
|
||||
const values = {};
|
||||
$.each(form.serializeArray(), function(i, obj) {
|
||||
if (obj.value !== "") {
|
||||
values[obj.name] = obj.value;
|
||||
}
|
||||
});
|
||||
|
||||
socket.emit(event, values);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$(".nick")
|
||||
.on("focusin", function() {
|
||||
// Need to set the first "lastvalue", so it can be used in the below function
|
||||
const nick = $(this);
|
||||
nick.data("lastvalue", nick.val());
|
||||
})
|
||||
.on("input", function() {
|
||||
const nick = $(this).val();
|
||||
const usernameInput = forms.find(".username");
|
||||
|
||||
// Because this gets called /after/ it has already changed, we need use the previous value
|
||||
const lastValue = $(this).data("lastvalue");
|
||||
|
||||
// They were the same before the change, so update the username field
|
||||
if (usernameInput.val() === lastValue) {
|
||||
usernameInput.val(nick);
|
||||
}
|
||||
|
||||
// Store the "previous" value, for next time
|
||||
$(this).data("lastvalue", nick);
|
||||
});
|
||||
});
|
@ -18,3 +18,4 @@ require("./topic");
|
||||
require("./users");
|
||||
require("./sign_out");
|
||||
require("./sessions_list");
|
||||
require("./configuration");
|
||||
|
@ -21,8 +21,6 @@ try {
|
||||
};
|
||||
}
|
||||
|
||||
$("#play").on("click", () => pop.play());
|
||||
|
||||
socket.on("msg", function(data) {
|
||||
// We set a maximum timeout of 2 seconds so that messages don't take too long to appear.
|
||||
utils.requestIdleCallback(() => processReceivedMessage(data), 2000);
|
||||
|
@ -4,7 +4,7 @@ const $ = require("jquery");
|
||||
const storage = require("./localStorage");
|
||||
const socket = require("./socket");
|
||||
|
||||
const pushNotificationsButton = $("#pushNotifications");
|
||||
let pushNotificationsButton;
|
||||
let clientSubscribed = null;
|
||||
let applicationServerKey;
|
||||
|
||||
@ -29,7 +29,13 @@ module.exports.configurePushNotifications = (subscribedOnServer, key) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (isAllowedServiceWorkersHost()) {
|
||||
module.exports.initialize = () => {
|
||||
pushNotificationsButton = $("#pushNotifications");
|
||||
|
||||
if (!isAllowedServiceWorkersHost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#pushNotificationsHttps").hide();
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
@ -57,7 +63,7 @@ if (isAllowedServiceWorkersHost()) {
|
||||
$("#pushNotificationsUnsupported span").text(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onPushButton() {
|
||||
pushNotificationsButton.attr("disabled", true);
|
||||
|
@ -20,6 +20,13 @@ module.exports = {
|
||||
whois: require("./actions/whois.tpl"),
|
||||
},
|
||||
|
||||
windows: {
|
||||
sign_in: require("./windows/sign_in.tpl"),
|
||||
settings: require("./windows/settings.tpl"),
|
||||
connect: require("./windows/connect.tpl"),
|
||||
help: require("./windows/help.tpl"),
|
||||
},
|
||||
|
||||
chan: require("./chan.tpl"),
|
||||
chat: require("./chat.tpl"),
|
||||
contextmenu_divider: require("./contextmenu_divider.tpl"),
|
||||
|
88
client/views/windows/connect.tpl
Normal file
88
client/views/windows/connect.tpl
Normal file
@ -0,0 +1,88 @@
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<form class="container" method="post" action="" data-event="conn">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h1 class="title">
|
||||
{{#if public}}The Lounge - {{/if}}
|
||||
Connect
|
||||
{{#unless displayNetwork}}
|
||||
{{#if lockNetwork}}
|
||||
to {{defaults.name}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</h1>
|
||||
</div>
|
||||
{{#if displayNetwork}}
|
||||
<div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Network settings</h2>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:name">Name</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:name" name="name" value="{{defaults.name}}">
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:host">Server</label>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-8">
|
||||
<input class="input" id="connect:host" name="host" value="{{defaults.host}}" aria-label="Server address" {{#if lockNetwork}}disabled{{/if}}>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-4">
|
||||
<div class="port">
|
||||
<input class="input" type="number" min="1" max="65535" name="port" value="{{defaults.port}}" aria-label="Server port" {{#if lockNetwork}}disabled{{/if}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:password">Password</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:password" type="password" name="password" value="{{defaults.password}}">
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3">
|
||||
<label class="tls">
|
||||
<input type="checkbox" name="tls" {{#if defaults.tls}}checked{{/if}} {{#if lockNetwork}}disabled{{/if}}>
|
||||
Enable TLS/SSL
|
||||
</label>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="col-sm-12">
|
||||
<h2>User preferences</h2>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:nick">Nick</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input nick" id="connect:nick" name="nick" value="{{defaults.nick}}">
|
||||
</div>
|
||||
{{#unless useHexIp}}
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:username">Username</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input username" id="connect:username" name="username" value="{{defaults.username}}">
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:realname">Real name</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:realname" name="realname" value="{{defaults.realname}}">
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<label for="connect:channels">Channels</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input class="input" id="connect:channels" name="join" value="{{defaults.join}}">
|
||||
</div>
|
||||
<div class="col-sm-9 col-sm-offset-3">
|
||||
<button type="submit" class="btn">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
482
client/views/windows/help.tpl
Normal file
482
client/views/windows/help.tpl
Normal file
@ -0,0 +1,482 @@
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1 class="title">Help</h1>
|
||||
|
||||
<h2>Keyboard Shortcuts</h2>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>↑</kbd> / <kbd>↓</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Switch to the previous/next window in the channel list</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>K</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Mark any text typed after this shortcut to be colored. After
|
||||
hitting this shortcut, enter an integer in the range
|
||||
<code>0—15</code> to select the desired color, or use the
|
||||
autocompletion menu to choose a color name (see below).
|
||||
</p>
|
||||
<p>
|
||||
Background color can be specified by putting a comma and
|
||||
another integer in the range <code>0—15</code> after the
|
||||
foreground color number (autocompletion works too).
|
||||
</p>
|
||||
<p>
|
||||
A color reference can be found
|
||||
<a href="https://modern.ircdocs.horse/formatting.html#colors" target="_blank" rel="noopener">here</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>B</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as bold.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>U</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as underlined.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>I</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark all text typed after this shortcut as italics.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>O</kbd>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Mark all text typed after this shortcut to be reset to its
|
||||
original formatting.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Autocompletion</h2>
|
||||
|
||||
<p>
|
||||
To auto-complete nicknames, channels, commands, and emoji, type one of the characters below to open
|
||||
a suggestion list. Use the <kbd>↑</kbd> and <kbd>↓</kbd> keys to highlight an item, and insert it by
|
||||
pressing <kbd>Tab</kbd> or <kbd>Enter</kbd> (or by clicking the desired item).
|
||||
</p>
|
||||
<p>
|
||||
Autocompletion can be disabled in settings.
|
||||
</p>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>@</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Nickname</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>#</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Channel</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Commands (see list of commands below)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>:</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Emoji (note: requires two search characters, to avoid conflicting with common emoticons like <code>:)</code>)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Commands</h2>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/away [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Mark yourself as away with an optional message.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/back</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Remove your away status (set with <code>/away</code>).</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/ban nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Ban (<code>+b</code>) a user from the current channel.
|
||||
This can be a nickname or a hostmask.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/banlist</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Load the banlist for the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/collapse</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Collapse all previews in the current channel (opposite of
|
||||
<code>/expand</code>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/connect host [port]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Connect to a new IRC network. If <code>port</code> starts with
|
||||
a <code>+</code> sign, the connection will be made secure
|
||||
using TLS.
|
||||
</p>
|
||||
<p>Alias: <code>/server</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/ctcp target cmd [args]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Send a <abbr title="Client-to-client protocol">CTCP</abbr>
|
||||
request. Read more about this on
|
||||
<a href="https://en.wikipedia.org/wiki/Client-to-client_protocol" target="_blank" rel="noopener">the dedicated Wikipedia article</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/deop nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Remove op (<code>-o</code>) from one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/devoice nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Remove voice (<code>-v</code>) from one or several users in
|
||||
the current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/disconnect [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Disconnect from the current network with an
|
||||
optionally-provided message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/expand</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Expand all previews in the current channel (opposite of
|
||||
<code>/collapse</code>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/invite nick [channel]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Invite a user to the specified channel. If
|
||||
<code>channel</code> is ommitted, user will be invited to the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/join channel</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Join a channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/kick nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Kick a user from the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/list</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Retrieve a list of available channels on this network.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/me message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Send an action message to the current channel. The Lounge will
|
||||
display it inline, as if the message was posted in the third
|
||||
person.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/mode flags [args]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Set the given flags to the current channel if the active
|
||||
window is a channel, another user if the active window is a
|
||||
private message window, or yourself if the current window is a
|
||||
server window.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/msg channel message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a message to the specified channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/nick newnick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Change your nickname on the current network.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/notice channel message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Sends a notice message to the specified channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/op nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Give op (<code>+o</code>) to one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/part [channel]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Close the specified channel or private message window, or the
|
||||
current channel if <code>channel</code> is ommitted.
|
||||
</p>
|
||||
<p>Aliases: <code>/close</code>, <code>/leave</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/rejoin</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Leave and immediately rejoin the current channel. Useful to
|
||||
quickly get op from ChanServ in an empty channel, for example.
|
||||
</p>
|
||||
<p>Alias: <code>/cycle</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/query nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a private message to the specified user.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/quit [message]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Disconnect from the current network with an optional message.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/raw message</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Send a raw message to the current IRC network.</p>
|
||||
<p>Aliases: <code>/quote</code>, <code>/send</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/slap nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Slap someone in the current channel with a trout!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/topic newtopic</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Set the topic in the current channel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/unban nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>Unban (<code>-b</code>) a user from the current channel.
|
||||
This can be a nickname or a hostmask.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/voice nick [...nick]</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Give voice (<code>+v</code>) to one or several users in the
|
||||
current channel.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-item">
|
||||
<div class="subject">
|
||||
<code>/whois nick</code>
|
||||
</div>
|
||||
<div class="description">
|
||||
<p>
|
||||
Retrieve information about the given user on the current
|
||||
network.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>About The Lounge</h2>
|
||||
|
||||
<p class="about">
|
||||
{{#if gitCommit}}
|
||||
The Lounge is running from source
|
||||
(<a href="https://github.com/thelounge/lounge/tree/{{ gitCommit }}" target="_blank" rel="noopener"><code>{{ gitCommit }}</code></a>).<br>
|
||||
{{else}}
|
||||
The Lounge is in version <strong>{{version}}</strong>
|
||||
(<a href="https://github.com/thelounge/lounge/releases/tag/v{{ version }}" target="_blank" rel="noopener">See release notes</a>).<br>
|
||||
{{/if}}
|
||||
|
||||
<a href="https://thelounge.github.io/" target="_blank" rel="noopener">Website</a><br>
|
||||
<a href="https://thelounge.github.io/docs/" target="_blank" rel="noopener">Documentation</a><br>
|
||||
<a href="https://github.com/thelounge/lounge/issues/new" target="_blank" rel="noopener">Report a bug</a>
|
||||
</p>
|
||||
</div>
|
193
client/views/windows/settings.tpl
Normal file
193
client/views/windows/settings.tpl
Normal file
@ -0,0 +1,193 @@
|
||||
<div class="header">
|
||||
<button class="lt" aria-label="Toggle channel list"></button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1 class="title">Settings</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h2>Messages</h2>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="motd">
|
||||
Show <abbr title="Message Of The Day">MOTD</abbr>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="showSeconds">
|
||||
Show seconds in timestamp
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>
|
||||
Status messages
|
||||
<span class="tooltipped tooltipped-n tooltipped-no-delay" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes">
|
||||
<button class="extra-help" aria-label="Joins, parts, kicks, nick changes, away changes, and mode changes"></button>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="shown">
|
||||
Show all status messages individually
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="condensed">
|
||||
Condense status messages together
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="radio" name="statusMessages" value="hidden">
|
||||
Hide all status messages
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Visual Aids</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="coloredNicks">
|
||||
Enable colored nicknames
|
||||
</label>
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="autocomplete">
|
||||
Enable autocomplete
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<h2>Theme</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="theme-select" class="sr-only">Theme</label>
|
||||
<select id="theme-select" name="theme" class="input">
|
||||
{{#each themes}}
|
||||
<option value="{{name}}">
|
||||
{{displayName}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
{{#if prefetch}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Link previews</h2>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="thumbnails">
|
||||
Auto-expand images
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="links">
|
||||
Auto-expand websites
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#unless public}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Push Notifications</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<button type="button" class="btn" id="pushNotifications" disabled data-text-alternate="Unsubscribe from push notifications">Subscribe to push notifications</button>
|
||||
<div class="error" id="pushNotificationsHttps">
|
||||
<strong>Warning</strong>:
|
||||
Push notifications are only supported over HTTPS connections.
|
||||
</div>
|
||||
<div class="error" id="pushNotificationsUnsupported">
|
||||
<strong>Warning</strong>:
|
||||
<span>Push notifications are not supported by your browser.</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Browser Notifications</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input id="desktopNotifications" type="checkbox" name="desktopNotifications">
|
||||
Enable browser notifications<br>
|
||||
<div class="error" id="warnUnsupportedDesktopNotifications">
|
||||
<strong>Warning</strong>:
|
||||
Notifications are not supported by your browser.
|
||||
</div>
|
||||
<div class="error" id="warnBlockedDesktopNotifications">
|
||||
<strong>Warning</strong>:
|
||||
Notifications are blocked by your browser.
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="notification">
|
||||
Enable notification sound
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="opt">
|
||||
<button id="play">Play sound</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<input type="checkbox" name="notifyAllMessages">
|
||||
Enable notification for all messages
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<label class="opt">
|
||||
<label for="highlights" class="sr-only">Custom highlights (comma-separated keywords)</label>
|
||||
<input type="text" id="highlights" name="highlights" class="input" placeholder="Custom highlights (comma-separated keywords)">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{#unless public}}
|
||||
{{#unless ldapEnabled}}
|
||||
<div id="change-password">
|
||||
<form action="" method="post" data-event="change-password">
|
||||
<div class="col-sm-12">
|
||||
<h2>Change password</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="old_password_input" class="sr-only">Enter current password</label>
|
||||
<input type="password" id="old_password_input" name="old_password" class="input" placeholder="Enter current password">
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="new_password_input" class="sr-only">Enter desired new password</label>
|
||||
<input type="password" id="new_password_input" name="new_password" class="input" placeholder="Enter desired new password">
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<label for="verify_password_input" class="sr-only">Repeat new password</label>
|
||||
<input type="password" id="verify_password_input" name="verify_password" class="input" placeholder="Repeat new password">
|
||||
</div>
|
||||
<div class="col-sm-12 feedback"></div>
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn">Change password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
<div class="col-sm-12">
|
||||
<h2>Custom Stylesheet</h2>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<textarea class="input" name="userStyles" id="user-specified-css-input" placeholder="/* You can override any style with CSS here */"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#unless public}}
|
||||
<div class="session-list">
|
||||
<h2>Sessions</h2>
|
||||
|
||||
<h3>Current session</h3>
|
||||
<div id="session-current"></div>
|
||||
|
||||
<h3>Other sessions</h3>
|
||||
<div id="session-list"></div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
23
client/views/windows/sign_in.tpl
Normal file
23
client/views/windows/sign_in.tpl
Normal file
@ -0,0 +1,23 @@
|
||||
<form class="container" method="post" action="">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h1 class="title">Sign in to The Lounge</h1>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<label>
|
||||
Username
|
||||
<input class="input" name="user" autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-12">
|
||||
<label>
|
||||
Password
|
||||
<input class="input" type="password" name="password">
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-12 error" style="display: none;">Authentication failed.</div>
|
||||
<div class="col-xs-12">
|
||||
<button type="submit" class="btn">Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -43,7 +43,6 @@
|
||||
"colors": "1.1.2",
|
||||
"commander": "2.11.0",
|
||||
"express": "4.16.2",
|
||||
"express-handlebars": "3.0.0",
|
||||
"fs-extra": "4.0.2",
|
||||
"irc-framework": "2.9.1",
|
||||
"ldapjs": "1.0.1",
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
global.log = require("../log.js");
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const program = require("commander");
|
||||
const colors = require("colors/safe");
|
||||
const Helper = require("../helper");
|
||||
@ -22,6 +24,18 @@ if (program.home) {
|
||||
log.warn(`Use the ${colors.green("LOUNGE_HOME")} environment variable instead.`);
|
||||
}
|
||||
|
||||
// Check if the app was built before calling setHome as it wants to load manifest.json from the public folder
|
||||
if (!fs.existsSync(path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"public",
|
||||
"manifest.json"
|
||||
))) {
|
||||
log.error(`The client application was not built. Run ${colors.bold("NODE_ENV=production npm run build")} to resolve this.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let home = program.home || process.env.LOUNGE_HOME;
|
||||
|
||||
if (!home) {
|
||||
|
@ -78,6 +78,10 @@ function setHome(homePath) {
|
||||
log.warn(`${colors.bold("displayNetwork")} and ${colors.bold("lockNetwork")} are false, setting ${colors.bold("lockNetwork")} to true.`);
|
||||
}
|
||||
|
||||
// Load theme color from manifest.json
|
||||
const manifest = require("../public/manifest.json");
|
||||
this.config.themeColor = manifest.theme_color;
|
||||
|
||||
// TODO: Remove in future release
|
||||
if (this.config.debug === true) {
|
||||
log.warn("debug option is now an object, see defaults file for more information.");
|
||||
|
@ -5,7 +5,6 @@ var pkg = require("../package.json");
|
||||
var Client = require("./client");
|
||||
var ClientManager = require("./clientManager");
|
||||
var express = require("express");
|
||||
var expressHandlebars = require("express-handlebars");
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var io = require("socket.io");
|
||||
@ -33,27 +32,15 @@ module.exports = function() {
|
||||
(Node.js ${colors.green(process.versions.node)} on ${colors.green(process.platform)} ${process.arch})`);
|
||||
log.info(`Configuration file: ${colors.green(Helper.CONFIG_PATH)}`);
|
||||
|
||||
if (!fs.existsSync("public/js/bundle.js")) {
|
||||
log.error(`The client application was not built. Run ${colors.bold("NODE_ENV=production npm run build")} to resolve this.`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
var app = express()
|
||||
.disable("x-powered-by")
|
||||
.use(allRequests)
|
||||
.use(index)
|
||||
.use(express.static("public"))
|
||||
.use("/storage/", express.static(Helper.getStoragePath(), {
|
||||
redirect: false,
|
||||
maxAge: 86400 * 1000,
|
||||
}))
|
||||
.engine("html", expressHandlebars({
|
||||
extname: ".html",
|
||||
helpers: {
|
||||
tojson: (c) => JSON.stringify(c),
|
||||
},
|
||||
}))
|
||||
.set("view engine", "html")
|
||||
.set("views", path.join(__dirname, "..", "public"));
|
||||
}));
|
||||
|
||||
app.get("/themes/:theme.css", (req, res) => {
|
||||
const themeName = req.params.theme;
|
||||
@ -205,13 +192,6 @@ function index(req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
var data = _.merge(
|
||||
pkg,
|
||||
Helper.config
|
||||
);
|
||||
data.gitCommit = Helper.getGitCommit();
|
||||
data.themes = themes.getAll();
|
||||
|
||||
const policies = [
|
||||
"default-src *",
|
||||
"connect-src 'self' ws: wss:",
|
||||
@ -228,9 +208,17 @@ function index(req, res, next) {
|
||||
policies.unshift("block-all-mixed-content");
|
||||
}
|
||||
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.setHeader("Content-Security-Policy", policies.join("; "));
|
||||
res.setHeader("Referrer-Policy", "no-referrer");
|
||||
res.render("index", data);
|
||||
|
||||
return fs.readFile(path.join(__dirname, "..", "public", "index.html"), "utf-8", (err, file) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
res.send(_.template(file)(Helper.config));
|
||||
});
|
||||
}
|
||||
|
||||
function initializeClient(socket, client, token, lastMessage) {
|
||||
@ -467,6 +455,28 @@ function initializeClient(socket, client, token, lastMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
function getClientConfiguration() {
|
||||
const config = _.pick(Helper.config, [
|
||||
"public",
|
||||
"lockNetwork",
|
||||
"displayNetwork",
|
||||
"useHexIp",
|
||||
"themes",
|
||||
"prefetch",
|
||||
]);
|
||||
|
||||
config.ldapEnabled = Helper.config.ldap.enable;
|
||||
config.version = pkg.version;
|
||||
config.gitCommit = Helper.getGitCommit();
|
||||
config.themes = themes.getAll();
|
||||
|
||||
if (config.displayNetwork) {
|
||||
config.defaults = Helper.config.defaults;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
function performAuthentication(data) {
|
||||
const socket = this;
|
||||
let client;
|
||||
@ -474,6 +484,8 @@ function performAuthentication(data) {
|
||||
const finalInit = () => initializeClient(socket, client, data.token || null, data.lastMessage || -1);
|
||||
|
||||
const initClient = () => {
|
||||
socket.emit("configuration", getClientConfiguration());
|
||||
|
||||
client.ip = getClientIp(socket);
|
||||
|
||||
// If webirc is enabled perform reverse dns lookup
|
||||
|
@ -23,7 +23,7 @@ describe("Server", () => {
|
||||
request(webURL, (error, response, body) => {
|
||||
expect(error).to.be.null;
|
||||
expect(body).to.include("<title>The Lounge</title>");
|
||||
expect(body).to.include("https://thelounge.github.io/");
|
||||
expect(body).to.include("js/bundle.js");
|
||||
|
||||
done();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user