Initial commit. (all done by @madr)
This commit is contained in:
commit
64f4fcc83e
7 changed files with 394 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
outchat.db
|
43
api.php
Normal file
43
api.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
$database = new SQLite3('outchat.db');
|
||||
|
||||
$action = $_GET['action'];
|
||||
|
||||
if($action === 'getMessages'){
|
||||
$timestamp = $database->escapeString(htmlspecialchars($_GET['timestamp']));
|
||||
$timestamp = ($timestamp == 0) ? strtotime('-6 hours') : $timestamp;
|
||||
|
||||
$results = $database->query('SELECT * FROM messages WHERE timestamp > '.$timestamp);
|
||||
|
||||
$messageArray = [];
|
||||
while($row = $results->fetchArray(SQLITE3_ASSOC)) {
|
||||
$row['datetime'] = date('d/m H:i', $row['timestamp']);
|
||||
|
||||
$image_search = preg_match('/(http|https):\/\/[^ ]+(\.gif|\.jpg|\.jpeg|\.png)/', $row['text'], $out);
|
||||
|
||||
if($image_search > 0){
|
||||
$row['text_processed'] = str_replace($out[0], '<p><img src="'.$out[0].'" /></p>', $row['text']);
|
||||
} else {
|
||||
$row['text_processed'] = $row['text'];
|
||||
}
|
||||
|
||||
$messageArray[] = $row;
|
||||
}
|
||||
echo json_encode($messageArray);
|
||||
}
|
||||
|
||||
if($action === 'createMessage'){
|
||||
$timestamp = time();
|
||||
$name = $database->escapeString(htmlspecialchars($_POST['name']));
|
||||
$text = $database->escapeString(htmlspecialchars($_POST['text']));
|
||||
/*
|
||||
$payload = file_get_contents('php://input');
|
||||
$data = json_decode($payload);
|
||||
var_dump($data);
|
||||
*/
|
||||
|
||||
$database->query('INSERT INTO messages (name, text, timestamp) VALUES ("'.$name.'", "'.$text.'", "'.$timestamp.'")');
|
||||
}
|
||||
|
||||
?>
|
39
helpers.js
Normal file
39
helpers.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
function htmlToElements(html) {
|
||||
const template = document.createElement('template')
|
||||
template.innerHTML = html
|
||||
return template.content.childNodes
|
||||
}
|
||||
|
||||
function hashCode(str) { // java String#hashCode
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
function intToRGB(i){
|
||||
const c = (i & 0x00FFFFFF)
|
||||
.toString(16)
|
||||
.toUpperCase();
|
||||
|
||||
return "00000".substring(0, 6 - c.length) + c;
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
const value = "; " + document.cookie;
|
||||
const parts = value.split("; " + name + "=");
|
||||
if (parts.length == 2) return parts.pop().split(";").shift();
|
||||
}
|
||||
|
||||
function nl2br(str, is_xhtml){
|
||||
if (typeof str === 'undefined' || str === null) {
|
||||
return '';
|
||||
}
|
||||
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
|
||||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
|
||||
}
|
||||
|
||||
function scrollWindowDown(){
|
||||
window.scrollTo(0, document.body.scrollHeight)
|
||||
}
|
39
index.php
Normal file
39
index.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>outposts</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
if(!isset($_COOKIE['outchat_name'])){
|
||||
echo '
|
||||
<div class="modal">
|
||||
<form action="" method="post" class="modal__form">
|
||||
<input type="text" name="name" placeholder="name" minLength="3" maxLength="20" class="modal__form-input">
|
||||
<input type="submit" value="»" class="modal__form-submit">
|
||||
</form>
|
||||
</div>
|
||||
';
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="chat">
|
||||
</div>
|
||||
|
||||
<form method="post" action="api.php?action=createMessage" class="form">
|
||||
<textarea name="text" class="form__input-message" placeholder="message"></textarea>
|
||||
<input type="hidden" name="name" value="kent">
|
||||
<input type="submit" value="»" class="form__input-submit">
|
||||
</form>
|
||||
|
||||
<script src="helpers.js"></script>
|
||||
<script src="main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
153
main.css
Normal file
153
main.css
Normal file
|
@ -0,0 +1,153 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
color: #fff;
|
||||
background-color: #111;
|
||||
font-family: Courier, Verdana;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chat {
|
||||
width: 100%;
|
||||
padding: 30px 30px 80px 30px;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.message {
|
||||
width: 100%;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.message__info {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.message__info-name {
|
||||
font-weight: 700;
|
||||
margin-right: 15px;
|
||||
font-size: 18px;
|
||||
line-height: .8;
|
||||
}
|
||||
|
||||
.message__text {
|
||||
position: relative;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.message__text p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message__text img {
|
||||
margin: 10px 0 0 0;
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
display: block;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
left: 15px;
|
||||
bottom: 15px;
|
||||
width: calc(100% - 30px);
|
||||
height: 60px;
|
||||
background-color: #fff;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form__input-message {
|
||||
padding: 20px;
|
||||
width: calc(100% - 60px);
|
||||
height: 100%;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
resize: none;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.form__input-submit {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.modal {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 100;
|
||||
background-color: #000;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.modal__form {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
height: 60px;
|
||||
margin: 50px auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.modal__form-input {
|
||||
padding: 0 90px 0 30px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.modal__form-submit {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.chat {
|
||||
padding: 10px 10px 50px 10px;
|
||||
}
|
||||
|
||||
.form {
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
}
|
112
main.js
Normal file
112
main.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
// modal form handle
|
||||
if(document.querySelector('.modal__form-submit')){
|
||||
document.querySelector('.modal__form-submit').addEventListener('click', function(e){
|
||||
e.preventDefault()
|
||||
const value = document.querySelector('.modal__form-input').value
|
||||
if(value.length >= 3){
|
||||
var date = new Date(Date.now())
|
||||
date = date.setTime(date.getTime() + (90*24*60*60*1000))
|
||||
date = new Date(date)
|
||||
const expires = '; expires=' + date.toUTCString()
|
||||
document.cookie = 'outchat_name=' + (value || '') + expires + '; path=/'
|
||||
window.location.href = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
document.querySelector('.form__input-message').addEventListener('keydown', function(e){
|
||||
if(e.ctrlKey && e.keyCode == 13){
|
||||
document.querySelector('.form__input-submit').click()
|
||||
}
|
||||
})
|
||||
|
||||
document.querySelector('.form__input-submit').addEventListener('click', function(e){
|
||||
e.preventDefault()
|
||||
|
||||
const name = getCookie('outchat_name')
|
||||
const text = document.querySelector('.form__input-message').value
|
||||
document.querySelector('.form__input-message').value = ''
|
||||
|
||||
var formData = new FormData()
|
||||
formData.append(name, text)
|
||||
|
||||
fetch('api.php?action=createMessage', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
body: 'name='+name+'&text='+text
|
||||
})
|
||||
.then(function(data){
|
||||
getMessages()
|
||||
})
|
||||
.catch(function(error){
|
||||
console.log(error)
|
||||
})
|
||||
})
|
||||
|
||||
function getMessages(){
|
||||
const timestamp = (document.querySelector('.chat').lastElementChild) ? document.querySelector('.chat').lastElementChild.getAttribute('data-timestamp') : 0
|
||||
|
||||
fetch('api.php?action=getMessages×tamp='+timestamp)
|
||||
.then(function(data){
|
||||
return data.json()
|
||||
})
|
||||
.then(function(data){
|
||||
if(data.length > 0){
|
||||
if((window.scrollY + window.innerHeight) == document.body.scrollHeight){
|
||||
setTimeout(function(){
|
||||
scrollWindowDown()
|
||||
}, 100)
|
||||
}
|
||||
for(message of data){
|
||||
createMessageNode(message.name, message.text_processed, message.timestamp, message.datetime)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(error){
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
function createMessageNode(name, text, timestamp, datetime){
|
||||
const elementMain = document.createElement('div', { class: 'message' })
|
||||
const elementText = document.createElement('div', { class: 'message__text'})
|
||||
const elementInfo = document.createElement('div', { class: 'message__info'})
|
||||
const elementName = document.createElement('div', { id: 'message__info-name'})
|
||||
const elementDatetime = document.createElement('div', { id: 'message__info-datetime'})
|
||||
const contentName = document.createTextNode(name)
|
||||
const contentDatetime = document.createTextNode('['+datetime+']')
|
||||
//const contentText = document.createTextNode(text)
|
||||
|
||||
// element creation
|
||||
elementMain.classList.add('message')
|
||||
elementText.classList.add('message__text')
|
||||
elementInfo.classList.add('message__info')
|
||||
elementDatetime.classList.add('message__info-datetime')
|
||||
elementName.classList.add('message__info-name')
|
||||
|
||||
// "hash" name color
|
||||
elementName.style.color = "#"+intToRGB(hashCode(name))
|
||||
|
||||
// append everything to chat
|
||||
elementName.appendChild(contentName)
|
||||
elementDatetime.appendChild(contentDatetime)
|
||||
elementText.innerHTML = text
|
||||
|
||||
elementInfo.appendChild(elementName)
|
||||
elementInfo.appendChild(elementDatetime)
|
||||
|
||||
elementMain.appendChild(elementInfo)
|
||||
elementMain.appendChild(elementText)
|
||||
elementMain.setAttribute('data-timestamp', timestamp)
|
||||
|
||||
document.querySelector('.chat').appendChild(elementMain)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
getMessages()
|
||||
setInterval(function(){
|
||||
getMessages()
|
||||
}, 2000)
|
7
outchat-ddl.sql
Normal file
7
outchat-ddl.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE `messages` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`text` TEXT,
|
||||
`timestamp` INTEGER
|
||||
);
|
||||
|
Loading…
Reference in a new issue