work on contact manager, removed old twitter-like ui for now

This commit is contained in:
Kevin Froman 2019-02-16 00:01:26 -06:00
parent 4827ef6def
commit 3fc623b8ee
29 changed files with 38 additions and 3562 deletions

View File

@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at beardog@firemail.cc. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at beardog at mailbox.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@ -21,7 +21,8 @@ import sqlite3, os, sys, time, math, base64, tarfile, nacl, logger, json, netcon
from onionrblockapi import Block
import onionrutils, onionrcrypto, onionrproofs, onionrevents as events, onionrexceptions
import onionrblacklist, onionrusers
import onionrblacklist
from onionrusers import onionrusers
import dbcreator, onionrstorage, serializeddata
from etc import onionrvalues

View File

@ -25,7 +25,7 @@ MIN_PY_VERSION = 6
if sys.version_info[0] == 2 or sys.version_info[1] < MIN_PY_VERSION:
print('Error, Onionr requires Python 3.%s+' % (MIN_PY_VERSION,))
sys.exit(1)
import os, base64, random, getpass, shutil, subprocess, requests, time, platform, datetime, re, json, getpass, sqlite3
import os, base64, random, getpass, shutil, time, platform, datetime, re, json, getpass, sqlite3
import webbrowser, uuid, signal
from threading import Thread
import api, core, config, logger, onionrplugins as plugins, onionrevents as events
@ -33,7 +33,8 @@ import onionrutils
import netcontroller, onionrstorage
from netcontroller import NetController
from onionrblockapi import Block
import onionrproofs, onionrexceptions, onionrusers, communicator
import onionrproofs, onionrexceptions, communicator
from onionrusers import onionrusers
try:
from urllib3.contrib.socks import SOCKSProxyManager

View File

@ -18,8 +18,9 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import core as onionrcore, logger, config, onionrexceptions, nacl.exceptions, onionrusers
import core as onionrcore, logger, config, onionrexceptions, nacl.exceptions
import json, os, sys, datetime, base64, onionrstorage
from onionrusers import onionrusers
class Block:
blockCacheOrder = list() # NEVER write your own code that writes to this!

View File

@ -0,0 +1,30 @@
'''
Onionr - P2P Anonymous Storage Network
Sets more abstract information related to a peer. Can be thought of as traditional 'contact' system
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import onionrusers
class ContactManager(onionrusers.OnionrUser):
def set_info(self, key, value):
return
def add_contact(self):
return
def delete_contact(self):
return

View File

@ -1,44 +0,0 @@
# Onionr UI
## About
The default GUI for Onionr
## Setup
To compile the application, simply execute the following:
```
python3 compile.py
```
If you are wanting to compile Onionr UI for another language, execute the following, replacing `[lang]` with the target language (supported languages include `eng` for English, `spa` para español, and `zho`为中国人):
```
python3 compile.py [lang]
```
## FAQ
### Why "compile" anyway?
This web application is compiled for a few reasons:
1. To make it easier to update; this way, we do not have to update the header in every file if we want to change something about it.
2. To make the application smaller in size; there is less duplicated code when the code like the header and footer can be stored in an individual file rather than every file.
3. For multi-language support; with the Python "tags" feature, we can reference strings by variable name, and based on a language file, they can be dynamically inserted into the page on compilation.
4. For compile-time customizations.
### What exactly happens when you compile?
Upon compilation, files from the `src/` directory will be copied to `dist/` directory, header and footers will be injected in the proper places, and Python "tags" will be interpreted.
### How do Python "tags" work?
There are two types of Python "tags":
1. Logic tags (`<$ logic $>`): These tags allow you to perform logic at compile time. Example: `<$ import datetime; lastUpdate = datetime.datetime.now() $>`: This gets the current time while compiling, then stores it in `lastUpdate`.
2. Data tags (`<$= data $>`): These tags take whatever the return value of the statement in the tags is, and write it directly to the page. Example: `<$= 'This application was compiled at %s.' % lastUpdate $>`: This will write the message in the string in the tags to the page.
**Note:** Logic tags take a higher priority and will always be interpreted first.
### How does the language feature work?
When you use a data tag to write a string to the page (e.g. `<$= LANG.HELLO_WORLD $>`), the language feature simply takes dictionary of the language that is currently being used from the language map file (`lang.json`), then searches for the key (being the variable name after the characters `LANG.` in the data tag, like `HELLO_WORLD` from the example before). It then writes that string to the page. Language variables are always prefixed with `LANG.` and should always be uppercase (as they are a constant).
### I changed a few things in the application and tried to view the updates in my browser, but nothing changed!
You most likely forgot to compile. Try running `python3 compile.py` and check again. If you are still having issues, [open up an issue](https://gitlab.com/beardog/Onionr/issues/new?issue[title]=Onionr UI not updating after compiling).

File diff suppressed because one or more lines are too long

View File

@ -1,19 +0,0 @@
<!-- Modal -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-title"><$= LANG.MODAL_TITLE $></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="modal-content"><$= LANG.MODAL_MESSAGE $></div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="js/main.js"></script>

View File

@ -1,30 +0,0 @@
<title><$= LANG.ONIONR_TITLE $></title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<link rel="stylesheet" type="text/css" href="css/main.css" />
<link rel="stylesheet" type="text/css" href="css/themes/dark.css" />
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Onionr</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="index.html"><$= LANG.TIMELINE $></a>
</li>
<li class="nav-item">
<a class="nav-link" href="notifications.html"><$= LANG.NOTIFICATIONS $></a>
</li>
<li class="nav-item">
<a class="nav-link" href="messages.html"><$= LANG.MESSAGES $></a>
</li>
</ul>
</div>
</nav>

View File

@ -1,31 +0,0 @@
<!-- POST REPLIES -->
<div class="onionr-post-creator">
<div class="row">
<div class="onionr-reply-creator container">
<div class="row">
<div class="col-3">
<img class="onionr-post-creator-user-icon" id="onionr-reply-creator-user-icon">
</div>
<div class="col-9">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-creator-user-name" id="onionr-reply-creator-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')"></a>
<a class="onionr-post-creator-user-id" id="onionr-reply-creator-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id"><$= LANG.REPLY_CREATOR_YOU $></a>
</div>
</div>
<textarea class="onionr-post-creator-content" id="onionr-reply-creator-content" oninput="replyCreatorChange()"></textarea>
<div class="onionr-post-creator-content-message" id="onionr-reply-creator-content-message"></div>
<input type="button" onclick="makeReply()" title="<$= LANG.REPLY_CREATOR_CREATE $>" value="<$= LANG.REPLY_CREATOR_CREATE $>" id="onionr-reply-creator-create" class="onionr-post-creator-create" />
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div id="onionr-replies"></div>
</div>
<!-- END POST REPLIES -->

View File

@ -1,32 +0,0 @@
<!-- POST -->
<div class="col-12">
<div class="onionr-post" id="onionr-post-$post-hash" onclick="focusPost('$post-hash', 'user-id-url', 'user-name-url', '')">
<div class="row">
<div class="col-2">
<img class="onionr-post-user-icon" src="$user-image">
</div>
<div class="col-10">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-user-name" id="onionr-post-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')">$user-name</a>
<a class="onionr-post-user-id" id="onionr-post-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id">$user-id-truncated</a>
</div>
<div class="col col-auto text-right ml-auto pl-0">
<div class="onionr-post-date text-right" data-placement="top" data-toggle="tooltip" title="$date">$date-relative</div>
</div>
</div>
<div class="onionr-post-content">
$content
</div>
<div class="onionr-post-controls pt-2">
<a href="#!" onclick="toggleLike('$post-hash')" class="glyphicon glyphicon-heart mr-2">$liked</a>
<a href="#!" onclick="reply('$post-hash')" class="glyphicon glyphicon-comment mr-2"><$= LANG.POST_REPLY $></a>
</div>
</div>
</div>
</div>
</div>
<!-- END POST -->

View File

@ -1,30 +0,0 @@
<!-- POST FOCUS REPLIES -->
<div class="col-12">
<div class="row">
<div class="onionr-post-focus-reply-creator">
<div class="row">
<div class="col-1"></div>
<div class="col-2">
<img class="onionr-post-creator-user-icon" id="onionr-post-focus-reply-creator-user-icon">
</div>
<div class="col-9">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-creator-user-name" id="onionr-post-focus-reply-creator-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')"></a>
<a class="onionr-post-creator-user-id" id="onionr-post-focus-reply-creator-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id"><$= LANG.REPLY_CREATOR_YOU $></a>
</div>
</div>
<textarea class="onionr-post-creator-content" id="onionr-post-focus-reply-creator-content" oninput="focusReplyCreatorChange()"></textarea>
<div class="onionr-post-creator-content-message" id="onionr-post-focus-reply-creator-content-message"></div>
<input type="button" onclick="makeFocusReply()" title="<$= LANG.REPLY_CREATOR_CREATE $>" value="<$= LANG.REPLY_CREATOR_CREATE $>" id="onionr-post-focus-reply-creator-create" class="onionr-post-creator-create" />
</div>
</div>
</div>
<div class="onionr-post-focus-replies"></div>
</div>
</div>
<!-- END POST FOCUS REPLIES -->

View File

@ -1,31 +0,0 @@
<!-- POST -->
<div class="col-12">
<div class="onionr-post" id="onionr-post-$post-hash" onclick="focusPost('$post-hash', 'user-id-url', 'user-name-url', '')">
<div class="row">
<div class="col-3">
<img class="onionr-post-user-icon" src="$user-image">
</div>
<div class="col-9">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-user-name" id="onionr-post-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')">$user-name</a>
</div>
<div class="col col-auto text-right ml-auto pl-0">
<div class="onionr-post-date text-right" data-placement="top" data-toggle="tooltip" title="$date">$date-relative-truncated</div>
</div>
</div>
<div class="onionr-post-content">
$content
</div>
<div class="onionr-post-controls pt-2">
<a href="#!" onclick="toggleLike('$post-hash')" class="glyphicon glyphicon-heart mr-2">$liked</a>
<a href="#!" onclick="reply('$post-hash')" class="glyphicon glyphicon-comment mr-2"><$= LANG.POST_REPLY $></a>
</div>
</div>
</div>
</div>
</div>
<!-- END POST -->

View File

@ -1,130 +0,0 @@
#!/usr/bin/python3
import shutil, os, re, json, traceback
# get user's config
settings = {}
with open('config.json', 'r') as file:
settings = json.loads(file.read())
# "hardcoded" config, not for user to mess with
HEADER_FILE = 'common/header.html'
FOOTER_FILE = 'common/footer.html'
SRC_DIR = 'src/'
DST_DIR = 'dist/'
HEADER_STRING = '<header />'
FOOTER_STRING = '<footer />'
# remove dst folder
shutil.rmtree(DST_DIR, ignore_errors=True)
# taken from https://stackoverflow.com/questions/1868714/how-do-i-copy-an-entire-directory-of-files-into-an-existing-directory-using-pyth
def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isdir(s):
shutil.copytree(s, d, symlinks, ignore)
else:
shutil.copy2(s, d)
# copy src to dst
copytree(SRC_DIR, DST_DIR, False)
# load in lang map
langmap = {}
with open('lang.json', 'r') as file:
langmap = json.loads(file.read())[settings['language']]
LANG = type('LANG', (), langmap)
# templating
class Template:
def jsTemplate(template, filename = ''):
with open('common/%s.html' % template, 'r') as file:
return Template.parseTags(file.read().replace('\\', '\\\\').replace('\'', '\\\'').replace('\n', "\\\n"), filename)
def htmlTemplate(template, filename = ''):
with open('common/%s.html' % template, 'r') as file:
return Template.parseTags(file.read(), filename)
# tag parser
def parseTags(contents, filename = ''):
# <$ logic $>
for match in re.findall(r'(<\$(?!=)(.*?)\$>)', contents):
try:
out = exec(match[1].strip())
contents = contents.replace(match[0], '' if out is None else str(out))
except Exception as e:
print('Error: Failed to execute python tag (%s): %s\n' % (filename, match[1]))
traceback.print_exc()
print('\nIgnoring this error, continuing to compile...\n')
# <$= data $>
for match in re.findall(r'(<\$=(.*?)\$>)', contents):
try:
out = eval(match[1].strip())
contents = contents.replace(match[0], '' if out is None else str(out))
except (NameError, AttributeError) as e:
name = match[1].strip()
print('Warning: %s does not exist, treating as an str' % name)
contents = contents.replace(match[0], name)
except Exception as e:
print('Error: Failed to execute python tag (%s): %s\n' % (filename, match[1]))
traceback.print_exc()
print('\nIgnoring this error, continuing to compile...\n')
return contents
def jsTemplate(contents):
return Template.jsTemplate(contents)
def htmlTemplate(contents):
return Template.htmlTemplate(contents)
# get header file
with open(HEADER_FILE, 'r') as file:
HEADER_FILE = file.read()
if settings['python_tags']:
HEADER_FILE = Template.parseTags(HEADER_FILE)
# get footer file
with open(FOOTER_FILE, 'r') as file:
FOOTER_FILE = file.read()
if settings['python_tags']:
FOOTER_FILE = Template.parseTags(FOOTER_FILE)
# iterate dst, replace files
def iterate(directory):
for filename in os.listdir(directory):
if filename.split('.')[-1].lower() in ['htm', 'html', 'css', 'js']:
try:
path = os.path.join(directory, filename)
if os.path.isdir(path):
iterate(path)
else:
contents = ''
with open(path, 'r') as file:
# get file contents
contents = file.read()
os.remove(path)
with open(path, 'w') as file:
# set the header & footer
contents = contents.replace(HEADER_STRING, HEADER_FILE)
contents = contents.replace(FOOTER_STRING, FOOTER_FILE)
# do python tags
if settings['python_tags']:
contents = Template.parseTags(contents, filename)
# write file
file.write(contents)
except Exception as e:
print('Error: Failed to parse file: %s\n' % filename)
traceback.print_exc()
print('\nIgnoring this error, continuing to compile...\n')
iterate(DST_DIR)

View File

@ -1,4 +0,0 @@
{
"language" : "eng",
"python_tags" : true
}

View File

@ -1,122 +0,0 @@
/* general formatting */
@media (min-width: 768px) {
.container-small {
width: 300px;
}
.container-large {
width: 970px;
}
}
@media (min-width: 992px) {
.container-small {
width: 500px;
}
.container-large {
width: 1170px;
}
}
@media (min-width: 1200px) {
.container-small {
width: 700px;
}
.container-large {
width: 1500px;
}
}
.container-small, .container-large {
max-width: 100%;
}
/* navbar */
body {
margin-top: 5rem;
}
/* timeline */
.onionr-post-focus-separator {
width: 100%;
padding: 1rem;
padding-left: 0;
padding-right: 0;
}
.onionr-post {
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
width: 100%;
}
.onionr-post-user-name {
display: inline;
}
.onionr-post-user-id:before { content: "("; }
.onionr-post-user-id:after { content: ")"; }
.onionr-post-content {
word-wrap: break-word;
}
.onionr-post-user-icon {
border-radius: 100%;
width: 100%;
}
.onionr-post-creator {
padding: 1rem;
margin-bottom: 1rem;
width: 100%;
}
.onionr-post-creator-user-name {
display: inline;
}
.onionr-post-creator-user-id:before { content: "("; }
.onionr-post-creator-user-id:after { content: ")"; }
.onionr-post-creator-content {
word-wrap: break-word;
width: 100%;
}
.onionr-post-creator-user-icon {
border-radius: 100%;
width: 100%;
}
.onionr-post-creator-create {
width: 100%;
text-align: center;
}
.h-divider {
margin: 5px 15px;
height: 1px;
width: 100%;
}
/* profile */
.onionr-profile-user-icon {
border-radius: 100%;
width: 100%;
margin-bottom: 1rem;
}
.onionr-profile-username {
text-align: center;
}
.onionr-profile-save {
width: 100%;
}

View File

@ -1,76 +0,0 @@
body {
background-color: #96928f;
color: #25383C;
}
/* timeline */
.onionr-post-focus-separator {
border-color: black;
}
.modal-content {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post-user-name {
color: green;
font-weight: bold;
}
.onionr-post-user-id {
color: gray;
}
.onionr-post-date {
color: gray;
}
.onionr-post-content {
font-family: sans-serif, serif;
border-top: 1px solid black;
font-size: 15pt;
}
.onionr-post-creator {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post-creator-user-name {
color: green;
font-weight: bold;
}
.onionr-post-creator-user-id {
color: gray;
}
.onionr-post-creator-date {
color: gray;
}
.onionr-post-creator-content {
font-family: sans-serif, serif;
border-top: 1px solid black;
font-size: 15pt;
background-color: lightgray;
color: black;
border-width: 0px;
}
.h-divider {
border-top:1px solid gray;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,215 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Onionr UI</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<link rel="stylesheet" type="text/css" href="css/main.css" />
<link rel="stylesheet" type="text/css" href="css/themes/dark.css" />
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Onionr</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="index.html">Timeline</a>
</li>
<li class="nav-item">
<a class="nav-link" href="notifications.html">Notifications</a>
</li>
<li class="nav-item">
<a class="nav-link" href="messages.html">Messages</a>
</li>
</ul>
</div>
</nav>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12 col-lg-3">
<div class="onionr-profile">
<div class="row">
<div class="col-4 col-lg-12">
<img id="onionr-profile-user-icon" class="onionr-profile-user-icon" src="">
</div>
<div class="col-8 col-lg-12">
<h2 maxlength="25" id="onionr-profile-username" class="onionr-profile-username text-left text-lg-center text-sm-left" data-placement="top" data-toggle="tooltip" title="unknown" data-editable></h2>
</div>
<div class="col-12">
<p maxlength="128" id="onionr-profile-description" class="onionr-profile-description" data-editable></p>
</div>
<div class="col-12 onionr-profile-edit" id="onionr-profile-edit" style="display: none">
<div class="row">
<div class="col-sm-6 col-lg-12">
<input type="button" onclick="updateUser()" class="onionr-profile-save text-center" id="onionr-profile-save" value="Save" />
</div>
<div class="col-sm-6 col-lg-12">
<input type="button" onclick="cancelUpdate()" class="onionr-profile-save text-center" id="onionr-profile-cancel" value="Cancel" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="h-divider pb-3 d-block d-lg-none"></div>
<div class="col-sm-12 col-lg-6">
<div class="row" id="onionr-timeline-post-creator">
<div class="col-12">
<div class="onionr-timeline">
<h2>Timeline</h2>
</div>
</div>
<!-- POST CREATOR -->
<div class="col-12">
<div class="onionr-post-creator">
<div class="row">
<div class="col-2">
<img class="onionr-post-creator-user-icon" id="onionr-post-creator-user-icon">
</div>
<div class="col-10">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-creator-user-name" id="onionr-post-creator-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')"></a>
<a class="onionr-post-creator-user-id" id="onionr-post-creator-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id">you</a>
</div>
</div>
<textarea class="onionr-post-creator-content" id="onionr-post-creator-content" oninput="postCreatorChange()"></textarea>
<div class="onionr-post-creator-content-message" id="onionr-post-creator-content-message"></div>
<input type="button" onclick="makePost()" title="Create post" value="Create post" id="onionr-post-creator-create" class="onionr-post-creator-create" />
</div>
</div>
</div>
</div>
<!-- END POST CREATOR -->
</div>
<div class="row" id="onionr-timeline-posts">
</div>
</div>
<div class="d-none d-lg-block col-lg-3">
<div class="row">
<div class="col-12">
<div class="onionr-replies">
<h2 id="onionr-replies-title"></h2>
</div>
</div>
<div id="onionr-reply-creator-panel">
</div>
</div>
<div class="row">
</div>
</div>
</div>
</div>
<!-- POST FOCUS DIALOG -->
<div class="modal fade" id="onionr-post-focus" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="row p-3">
<div class="col-2">
<img src="" id="onionr-post-focus-user-icon" class="onionr-post-user-icon">
</div>
<div class="col-10">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-user-name" id="onionr-post-focus-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url'); jQuery('#onionr-post-focus').modal('hide');">$user-name</a>
<a class="onionr-post-user-id" id="onionr-post-focus-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url'); jQuery('#onionr-post-focus').modal('hide');" data-placement="top" data-toggle="tooltip" title="$user-id">$user-id-truncated</a>
</div>
<div class="col col-auto text-right ml-auto pl-0">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<div class="onionr-post-content" id="onionr-post-focus-content">
</div>
</div>
<hr class="col-12 onionr-post-focus-separator" />
<!-- POST FOCUS REPLIES -->
<div class="col-12">
<div class="row">
<div class="onionr-post-focus-reply-creator">
<div class="row">
<div class="col-1"></div>
<div class="col-2">
<img class="onionr-post-creator-user-icon" id="onionr-post-focus-reply-creator-user-icon">
</div>
<div class="col-9">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-creator-user-name" id="onionr-post-focus-reply-creator-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')"></a>
<a class="onionr-post-creator-user-id" id="onionr-post-focus-reply-creator-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id">you</a>
</div>
</div>
<textarea class="onionr-post-creator-content" id="onionr-post-focus-reply-creator-content" oninput="focusReplyCreatorChange()"></textarea>
<div class="onionr-post-creator-content-message" id="onionr-post-focus-reply-creator-content-message"></div>
<input type="button" onclick="makeFocusReply()" title="Reply" value="Reply" id="onionr-post-focus-reply-creator-create" class="onionr-post-creator-create" />
</div>
</div>
</div>
<div class="onionr-post-focus-replies"></div>
</div>
</div>
<!-- END POST FOCUS REPLIES -->
</div>
</div>
</div>
</div>
<!-- END POST FOCUS DIALOG -->
<!-- Modal -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-title">Loading...</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="modal-content">Onionr has begun performing a CPU-intensive operation. If this operation does not complete in the next 10 seconds, try reloading the page.</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="js/main.js"></script>
<script src="js/timeline.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,491 +0,0 @@
/* just for testing rn */
Block.getBlocks({'type' : 'onionr-post', 'signed' : true, 'reverse' : true}, function(data) {
for(var i = 0; i < data.length; i++) {
try {
var block = data[i];
var finished = false;
User.getUser(new String(block.getHeader('signer', 'unknown')), function(user) {
var post = new Post();
var blockContent = JSON.parse(block.getContent());
// just ignore anything shorter than 280 characters
if(String(blockContent['content']).length <= 280 && block.getParent() === null) {
post.setContent(blockContent['content']);
post.setPostDate(block.getDate());
post.setUser(user);
post.setHash(block.getHash());
document.getElementById('onionr-timeline-posts').innerHTML += post.getHTML();
}
finished = true;
});
while(!finished);
} catch(e) {
console.log('Troublemaker block: ' + data[i].getHash());
console.log(e);
}
}
});
function toggleLike(hash) {
var post = getPostMap(hash);
if(post === null || !getPostMap()[hash]['liked']) {
console.log('Liking ' + hash + '...');
if(post === null)
getPostMap()[hash] = {};
getPostMap()[hash]['liked'] = true;
set('postmap', JSON.stringify(getPostMap()));
var block = new Block();
block.setType('onionr-post-like');
block.setContent(JSON.stringify({'hash' : hash}));
block.save(true, function(hash) {});
} else {
console.log('Unliking ' + hash + '...');
}
}
function postCreatorChange() {
var content = document.getElementById('onionr-post-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = 'Please use less than 16 newlines';
} else if(content.length <= maxlength) {
// 280 max characters
message = '%s characters remaining'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '%s characters over maximum'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-post-creator-content-message');
var button = document.getElementById("onionr-post-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function replyCreatorChange() {
var content = document.getElementById('onionr-reply-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = 'Please use less than 16 newlines';
} else if(content.length <= maxlength) {
// 280 max characters
message = '%s characters remaining'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '%s characters over maximum'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-reply-creator-content-message');
var button = document.getElementById("onionr-reply-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function focusReplyCreatorChange() {
var content = document.getElementById('onionr-post-focus-reply-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = 'Please use less than 16 newlines';
} else if(content.length <= maxlength) {
// 280 max characters
message = '%s characters remaining'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '%s characters over maximum'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-post-focus-reply-creator-content-message');
var button = document.getElementById("onionr-post-focus-reply-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function viewProfile(id, name) {
id = decodeURIComponent(id);
document.getElementById("onionr-profile-username").innerHTML = Sanitize.html(decodeURIComponent(name));
User.getUser(id, function(data) {
if(data !== null) {
document.getElementById("onionr-profile-user-icon").src = "data:image/jpeg;base64," + Sanitize.html(data.getIcon());
document.getElementById("onionr-profile-user-icon").b64 = Sanitize.html(data.getIcon());
document.getElementById("onionr-profile-username").innerHTML = Sanitize.html(Sanitize.username(data.getName()));
document.getElementById("onionr-profile-username").title = Sanitize.html(data.getID());
document.getElementById("onionr-profile-description").innerHTML = Sanitize.html(Sanitize.description(data.getDescription()));
}
});
}
function updateUser() {
toggleSaveButton(false);
// jQuery('#modal').modal('show');
var name = jQuery('#onionr-profile-username').text();
var id = document.getElementById("onionr-profile-username").title;
var icon = document.getElementById("onionr-profile-user-icon").b64;
var description = jQuery("#onionr-profile-description").text();
var user = new User();
user.setName(name);
user.setID(id);
user.setIcon(icon);
user.setDescription(Sanitize.description(description));
user.remember();
user.save(function() {
setCurrentUser(user);
window.location.reload();
});
}
function cancelUpdate() {
toggleSaveButton(false);
var name = jQuery('#onionr-profile-username').text();
var id = document.getElementById("onionr-profile-username").title;
viewProfile(id, name);
}
function toggleSaveButton(show) {
document.getElementById("onionr-profile-edit").style.display = (show ? 'block' : 'none');
}
function makePost() {
var content = document.getElementById("onionr-post-creator-content").value;
if(content.trim() !== '') {
var post = new Post();
post.setUser(getCurrentUser());
post.setContent(content);
post.setPostDate(new Date());
post.save(function(data) {}); // async, but no function
document.getElementById('onionr-timeline-posts').innerHTML = post.getHTML() + document.getElementById('onionr-timeline-posts').innerHTML;
document.getElementById("onionr-post-creator-content").value = "";
document.getElementById("onionr-post-creator-content").focus();
postCreatorChange();
} else {
console.log('Not making empty post.');
}
}
function getReplies(id, callback) {
Block.getBlocks({'type' : 'onionr-post', 'parent' : id, 'signed' : true, 'reverse' : true}, callback);
}
function focusPost(id) {
viewReplies(id);
}
function viewRepliesMobile(id) {
var post = document.getElementById('onionr-post-' + id);
var user_name = '';
var user_id = '';
var user_id_trunc = '';
var user_icon = '';
var post_content = '';
if(post !== null && post !== undefined) {
// if the post is in the timeline, get the data from it
user_name = post.getElementsByClassName('onionr-post-user-name')[0].innerHTML;
user_id = post.getElementsByClassName('onionr-post-user-id')[0].title;
user_id_trunc = post.getElementsByClassName('onionr-post-user-id')[0].innerHTML;
user_icon = post.getElementsByClassName('onionr-post-user-icon')[0].src;
post_content = post.getElementsByClassName('onionr-post-content')[0].innerHTML;
} else {
// otherwise, fetch the data
}
document.getElementById('onionr-post-focus-user-icon').src = user_icon;
document.getElementById('onionr-post-focus-user-name').innerHTML = user_name;
document.getElementById('onionr-post-focus-user-id').innerHTML = user_id_trunc;
document.getElementById('onionr-post-focus-user-id').title = user_id;
document.getElementById('onionr-post-focus-content').innerHTML = post_content;
document.getElementById('onionr-post-focus-reply-creator-user-name').innerHTML = Sanitize.html(Sanitize.username(getCurrentUser().getName()));
document.getElementById('onionr-post-focus-reply-creator-user-icon').src = "data:image/jpeg;base64," + Sanitize.html(getCurrentUser().getIcon());
document.getElementById('onionr-post-focus-reply-creator-content').value = '';
document.getElementById('onionr-post-focus-reply-creator-content-message').value = '';
jQuery('#onionr-post-focus').modal('show');
}
function viewReplies(id) {
document.getElementById('onionr-replies-title').innerHTML = 'Replies';
document.getElementById('onionr-reply-creator-panel').originalPost = id;
document.getElementById('onionr-reply-creator-panel').innerHTML = '<!-- POST REPLIES -->\
<div class="onionr-post-creator">\
<div class="row">\
<div class="onionr-reply-creator container">\
<div class="row">\
<div class="col-3">\
<img class="onionr-post-creator-user-icon" id="onionr-reply-creator-user-icon">\
</div>\
<div class="col-9">\
<div class="row">\
<div class="col col-auto">\
<a class="onionr-post-creator-user-name" id="onionr-reply-creator-user-name" href="#!" onclick="viewProfile(\'$user-id-url\', \'$user-name-url\')"></a>\
<a class="onionr-post-creator-user-id" id="onionr-reply-creator-user-id" href="#!" onclick="viewProfile(\'$user-id-url\', \'$user-name-url\')" data-placement="top" data-toggle="tooltip" title="$user-id">you</a>\
</div>\
</div>\
\
<textarea class="onionr-post-creator-content" id="onionr-reply-creator-content" oninput="replyCreatorChange()"></textarea>\
\
<div class="onionr-post-creator-content-message" id="onionr-reply-creator-content-message"></div>\
\
<input type="button" onclick="makeReply()" title="Reply" value="Reply" id="onionr-reply-creator-create" class="onionr-post-creator-create" />\
</div>\
</div>\
</div>\
</div>\
</div>\
\
<div class="row">\
<div id="onionr-replies"></div>\
</div>\
<!-- END POST REPLIES -->\
';
document.getElementById('onionr-reply-creator-content').innerHTML = '';
document.getElementById("onionr-reply-creator-content").placeholder = "Enter a message here...";
document.getElementById('onionr-reply-creator-user-name').innerHTML = Sanitize.html(Sanitize.username(getCurrentUser().getName()));
document.getElementById('onionr-reply-creator-user-icon').src = "data:image/jpeg;base64," + Sanitize.html(getCurrentUser().getIcon());
document.getElementById('onionr-replies').innerHTML = '';
getReplies(id, function(data) {
var replies = document.getElementById('onionr-replies');
replies.innerHTML = '';
for(var i = 0; i < data.length; i++) {
try {
var block = data[i];
var finished = false;
User.getUser(new String(block.getHeader('signer', 'unknown')), function(user) {
var post = new Post();
var blockContent = JSON.parse(block.getContent());
// just ignore anything shorter than 280 characters
if(String(blockContent['content']).length <= 280) {
post.setContent(blockContent['content']);
post.setPostDate(block.getDate());
post.setUser(user);
post.setHash(block.getHash());
replies.innerHTML += post.getHTML('reply');
}
finished = true;
});
while(!finished);
} catch(e) {
console.log('Troublemaker block: ' + data[i].getHash());
console.log(e);
}
}
});
}
function makeReply() {
var content = document.getElementById("onionr-reply-creator-content").value;
if(content.trim() !== '') {
var post = new Post();
var originalPost = document.getElementById('onionr-reply-creator-panel').originalPost;
console.log('Original post hash: ' + originalPost);
post.setUser(getCurrentUser());
post.setParent(originalPost);
post.setContent(content);
post.setPostDate(new Date());
post.save(function(data) {}); // async, but no function
document.getElementById('onionr-replies').innerHTML = post.getHTML('reply') + document.getElementById('onionr-replies').innerHTML;
document.getElementById("onionr-reply-creator-content").value = "";
document.getElementById("onionr-reply-creator-content").focus();
replyCreatorChange();
} else {
console.log('Not making empty reply.');
}
}
jQuery('body').on('click', '[data-editable]', function() {
var el = jQuery(this);
var txt = el.text();
var maxlength = el.attr("maxlength");
var input = jQuery('<input/>').val(txt);
input.attr('maxlength', maxlength);
el.replaceWith(input);
var save = function() {
var newTxt = input.val();
if(el.attr('id') === 'onionr-profile-username')
newTxt = Sanitize.username(newTxt);
if(el.attr('id') === 'onionr-profile-description')
newTxt = Sanitize.description(newTxt);
var p = el.text(newTxt);
input.replaceWith(p);
if(newTxt !== txt)
toggleSaveButton(true);
};
var saveEnter = function(event) {
console.log(event);
console.log(event.keyCode);
if (event.keyCode === 13)
save();
};
input.one('blur', save).bind('keyup', saveEnter).focus();
});
//viewProfile('$user-id-url', '$user-name-url')
// jQuery('#onionr-post-user-id').on('click', function(e) { alert(3);});
//jQuery('#onionr-post *').on('click', function(e) { e.stopPropagation(); });
// jQuery('#onionr-post').click(function(e) { alert(1); });
currentUser = getCurrentUser();
if(currentUser !== undefined && currentUser !== null) {
document.getElementById("onionr-post-creator-user-name").innerHTML = Sanitize.html(currentUser.getName());
document.getElementById("onionr-post-creator-user-id").innerHTML = "you";
document.getElementById("onionr-post-creator-user-icon").src = "data:image/jpeg;base64," + Sanitize.html(currentUser.getIcon());
document.getElementById("onionr-post-creator-user-id").title = currentUser.getID();
document.getElementById("onionr-post-creator-content").placeholder = "Enter a message here...";
document.getElementById("onionr-post-focus-reply-creator-content").placeholder = "Enter a message here...";
document.getElementById("onionr-post-focus-reply-creator-user-id").innerHTML = "you";
}
viewCurrentProfile = function() {
viewProfile(encodeURIComponent(currentUser.getID()), encodeURIComponent(currentUser.getName()));
}
document.getElementById("onionr-post-creator-user-id").onclick = viewCurrentProfile;
document.getElementById("onionr-post-creator-user-name").onclick = viewCurrentProfile;
// on some browsers it saves the user input on reload. So, it should also recheck the input.
postCreatorChange();

View File

@ -1,65 +0,0 @@
{
"eng" : {
"ONIONR_TITLE" : "Onionr UI",
"TIMELINE" : "Timeline",
"NOTIFICATIONS" : "Notifications",
"MESSAGES" : "Messages",
"LATEST" : "Latest...",
"TRENDING" : "Trending",
"REPLIES" : "Replies",
"MODAL_TITLE" : "Loading...",
"MODAL_MESSAGE" : "Onionr has begun performing a CPU-intensive operation. If this operation does not complete in the next 10 seconds, try reloading the page.",
"POST_LIKE" : "like",
"POST_UNLIKE" : "unlike",
"POST_REPLY" : "reply",
"POST_CREATOR_YOU" : "you",
"POST_CREATOR_PLACEHOLDER" : "Enter a message here...",
"POST_CREATOR_CREATE" : "Create post",
"REPLY_CREATOR_YOU" : "you",
"REPLY_CREATOR_PLACEHOLDER" : "Enter reply here...",
"REPLY_CREATOR_CREATE" : "Reply",
"POST_CREATOR_MESSAGE_MAXIMUM_NEWLINES" : "Please use less than 16 newlines",
"POST_CREATOR_MESSAGE_REMAINING" : "%s characters remaining",
"POST_CREATOR_MESSAGE_OVER" : "%s characters over maximum",
"REPLY_CREATOR_MESSAGE_MAXIMUM_NEWLINES" : "Please use less than 16 newlines",
"REPLY_CREATOR_MESSAGE_REMAINING" : "%s characters remaining",
"REPLY_CREATOR_MESSAGE_OVER" : "%s characters over maximum",
"PROFILE_EDIT_SAVE" : "Save",
"PROFILE_EDIT_CANCEL" : "Cancel"
},
"spa" : {
"ONIONR_TITLE" : "Onionr UI",
"TIMELINE" : "Linea de Tiempo",
"NOTIFICATIONS" : "Notificaciones",
"MESSAGES" : "Mensaje",
"TRENDING" : "Trending",
"POST_LIKE" : "me gusta",
"POST_REPLY" : "comentario"
},
"zho" : {
"ONIONR_TITLE" : "洋葱 用户界面",
"TIMELINE" : "时间线",
"NOTIFICATIONS" : "通知",
"MESSAGES" : "消息",
"TRENDING" : "趋势",
"POST_LIKE" : "喜欢",
"POST_REPLY" : "回复"
}
}

View File

@ -1,122 +0,0 @@
/* general formatting */
@media (min-width: 768px) {
.container-small {
width: 300px;
}
.container-large {
width: 970px;
}
}
@media (min-width: 992px) {
.container-small {
width: 500px;
}
.container-large {
width: 1170px;
}
}
@media (min-width: 1200px) {
.container-small {
width: 700px;
}
.container-large {
width: 1500px;
}
}
.container-small, .container-large {
max-width: 100%;
}
/* navbar */
body {
margin-top: 5rem;
}
/* timeline */
.onionr-post-focus-separator {
width: 100%;
padding: 1rem;
padding-left: 0;
padding-right: 0;
}
.onionr-post {
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
width: 100%;
}
.onionr-post-user-name {
display: inline;
}
.onionr-post-user-id:before { content: "("; }
.onionr-post-user-id:after { content: ")"; }
.onionr-post-content {
word-wrap: break-word;
}
.onionr-post-user-icon {
border-radius: 100%;
width: 100%;
}
.onionr-post-creator {
padding: 1rem;
margin-bottom: 1rem;
width: 100%;
}
.onionr-post-creator-user-name {
display: inline;
}
.onionr-post-creator-user-id:before { content: "("; }
.onionr-post-creator-user-id:after { content: ")"; }
.onionr-post-creator-content {
word-wrap: break-word;
width: 100%;
}
.onionr-post-creator-user-icon {
border-radius: 100%;
width: 100%;
}
.onionr-post-creator-create {
width: 100%;
text-align: center;
}
.h-divider {
margin: 5px 15px;
height: 1px;
width: 100%;
}
/* profile */
.onionr-profile-user-icon {
border-radius: 100%;
width: 100%;
margin-bottom: 1rem;
}
.onionr-profile-username {
text-align: center;
}
.onionr-profile-save {
width: 100%;
}

View File

@ -1,76 +0,0 @@
body {
background-color: #96928f;
color: #25383C;
}
/* timeline */
.onionr-post-focus-separator {
border-color: black;
}
.modal-content {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post-user-name {
color: green;
font-weight: bold;
}
.onionr-post-user-id {
color: gray;
}
.onionr-post-date {
color: gray;
}
.onionr-post-content {
font-family: sans-serif, serif;
border-top: 1px solid black;
font-size: 15pt;
}
.onionr-post-creator {
border: 1px solid black;
border-radius: 1rem;
background-color: lightgray;
}
.onionr-post-creator-user-name {
color: green;
font-weight: bold;
}
.onionr-post-creator-user-id {
color: gray;
}
.onionr-post-creator-date {
color: gray;
}
.onionr-post-creator-content {
font-family: sans-serif, serif;
border-top: 1px solid black;
font-size: 15pt;
background-color: lightgray;
color: black;
border-width: 0px;
}
.h-divider {
border-top:1px solid gray;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,136 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<header />
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12 col-lg-3">
<div class="onionr-profile">
<div class="row">
<div class="col-4 col-lg-12">
<img id="onionr-profile-user-icon" class="onionr-profile-user-icon" src="">
</div>
<div class="col-8 col-lg-12">
<h2 maxlength="25" id="onionr-profile-username" class="onionr-profile-username text-left text-lg-center text-sm-left" data-placement="top" data-toggle="tooltip" title="unknown" data-editable></h2>
</div>
<div class="col-12">
<p maxlength="128" id="onionr-profile-description" class="onionr-profile-description" data-editable></p>
</div>
<div class="col-12 onionr-profile-edit" id="onionr-profile-edit" style="display: none">
<div class="row">
<div class="col-sm-6 col-lg-12">
<input type="button" onclick="updateUser()" class="onionr-profile-save text-center" id="onionr-profile-save" value="<$= LANG.PROFILE_EDIT_SAVE $>" />
</div>
<div class="col-sm-6 col-lg-12">
<input type="button" onclick="cancelUpdate()" class="onionr-profile-save text-center" id="onionr-profile-cancel" value="<$= LANG.PROFILE_EDIT_CANCEL $>" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="h-divider pb-3 d-block d-lg-none"></div>
<div class="col-sm-12 col-lg-6">
<div class="row" id="onionr-timeline-post-creator">
<div class="col-12">
<div class="onionr-timeline">
<h2><$= LANG.TIMELINE $></h2>
</div>
</div>
<!-- POST CREATOR -->
<div class="col-12">
<div class="onionr-post-creator">
<div class="row">
<div class="col-2">
<img class="onionr-post-creator-user-icon" id="onionr-post-creator-user-icon">
</div>
<div class="col-10">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-creator-user-name" id="onionr-post-creator-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')"></a>
<a class="onionr-post-creator-user-id" id="onionr-post-creator-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url')" data-placement="top" data-toggle="tooltip" title="$user-id"><$= LANG.POST_CREATOR_YOU $></a>
</div>
</div>
<textarea class="onionr-post-creator-content" id="onionr-post-creator-content" oninput="postCreatorChange()"></textarea>
<div class="onionr-post-creator-content-message" id="onionr-post-creator-content-message"></div>
<input type="button" onclick="makePost()" title="<$= LANG.POST_CREATOR_CREATE $>" value="<$= LANG.POST_CREATOR_CREATE $>" id="onionr-post-creator-create" class="onionr-post-creator-create" />
</div>
</div>
</div>
</div>
<!-- END POST CREATOR -->
</div>
<div class="row" id="onionr-timeline-posts">
</div>
</div>
<div class="d-none d-lg-block col-lg-3">
<div class="row">
<div class="col-12">
<div class="onionr-replies">
<h2 id="onionr-replies-title"></h2>
</div>
</div>
<div id="onionr-reply-creator-panel">
</div>
</div>
<div class="row">
</div>
</div>
</div>
</div>
<!-- POST FOCUS DIALOG -->
<div class="modal fade" id="onionr-post-focus" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="row p-3">
<div class="col-2">
<img src="" id="onionr-post-focus-user-icon" class="onionr-post-user-icon">
</div>
<div class="col-10">
<div class="row">
<div class="col col-auto">
<a class="onionr-post-user-name" id="onionr-post-focus-user-name" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url'); jQuery('#onionr-post-focus').modal('hide');">$user-name</a>
<a class="onionr-post-user-id" id="onionr-post-focus-user-id" href="#!" onclick="viewProfile('$user-id-url', '$user-name-url'); jQuery('#onionr-post-focus').modal('hide');" data-placement="top" data-toggle="tooltip" title="$user-id">$user-id-truncated</a>
</div>
<div class="col col-auto text-right ml-auto pl-0">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<div class="onionr-post-content" id="onionr-post-focus-content">
</div>
</div>
<hr class="col-12 onionr-post-focus-separator" />
<$= htmlTemplate('onionr-timeline-reply-creator') $>
</div>
</div>
</div>
</div>
<!-- END POST FOCUS DIALOG -->
<footer />
<script src="js/timeline.js"></script>
</body>
</html>

View File

@ -1,689 +0,0 @@
/* handy localstorage functions for quick usage */
function set(key, val) {
return localStorage.setItem(key, val);
}
function get(key, df) { // df is default
value = localStorage.getItem(key);
if(value == null)
value = df;
return value;
}
function remove(key) {
return localStorage.removeItem(key);
}
function getParameter(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
/* usermap localStorage stuff */
var usermap = JSON.parse(get('usermap', '{}'));
var postmap = JSON.parse(get('postmap', '{}'))
function getUserMap() {
return usermap;
}
function getPostMap(hash) {
if(hash !== undefined) {
if(hash in postmap)
return postmap[hash];
return null;
}
return postmap;
}
function deserializeUser(id) {
if(!(id in getUserMap()))
return null;
var serialized = getUserMap()[id]
var user = new User();
user.setName(serialized['name']);
user.setID(serialized['id']);
user.setIcon(serialized['icon']);
user.setDescription(serialized['description']);
return user;
}
function getCurrentUser() {
var user = get('currentUser', null);
if(user === null)
return null;
return User.getUser(user, function() {});
}
function setCurrentUser(user) {
set('currentUser', user.getID());
}
/* returns a relative date format, e.g. "5 minutes" */
function timeSince(date, size) {
// taken from https://stackoverflow.com/a/3177838/3678023
var seconds = Math.floor((new Date() - date) / 1000);
var interval = Math.floor(seconds / 31536000);
if (size === null)
size = 'desktop';
var dates = {
'mobile' : {
'yr' : 'yrs',
'mo' : 'mo',
'd' : 'd',
'hr' : 'h',
'min' : 'm',
'secs' : 's',
'sec' : 's',
},
'desktop' : {
'yr' : ' years',
'mo' : ' months',
'd' : ' days',
'hr' : ' hours',
'min' : ' minutes',
'secs' : ' seconds',
'sec' : ' second',
},
};
if (interval > 1)
return interval + dates[size]['yr'];
interval = Math.floor(seconds / 2592000);
if (interval > 1)
return interval + dates[size]['mo'];
interval = Math.floor(seconds / 86400);
if (interval > 1)
return interval + dates[size]['d'];
interval = Math.floor(seconds / 3600);
if (interval > 1)
return interval + dates[size]['hr'];
interval = Math.floor(seconds / 60);
if (interval > 1)
return interval + dates[size]['min'];
if(Math.floor(seconds) !== 1)
return Math.floor(seconds) + dates[size]['secs'];
return '1' + dates[size]['sec'];
}
/* replace all instances of string */
String.prototype.replaceAll = function(search, replacement, limit) {
// taken from https://stackoverflow.com/a/17606289/3678023
var target = this;
return target.split(search, limit).join(replacement);
};
/* useful functions to sanitize data */
class Sanitize {
/* sanitizes HTML in a string */
static html(html) {
return String(html).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
/* URL encodes a string */
static url(url) {
return encodeURIComponent(url);
}
/* usernames */
static username(username) {
return String(username).replace(/[\W_]+/g, " ").substring(0, 25);
}
/* profile descriptions */
static description(description) {
return String(description).substring(0, 128);
}
}
/* config stuff */
function getWebPassword() {
return get("web-password", null);
}
function setWebPassword(password) {
return set("web-password", password);
}
function getTimingToken() {
return get("timing-token", null);
}
function setTimingToken(token) {
return set("timing-token", token);
}
/* user class */
class User {
constructor() {
this.name = 'Unknown';
this.id = 'unknown';
this.image = 'img/default.png';
}
setName(name) {
this.name = name;
}
getName() {
return this.name;
}
setID(id) {
this.id = id;
}
getID() {
return this.id;
}
setIcon(image) {
this.image = image;
}
getIcon() {
return this.image;
}
setDescription(description) {
this.description = description;
}
getDescription() {
return this.description;
}
serialize() {
return {
'name' : this.getName(),
'id' : this.getID(),
'icon' : this.getIcon(),
'description' : this.getDescription()
};
}
/* save in usermap */
remember() {
usermap[this.getID()] = this.serialize();
set('usermap', JSON.stringify(usermap));
}
/* save as a block */
save(callback) {
var block = new Block();
block.setType('onionr-user');
block.setContent(JSON.stringify(this.serialize()));
return block.save(true, callback);
}
static getUser(id, callback) {
// console.log(callback);
var user = deserializeUser(id);
if(user === null) {
Block.getBlocks({'type' : 'onionr-user-info', 'signed' : true, 'reverse' : true}, function(data) {
if(data.length !== 0) {
try {
user = new User();
var userInfo = JSON.parse(data[0].getContent());
if(userInfo['id'] === id) {
user.setName(userInfo['name']);
user.setIcon(userInfo['icon']);
user.setDescription(userInfo['description']);
user.setID(id);
user.remember();
// console.log(callback);
callback(user);
return user;
}
} catch(e) {
console.log(e);
callback(null);
return null;
}
} else {
callback(null);
return null;
}
});
} else {
// console.log(callback);
callback(user);
return user;
}
}
}
/* post class */
class Post {
/* returns the html content of a post */
getHTML(type) {
var replyTemplate = '<$= jsTemplate('onionr-timeline-reply') $>';
var postTemplate = '<$= jsTemplate('onionr-timeline-post') $>';
var template = '';
if(type !== undefined && type !== null && type == 'reply')
template = replyTemplate;
else
template = postTemplate;
var device = (jQuery(document).width() < 768 ? 'mobile' : 'desktop');
template = template.replaceAll('$user-name-url', Sanitize.html(Sanitize.url(this.getUser().getName())));
template = template.replaceAll('$user-name', Sanitize.html(this.getUser().getName()));
template = template.replaceAll('$user-id-url', Sanitize.html(Sanitize.url(this.getUser().getID())));
template = template.replaceAll('$user-id-truncated', Sanitize.html(this.getUser().getID().substring(0, 12) + '...'));
// template = template.replaceAll('$user-id-truncated', Sanitize.html(this.getUser().getID().split('-').slice(0, 4).join('-')));
template = template.replaceAll('$user-id', Sanitize.html(this.getUser().getID()));
template = template.replaceAll('$user-image', "data:image/jpeg;base64," + Sanitize.html(this.getUser().getIcon()));
template = template.replaceAll('$content', Sanitize.html(this.getContent()).replaceAll('\n', '<br />', 16)); // Maximum of 16 lines
template = template.replaceAll('$post-hash', this.getHash());
template = template.replaceAll('$date-relative-truncated', timeSince(this.getPostDate(), 'mobile'));
template = template.replaceAll('$date-relative', timeSince(this.getPostDate(), device) + (device === 'desktop' ? ' ago' : ''));
template = template.replaceAll('$date', this.getPostDate().toLocaleString());
if(this.getHash() in getPostMap() && getPostMap()[this.getHash()]['liked']) {
template = template.replaceAll('$liked', '<$= LANG.POST_UNLIKE $>');
} else {
template = template.replaceAll('$liked', '<$= LANG.POST_LIKE $>');
}
return template;
}
setUser(user) {
this.user = user;
}
getUser() {
return this.user;
}
setContent(content) {
this.content = content;
}
getContent() {
return this.content;
}
setParent(parent) {
this.parent = parent;
}
getParent() {
return this.parent;
}
setPostDate(date) { // unix timestamp input
if(date instanceof Date)
this.date = date;
else
this.date = new Date(date * 1000);
}
getPostDate() {
return this.date;
}
setHash(hash) {
this.hash = hash;
}
getHash() {
return this.hash;
}
save(callback) {
var args = {'type' : 'onionr-post', 'sign' : true, 'content' : JSON.stringify({'content' : this.getContent()})};
if(this.getParent() !== undefined && this.getParent() !== null)
args['parent'] = (this.getParent() instanceof Post ? this.getParent().getHash() : (this.getParent() instanceof Block ? this.getParent().getHash() : this.getParent()));
var url = '/client/?action=insertBlock&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken());
console.log(url);
var http = new XMLHttpRequest();
if(callback !== undefined) {
// async
var thisObject = this;
http.addEventListener('load', function() {
thisObject.setHash(Block.parseBlockArray(JSON.parse(http.responseText)['hash']));
callback(thisObject.getHash());
}, false);
http.open('GET', url, true);
http.timeout = 5000;
http.send(null);
} else {
// sync
http.open('GET', url, false);
http.send(null);
this.setHash(Block.parseBlockArray(JSON.parse(http.responseText)['hash']));
return this.getHash();
}
}
}
/* block class */
class Block {
constructor(type, content) {
this.type = type;
this.content = content;
}
// returns the block hash, if any
getHash() {
return this.hash;
}
// returns the block type
getType() {
return this.type;
}
// returns the block header
getHeader(key, df) { // df is default
if(key !== undefined) {
if(this.getHeader().hasOwnProperty(key))
return this.getHeader()[key];
else
return (df === undefined ? null : df);
} else
return this.header;
}
// returns the block metadata
getMetadata(key, df) { // df is default
if(key !== undefined) {
if(this.getMetadata().hasOwnProperty(key))
return this.getMetadata()[key];
else
return (df === undefined ? null : df);
} else
return this.metadata;
}
// returns the block content
getContent() {
return this.content;
}
// returns the parent block's hash (not Block object, for performance)
getParent() {
// console.log(this.parent);
// TODO: Create a function to fetch the block contents and parse it from the server; right now it is only possible to search for types of blocks (see Block.getBlocks), so it is impossible to return a Block object here
// if(!(this.parent instanceof Block) && this.parent !== undefined && this.parent !== null)
// this.parent = Block.openBlock(this.parent); // convert hash to Block object
return this.parent;
}
// returns the date that the block was received
getDate() {
return this.date;
}
// returns a boolean that indicates whether or not the block is valid
isValid() {
return this.valid;
}
// returns a boolean thati ndicates whether or not the block is signed
isSigned() {
return this.signed;
}
// returns the block signature
getSignature() {
return this.signature;
}
// returns the block type
setType(type) {
this.type = type;
return this;
}
// sets block metadata by key
setMetadata(key, val) {
this.metadata[key] = val;
return this;
}
// sets block content
setContent(content) {
this.content = content;
return this;
}
// sets the block parent by hash or Block object
setParent(parent) {
this.parent = parent;
return this;
}
// indicates if the Block exists or not
exists() {
return !(this.hash === null || this.hash === undefined);
}
// saves the block, returns the hash
save(sign, callback) {
var type = this.getType();
var content = this.getContent();
var parent = this.getParent();
if(content !== undefined && content !== null && type !== '') {
var args = {'content' : content};
if(type !== undefined && type !== null && type !== '')
args['type'] = type;
if(parent !== undefined && parent !== null && parent.getHash() !== undefined && parent.getHash() !== null && parent.getHash() !== '')
args['parent'] = parent.getHash();
if(sign !== undefined && sign !== null)
args['sign'] = String(sign) !== 'false'
var url = '/client/?action=insertBlock&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken());
console.log(url);
var http = new XMLHttpRequest();
if(callback !== undefined) {
// async
http.addEventListener('load', function() {
callback(Block.parseBlockArray(JSON.parse(http.responseText)['hash']));
}, false);
http.open('GET', url, true);
http.timeout = 5000;
http.send(null);
} else {
// sync
http.open('GET', url, false);
http.send(null);
return Block.parseBlockArray(JSON.parse(http.responseText)['hash']);
}
}
return false;
}
/* static functions */
// recreates a block by hash
static openBlock(hash) {
return Block.parseBlock(hash);
}
// converts an associative array to a Block
static parseBlock(val) {
var block = new Block();
block.type = val['type'];
block.content = val['content'];
block.header = val['header'];
block.metadata = val['metadata'];
block.date = new Date(val['date'] * 1000);
block.hash = val['hash'];
block.signature = val['signature'];
block.signed = val['signed'];
block.valid = val['valid'];
block.parent = val['parent'];
if(block.getParent() !== null) {
// if the block data is already in the associative array
/*
if (blocks.hasOwnProperty(block.getParent()))
block.setParent(Block.parseAssociativeArray({blocks[block.getParent()]})[0]);
*/
}
return block;
}
// converts an array of associative arrays to an array of Blocks
static parseBlockArray(blocks) {
var outputBlocks = [];
for(var key in blocks) {
if(blocks.hasOwnProperty(key)) {
var val = blocks[key];
var block = Block.parseBlock(val);
outputBlocks.push(block);
}
}
return outputBlocks;
}
static getBlocks(args, callback) { // callback is optional
args = args || {}
var url = '/client/?action=searchBlocks&data=' + Sanitize.url(JSON.stringify(args)) + '&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken());
console.log(url);
var http = new XMLHttpRequest();
if(callback !== undefined) {
// async
http.addEventListener('load', function() {
callback(Block.parseBlockArray(JSON.parse(http.responseText)['blocks']));
}, false);
http.open('GET', url, true);
http.timeout = 5000;
http.send(null);
} else {
// sync
http.open('GET', url, false);
http.send(null);
return Block.parseBlockArray(JSON.parse(http.responseText)['blocks']);
}
}
}
/* temporary code */
var tt = getParameter("timingToken");
if(tt !== null && tt !== undefined) {
setTimingToken(tt);
}
if(getWebPassword() === null) {
var password = "";
while(password.length != 64) {
password = prompt("Please enter the web password (run `./RUN-LINUX.sh --details`)");
}
setWebPassword(password);
}
if(getCurrentUser() === null) {
jQuery('#modal').modal('show');
var url = '/client/?action=info&token=' + Sanitize.url(getWebPassword()) + '&timingToken=' + Sanitize.url(getTimingToken());
console.log(url);
var http = new XMLHttpRequest();
// sync
http.addEventListener('load', function() {
var id = JSON.parse(http.responseText)['pubkey'];
User.getUser(id, function(data) {
if(data === null || data === undefined) {
var user = new User();
user.setName('New User');
user.setID(id);
user.setIcon('<$= Template.jsTemplate("default-icon") $>');
user.setDescription('A new OnionrUI user');
user.remember();
user.save();
setCurrentUser(user);
} else {
setCurrentUser(data);
}
window.location.reload();
});
}, false);
http.open('GET', url, true);
http.send(null);
}
currentUser = getCurrentUser();

View File

@ -1,460 +0,0 @@
/* just for testing rn */
Block.getBlocks({'type' : 'onionr-post', 'signed' : true, 'reverse' : true}, function(data) {
for(var i = 0; i < data.length; i++) {
try {
var block = data[i];
var finished = false;
User.getUser(new String(block.getHeader('signer', 'unknown')), function(user) {
var post = new Post();
var blockContent = JSON.parse(block.getContent());
// just ignore anything shorter than 280 characters
if(String(blockContent['content']).length <= 280 && block.getParent() === null) {
post.setContent(blockContent['content']);
post.setPostDate(block.getDate());
post.setUser(user);
post.setHash(block.getHash());
document.getElementById('onionr-timeline-posts').innerHTML += post.getHTML();
}
finished = true;
});
while(!finished);
} catch(e) {
console.log('Troublemaker block: ' + data[i].getHash());
console.log(e);
}
}
});
function toggleLike(hash) {
var post = getPostMap(hash);
if(post === null || !getPostMap()[hash]['liked']) {
console.log('Liking ' + hash + '...');
if(post === null)
getPostMap()[hash] = {};
getPostMap()[hash]['liked'] = true;
set('postmap', JSON.stringify(getPostMap()));
var block = new Block();
block.setType('onionr-post-like');
block.setContent(JSON.stringify({'hash' : hash}));
block.save(true, function(hash) {});
} else {
console.log('Unliking ' + hash + '...');
}
}
function postCreatorChange() {
var content = document.getElementById('onionr-post-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = '<$= LANG.POST_CREATOR_MESSAGE_MAXIMUM_NEWLINES $>';
} else if(content.length <= maxlength) {
// 280 max characters
message = '<$= LANG.POST_CREATOR_MESSAGE_REMAINING $>'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '<$= LANG.POST_CREATOR_MESSAGE_OVER $>'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-post-creator-content-message');
var button = document.getElementById("onionr-post-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function replyCreatorChange() {
var content = document.getElementById('onionr-reply-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = '<$= LANG.POST_CREATOR_MESSAGE_MAXIMUM_NEWLINES $>';
} else if(content.length <= maxlength) {
// 280 max characters
message = '<$= LANG.POST_CREATOR_MESSAGE_REMAINING $>'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '<$= LANG.POST_CREATOR_MESSAGE_OVER $>'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-reply-creator-content-message');
var button = document.getElementById("onionr-reply-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function focusReplyCreatorChange() {
var content = document.getElementById('onionr-post-focus-reply-creator-content').value;
var message = '';
var maxlength = 280;
var disable = true;
var warn = false;
if(content.length !== 0) {
if(content.length - content.replaceAll('\n', '').length > 16) {
// 16 max newlines
message = '<$= LANG.POST_CREATOR_MESSAGE_MAXIMUM_NEWLINES $>';
} else if(content.length <= maxlength) {
// 280 max characters
message = '<$= LANG.POST_CREATOR_MESSAGE_REMAINING $>'.replaceAll('%s', (280 - content.length));
disable = false;
if(maxlength - content.length < maxlength / 4) {
warn = true;
}
} else {
message = '<$= LANG.POST_CREATOR_MESSAGE_OVER $>'.replaceAll('%s', (content.length - maxlength));
}
}
var element = document.getElementById('onionr-post-focus-reply-creator-content-message');
var button = document.getElementById("onionr-post-focus-reply-creator-create");
if(message === '')
element.style.visibility = 'hidden';
else {
element.style.visibility = 'visible';
element.innerHTML = message;
if(disable)
element.style.color = 'red';
else if(warn)
element.style.color = '#FF8C00';
else
element.style.color = 'gray';
}
if(disable)
button.disabled = true;
else
button.disabled = false;
}
function viewProfile(id, name) {
id = decodeURIComponent(id);
document.getElementById("onionr-profile-username").innerHTML = Sanitize.html(decodeURIComponent(name));
User.getUser(id, function(data) {
if(data !== null) {
document.getElementById("onionr-profile-user-icon").src = "data:image/jpeg;base64," + Sanitize.html(data.getIcon());
document.getElementById("onionr-profile-user-icon").b64 = Sanitize.html(data.getIcon());
document.getElementById("onionr-profile-username").innerHTML = Sanitize.html(Sanitize.username(data.getName()));
document.getElementById("onionr-profile-username").title = Sanitize.html(data.getID());
document.getElementById("onionr-profile-description").innerHTML = Sanitize.html(Sanitize.description(data.getDescription()));
}
});
}
function updateUser() {
toggleSaveButton(false);
// jQuery('#modal').modal('show');
var name = jQuery('#onionr-profile-username').text();
var id = document.getElementById("onionr-profile-username").title;
var icon = document.getElementById("onionr-profile-user-icon").b64;
var description = jQuery("#onionr-profile-description").text();
var user = new User();
user.setName(name);
user.setID(id);
user.setIcon(icon);
user.setDescription(Sanitize.description(description));
user.remember();
user.save(function() {
setCurrentUser(user);
window.location.reload();
});
}
function cancelUpdate() {
toggleSaveButton(false);
var name = jQuery('#onionr-profile-username').text();
var id = document.getElementById("onionr-profile-username").title;
viewProfile(id, name);
}
function toggleSaveButton(show) {
document.getElementById("onionr-profile-edit").style.display = (show ? 'block' : 'none');
}
function makePost() {
var content = document.getElementById("onionr-post-creator-content").value;
if(content.trim() !== '') {
var post = new Post();
post.setUser(getCurrentUser());
post.setContent(content);
post.setPostDate(new Date());
post.save(function(data) {}); // async, but no function
document.getElementById('onionr-timeline-posts').innerHTML = post.getHTML() + document.getElementById('onionr-timeline-posts').innerHTML;
document.getElementById("onionr-post-creator-content").value = "";
document.getElementById("onionr-post-creator-content").focus();
postCreatorChange();
} else {
console.log('Not making empty post.');
}
}
function getReplies(id, callback) {
Block.getBlocks({'type' : 'onionr-post', 'parent' : id, 'signed' : true, 'reverse' : true}, callback);
}
function focusPost(id) {
viewReplies(id);
}
function viewRepliesMobile(id) {
var post = document.getElementById('onionr-post-' + id);
var user_name = '';
var user_id = '';
var user_id_trunc = '';
var user_icon = '';
var post_content = '';
if(post !== null && post !== undefined) {
// if the post is in the timeline, get the data from it
user_name = post.getElementsByClassName('onionr-post-user-name')[0].innerHTML;
user_id = post.getElementsByClassName('onionr-post-user-id')[0].title;
user_id_trunc = post.getElementsByClassName('onionr-post-user-id')[0].innerHTML;
user_icon = post.getElementsByClassName('onionr-post-user-icon')[0].src;
post_content = post.getElementsByClassName('onionr-post-content')[0].innerHTML;
} else {
// otherwise, fetch the data
}
document.getElementById('onionr-post-focus-user-icon').src = user_icon;
document.getElementById('onionr-post-focus-user-name').innerHTML = user_name;
document.getElementById('onionr-post-focus-user-id').innerHTML = user_id_trunc;
document.getElementById('onionr-post-focus-user-id').title = user_id;
document.getElementById('onionr-post-focus-content').innerHTML = post_content;
document.getElementById('onionr-post-focus-reply-creator-user-name').innerHTML = Sanitize.html(Sanitize.username(getCurrentUser().getName()));
document.getElementById('onionr-post-focus-reply-creator-user-icon').src = "data:image/jpeg;base64," + Sanitize.html(getCurrentUser().getIcon());
document.getElementById('onionr-post-focus-reply-creator-content').value = '';
document.getElementById('onionr-post-focus-reply-creator-content-message').value = '';
jQuery('#onionr-post-focus').modal('show');
}
function viewReplies(id) {
document.getElementById('onionr-replies-title').innerHTML = '<$= LANG.REPLIES $>';
document.getElementById('onionr-reply-creator-panel').originalPost = id;
document.getElementById('onionr-reply-creator-panel').innerHTML = '<$= jsTemplate('onionr-reply-creator') $>';
document.getElementById('onionr-reply-creator-content').innerHTML = '';
document.getElementById("onionr-reply-creator-content").placeholder = "<$= LANG.POST_CREATOR_PLACEHOLDER $>";
document.getElementById('onionr-reply-creator-user-name').innerHTML = Sanitize.html(Sanitize.username(getCurrentUser().getName()));
document.getElementById('onionr-reply-creator-user-icon').src = "data:image/jpeg;base64," + Sanitize.html(getCurrentUser().getIcon());
document.getElementById('onionr-replies').innerHTML = '';
getReplies(id, function(data) {
var replies = document.getElementById('onionr-replies');
replies.innerHTML = '';
for(var i = 0; i < data.length; i++) {
try {
var block = data[i];
var finished = false;
User.getUser(new String(block.getHeader('signer', 'unknown')), function(user) {
var post = new Post();
var blockContent = JSON.parse(block.getContent());
// just ignore anything shorter than 280 characters
if(String(blockContent['content']).length <= 280) {
post.setContent(blockContent['content']);
post.setPostDate(block.getDate());
post.setUser(user);
post.setHash(block.getHash());
replies.innerHTML += post.getHTML('reply');
}
finished = true;
});
while(!finished);
} catch(e) {
console.log('Troublemaker block: ' + data[i].getHash());
console.log(e);
}
}
});
}
function makeReply() {
var content = document.getElementById("onionr-reply-creator-content").value;
if(content.trim() !== '') {
var post = new Post();
var originalPost = document.getElementById('onionr-reply-creator-panel').originalPost;
console.log('Original post hash: ' + originalPost);
post.setUser(getCurrentUser());
post.setParent(originalPost);
post.setContent(content);
post.setPostDate(new Date());
post.save(function(data) {}); // async, but no function
document.getElementById('onionr-replies').innerHTML = post.getHTML('reply') + document.getElementById('onionr-replies').innerHTML;
document.getElementById("onionr-reply-creator-content").value = "";
document.getElementById("onionr-reply-creator-content").focus();
replyCreatorChange();
} else {
console.log('Not making empty reply.');
}
}
jQuery('body').on('click', '[data-editable]', function() {
var el = jQuery(this);
var txt = el.text();
var maxlength = el.attr("maxlength");
var input = jQuery('<input/>').val(txt);
input.attr('maxlength', maxlength);
el.replaceWith(input);
var save = function() {
var newTxt = input.val();
if(el.attr('id') === 'onionr-profile-username')
newTxt = Sanitize.username(newTxt);
if(el.attr('id') === 'onionr-profile-description')
newTxt = Sanitize.description(newTxt);
var p = el.text(newTxt);
input.replaceWith(p);
if(newTxt !== txt)
toggleSaveButton(true);
};
var saveEnter = function(event) {
console.log(event);
console.log(event.keyCode);
if (event.keyCode === 13)
save();
};
input.one('blur', save).bind('keyup', saveEnter).focus();
});
//viewProfile('$user-id-url', '$user-name-url')
// jQuery('#onionr-post-user-id').on('click', function(e) { alert(3);});
//jQuery('#onionr-post *').on('click', function(e) { e.stopPropagation(); });
// jQuery('#onionr-post').click(function(e) { alert(1); });
currentUser = getCurrentUser();
if(currentUser !== undefined && currentUser !== null) {
document.getElementById("onionr-post-creator-user-name").innerHTML = Sanitize.html(currentUser.getName());
document.getElementById("onionr-post-creator-user-id").innerHTML = "<$= LANG.POST_CREATOR_YOU $>";
document.getElementById("onionr-post-creator-user-icon").src = "data:image/jpeg;base64," + Sanitize.html(currentUser.getIcon());
document.getElementById("onionr-post-creator-user-id").title = currentUser.getID();
document.getElementById("onionr-post-creator-content").placeholder = "<$= LANG.POST_CREATOR_PLACEHOLDER $>";
document.getElementById("onionr-post-focus-reply-creator-content").placeholder = "<$= LANG.POST_CREATOR_PLACEHOLDER $>";
document.getElementById("onionr-post-focus-reply-creator-user-id").innerHTML = "<$= LANG.POST_CREATOR_YOU $>";
}
viewCurrentProfile = function() {
viewProfile(encodeURIComponent(currentUser.getID()), encodeURIComponent(currentUser.getName()));
}
document.getElementById("onionr-post-creator-user-id").onclick = viewCurrentProfile;
document.getElementById("onionr-post-creator-user-name").onclick = viewCurrentProfile;
// on some browsers it saves the user input on reload. So, it should also recheck the input.
postCreatorChange();