First
This commit is contained in:
parent
4777db7372
commit
f494f0a959
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
## Local install
|
||||||
|
|
||||||
|
Project is created with netbeans. It would be better to use netbeans to build it.
|
||||||
|
|
||||||
|
## You'll need
|
||||||
|
|
||||||
|
* Netbeans
|
||||||
|
* Java
|
||||||
|
* Server (Tomcat)
|
||||||
|
* Postgresql
|
||||||
|
|
||||||
|
## Modifications you need to do before build
|
||||||
|
|
||||||
|
* 'urrsm.sng.Config' contains important configurations. You need to change them accordingly
|
||||||
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- You may freely edit this file. See commented blocks below for -->
|
||||||
|
<!-- some examples of how to customize the build. -->
|
||||||
|
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||||
|
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||||
|
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||||
|
<!-- the Compile on Save feature is turned off for the project. -->
|
||||||
|
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||||
|
<!-- in the project's Project Properties dialog box.-->
|
||||||
|
<project name="UChats" default="default" basedir=".">
|
||||||
|
<description>Builds, tests, and runs the project UChats.</description>
|
||||||
|
<import file="nbproject/build-impl.xml"/>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
There exist several targets which are by default empty and which can be
|
||||||
|
used for execution of your tasks. These targets are usually executed
|
||||||
|
before and after some main targets. They are:
|
||||||
|
|
||||||
|
-pre-init: called before initialization of project properties
|
||||||
|
-post-init: called after initialization of project properties
|
||||||
|
-pre-compile: called before javac compilation
|
||||||
|
-post-compile: called after javac compilation
|
||||||
|
-pre-compile-single: called before javac compilation of single file
|
||||||
|
-post-compile-single: called after javac compilation of single file
|
||||||
|
-pre-compile-test: called before javac compilation of JUnit tests
|
||||||
|
-post-compile-test: called after javac compilation of JUnit tests
|
||||||
|
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||||
|
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||||
|
-pre-dist: called before archive building
|
||||||
|
-post-dist: called after archive building
|
||||||
|
-post-clean: called after cleaning build products
|
||||||
|
-pre-run-deploy: called before deploying
|
||||||
|
-post-run-deploy: called after deploying
|
||||||
|
|
||||||
|
Example of pluging an obfuscator after the compilation could look like
|
||||||
|
|
||||||
|
<target name="-post-compile">
|
||||||
|
<obfuscate>
|
||||||
|
<fileset dir="${build.classes.dir}"/>
|
||||||
|
</obfuscate>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
For list of available properties check the imported
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
|
||||||
|
Other way how to customize the build is by overriding existing main targets.
|
||||||
|
The target of interest are:
|
||||||
|
|
||||||
|
init-macrodef-javac: defines macro for javac compilation
|
||||||
|
init-macrodef-junit: defines macro for junit execution
|
||||||
|
init-macrodef-debug: defines macro for class debugging
|
||||||
|
do-dist: archive building
|
||||||
|
run: execution of project
|
||||||
|
javadoc-build: javadoc generation
|
||||||
|
|
||||||
|
Example of overriding the target for project execution could look like
|
||||||
|
|
||||||
|
<target name="run" depends="<PROJNAME>-impl.jar">
|
||||||
|
<exec dir="bin" executable="launcher.exe">
|
||||||
|
<arg file="${dist.jar}"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
Notice that overridden target depends on jar target and not only on
|
||||||
|
compile target as regular run target does. Again, for list of available
|
||||||
|
properties which you can use check the target you are overriding in
|
||||||
|
nbproject/build-impl.xml file.
|
||||||
|
|
||||||
|
-->
|
||||||
|
</project>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||||
|
|
||||||
|
Copyright (c) 2006, 2016 Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
|
||||||
|
Other names may be trademarks of their respective owners.
|
||||||
|
|
||||||
|
The contents of this file are subject to the terms of either the GNU
|
||||||
|
General Public License Version 2 only ("GPL") or the Common
|
||||||
|
Development and Distribution License("CDDL") (collectively, the
|
||||||
|
"License"). You may not use this file except in compliance with the
|
||||||
|
License. You can obtain a copy of the License at
|
||||||
|
http://www.netbeans.org/cddl-gplv2.html
|
||||||
|
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
|
||||||
|
specific language governing permissions and limitations under the
|
||||||
|
License. When distributing the software, include this License Header
|
||||||
|
Notice in each file and include the License file at
|
||||||
|
nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
|
||||||
|
particular file as subject to the "Classpath" exception as provided
|
||||||
|
by Oracle in the GPL Version 2 section of the License file that
|
||||||
|
accompanied this code. If applicable, add the following below the
|
||||||
|
License Header, with the fields enclosed by brackets [] replaced by
|
||||||
|
your own identifying information:
|
||||||
|
"Portions Copyrighted [year] [name of copyright owner]"
|
||||||
|
|
||||||
|
If you wish your version of this file to be governed by only the CDDL
|
||||||
|
or only the GPL Version 2, indicate your decision by adding
|
||||||
|
"[Contributor] elects to include this software in this distribution
|
||||||
|
under the [CDDL or GPL Version 2] license." If you do not indicate a
|
||||||
|
single choice of license, a recipient has the option to distribute
|
||||||
|
your version of this file under either the CDDL, the GPL Version 2 or
|
||||||
|
to extend the choice of license to its licensees as provided above.
|
||||||
|
However, if you add GPL Version 2 code and therefore, elected the GPL
|
||||||
|
Version 2 license, then the option applies only if the new code is
|
||||||
|
made subject to such option by the copyright holder.
|
||||||
|
|
||||||
|
Contributor(s):
|
||||||
|
-->
|
||||||
|
<project default="-deploy-ant" basedir=".">
|
||||||
|
<target name="-init" if="deploy.ant.enabled">
|
||||||
|
<property file="${deploy.ant.properties.file}"/>
|
||||||
|
<tempfile property="temp.module.folder" prefix="tomcat" destdir="${java.io.tmpdir}"/>
|
||||||
|
<unwar src="${deploy.ant.archive}" dest="${temp.module.folder}">
|
||||||
|
<patternset includes="META-INF/context.xml"/>
|
||||||
|
</unwar>
|
||||||
|
<xmlproperty file="${temp.module.folder}/META-INF/context.xml"/>
|
||||||
|
<delete dir="${temp.module.folder}"/>
|
||||||
|
</target>
|
||||||
|
<target name="-check-credentials" if="deploy.ant.enabled" depends="-init">
|
||||||
|
<fail message="Tomcat password has to be passed as tomcat.password property.">
|
||||||
|
<condition>
|
||||||
|
<not>
|
||||||
|
<isset property="tomcat.password"/>
|
||||||
|
</not>
|
||||||
|
</condition>
|
||||||
|
</fail>
|
||||||
|
</target>
|
||||||
|
<target name="-deploy-ant" if="deploy.ant.enabled" depends="-init,-check-credentials">
|
||||||
|
<echo message="Deploying ${deploy.ant.archive} to ${Context(path)}"/>
|
||||||
|
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"
|
||||||
|
classpath="${tomcat.home}/server/lib/catalina-ant.jar"/>
|
||||||
|
<deploy url="${tomcat.url}/manager" username="${tomcat.username}"
|
||||||
|
password="${tomcat.password}" path="${Context(path)}"
|
||||||
|
war="${deploy.ant.archive}"/>
|
||||||
|
<property name="deploy.ant.client.url" value="${tomcat.url}${Context(path)}"/>
|
||||||
|
</target>
|
||||||
|
<target name="-undeploy-ant" if="deploy.ant.enabled" depends="-init,-check-credentials">
|
||||||
|
<echo message="Undeploying ${Context(path)}"/>
|
||||||
|
<taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask"
|
||||||
|
classpath="${tomcat.home}/server/lib/catalina-ant.jar"/>
|
||||||
|
<undeploy url="${tomcat.url}/manager" username="${tomcat.username}"
|
||||||
|
password="${tomcat.password}" path="${Context(path)}"/>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,8 @@
|
||||||
|
build.xml.data.CRC32=eee99f0d
|
||||||
|
build.xml.script.CRC32=f46ca8d5
|
||||||
|
build.xml.stylesheet.CRC32=651128d4@1.77.1.1
|
||||||
|
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||||
|
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||||
|
nbproject/build-impl.xml.data.CRC32=eee99f0d
|
||||||
|
nbproject/build-impl.xml.script.CRC32=755bcc5e
|
||||||
|
nbproject/build-impl.xml.stylesheet.CRC32=99ea4b56@1.77.1.1
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
annotation.processing.enabled=true
|
||||||
|
annotation.processing.enabled.in.editor=true
|
||||||
|
annotation.processing.processors.list=
|
||||||
|
annotation.processing.run.all.processors=true
|
||||||
|
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
|
||||||
|
build.classes.dir=${build.web.dir}/WEB-INF/classes
|
||||||
|
build.classes.excludes=**/*.java,**/*.form
|
||||||
|
build.dir=build
|
||||||
|
build.generated.dir=${build.dir}/generated
|
||||||
|
build.generated.sources.dir=${build.dir}/generated-sources
|
||||||
|
build.test.classes.dir=${build.dir}/test/classes
|
||||||
|
build.test.results.dir=${build.dir}/test/results
|
||||||
|
build.web.dir=${build.dir}/web
|
||||||
|
build.web.excludes=${build.classes.excludes}
|
||||||
|
client.urlPart=
|
||||||
|
compile.jsps=false
|
||||||
|
conf.dir=${source.root}/conf
|
||||||
|
debug.classpath=${build.classes.dir}:${javac.classpath}
|
||||||
|
debug.test.classpath=\
|
||||||
|
${run.test.classpath}
|
||||||
|
display.browser=true
|
||||||
|
# Files to be excluded from distribution war
|
||||||
|
dist.archive.excludes=
|
||||||
|
dist.dir=dist
|
||||||
|
dist.ear.war=${dist.dir}/${war.ear.name}
|
||||||
|
dist.javadoc.dir=${dist.dir}/javadoc
|
||||||
|
dist.war=${dist.dir}/${war.name}
|
||||||
|
endorsed.classpath=\
|
||||||
|
${libs.javaee-endorsed-api-7.0.classpath}
|
||||||
|
excludes=
|
||||||
|
file.reference.gson-2.8.2.jar=lib/gson-2.8.2.jar
|
||||||
|
file.reference.postgresql-42.2.1.jar=lib/postgresql-42.2.1.jar
|
||||||
|
includes=**
|
||||||
|
j2ee.compile.on.save=true
|
||||||
|
j2ee.copy.static.files.on.save=true
|
||||||
|
j2ee.deploy.on.save=true
|
||||||
|
j2ee.platform=1.7-web
|
||||||
|
j2ee.platform.classpath=${j2ee.server.home}/lib/annotations-api.jar:${j2ee.server.home}/lib/catalina-ant.jar:${j2ee.server.home}/lib/catalina-ha.jar:${j2ee.server.home}/lib/catalina-storeconfig.jar:${j2ee.server.home}/lib/catalina-tribes.jar:${j2ee.server.home}/lib/catalina.jar:${j2ee.server.home}/lib/ecj-4.4.2.jar:${j2ee.server.home}/lib/el-api.jar:${j2ee.server.home}/lib/jasper-el.jar:${j2ee.server.home}/lib/jasper.jar:${j2ee.server.home}/lib/jsp-api.jar:${j2ee.server.home}/lib/servlet-api.jar:${j2ee.server.home}/lib/tomcat-api.jar:${j2ee.server.home}/lib/tomcat-coyote.jar:${j2ee.server.home}/lib/tomcat-dbcp.jar:${j2ee.server.home}/lib/tomcat-i18n-es.jar:${j2ee.server.home}/lib/tomcat-i18n-fr.jar:${j2ee.server.home}/lib/tomcat-i18n-ja.jar:${j2ee.server.home}/lib/tomcat-jdbc.jar:${j2ee.server.home}/lib/tomcat-jni.jar:${j2ee.server.home}/lib/tomcat-util-scan.jar:${j2ee.server.home}/lib/tomcat-util.jar:${j2ee.server.home}/lib/tomcat-websocket.jar:${j2ee.server.home}/lib/websocket-api.jar
|
||||||
|
j2ee.server.type=Tomcat
|
||||||
|
jar.compress=false
|
||||||
|
javac.classpath=\
|
||||||
|
${file.reference.postgresql-42.2.1.jar}:\
|
||||||
|
${file.reference.gson-2.8.2.jar}
|
||||||
|
# Space-separated list of extra javac options
|
||||||
|
javac.compilerargs=
|
||||||
|
javac.debug=true
|
||||||
|
javac.deprecation=false
|
||||||
|
javac.processorpath=\
|
||||||
|
${javac.classpath}
|
||||||
|
javac.source=1.8
|
||||||
|
javac.target=1.8
|
||||||
|
javac.test.classpath=\
|
||||||
|
${javac.classpath}:\
|
||||||
|
${build.classes.dir}
|
||||||
|
javac.test.processorpath=\
|
||||||
|
${javac.test.classpath}
|
||||||
|
javadoc.additionalparam=
|
||||||
|
javadoc.author=false
|
||||||
|
javadoc.encoding=${source.encoding}
|
||||||
|
javadoc.noindex=false
|
||||||
|
javadoc.nonavbar=false
|
||||||
|
javadoc.notree=false
|
||||||
|
javadoc.preview=true
|
||||||
|
javadoc.private=false
|
||||||
|
javadoc.splitindex=true
|
||||||
|
javadoc.use=true
|
||||||
|
javadoc.version=false
|
||||||
|
javadoc.windowtitle=
|
||||||
|
lib.dir=${web.docbase.dir}/WEB-INF/lib
|
||||||
|
no.dependencies=false
|
||||||
|
persistence.xml.dir=${conf.dir}
|
||||||
|
platform.active=default_platform
|
||||||
|
resource.dir=setup
|
||||||
|
run.test.classpath=\
|
||||||
|
${javac.test.classpath}:\
|
||||||
|
${build.test.classes.dir}
|
||||||
|
# Space-separated list of JVM arguments used when running a class with a main method or a unit test
|
||||||
|
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value):
|
||||||
|
runmain.jvmargs=
|
||||||
|
source.encoding=UTF-8
|
||||||
|
source.reference.gson-2.8.2.jar=lib/gson-2.8.2-sources.jar
|
||||||
|
source.root=src
|
||||||
|
src.dir=${source.root}/java
|
||||||
|
test.src.dir=test
|
||||||
|
war.content.additional=
|
||||||
|
war.ear.name=${war.name}
|
||||||
|
war.name=UChats.war
|
||||||
|
web.docbase.dir=web
|
||||||
|
webinf.dir=web/WEB-INF
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||||
|
<type>org.netbeans.modules.web.project</type>
|
||||||
|
<configuration>
|
||||||
|
<data xmlns="http://www.netbeans.org/ns/web-project/3">
|
||||||
|
<name>UChats</name>
|
||||||
|
<minimum-ant-version>1.6.5</minimum-ant-version>
|
||||||
|
<web-module-libraries>
|
||||||
|
<library dirs="200">
|
||||||
|
<file>${file.reference.postgresql-42.2.1.jar}</file>
|
||||||
|
<path-in-war>WEB-INF/lib</path-in-war>
|
||||||
|
</library>
|
||||||
|
<library dirs="200">
|
||||||
|
<file>${file.reference.gson-2.8.2.jar}</file>
|
||||||
|
<path-in-war>WEB-INF/lib</path-in-war>
|
||||||
|
</library>
|
||||||
|
</web-module-libraries>
|
||||||
|
<web-module-additional-libraries/>
|
||||||
|
<source-roots>
|
||||||
|
<root id="src.dir" name="Source Packages"/>
|
||||||
|
</source-roots>
|
||||||
|
<test-roots>
|
||||||
|
<root id="test.src.dir" name="Test Packages"/>
|
||||||
|
</test-roots>
|
||||||
|
</data>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*
|
||||||
|
* Configurations for UChat Server
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
public static String admin = ""; // name of admin
|
||||||
|
public static String password = "bawal"; // password of admin
|
||||||
|
public static String mods[] = {"4f26ae"}; // trips of mods
|
||||||
|
public static String salt = "My Salt Is This"; // salt for trips
|
||||||
|
|
||||||
|
public static String dburl = "jdbc:postgresql://localhost:5432/database"; // url to database
|
||||||
|
public static String dbusername = "user"; // database username
|
||||||
|
public static String dbpassword = "pass"; // database password
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
if(System.getenv("DATABASE_URL")!=null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URI dbUri = new URI(System.getenv("DATABASE_URL"));
|
||||||
|
dbusername = dbUri.getUserInfo().split(":")[0];
|
||||||
|
dbpassword = dbUri.getUserInfo().split(":")[1];
|
||||||
|
dburl = "jdbc:postgresql://" + dbUri.getHost() + ':' + dbUri.getPort() + dbUri.getPath();
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(Config.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*
|
||||||
|
* Structure of json send between user and server
|
||||||
|
* It is required because we are using Gson
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JsonStructure {
|
||||||
|
String cmd;
|
||||||
|
String nick;
|
||||||
|
String channel;
|
||||||
|
String text;
|
||||||
|
boolean admin;
|
||||||
|
boolean mod;
|
||||||
|
String trip;
|
||||||
|
String ip;
|
||||||
|
ArrayList<String> nicks;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*
|
||||||
|
* Record Manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RecordManager {
|
||||||
|
|
||||||
|
long blocktime = 3600000;
|
||||||
|
|
||||||
|
public RecordManager() throws ClassNotFoundException, SQLException
|
||||||
|
{
|
||||||
|
Class.forName("org.postgresql.Driver");
|
||||||
|
|
||||||
|
String sql = "CREATE TABLE IF NOT EXISTS blocklist(\n" +
|
||||||
|
" ip TEXT NOT NULL,\n" +
|
||||||
|
" time REAL NOT NULL\n"+
|
||||||
|
");";
|
||||||
|
|
||||||
|
try(Connection con = this.connect(); Statement stmt = con.createStatement())
|
||||||
|
{
|
||||||
|
stmt.execute(sql);
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block an ip
|
||||||
|
* @param ip
|
||||||
|
*/
|
||||||
|
public void block(String ip)
|
||||||
|
{
|
||||||
|
String sql = "INSERT INTO blocklist VALUES ( ? , ? );";
|
||||||
|
|
||||||
|
try(Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql))
|
||||||
|
{
|
||||||
|
pstmt.setString(1, ip);
|
||||||
|
pstmt.setLong(2, System.currentTimeMillis());
|
||||||
|
pstmt.executeUpdate();
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unblock an ip
|
||||||
|
* @param ip
|
||||||
|
*/
|
||||||
|
public void unblock(String ip)
|
||||||
|
{
|
||||||
|
String sql = "DELETE FROM blocklist WHERE ip = ? ;";
|
||||||
|
|
||||||
|
try(Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql))
|
||||||
|
{
|
||||||
|
pstmt.setString(1, ip);
|
||||||
|
pstmt.executeUpdate();
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unblock those ip who have passed blocktime in blocklist
|
||||||
|
*/
|
||||||
|
public void unblockrun()
|
||||||
|
{
|
||||||
|
String sql = "DELETE FROM blocklist WHERE ? >= time + ?;";
|
||||||
|
|
||||||
|
try(Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql))
|
||||||
|
{
|
||||||
|
pstmt.setLong(1, System.currentTimeMillis());
|
||||||
|
pstmt.setLong(2, blocktime);
|
||||||
|
pstmt.executeUpdate();
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user is blocked
|
||||||
|
* @param ip
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isUserBlocked(String ip)
|
||||||
|
{
|
||||||
|
String sql = "SELECT * FROM blocklist WHERE ip = ? ;";
|
||||||
|
|
||||||
|
try(Connection conn = this.connect(); PreparedStatement pstmt = conn.prepareStatement(sql))
|
||||||
|
{
|
||||||
|
pstmt.setString(1, ip);
|
||||||
|
ResultSet res = pstmt.executeQuery();
|
||||||
|
return res.next();
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect database
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Connection connect()
|
||||||
|
{
|
||||||
|
Connection con = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
con = DriverManager.getConnection(Config.dburl, Config.dbusername, Config.dbpassword);
|
||||||
|
}
|
||||||
|
catch(SQLException ex)
|
||||||
|
{
|
||||||
|
Logger.getLogger(RecordManager.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.servlet.annotation.WebListener;
|
||||||
|
import javax.servlet.ServletRequestEvent;
|
||||||
|
import javax.servlet.ServletRequestListener;
|
||||||
|
|
||||||
|
@WebListener()
|
||||||
|
public class RequestListener implements ServletRequestListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestDestroyed(ServletRequestEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestInitialized(ServletRequestEvent event) {
|
||||||
|
WebScocketEnd.ipaddress = event.getServletRequest().getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*/
|
||||||
|
public class USession
|
||||||
|
{
|
||||||
|
Session peer;
|
||||||
|
String nick = "";
|
||||||
|
String channel = "";
|
||||||
|
String trip = new String();
|
||||||
|
String ip = "";
|
||||||
|
|
||||||
|
public USession(Session peer, String nick, String channel, String trip, long lastping)
|
||||||
|
{
|
||||||
|
this.peer = peer;
|
||||||
|
this.nick = nick;
|
||||||
|
this.channel = channel;
|
||||||
|
this.trip = trip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public USession(Session peer)
|
||||||
|
{
|
||||||
|
this.peer = peer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,402 @@
|
||||||
|
package urrsm.sng;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.websocket.EncodeException;
|
||||||
|
import javax.websocket.OnClose;
|
||||||
|
import javax.websocket.OnError;
|
||||||
|
import javax.websocket.OnMessage;
|
||||||
|
import javax.websocket.OnOpen;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author piyush
|
||||||
|
*
|
||||||
|
* Websocket Manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@ServerEndpoint("/uchatsserver")
|
||||||
|
public class WebScocketEnd {
|
||||||
|
|
||||||
|
public static ArrayList<USession> sessions = new ArrayList<USession>();
|
||||||
|
public static String ipaddress = null;
|
||||||
|
public RecordManager recordmanager;
|
||||||
|
public Gson gson = new Gson();
|
||||||
|
|
||||||
|
public WebScocketEnd() throws ClassNotFoundException, SQLException
|
||||||
|
{
|
||||||
|
recordmanager = new RecordManager();
|
||||||
|
Timer timer = new javax.swing.Timer(1000, new java.awt.event.ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
recordmanager.unblockrun();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnMessage
|
||||||
|
public String onMessage(String message, Session session) throws IOException, EncodeException, NoSuchAlgorithmException {
|
||||||
|
JsonStructure json = gson.fromJson(message, JsonStructure.class);
|
||||||
|
USession ses = getSessionByPeer(session);
|
||||||
|
if(recordmanager.isUserBlocked(ipaddress))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// If user do Ping
|
||||||
|
if(json.cmd.equals("ping"))
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user join
|
||||||
|
else if(json.cmd.equals("join"))
|
||||||
|
{
|
||||||
|
String trip = " ";
|
||||||
|
if(json.nick.split("#").length != 1)
|
||||||
|
{
|
||||||
|
trip = calcHash(json.nick.split("#")[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(json.nick.split("#")[0].toLowerCase().equals(Config.admin.toLowerCase()) &&
|
||||||
|
!trip.toLowerCase().equals(calcHash(Config.password).toLowerCase()))
|
||||||
|
{
|
||||||
|
JsonStructure msg = new JsonStructure();
|
||||||
|
msg.cmd = "warn";
|
||||||
|
msg.text = "***Impersonating Admin???***";
|
||||||
|
session.getBasicRemote().sendText(gson.toJson(msg));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!json.nick.split("#")[0].matches("[a-zA-Z0-9_]*") && !(json.nick.split("#")[0].length() < 25))
|
||||||
|
{
|
||||||
|
JsonStructure msg = new JsonStructure();
|
||||||
|
msg.cmd = "warn";
|
||||||
|
msg.text = "Nickname must consist of up to 24 letters, numbers, and underscores!";
|
||||||
|
session.getBasicRemote().sendText(gson.toJson(msg));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(json.channel.trim().equals(""))
|
||||||
|
{
|
||||||
|
JsonStructure msg = new JsonStructure();
|
||||||
|
msg.cmd = "warn";
|
||||||
|
msg.text = "Channel must not be spaces";
|
||||||
|
session.getBasicRemote().sendText(gson.toJson(msg));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
if(sess.channel.equals(json.channel) && sess.nick.equals(json.nick.split("#")[0]))
|
||||||
|
{
|
||||||
|
JsonStructure msg = new JsonStructure();
|
||||||
|
msg.cmd = "warn";
|
||||||
|
msg.text = "Nickname already taken!";
|
||||||
|
session.getBasicRemote().sendText(gson.toJson(msg));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ses.nick = json.nick.split("#")[0];
|
||||||
|
ses.channel = json.channel;
|
||||||
|
ses.trip = trip;
|
||||||
|
ses.ip = ipaddress;
|
||||||
|
|
||||||
|
JsonStructure cmdonline = new JsonStructure();
|
||||||
|
cmdonline.cmd = "onlineAdd";
|
||||||
|
cmdonline.nick = json.nick.split("#")[0];
|
||||||
|
boardcast(gson.toJson(cmdonline),json.channel);
|
||||||
|
|
||||||
|
ArrayList<String> nicks = new ArrayList<String>();
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
if(sess.channel.endsWith(json.channel)) nicks.add(sess.nick);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonStructure onlinelist = new JsonStructure();
|
||||||
|
onlinelist.cmd = "onlineSet";
|
||||||
|
onlinelist.nicks = nicks;
|
||||||
|
session.getBasicRemote().sendText(gson.toJson(onlinelist));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//user sends message
|
||||||
|
else if(json.cmd.equals("chat"))
|
||||||
|
{
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "chat";
|
||||||
|
chat.nick = ses.nick;
|
||||||
|
chat.text = json.text;
|
||||||
|
chat.trip = ses.trip;
|
||||||
|
if(isAdmin(ses)) chat.admin = true;
|
||||||
|
if(isMod(ses)) chat.mod = true;
|
||||||
|
boardcast(gson.toJson(chat), ses.channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//user invites user to another random channel
|
||||||
|
else if(json.cmd.equals("invite"))
|
||||||
|
{
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
if(sess.nick.equals(json.nick))
|
||||||
|
{
|
||||||
|
String channel = String.valueOf(calcHash(String.valueOf(Math.random()))).substring(0, 6);
|
||||||
|
|
||||||
|
if(sess.peer == ses.peer)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "info";
|
||||||
|
chat.text = ses.nick + " invited you to ?"+channel;
|
||||||
|
sess.peer.getBasicRemote().sendText(gson.toJson(chat));
|
||||||
|
chat.text = "you invited "+ses.nick +" to ?"+channel;
|
||||||
|
ses.peer.getBasicRemote().sendText(gson.toJson(chat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status
|
||||||
|
if(json.cmd.equals("stats"))
|
||||||
|
{
|
||||||
|
ArrayList<String> channels = new ArrayList<String>();
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
if(channels.indexOf(sess.channel) == -1)
|
||||||
|
{
|
||||||
|
channels.add(sess.channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "info";
|
||||||
|
chat.text = " Users Connected : "+sessions.size()+"\n"
|
||||||
|
+ " Channels : "+channels.size();
|
||||||
|
|
||||||
|
boardcast(gson.toJson(chat), ses.channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// super power
|
||||||
|
// Ban a user
|
||||||
|
else if(json.cmd.equals("ban"))
|
||||||
|
{
|
||||||
|
if(!isAdmin(ses) && !isMod(ses))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
if(ses.channel.equals(sess.channel) && json.nick.equals(sess.nick))
|
||||||
|
{
|
||||||
|
recordmanager.block(sess.ip);
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "chat";
|
||||||
|
chat.nick = "Server";
|
||||||
|
chat.text = "Ip "+sess.ip+" is banned!";
|
||||||
|
chat.trip = "server";
|
||||||
|
ses.peer.getBasicRemote().sendText(gson.toJson(chat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unban a user
|
||||||
|
else if(json.cmd.equals("unban"))
|
||||||
|
{
|
||||||
|
if(!isAdmin(ses) && !isMod(ses))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
recordmanager.unblock(json.ip);
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "chat";
|
||||||
|
chat.nick = "Server";
|
||||||
|
chat.text = "Ip "+json.ip+" is unbanned!";
|
||||||
|
chat.trip = "server";
|
||||||
|
ses.peer.getBasicRemote().sendText(gson.toJson(chat));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIP Super Power
|
||||||
|
// List users and their info
|
||||||
|
else if(json.cmd.equals("listUsers"))
|
||||||
|
{
|
||||||
|
if(!isAdmin(ses))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String data = "";
|
||||||
|
for(USession sess : sessions)
|
||||||
|
{
|
||||||
|
data = data + "\n"+sess.nick+"\t"+sess.channel+"\t"+sess.trip+"\t"+sess.ip;
|
||||||
|
}
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "chat";
|
||||||
|
chat.nick = "Server";
|
||||||
|
chat.text = "Nick\tChannel\tTrip\tIp\n"+data;
|
||||||
|
chat.trip = "server";
|
||||||
|
ses.peer.getBasicRemote().sendText(gson.toJson(chat));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bordcast a message to all channel
|
||||||
|
else if(json.cmd.equals("broadcast"))
|
||||||
|
{
|
||||||
|
if(!isAdmin(ses))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JsonStructure chat = new JsonStructure();
|
||||||
|
chat.cmd = "chat";
|
||||||
|
chat.nick = "Server";
|
||||||
|
chat.text = json.text;
|
||||||
|
chat.trip = "server";
|
||||||
|
wildspread(gson.toJson(chat));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen (Session peer) throws IOException {
|
||||||
|
// Limit number of user and filter blocked users
|
||||||
|
if(sessions.size() < 10000 && !recordmanager.isUserBlocked(ipaddress))
|
||||||
|
{
|
||||||
|
sessions.add(new USession(peer, " ", " ", " ", System.currentTimeMillis()));
|
||||||
|
peer.getBasicRemote().sendText(ipaddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
peer.getBasicRemote().sendText("Unable to book your seat in chatroom!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClose
|
||||||
|
public void onClose (Session peer) {
|
||||||
|
USession leavingsession = getSessionByPeer(peer);
|
||||||
|
if(!leavingsession.trip.equals(" "))
|
||||||
|
{
|
||||||
|
JsonStructure cmdonline = new JsonStructure();
|
||||||
|
cmdonline.cmd = "onlineRemove";
|
||||||
|
cmdonline.nick = leavingsession.nick;
|
||||||
|
String channel = leavingsession.channel;
|
||||||
|
sessions.remove(leavingsession);
|
||||||
|
boardcast(gson.toJson(cmdonline),channel);
|
||||||
|
}
|
||||||
|
else sessions.remove(leavingsession);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnError
|
||||||
|
public void onError(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get USession from Arraylist
|
||||||
|
* @param peer
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public USession getSessionByPeer(Session peer)
|
||||||
|
{
|
||||||
|
for(USession session : sessions)
|
||||||
|
{
|
||||||
|
if(session.peer == peer) return session;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message to a channel
|
||||||
|
* @param message
|
||||||
|
* @param channel
|
||||||
|
*/
|
||||||
|
public void boardcast(String message, String channel)
|
||||||
|
{
|
||||||
|
for(USession session : sessions)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if(session.channel.equals(channel)) session.peer.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(WebScocketEnd.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send message to all channels
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
public void wildspread(String message)
|
||||||
|
{
|
||||||
|
for(USession session : sessions)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
session.peer.getBasicRemote().sendText(message);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.getLogger(WebScocketEnd.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate SHA-1 sum with salt
|
||||||
|
* @param passwd
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public String calcHash(String passwd) throws NoSuchAlgorithmException, UnsupportedEncodingException
|
||||||
|
{
|
||||||
|
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
|
||||||
|
crypt.reset();
|
||||||
|
crypt.update(passwd.getBytes("UTF-8"));
|
||||||
|
String hash = new BigInteger(1, crypt.digest()).toString(16);
|
||||||
|
return hash.substring(0, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if USession is of admin
|
||||||
|
* @param session
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public boolean isAdmin(USession session) throws NoSuchAlgorithmException, UnsupportedEncodingException
|
||||||
|
{
|
||||||
|
if(session.trip.toLowerCase().equals(calcHash(Config.password).toLowerCase()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if USession is of mod
|
||||||
|
* @param session
|
||||||
|
* @return
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public boolean isMod(USession session) throws NoSuchAlgorithmException, UnsupportedEncodingException
|
||||||
|
{
|
||||||
|
for(String trip: Config.mods)
|
||||||
|
{
|
||||||
|
if(session.trip.toLowerCase().equals(trip.toLowerCase()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Context path="/"/>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// Base16 Bright
|
||||||
|
// Created by Fredrik Broman (http://frebro.com)
|
||||||
|
// Based on the Android visual guidelines
|
||||||
|
// http://developer.android.com/design/style/color.html
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #212121;
|
||||||
|
@base01: #333333;
|
||||||
|
@base02: #626261;
|
||||||
|
@base03: #858585;
|
||||||
|
@base04: #dddddd;
|
||||||
|
@base05: #e0e0e0;
|
||||||
|
@base06: #f2f2f2;
|
||||||
|
@base07: #ffffff;
|
||||||
|
|
||||||
|
@base08: #e94749;
|
||||||
|
@base09: #f18618;
|
||||||
|
@base0A: #fbba37;
|
||||||
|
@base0B: #99C21D;
|
||||||
|
@base0C: #33B5E5;
|
||||||
|
@base0D: #0099CC;
|
||||||
|
@base0E: #9568AA;
|
||||||
|
@base0F: #754595;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Atelier Dune
|
||||||
|
// Created by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #20201d;
|
||||||
|
@base01: #292824;
|
||||||
|
@base02: #6e6b5e;
|
||||||
|
@base03: #7d7a68;
|
||||||
|
@base04: #999580;
|
||||||
|
@base05: #a6a28c;
|
||||||
|
@base06: #e8e4cf;
|
||||||
|
@base07: #fefbec;
|
||||||
|
@base08: #d73737;
|
||||||
|
@base09: #b65611;
|
||||||
|
@base0A: #cfb017;
|
||||||
|
@base0B: #60ac39;
|
||||||
|
@base0C: #1fad83;
|
||||||
|
@base0D: #6684e1;
|
||||||
|
@base0E: #b854d4;
|
||||||
|
@base0F: #d43552;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Atelier Forest
|
||||||
|
// Created by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #1b1918;
|
||||||
|
@base01: #2c2421;
|
||||||
|
@base02: #68615e;
|
||||||
|
@base03: #766e6b;
|
||||||
|
@base04: #9c9491;
|
||||||
|
@base05: #a8a19f;
|
||||||
|
@base06: #e6e2e0;
|
||||||
|
@base07: #f1efee;
|
||||||
|
@base08: #f22c40;
|
||||||
|
@base09: #df5320;
|
||||||
|
@base0A: #d5911a;
|
||||||
|
@base0B: #5ab738;
|
||||||
|
@base0C: #00ad9c;
|
||||||
|
@base0D: #407ee7;
|
||||||
|
@base0E: #6666ea;
|
||||||
|
@base0F: #c33ff3;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Atelier Heath
|
||||||
|
// Created by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #1b181b;
|
||||||
|
@base01: #292329;
|
||||||
|
@base02: #695d69;
|
||||||
|
@base03: #776977;
|
||||||
|
@base04: #9e8f9e;
|
||||||
|
@base05: #ab9bab;
|
||||||
|
@base06: #d8cad8;
|
||||||
|
@base07: #f7f3f7;
|
||||||
|
@base08: #ca402b;
|
||||||
|
@base09: #a65926;
|
||||||
|
@base0A: #bb8a35;
|
||||||
|
@base0B: #379a37;
|
||||||
|
@base0C: #159393;
|
||||||
|
@base0D: #516aec;
|
||||||
|
@base0E: #7b59c0;
|
||||||
|
@base0F: #cc33cc;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Atelier Lakeside
|
||||||
|
// Created by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #161b1d;
|
||||||
|
@base01: #1f292e;
|
||||||
|
@base02: #516d7b;
|
||||||
|
@base03: #5a7b8c;
|
||||||
|
@base04: #7195a8;
|
||||||
|
@base05: #7ea2b4;
|
||||||
|
@base06: #c1e4f6;
|
||||||
|
@base07: #ebf8ff;
|
||||||
|
@base08: #d22d72;
|
||||||
|
@base09: #935c25;
|
||||||
|
@base0A: #8a8a0f;
|
||||||
|
@base0B: #568c3b;
|
||||||
|
@base0C: #2d8f6f;
|
||||||
|
@base0D: #257fad;
|
||||||
|
@base0E: #5d5db1;
|
||||||
|
@base0F: #b72dd2;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Atelier Seaside
|
||||||
|
// Created by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #131513;
|
||||||
|
@base01: #242924;
|
||||||
|
@base02: #5e6e5e;
|
||||||
|
@base03: #687d68;
|
||||||
|
@base04: #809980;
|
||||||
|
@base05: #8ca68c;
|
||||||
|
@base06: #cfe8cf;
|
||||||
|
@base07: #f0fff0;
|
||||||
|
@base08: #e6193c;
|
||||||
|
@base09: #87711d;
|
||||||
|
@base0A: #c3c322;
|
||||||
|
@base0B: #29a329;
|
||||||
|
@base0C: #1999b3;
|
||||||
|
@base0D: #3d62f5;
|
||||||
|
@base0E: #ad2bee;
|
||||||
|
@base0F: #e619c3;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Bright
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #000000;
|
||||||
|
@base01: #303030;
|
||||||
|
@base02: #505050;
|
||||||
|
@base03: #b0b0b0;
|
||||||
|
@base04: #d0d0d0;
|
||||||
|
@base05: #e0e0e0;
|
||||||
|
@base06: #f5f5f5;
|
||||||
|
@base07: #ffffff;
|
||||||
|
@base08: #fb0120;
|
||||||
|
@base09: #fc6d24;
|
||||||
|
@base0A: #fda331;
|
||||||
|
@base0B: #a1c659;
|
||||||
|
@base0C: #76c7b7;
|
||||||
|
@base0D: #6fb3d2;
|
||||||
|
@base0E: #d381c3;
|
||||||
|
@base0F: #be643c;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Chalk
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #151515;
|
||||||
|
@base01: #202020;
|
||||||
|
@base02: #303030;
|
||||||
|
@base03: #505050;
|
||||||
|
@base04: #b0b0b0;
|
||||||
|
@base05: #d0d0d0;
|
||||||
|
@base06: #e0e0e0;
|
||||||
|
@base07: #f5f5f5;
|
||||||
|
@base08: #fb9fb1;
|
||||||
|
@base09: #eda987;
|
||||||
|
@base0A: #ddb26f;
|
||||||
|
@base0B: #acc267;
|
||||||
|
@base0C: #12cfc0;
|
||||||
|
@base0D: #6fc2ef;
|
||||||
|
@base0E: #e1a3ee;
|
||||||
|
@base0F: #deaf8f;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Default
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #151515;
|
||||||
|
@base01: #202020;
|
||||||
|
@base02: #303030;
|
||||||
|
@base03: #505050;
|
||||||
|
@base04: #b0b0b0;
|
||||||
|
@base05: #d0d0d0;
|
||||||
|
@base06: #e0e0e0;
|
||||||
|
@base07: #f5f5f5;
|
||||||
|
@base08: #ac4142;
|
||||||
|
@base09: #d28445;
|
||||||
|
@base0A: #f4bf75;
|
||||||
|
@base0B: #90a959;
|
||||||
|
@base0C: #75b5aa;
|
||||||
|
@base0D: #6a9fb5;
|
||||||
|
@base0E: #aa759f;
|
||||||
|
@base0F: #8f5536;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Eighties
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #2d2d2d;
|
||||||
|
@base01: #393939;
|
||||||
|
@base02: #515151;
|
||||||
|
@base03: #747369;
|
||||||
|
@base04: #a09f93;
|
||||||
|
@base05: #d3d0c8;
|
||||||
|
@base06: #e8e6df;
|
||||||
|
@base07: #f2f0ec;
|
||||||
|
@base08: #f2777a;
|
||||||
|
@base09: #f99157;
|
||||||
|
@base0A: #ffcc66;
|
||||||
|
@base0B: #99cc99;
|
||||||
|
@base0C: #66cccc;
|
||||||
|
@base0D: #6699cc;
|
||||||
|
@base0E: #cc99cc;
|
||||||
|
@base0F: #d27b53;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Greenscreen
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #001100;
|
||||||
|
@base01: #003300;
|
||||||
|
@base02: #005500;
|
||||||
|
@base03: #007700;
|
||||||
|
@base04: #009900;
|
||||||
|
@base05: #00bb00;
|
||||||
|
@base06: #00dd00;
|
||||||
|
@base07: #00ff00;
|
||||||
|
@base08: #007700;
|
||||||
|
@base09: #009900;
|
||||||
|
@base0A: #007700;
|
||||||
|
@base0B: #00bb00;
|
||||||
|
@base0C: #005500;
|
||||||
|
@base0D: #009900;
|
||||||
|
@base0E: #00bb00;
|
||||||
|
@base0F: #005500;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Mocha
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #3B3228;
|
||||||
|
@base01: #534636;
|
||||||
|
@base02: #645240;
|
||||||
|
@base03: #7e705a;
|
||||||
|
@base04: #b8afad;
|
||||||
|
@base05: #d0c8c6;
|
||||||
|
@base06: #e9e1dd;
|
||||||
|
@base07: #f5eeeb;
|
||||||
|
@base08: #cb6077;
|
||||||
|
@base09: #d28b71;
|
||||||
|
@base0A: #f4bc87;
|
||||||
|
@base0B: #beb55b;
|
||||||
|
@base0C: #7bbda4;
|
||||||
|
@base0D: #8ab3b5;
|
||||||
|
@base0E: #a89bb9;
|
||||||
|
@base0F: #bb9584;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Monokai
|
||||||
|
// Created by Wimer Hazenberg (http://www.monokai.nl)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #272822;
|
||||||
|
@base01: #383830;
|
||||||
|
@base02: #49483e;
|
||||||
|
@base03: #75715e;
|
||||||
|
@base04: #a59f85;
|
||||||
|
@base05: #f8f8f2;
|
||||||
|
@base06: #f5f4f1;
|
||||||
|
@base07: #f9f8f5;
|
||||||
|
@base08: #f92672;
|
||||||
|
@base09: #fd971f;
|
||||||
|
@base0A: #f4bf75;
|
||||||
|
@base0B: #a6e22e;
|
||||||
|
@base0C: #a1efe4;
|
||||||
|
@base0D: #66d9ef;
|
||||||
|
@base0E: #ae81ff;
|
||||||
|
@base0F: #cc6633;
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// Base16 NE.se color system
|
||||||
|
// Created by Fredrik Broman (http://frebro.com)
|
||||||
|
//
|
||||||
|
// Reference on Adobe Kuler:
|
||||||
|
// https://kuler.adobe.com/NESE-Warm-color-theme-2900819/
|
||||||
|
// https://kuler.adobe.com/NESE-Cold-color-theme-2900847/
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: lighten(black, 1%); // #030303
|
||||||
|
@base01: lighten(black, 5%); // #0c0c0c
|
||||||
|
@base02: lighten(black, 20%); // #262626
|
||||||
|
@base03: lighten(black, 45%); // #5f5f5f
|
||||||
|
@base04: lighten(black, 65%); // #959595
|
||||||
|
@base05: lighten(black, 80%); // #c1c1c1
|
||||||
|
@base06: lighten(black, 95%); // #efefef
|
||||||
|
@base07: lighten(black, 99%); // #fcfcfc
|
||||||
|
|
||||||
|
@base08: #f73e30; // Red
|
||||||
|
@base09: mix(@base08, @base0A, 50%); // Orange = #f97c37
|
||||||
|
@base0A: #faba3d; // Yellow
|
||||||
|
@base0B: #1ab857; // Green
|
||||||
|
@base0C: mix(@base0B, @base0D, 50%); // Cyan = #29a188
|
||||||
|
@base0D: #388bb8; // Blue
|
||||||
|
@base0E: mix(@base0D, @base0F, 50%); // Purple = #9c73a7
|
||||||
|
@base0F: #ff5a96; // Pink
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Ocean
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #2b303b;
|
||||||
|
@base01: #343d46;
|
||||||
|
@base02: #4f5b66;
|
||||||
|
@base03: #65737e;
|
||||||
|
@base04: #a7adba;
|
||||||
|
@base05: #c0c5ce;
|
||||||
|
@base06: #dfe1e8;
|
||||||
|
@base07: #eff1f5;
|
||||||
|
@base08: #bf616a;
|
||||||
|
@base09: #d08770;
|
||||||
|
@base0A: #ebcb8b;
|
||||||
|
@base0B: #a3be8c;
|
||||||
|
@base0C: #96b5b4;
|
||||||
|
@base0D: #8fa1b3;
|
||||||
|
@base0E: #b48ead;
|
||||||
|
@base0F: #ab7967;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Pop
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #000000;
|
||||||
|
@base01: #202020;
|
||||||
|
@base02: #303030;
|
||||||
|
@base03: #505050;
|
||||||
|
@base04: #b0b0b0;
|
||||||
|
@base05: #d0d0d0;
|
||||||
|
@base06: #e0e0e0;
|
||||||
|
@base07: #ffffff;
|
||||||
|
@base08: #eb008a;
|
||||||
|
@base09: #f29333;
|
||||||
|
@base0A: #f8ca12;
|
||||||
|
@base0B: #37b349;
|
||||||
|
@base0C: #00aabb;
|
||||||
|
@base0D: #0e5a94;
|
||||||
|
@base0E: #b31e8d;
|
||||||
|
@base0F: #7a2d00;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Railscasts
|
||||||
|
// Created by Ryan Bates (http://railscasts.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #2b2b2b;
|
||||||
|
@base01: #272935;
|
||||||
|
@base02: #3a4055;
|
||||||
|
@base03: #5a647e;
|
||||||
|
@base04: #d4cfc9;
|
||||||
|
@base05: #e6e1dc;
|
||||||
|
@base06: #f4f1ed;
|
||||||
|
@base07: #f9f7f3;
|
||||||
|
@base08: #da4939;
|
||||||
|
@base09: #cc7833;
|
||||||
|
@base0A: #ffc66d;
|
||||||
|
@base0B: #a5c261;
|
||||||
|
@base0C: #519f50;
|
||||||
|
@base0D: #6d9cbe;
|
||||||
|
@base0E: #b6b3eb;
|
||||||
|
@base0F: #bc9458;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Solarized
|
||||||
|
// Created by Ethan Schoonover (http://ethanschoonover.com/solarized)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #002b36;
|
||||||
|
@base01: #073642;
|
||||||
|
@base02: #586e75;
|
||||||
|
@base03: #657b83;
|
||||||
|
@base04: #839496;
|
||||||
|
@base05: #93a1a1;
|
||||||
|
@base06: #eee8d5;
|
||||||
|
@base07: #fdf6e3;
|
||||||
|
@base08: #dc322f;
|
||||||
|
@base09: #cb4b16;
|
||||||
|
@base0A: #b58900;
|
||||||
|
@base0B: #859900;
|
||||||
|
@base0C: #2aa198;
|
||||||
|
@base0D: #268bd2;
|
||||||
|
@base0E: #6c71c4;
|
||||||
|
@base0F: #d33682;
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// Base16 Tomorrow
|
||||||
|
// Created by Chris Kempson (http://chriskempson.com)
|
||||||
|
//
|
||||||
|
|
||||||
|
@base00: #1d1f21;
|
||||||
|
@base01: #282a2e;
|
||||||
|
@base02: #373b41;
|
||||||
|
@base03: #969896;
|
||||||
|
@base04: #b4b7b4;
|
||||||
|
@base05: #c5c8c6;
|
||||||
|
@base06: #e0e0e0;
|
||||||
|
@base07: #ffffff;
|
||||||
|
@base08: #cc6666;
|
||||||
|
@base09: #de935f;
|
||||||
|
@base0A: #f0c674;
|
||||||
|
@base0B: #b5bd68;
|
||||||
|
@base0C: #8abeb7;
|
||||||
|
@base0D: #81a2be;
|
||||||
|
@base0E: #b294bb;
|
||||||
|
@base0F: #a3685a;
|
||||||
|
|
@ -0,0 +1,537 @@
|
||||||
|
var frontpage = [
|
||||||
|
" _ _ ___ _ _ ",
|
||||||
|
" | || || _|| |_ ___| |_ __",
|
||||||
|
" | || || |_ | |_ || _||_ |",
|
||||||
|
" |____||___||_|_|__/|_| |__|",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"Welcome to UChats, a minimal, distraction-free chat application made with java. It is clone of Hack.chat(https://github.com/AndrewBelt/hack.chat)",
|
||||||
|
"Channels are created and joined by going to https://uchats.herokuapp.com/?your-channel. There are no channel lists, so a secret channel name can be used for private discussions.",
|
||||||
|
"",
|
||||||
|
"Here are some pre-made channels you can join:",
|
||||||
|
"?devs ?meta",
|
||||||
|
"?math ?physics ?chemistry",
|
||||||
|
"?technology ?programming",
|
||||||
|
"?games ?banana",
|
||||||
|
"And here's a random one generated just for you: ?" + Math.random().toString(36).substr(2, 8),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"Formatting:",
|
||||||
|
"Whitespace is preserved, so source code can be pasted verbatim.",
|
||||||
|
"Surround LaTeX with a dollar sign for inline style $\\zeta(2) = \\pi^2/6$, and two dollars for display. $$\\int_0^1 \\int_0^1 \\frac{1}{1-xy} dx dy = \\frac{\\pi^2}{6}$$",
|
||||||
|
"",
|
||||||
|
"GitHub: https://github.com/PiyushXCoder/uchat",
|
||||||
|
"",
|
||||||
|
"Server and web client released under the MIT license.",
|
||||||
|
"No message history is retained on the UChats server.",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
function $(query) {return document.querySelector(query)}
|
||||||
|
|
||||||
|
function localStorageGet(key) {
|
||||||
|
try {
|
||||||
|
return window.localStorage[key]
|
||||||
|
}
|
||||||
|
catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function localStorageSet(key, val) {
|
||||||
|
try {
|
||||||
|
window.localStorage[key] = val
|
||||||
|
}
|
||||||
|
catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ws
|
||||||
|
var myNick = localStorageGet('my-nick')
|
||||||
|
var myChannel = window.location.search.replace(/^\?/, '')
|
||||||
|
var lastSent = [""]
|
||||||
|
var lastSentPos = 0
|
||||||
|
|
||||||
|
|
||||||
|
// Ping server every 50 seconds to retain WebSocket connection
|
||||||
|
window.setInterval(function() {
|
||||||
|
send({cmd: 'ping'})
|
||||||
|
}, 50000)
|
||||||
|
|
||||||
|
|
||||||
|
function join(channel) {
|
||||||
|
|
||||||
|
if (document.domain == 'uchats.herokuapp.com') {
|
||||||
|
ws = new WebSocket('wss://uchats.herokuapp.com/uchatsserver')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// for local installs
|
||||||
|
ws = new WebSocket('ws://' + location.hostname+":"+location.port + '/uchatsserver')
|
||||||
|
}
|
||||||
|
|
||||||
|
var wasConnected = false
|
||||||
|
|
||||||
|
ws.onopen = function() {
|
||||||
|
if (!wasConnected) {
|
||||||
|
if (location.hash) {
|
||||||
|
myNick = location.hash.substr(1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
myNick = prompt('Nickname:', myNick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (myNick) {
|
||||||
|
localStorageSet('my-nick', myNick)
|
||||||
|
send({cmd: 'join', channel: channel, nick: myNick})
|
||||||
|
}
|
||||||
|
wasConnected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onclose = function() {
|
||||||
|
if (wasConnected) {
|
||||||
|
pushMessage({nick: '!', text: "Server disconnected. Attempting to reconnect..."})
|
||||||
|
}
|
||||||
|
window.setTimeout(function() {
|
||||||
|
join(channel)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onmessage = function(message) {
|
||||||
|
var args = JSON.parse(message.data)
|
||||||
|
var cmd = args.cmd
|
||||||
|
var command = COMMANDS[cmd]
|
||||||
|
command.call(null, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var COMMANDS = {
|
||||||
|
chat: function(args) {
|
||||||
|
if (ignoredUsers.indexOf(args.nick) >= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pushMessage(args)
|
||||||
|
},
|
||||||
|
info: function(args) {
|
||||||
|
args.nick = '*'
|
||||||
|
pushMessage(args)
|
||||||
|
},
|
||||||
|
warn: function(args) {
|
||||||
|
args.nick = '!'
|
||||||
|
pushMessage(args)
|
||||||
|
},
|
||||||
|
onlineSet: function(args) {
|
||||||
|
var nicks = args.nicks
|
||||||
|
usersClear()
|
||||||
|
nicks.forEach(function(nick) {
|
||||||
|
userAdd(nick)
|
||||||
|
})
|
||||||
|
pushMessage({nick: '*', text: "Users online: " + nicks.join(", ")})
|
||||||
|
},
|
||||||
|
onlineAdd: function(args) {
|
||||||
|
var nick = args.nick
|
||||||
|
userAdd(nick)
|
||||||
|
if ($('#joined-left').checked) {
|
||||||
|
pushMessage({nick: '*', text: nick + " joined"})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onlineRemove: function(args) {
|
||||||
|
var nick = args.nick
|
||||||
|
userRemove(nick)
|
||||||
|
if ($('#joined-left').checked) {
|
||||||
|
pushMessage({nick: '*', text: nick + " left"})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function pushMessage(args) {
|
||||||
|
// Message container
|
||||||
|
var messageEl = document.createElement('div')
|
||||||
|
messageEl.classList.add('message')
|
||||||
|
|
||||||
|
if (args.nick == myNick) {
|
||||||
|
messageEl.classList.add('me')
|
||||||
|
}
|
||||||
|
else if (args.nick == '!') {
|
||||||
|
messageEl.classList.add('warn')
|
||||||
|
}
|
||||||
|
else if (args.nick == '*') {
|
||||||
|
messageEl.classList.add('info')
|
||||||
|
}
|
||||||
|
else if (args.admin) {
|
||||||
|
messageEl.classList.add('admin')
|
||||||
|
}
|
||||||
|
else if (args.mod) {
|
||||||
|
messageEl.classList.add('mod')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nickname
|
||||||
|
var nickSpanEl = document.createElement('span')
|
||||||
|
nickSpanEl.classList.add('nick')
|
||||||
|
messageEl.appendChild(nickSpanEl)
|
||||||
|
|
||||||
|
if (args.trip) {
|
||||||
|
var tripEl = document.createElement('span')
|
||||||
|
tripEl.textContent = args.trip + " "
|
||||||
|
tripEl.classList.add('trip')
|
||||||
|
nickSpanEl.appendChild(tripEl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.nick) {
|
||||||
|
var nickLinkEl = document.createElement('a')
|
||||||
|
nickLinkEl.textContent = args.nick
|
||||||
|
nickLinkEl.onclick = function() {
|
||||||
|
insertAtCursor("@" + args.nick + " ")
|
||||||
|
$('#chatinput').focus()
|
||||||
|
}
|
||||||
|
var date = new Date(args.time || Date.now())
|
||||||
|
nickLinkEl.title = date.toLocaleString()
|
||||||
|
nickSpanEl.appendChild(nickLinkEl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text
|
||||||
|
var textEl = document.createElement('pre')
|
||||||
|
textEl.classList.add('text')
|
||||||
|
|
||||||
|
textEl.textContent = args.text || ''
|
||||||
|
textEl.innerHTML = textEl.innerHTML.replace(/(\?|https?:\/\/)\S+?(?=[,.!?:)]?\s|$)/g, parseLinks)
|
||||||
|
|
||||||
|
if ($('#parse-latex').checked) {
|
||||||
|
// Temporary hotfix for \rule spamming, see https://github.com/Khan/KaTeX/issues/109
|
||||||
|
textEl.innerHTML = textEl.innerHTML.replace(/\\rule|\\\\\s*\[.*?\]/g, '')
|
||||||
|
try {
|
||||||
|
renderMathInElement(textEl, {delimiters: [
|
||||||
|
{left: "$$", right: "$$", display: true},
|
||||||
|
{left: "$", right: "$", display: false},
|
||||||
|
]})
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.warn(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messageEl.appendChild(textEl)
|
||||||
|
|
||||||
|
// Scroll to bottom
|
||||||
|
var atBottom = isAtBottom()
|
||||||
|
$('#messages').appendChild(messageEl)
|
||||||
|
if (atBottom) {
|
||||||
|
window.scrollTo(0, document.body.scrollHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
unread += 1
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function insertAtCursor(text) {
|
||||||
|
var input = $('#chatinput')
|
||||||
|
var start = input.selectionStart || 0
|
||||||
|
var before = input.value.substr(0, start)
|
||||||
|
var after = input.value.substr(start)
|
||||||
|
before += text
|
||||||
|
input.value = before + after
|
||||||
|
input.selectionStart = input.selectionEnd = before.length
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function send(data) {
|
||||||
|
if (ws && ws.readyState == ws.OPEN) {
|
||||||
|
ws.send(JSON.stringify(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function parseLinks(g0) {
|
||||||
|
var a = document.createElement('a')
|
||||||
|
a.innerHTML = g0
|
||||||
|
var url = a.textContent
|
||||||
|
a.href = url
|
||||||
|
a.target = '_blank'
|
||||||
|
return a.outerHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var windowActive = true
|
||||||
|
var unread = 0
|
||||||
|
|
||||||
|
window.onfocus = function() {
|
||||||
|
windowActive = true
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onblur = function() {
|
||||||
|
windowActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onscroll = function() {
|
||||||
|
if (isAtBottom()) {
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAtBottom() {
|
||||||
|
return (window.innerHeight + window.scrollY) >= (document.body.scrollHeight - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTitle() {
|
||||||
|
if (windowActive && isAtBottom()) {
|
||||||
|
unread = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var title
|
||||||
|
if (myChannel) {
|
||||||
|
title = "?" + myChannel
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
title = "UChats"
|
||||||
|
}
|
||||||
|
if (unread > 0) {
|
||||||
|
title = '(' + unread + ') ' + title
|
||||||
|
}
|
||||||
|
document.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
/* footer */
|
||||||
|
|
||||||
|
$('#footer').onclick = function() {
|
||||||
|
$('#chatinput').focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#chatinput').onkeydown = function(e) {
|
||||||
|
if (e.keyCode == 13 /* ENTER */ && !e.shiftKey) {
|
||||||
|
e.preventDefault()
|
||||||
|
// Submit message
|
||||||
|
if (e.target.value != '') {
|
||||||
|
var text = e.target.value
|
||||||
|
e.target.value = ''
|
||||||
|
send({cmd: 'chat', text: text})
|
||||||
|
lastSent[0] = text
|
||||||
|
lastSent.unshift("")
|
||||||
|
lastSentPos = 0
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.keyCode == 38 /* UP */) {
|
||||||
|
// Restore previous sent messages
|
||||||
|
if (e.target.selectionStart === 0 && lastSentPos < lastSent.length - 1) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (lastSentPos == 0) {
|
||||||
|
lastSent[0] = e.target.value
|
||||||
|
}
|
||||||
|
lastSentPos += 1
|
||||||
|
e.target.value = lastSent[lastSentPos]
|
||||||
|
e.target.selectionStart = e.target.selectionEnd = e.target.value.length
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.keyCode == 40 /* DOWN */) {
|
||||||
|
if (e.target.selectionStart === e.target.value.length && lastSentPos > 0) {
|
||||||
|
e.preventDefault()
|
||||||
|
lastSentPos -= 1
|
||||||
|
e.target.value = lastSent[lastSentPos]
|
||||||
|
e.target.selectionStart = e.target.selectionEnd = 0
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.keyCode == 27 /* ESC */) {
|
||||||
|
e.preventDefault()
|
||||||
|
// Clear input field
|
||||||
|
e.target.value = ""
|
||||||
|
lastSentPos = 0
|
||||||
|
lastSent[lastSentPos] = ""
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
else if (e.keyCode == 9 /* TAB */) {
|
||||||
|
// Tab complete nicknames starting with @
|
||||||
|
e.preventDefault()
|
||||||
|
var pos = e.target.selectionStart || 0
|
||||||
|
var text = e.target.value
|
||||||
|
var index = text.lastIndexOf('@', pos)
|
||||||
|
if (index >= 0) {
|
||||||
|
var stub = text.substring(index + 1, pos).toLowerCase()
|
||||||
|
// Search for nick beginning with stub
|
||||||
|
var nicks = onlineUsers.filter(function(nick) {
|
||||||
|
return nick.toLowerCase().indexOf(stub) == 0
|
||||||
|
})
|
||||||
|
if (nicks.length == 1) {
|
||||||
|
insertAtCursor(nicks[0].substr(stub.length) + " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateInputSize() {
|
||||||
|
var atBottom = isAtBottom()
|
||||||
|
|
||||||
|
var input = $('#chatinput')
|
||||||
|
input.style.height = 0
|
||||||
|
input.style.height = input.scrollHeight + 'px'
|
||||||
|
document.body.style.marginBottom = $('#footer').offsetHeight + 'px'
|
||||||
|
|
||||||
|
if (atBottom) {
|
||||||
|
window.scrollTo(0, document.body.scrollHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#chatinput').oninput = function() {
|
||||||
|
updateInputSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInputSize()
|
||||||
|
|
||||||
|
|
||||||
|
/* sidebar */
|
||||||
|
|
||||||
|
$('#sidebar').onmouseenter = $('#sidebar').ontouchstart = function(e) {
|
||||||
|
$('#sidebar-content').classList.remove('hidden')
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#sidebar').onmouseleave = document.ontouchstart = function() {
|
||||||
|
if (!$('#pin-sidebar').checked) {
|
||||||
|
$('#sidebar-content').classList.add('hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#clear-messages').onclick = function() {
|
||||||
|
// Delete children elements
|
||||||
|
var messages = $('#messages')
|
||||||
|
while (messages.firstChild) {
|
||||||
|
messages.removeChild(messages.firstChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore settings from localStorage
|
||||||
|
|
||||||
|
if (localStorageGet('pin-sidebar') == 'true') {
|
||||||
|
$('#pin-sidebar').checked = true
|
||||||
|
$('#sidebar-content').classList.remove('hidden')
|
||||||
|
}
|
||||||
|
if (localStorageGet('joined-left') == 'false') {
|
||||||
|
$('#joined-left').checked = false
|
||||||
|
}
|
||||||
|
if (localStorageGet('parse-latex') == 'false') {
|
||||||
|
$('#parse-latex').checked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#pin-sidebar').onchange = function(e) {
|
||||||
|
localStorageSet('pin-sidebar', !!e.target.checked)
|
||||||
|
}
|
||||||
|
$('#joined-left').onchange = function(e) {
|
||||||
|
localStorageSet('joined-left', !!e.target.checked)
|
||||||
|
}
|
||||||
|
$('#parse-latex').onchange = function(e) {
|
||||||
|
localStorageSet('parse-latex', !!e.target.checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User list
|
||||||
|
|
||||||
|
var onlineUsers = []
|
||||||
|
var ignoredUsers = []
|
||||||
|
|
||||||
|
function userAdd(nick) {
|
||||||
|
var user = document.createElement('a')
|
||||||
|
user.textContent = nick
|
||||||
|
user.onclick = function(e) {
|
||||||
|
userInvite(nick)
|
||||||
|
}
|
||||||
|
var userLi = document.createElement('li')
|
||||||
|
userLi.appendChild(user)
|
||||||
|
$('#users').appendChild(userLi)
|
||||||
|
onlineUsers.push(nick)
|
||||||
|
}
|
||||||
|
|
||||||
|
function userRemove(nick) {
|
||||||
|
var users = $('#users')
|
||||||
|
var children = users.children
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
var user = children[i]
|
||||||
|
if (user.textContent == nick) {
|
||||||
|
users.removeChild(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var index = onlineUsers.indexOf(nick)
|
||||||
|
if (index >= 0) {
|
||||||
|
onlineUsers.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function usersClear() {
|
||||||
|
var users = $('#users')
|
||||||
|
while (users.firstChild) {
|
||||||
|
users.removeChild(users.firstChild)
|
||||||
|
}
|
||||||
|
onlineUsers.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function userInvite(nick) {
|
||||||
|
send({cmd: 'invite', nick: nick})
|
||||||
|
}
|
||||||
|
|
||||||
|
function userIgnore(nick) {
|
||||||
|
ignoredUsers.push(nick)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* color scheme switcher */
|
||||||
|
|
||||||
|
var schemes = [
|
||||||
|
'android',
|
||||||
|
'atelier-dune',
|
||||||
|
'atelier-forest',
|
||||||
|
'atelier-heath',
|
||||||
|
'atelier-lakeside',
|
||||||
|
'atelier-seaside',
|
||||||
|
'bright',
|
||||||
|
'chalk',
|
||||||
|
'default',
|
||||||
|
'eighties',
|
||||||
|
'greenscreen',
|
||||||
|
'mocha',
|
||||||
|
'monokai',
|
||||||
|
'nese',
|
||||||
|
'ocean',
|
||||||
|
'pop',
|
||||||
|
'railscasts',
|
||||||
|
'solarized',
|
||||||
|
'tomorrow',
|
||||||
|
]
|
||||||
|
|
||||||
|
var currentScheme = 'atelier-dune'
|
||||||
|
|
||||||
|
function setScheme(scheme) {
|
||||||
|
currentScheme = scheme
|
||||||
|
$('#scheme-link').href = "/schemes/" + scheme + ".css"
|
||||||
|
localStorageSet('scheme', scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add scheme options to dropdown selector
|
||||||
|
schemes.forEach(function(scheme) {
|
||||||
|
var option = document.createElement('option')
|
||||||
|
option.textContent = scheme
|
||||||
|
option.value = scheme
|
||||||
|
$('#scheme-selector').appendChild(option)
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#scheme-selector').onchange = function(e) {
|
||||||
|
setScheme(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sidebar configaration values from local storage if available
|
||||||
|
if (localStorageGet('scheme')) {
|
||||||
|
setScheme(localStorageGet('scheme'))
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#scheme-selector').value = currentScheme
|
||||||
|
|
||||||
|
|
||||||
|
/* main */
|
||||||
|
|
||||||
|
if (myChannel == '') {
|
||||||
|
pushMessage({text: frontpage})
|
||||||
|
$('#footer').classList.add('hidden')
|
||||||
|
$('#sidebar').classList.add('hidden')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
join(myChannel)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>hack.chat</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="katex/katex.min.css">
|
||||||
|
<link id="scheme-link" rel="stylesheet" href="schemes/atelier-dune.css">
|
||||||
|
<script src="katex/katex.min.js"></script><script src="katex/contrib/auto-render.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<article class="container">
|
||||||
|
<div id="messages" class="messages"></div>
|
||||||
|
</article>
|
||||||
|
<footer id="footer">
|
||||||
|
<div class="container">
|
||||||
|
<form id="chatform" class="messages"><textarea id="chatinput" type="text" autocomplete="off" autofocus></textarea></form>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<nav id="sidebar">
|
||||||
|
<div id="sidebar-content" class="hidden">
|
||||||
|
<p><input id="pin-sidebar" type="checkbox"><label for="pin-sidebar">Pin sidebar</label></p>
|
||||||
|
<h4>Settings</h4>
|
||||||
|
<p><input id="joined-left" type="checkbox" checked><label for="joined-left">Join/left notify</label></p>
|
||||||
|
<p><input id="parse-latex" type="checkbox" checked><label for="parse-latex">Parse LaTeX</label></p>
|
||||||
|
<p><button id="clear-messages">Clear messages</button></p>
|
||||||
|
<h4>Color scheme</h4>
|
||||||
|
<select id="scheme-selector"></select>
|
||||||
|
<h4>Users online</h4>
|
||||||
|
<p>(Click user to invite)</p>
|
||||||
|
<ul id="users"></ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<script src="client.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,219 @@
|
||||||
|
(function(e){if("function"==typeof bootstrap)bootstrap("rendermathinelement",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeRenderMathInElement=e}else"undefined"!=typeof window?window.renderMathInElement=e():global.renderMathInElement=e()})(function(){var define,ses,bootstrap,module,exports;
|
||||||
|
return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
|
/* global katex */
|
||||||
|
|
||||||
|
var splitAtDelimiters = require("./splitAtDelimiters");
|
||||||
|
|
||||||
|
var splitWithDelimiters = function(text, delimiters) {
|
||||||
|
var data = [{type: "text", data: text}];
|
||||||
|
for (var i = 0; i < delimiters.length; i++) {
|
||||||
|
var delimiter = delimiters[i];
|
||||||
|
data = splitAtDelimiters(
|
||||||
|
data, delimiter.left, delimiter.right,
|
||||||
|
delimiter.display || false);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderMathInText = function(text, delimiters) {
|
||||||
|
var data = splitWithDelimiters(text, delimiters);
|
||||||
|
|
||||||
|
var fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].type === "text") {
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].data));
|
||||||
|
} else {
|
||||||
|
var span = document.createElement("span");
|
||||||
|
var math = data[i].data;
|
||||||
|
try {
|
||||||
|
katex.render(math, span, {
|
||||||
|
displayMode: data[i].display
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof katex.ParseError)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
console.error(
|
||||||
|
"KaTeX auto-render: Failed to parse `" + data[i].data +
|
||||||
|
"` with ",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].rawData));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fragment.appendChild(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderElem = function(elem, delimiters, ignoredTags) {
|
||||||
|
for (var i = 0; i < elem.childNodes.length; i++) {
|
||||||
|
var childNode = elem.childNodes[i];
|
||||||
|
if (childNode.nodeType === 3) {
|
||||||
|
// Text node
|
||||||
|
var frag = renderMathInText(childNode.textContent, delimiters);
|
||||||
|
i += frag.childNodes.length - 1;
|
||||||
|
elem.replaceChild(frag, childNode);
|
||||||
|
} else if (childNode.nodeType === 1) {
|
||||||
|
// Element node
|
||||||
|
var shouldRender = ignoredTags.indexOf(
|
||||||
|
childNode.nodeName.toLowerCase()) === -1;
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
renderElem(childNode, delimiters, ignoredTags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, it's something else, and ignore it.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var defaultOptions = {
|
||||||
|
delimiters: [
|
||||||
|
{left: "$$", right: "$$", display: true},
|
||||||
|
{left: "\\[", right: "\\]", display: true},
|
||||||
|
{left: "\\(", right: "\\)", display: false}
|
||||||
|
// LaTeX uses this, but it ruins the display of normal `$` in text:
|
||||||
|
// {left: "$", right: "$", display: false}
|
||||||
|
],
|
||||||
|
|
||||||
|
ignoredTags: [
|
||||||
|
"script", "noscript", "style", "textarea", "pre", "code"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var extend = function(obj) {
|
||||||
|
// Adapted from underscore.js' `_.extend`. See LICENSE.txt for license.
|
||||||
|
var source, prop;
|
||||||
|
for (var i = 1, length = arguments.length; i < length; i++) {
|
||||||
|
source = arguments[i];
|
||||||
|
for (prop in source) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(source, prop)) {
|
||||||
|
obj[prop] = source[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderMathInElement = function(elem, options) {
|
||||||
|
if (!elem) {
|
||||||
|
throw new Error("No element provided to render");
|
||||||
|
}
|
||||||
|
|
||||||
|
options = extend({}, defaultOptions, options);
|
||||||
|
|
||||||
|
renderElem(elem, options.delimiters, options.ignoredTags);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = renderMathInElement;
|
||||||
|
|
||||||
|
},{"./splitAtDelimiters":2}],2:[function(require,module,exports){
|
||||||
|
var findEndOfMath = function(delimiter, text, startIndex) {
|
||||||
|
// Adapted from
|
||||||
|
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||||
|
var index = startIndex;
|
||||||
|
var braceLevel = 0;
|
||||||
|
|
||||||
|
var delimLength = delimiter.length;
|
||||||
|
|
||||||
|
while (index < text.length) {
|
||||||
|
var character = text[index];
|
||||||
|
|
||||||
|
if (braceLevel <= 0 &&
|
||||||
|
text.slice(index, index + delimLength) === delimiter) {
|
||||||
|
return index;
|
||||||
|
} else if (character === "\\") {
|
||||||
|
index++;
|
||||||
|
} else if (character === "{") {
|
||||||
|
braceLevel++;
|
||||||
|
} else if (character === "}") {
|
||||||
|
braceLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
|
||||||
|
var finalData = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < startData.length; i++) {
|
||||||
|
if (startData[i].type === "text") {
|
||||||
|
var text = startData[i].data;
|
||||||
|
|
||||||
|
var lookingForLeft = true;
|
||||||
|
var currIndex = 0;
|
||||||
|
var nextIndex;
|
||||||
|
|
||||||
|
nextIndex = text.indexOf(leftDelim);
|
||||||
|
if (nextIndex !== -1) {
|
||||||
|
currIndex = nextIndex;
|
||||||
|
finalData.push({
|
||||||
|
type: "text",
|
||||||
|
data: text.slice(0, currIndex)
|
||||||
|
});
|
||||||
|
lookingForLeft = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (lookingForLeft) {
|
||||||
|
nextIndex = text.indexOf(leftDelim, currIndex);
|
||||||
|
if (nextIndex === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalData.push({
|
||||||
|
type: "text",
|
||||||
|
data: text.slice(currIndex, nextIndex)
|
||||||
|
});
|
||||||
|
|
||||||
|
currIndex = nextIndex;
|
||||||
|
} else {
|
||||||
|
nextIndex = findEndOfMath(
|
||||||
|
rightDelim,
|
||||||
|
text,
|
||||||
|
currIndex + leftDelim.length);
|
||||||
|
if (nextIndex === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalData.push({
|
||||||
|
type: "math",
|
||||||
|
data: text.slice(
|
||||||
|
currIndex + leftDelim.length,
|
||||||
|
nextIndex),
|
||||||
|
rawData: text.slice(
|
||||||
|
currIndex,
|
||||||
|
nextIndex + rightDelim.length),
|
||||||
|
display: display
|
||||||
|
});
|
||||||
|
|
||||||
|
currIndex = nextIndex + rightDelim.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
lookingForLeft = !lookingForLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalData.push({
|
||||||
|
type: "text",
|
||||||
|
data: text.slice(currIndex)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
finalData.push(startData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalData;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = splitAtDelimiters;
|
||||||
|
|
||||||
|
},{}]},{},[1])
|
||||||
|
(1)
|
||||||
|
});
|
||||||
|
;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
(function(e){if("function"==typeof bootstrap)bootstrap("rendermathinelement",e);else if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeRenderMathInElement=e}else"undefined"!=typeof window?window.renderMathInElement=e():global.renderMathInElement=e()})(function(){var e,t,r,n,a;return function i(e,t,r){function n(o,l){if(!t[o]){if(!e[o]){var f=typeof require=="function"&&require;if(!l&&f)return f(o,!0);if(a)return a(o,!0);throw new Error("Cannot find module '"+o+"'")}var s=t[o]={exports:{}};e[o][0].call(s.exports,function(t){var r=e[o][1][t];return n(r?r:t)},s,s.exports,i,e,t,r)}return t[o].exports}var a=typeof require=="function"&&require;for(var o=0;o<r.length;o++)n(r[o]);return n}({1:[function(e,t,r){var n=e("./splitAtDelimiters");var a=function(e,t){var r=[{type:"text",data:e}];for(var a=0;a<t.length;a++){var i=t[a];r=n(r,i.left,i.right,i.display||false)}return r};var i=function(e,t){var r=a(e,t);var n=document.createDocumentFragment();for(var i=0;i<r.length;i++){if(r[i].type==="text"){n.appendChild(document.createTextNode(r[i].data))}else{var o=document.createElement("span");var l=r[i].data;try{katex.render(l,o,{displayMode:r[i].display})}catch(f){if(!(f instanceof katex.ParseError)){throw f}console.error("KaTeX auto-render: Failed to parse `"+r[i].data+"` with ",f);n.appendChild(document.createTextNode(r[i].rawData));continue}n.appendChild(o)}}return n};var o=function(e,t,r){for(var n=0;n<e.childNodes.length;n++){var a=e.childNodes[n];if(a.nodeType===3){var l=i(a.textContent,t);n+=l.childNodes.length-1;e.replaceChild(l,a)}else if(a.nodeType===1){var f=r.indexOf(a.nodeName.toLowerCase())===-1;if(f){o(a,t,r)}}}};var l={delimiters:[{left:"$$",right:"$$",display:true},{left:"\\[",right:"\\]",display:true},{left:"\\(",right:"\\)",display:false}],ignoredTags:["script","noscript","style","textarea","pre","code"]};var f=function(e){var t,r;for(var n=1,a=arguments.length;n<a;n++){t=arguments[n];for(r in t){if(Object.prototype.hasOwnProperty.call(t,r)){e[r]=t[r]}}}return e};var s=function(e,t){if(!e){throw new Error("No element provided to render")}t=f({},l,t);o(e,t.delimiters,t.ignoredTags)};t.exports=s},{"./splitAtDelimiters":2}],2:[function(e,t,r){var n=function(e,t,r){var n=r;var a=0;var i=e.length;while(n<t.length){var o=t[n];if(a<=0&&t.slice(n,n+i)===e){return n}else if(o==="\\"){n++}else if(o==="{"){a++}else if(o==="}"){a--}n++}return-1};var a=function(e,t,r,a){var i=[];for(var o=0;o<e.length;o++){if(e[o].type==="text"){var l=e[o].data;var f=true;var s=0;var d;d=l.indexOf(t);if(d!==-1){s=d;i.push({type:"text",data:l.slice(0,s)});f=false}while(true){if(f){d=l.indexOf(t,s);if(d===-1){break}i.push({type:"text",data:l.slice(s,d)});s=d}else{d=n(r,l,s+t.length);if(d===-1){break}i.push({type:"math",data:l.slice(s+t.length,d),rawData:l.slice(s,d+r.length),display:a});s=d+r.length}f=!f}i.push({type:"text",data:l.slice(s)})}else{i.push(e[o])}}return i};t.exports=a},{}]},{},[1])(1)});
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue