diff --git a/onionr/api.py b/onionr/api.py index 606aaffe..d80ec21c 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -31,7 +31,7 @@ class API: Main HTTP API (Flask) ''' - callbacks = {'public' : {}, 'private' : {}, 'ui' : {}} + callbacks = {'public' : {}, 'private' : {}} def validateToken(self, token): ''' @@ -45,6 +45,30 @@ class API: except TypeError: return False + def guessMime(path): + ''' + Guesses the mime type from the input filename + ''' + + mimetypes = { + 'html' : 'text/html', + 'js' : 'application/javascript', + 'css' : 'text/css', + 'png' : 'image/png', + 'jpg' : 'image/jpeg' + } + + for mimetype in mimetypes: + logger.debug(path + ' endswith .' + mimetype + '?') + if path.endswith('.%s' % mimetype): + logger.debug('- True!') + return mimetypes[mimetype] + else: + logger.debug('- no') + + logger.debug('%s not in %s' % (path, mimetypes)) + return 'text/plain' + def __init__(self, debug): ''' Initialize the api server, preping variables for later use @@ -76,6 +100,7 @@ class API: self.i2pEnabled = config.get('i2p.host', False) self.mimeType = 'text/plain' + self.overrideCSP = False with open('data/time-bypass.txt', 'w') as bypass: bypass.write(self.timeBypassToken) @@ -106,14 +131,15 @@ class API: #else: # resp.headers['server'] = 'Onionr' resp.headers['Content-Type'] = self.mimeType - resp.headers["Content-Security-Policy"] = "default-src 'none'; script-src 'none'; object-src 'none'; style-src data: 'unsafe-inline'; img-src data:; media-src 'none'; frame-src 'none'; font-src 'none'; connect-src 'none'" + if not self.overrideCSP: + resp.headers["Content-Security-Policy"] = "default-src 'none'; script-src 'none'; object-src 'none'; style-src data: 'unsafe-inline'; img-src data:; media-src 'none'; frame-src 'none'; font-src 'none'; connect-src 'none'" resp.headers['X-Frame-Options'] = 'deny' resp.headers['X-Content-Type-Options'] = "nosniff" resp.headers['server'] = 'Onionr' # reset to text/plain to help prevent browser attacks - if self.mimeType != 'text/plain': - self.mimeType = 'text/plain' + self.mimeType = 'text/plain' + self.overrideCSP = False return resp @@ -153,10 +179,12 @@ class API: def ui_private(path): startTime = math.floor(time.time()) + ''' if request.args.get('timingToken') is None: timingToken = '' else: timingToken = request.args.get('timingToken') + ''' if not config.get("www.ui.run", True): abort(403) @@ -166,14 +194,21 @@ class API: else: self.validateHost('public') + ''' endTime = math.floor(time.time()) elapsed = endTime - startTime if not hmac.compare_digest(timingToken, self.timeBypassToken): if elapsed < self._privateDelayTime: time.sleep(self._privateDelayTime - elapsed) + ''' - return send_from_directory('static-data/www/ui/dist/', path) + logger.debug('Serving %s' % path) + + self.mimeType = API.guessMime(path) + self.overrideCSP = True + + return send_from_directory('static-data/www/ui/dist/', path, mimetype = API.guessMime(path)) @app.route('/client/') def private_handler(): diff --git a/onionr/core.py b/onionr/core.py index 3219d8aa..eb45e182 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -647,7 +647,7 @@ class Core: def updateBlockInfo(self, hash, key, data): ''' sets info associated with a block - + hash - the hash of a block dateReceived - the date the block was recieved, not necessarily when it was created decrypted - if we can successfully decrypt the block (does not describe its current state) @@ -729,7 +729,7 @@ class Core: signer = self._crypto.pubKeyEncrypt(signer, asymPeer, encodedData=True, anonymous=True).decode() else: raise onionrexceptions.InvalidPubkey(asymPeer + ' is not a valid base32 encoded ed25519 key') - + # compile metadata metadata['meta'] = jsonMeta metadata['sig'] = signature diff --git a/onionr/onionr.py b/onionr/onionr.py index a430fe08..278ba60d 100755 --- a/onionr/onionr.py +++ b/onionr/onionr.py @@ -198,6 +198,9 @@ class Onionr: 'kex': self.doKEX, 'pex': self.doPEX, + 'ui' : self.openUI, + 'gui' : self.openUI, + 'getpassword': self.printWebPassword } @@ -705,5 +708,12 @@ class Onionr: else: logger.error('%s add-file ' % sys.argv[0], timestamp = False) + def openUI(self): + import webbrowser + url = 'http://127.0.0.1:%s/ui/index.html?timingToken=%s' % (config.get('client.port', 59496), self.onionrUtils.getTimeBypassToken()) + + print('Opening %s ...' % url) + webbrowser.open(url, new = 1, autoraise = True) + if __name__ == "__main__": Onionr() diff --git a/onionr/onionrutils.py b/onionr/onionrutils.py index 7a6b9cd3..1bb11b36 100644 --- a/onionr/onionrutils.py +++ b/onionr/onionrutils.py @@ -52,7 +52,9 @@ class OnionrUtils: with open('data/time-bypass.txt', 'r') as bypass: self.timingToken = bypass.read() except Exception as error: - logger.error('Failed to fetch time bypass token.', error=error) + logger.error('Failed to fetch time bypass token.', error = error) + + return self.timingToken def getRoundedEpoch(self, roundS=60): ''' diff --git a/onionr/static-data/www/ui/common/onionr-timeline-post.html b/onionr/static-data/www/ui/common/onionr-timeline-post.html index ceff5c65..8e187b51 100644 --- a/onionr/static-data/www/ui/common/onionr-timeline-post.html +++ b/onionr/static-data/www/ui/common/onionr-timeline-post.html @@ -7,19 +7,20 @@
-
+ -
- + +
+
- +
$content
- +
like comment diff --git a/onionr/static-data/www/ui/dist/css/main.css b/onionr/static-data/www/ui/dist/css/main.css index d9e76be6..dab080ef 100644 --- a/onionr/static-data/www/ui/dist/css/main.css +++ b/onionr/static-data/www/ui/dist/css/main.css @@ -60,6 +60,12 @@ body { width: 100%; } +.h-divider { + margin: 5px 15px; + height: 1px; + width: 100%; +} + /* profile */ .onionr-profile-user-icon { @@ -68,7 +74,6 @@ body { margin-bottom: 1rem; } -.onionr-profile-username { +.onionr-profile-username { text-align: center; } - diff --git a/onionr/static-data/www/ui/dist/css/themes/dark.css b/onionr/static-data/www/ui/dist/css/themes/dark.css index 4a547a29..b0473390 100644 --- a/onionr/static-data/www/ui/dist/css/themes/dark.css +++ b/onionr/static-data/www/ui/dist/css/themes/dark.css @@ -8,7 +8,7 @@ body { .onionr-post { border: 1px solid black; border-radius: 1rem; - + background-color: lightgray; } @@ -30,3 +30,7 @@ body { border-top: 1px solid black; font-size: 15pt; } + +.h-divider { + border-top:1px solid gray; +} diff --git a/onionr/static-data/www/ui/dist/index.html b/onionr/static-data/www/ui/dist/index.html index 886404dc..25cbb63b 100644 --- a/onionr/static-data/www/ui/dist/index.html +++ b/onionr/static-data/www/ui/dist/index.html @@ -36,22 +36,24 @@
-
+
-
+
-
-

arinerron

+
+

arinerron

+
+
- +
diff --git a/onionr/static-data/www/ui/dist/js/main.js b/onionr/static-data/www/ui/dist/js/main.js index 0141c5a1..1577beb2 100644 --- a/onionr/static-data/www/ui/dist/js/main.js +++ b/onionr/static-data/www/ui/dist/js/main.js @@ -1,26 +1,91 @@ + +/* 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); +} + +var usermap = JSON.parse(get('usermap', '{}')); + +function getUserMap() { + return usermap; +} + +function deserializeUser(id) { + var serialized = getUserMap()[id] + var user = new User(); + user.setName(serialized['name']); + user.setID(serialized['id']); + user.setIcon(serialized['icon']); +} + /* returns a relative date format, e.g. "5 minutes" */ -function timeSince(date) { +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 + " years"; + return interval + dates[size]['yr']; interval = Math.floor(seconds / 2592000); + if (interval > 1) - return interval + " months"; + return interval + dates[size]['mo']; interval = Math.floor(seconds / 86400); + if (interval > 1) - return interval + " days"; + return interval + dates[size]['d']; interval = Math.floor(seconds / 3600); + if (interval > 1) - return interval + " hours"; + return interval + dates[size]['hr']; interval = Math.floor(seconds / 60); + if (interval > 1) - return interval + " minutes"; - - return Math.floor(seconds) + " seconds"; + 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 */ @@ -41,7 +106,7 @@ function encodeURL(url) { } /* user class */ -class User { +class User { constructor() { this.name = 'Unknown'; this.id = 'unknown'; @@ -55,22 +120,35 @@ class User { getName() { return this.name; } - + setID(id) { this.id = id; } - + getID() { return this.id; } - + setIcon(image) { this.image = image; } - + getIcon() { return this.image; } + + serialize() { + return { + 'name' : this.getName(), + 'id' : this.getID(), + 'icon' : this.getIcon() + }; + } + + remember() { + usermap[this.getID()] = this.serialize(); + set('usermap', JSON.stringify(usermap)); + } } /* post class */ @@ -86,19 +164,20 @@ class Post {
\
\
\ -
\ + \ -
\ - \ +\ +
\ + \
\
\ - \ +\
\ $content\
\ - \ +\
\ like\ comment\ @@ -109,7 +188,9 @@ class Post {
\ \ '; - + + var device = (jQuery(document).width() < 768 ? 'mobile' : 'desktop'); + postTemplate = postTemplate.replaceAll('$user-name-url', encodeHTML(encodeURL(this.getUser().getName()))); postTemplate = postTemplate.replaceAll('$user-name', encodeHTML(this.getUser().getName())); postTemplate = postTemplate.replaceAll('$user-id-url', encodeHTML(encodeURL(this.getUser().getID()))); @@ -117,54 +198,36 @@ class Post { postTemplate = postTemplate.replaceAll('$user-id', encodeHTML(this.getUser().getID())); postTemplate = postTemplate.replaceAll('$user-image', encodeHTML(this.getUser().getIcon())); postTemplate = postTemplate.replaceAll('$content', encodeHTML(this.getContent())); - postTemplate = postTemplate.replaceAll('$date-relative', timeSince(this.getPostDate()) + ' ago'); + postTemplate = postTemplate.replaceAll('$date-relative', timeSince(this.getPostDate(), device) + (device === 'desktop' ? ' ago' : '')); postTemplate = postTemplate.replaceAll('$date', this.getPostDate().toLocaleString()); - + return postTemplate; } - + setUser(user) { this.user = user; } - + getUser() { return this.user; } - + setContent(content) { this.content = content; } - + getContent() { return this.content; } - + setPostDate(date) { // unix timestamp input if(date instanceof Date) this.date = date; else this.date = new Date(date * 1000); } - + getPostDate() { return this.date; } } - -/* 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); -} diff --git a/onionr/static-data/www/ui/dist/js/timeline.js b/onionr/static-data/www/ui/dist/js/timeline.js index 834568ca..c03ee24e 100644 --- a/onionr/static-data/www/ui/dist/js/timeline.js +++ b/onionr/static-data/www/ui/dist/js/timeline.js @@ -1,20 +1,96 @@ /* write a random post to the page, for testing */ + +var verbs = +[ + ["go to", "goes to", "going to", "went to", "gone to"], + ["look at", "looks at", "looking at", "looked at", "looked at"], + ["choose", "chooses", "choosing", "chose", "chosen"], + ["torrent", "downloads", "downloading", "torrented", "downloaded"], + ["detonate", "detonates", "detonating", "detonated", "detonated"], + ["run", "runs", "running", "ran", "running"], + ["program", "programs", "programming", "coded", "programmed"], + ["start", "starts", "starting", "started", "started"] +]; +var tenses = +[ + {name:"Present", singular:1, plural:0, format:"%subject %verb %complement"}, + {name:"Present", singular:1, plural:0, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Present Continues", singular:2, plural:2, format:"%subject %be %verb %complement"} +]; +var subjects = +[ + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"My cat", be:"is", singular:0}, + {name:"My cat", be:"is", singular:0}, + {name:"My dog", be:"is", singular:0}, + {name:"My dog", be:"is", singular:0}, + {name:"My mom", be:"is", singular:0}, + {name:"My dad", be:"is", singular:0}, + {name:"You", be:"are", singular:0}, + {name:"He", be:"is", singular:1} +]; +var complementsForVerbs = +[ + ["cinema", "Egypt", "home", "concert"], + ["for a map", "them", "the stars", "the lake"], + ["a book for reading", "a dvd for tonight"], + ["the virus", "the malware", "that 0day", "Onionr"], + ["a bomb", "a nuke", "some C4", "some ammonium nitrate"], + ["the race", "towards someone", "to the stars", "on top of your roof"], + ["Onionr", "the malware", "some software", "Onionr"], + ["Onionr", "Onionr", "the race", "the timer"], +] + +Array.prototype.random = function(){return this[Math.floor(Math.random() * this.length)];}; + +function generate(){ + var index = Math.floor(verbs.length * Math.random()); + var tense = tenses.random(); + var subject = subjects.random(); + var verb = verbs[index]; + var complement = complementsForVerbs[index]; + var str = tense.format; + str = str.replace("%subject", subject.name).replace("%be", subject.be); + str = str.replace("%verb", verb[subject.singular ? tense.singular : tense.plural]); + str = str.replace("%complement", complement.random()); + return str; +} + +var curDate = new Date() function addRandomPost() { var post = new Post(); var user = new User(); var items = ['arinerron', 'beardog108', 'samyk', 'snowden', 'aaronswartz']; user.setName(items[Math.floor(Math.random()*items.length)]); user.setID('i-eat-waffles-often-its-actually-crazy-like-i-dont-know-wow'); - post.setContent('spammm ' + btoa(Math.random() + ' wow')); + post.setContent(generate()); post.setUser(user); - post.setPostDate(new Date(new Date() - (Math.random() * 1000000))); - + + curDate = new Date(curDate - (Math.random() * 1000000)); + post.setPostDate(curDate); + document.getElementById('onionr-timeline-posts').innerHTML += post.getHTML(); } for(var i = 0; i < Math.round(50 * Math.random()); i++) addRandomPost(); - + function viewProfile(id, name) { document.getElementById("onionr-profile-username").innerHTML = encodeHTML(decodeURIComponent(name)); } diff --git a/onionr/static-data/www/ui/src/css/main.css b/onionr/static-data/www/ui/src/css/main.css index d9e76be6..dab080ef 100644 --- a/onionr/static-data/www/ui/src/css/main.css +++ b/onionr/static-data/www/ui/src/css/main.css @@ -60,6 +60,12 @@ body { width: 100%; } +.h-divider { + margin: 5px 15px; + height: 1px; + width: 100%; +} + /* profile */ .onionr-profile-user-icon { @@ -68,7 +74,6 @@ body { margin-bottom: 1rem; } -.onionr-profile-username { +.onionr-profile-username { text-align: center; } - diff --git a/onionr/static-data/www/ui/src/css/themes/dark.css b/onionr/static-data/www/ui/src/css/themes/dark.css index 4a547a29..b0473390 100644 --- a/onionr/static-data/www/ui/src/css/themes/dark.css +++ b/onionr/static-data/www/ui/src/css/themes/dark.css @@ -8,7 +8,7 @@ body { .onionr-post { border: 1px solid black; border-radius: 1rem; - + background-color: lightgray; } @@ -30,3 +30,7 @@ body { border-top: 1px solid black; font-size: 15pt; } + +.h-divider { + border-top:1px solid gray; +} diff --git a/onionr/static-data/www/ui/src/index.html b/onionr/static-data/www/ui/src/index.html index b23945b8..23de024c 100644 --- a/onionr/static-data/www/ui/src/index.html +++ b/onionr/static-data/www/ui/src/index.html @@ -6,22 +6,24 @@
-
+
-
+
-
-

arinerron

+
+

arinerron

+
+
- +
diff --git a/onionr/static-data/www/ui/src/js/main.js b/onionr/static-data/www/ui/src/js/main.js index 7208cd21..da969094 100644 --- a/onionr/static-data/www/ui/src/js/main.js +++ b/onionr/static-data/www/ui/src/js/main.js @@ -9,7 +9,7 @@ function get(key, df) { // df is default value = localStorage.getItem(key); if(value == null) value = df; - + return value; } @@ -32,28 +32,60 @@ function deserializeUser(id) { } /* returns a relative date format, e.g. "5 minutes" */ -function timeSince(date) { +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 + " years"; + return interval + dates[size]['yr']; interval = Math.floor(seconds / 2592000); + if (interval > 1) - return interval + " months"; + return interval + dates[size]['mo']; interval = Math.floor(seconds / 86400); + if (interval > 1) - return interval + " days"; + return interval + dates[size]['d']; interval = Math.floor(seconds / 3600); + if (interval > 1) - return interval + " hours"; + return interval + dates[size]['hr']; interval = Math.floor(seconds / 60); + if (interval > 1) - return interval + " minutes"; - - return Math.floor(seconds) + " seconds"; + 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 */ @@ -74,7 +106,7 @@ function encodeURL(url) { } /* user class */ -class User { +class User { constructor() { this.name = 'Unknown'; this.id = 'unknown'; @@ -88,23 +120,23 @@ class User { getName() { return this.name; } - + setID(id) { this.id = id; } - + getID() { return this.id; } - + setIcon(image) { this.image = image; } - + getIcon() { return this.image; } - + serialize() { return { 'name' : this.getName(), @@ -112,7 +144,7 @@ class User { 'icon' : this.getIcon() }; } - + remember() { usermap[this.getID()] = this.serialize(); set('usermap', JSON.stringify(usermap)); @@ -124,7 +156,9 @@ class Post { /* returns the html content of a post */ getHTML() { var postTemplate = '<$= jsTemplate('onionr-timeline-post') $>'; - + + var device = (jQuery(document).width() < 768 ? 'mobile' : 'desktop'); + postTemplate = postTemplate.replaceAll('$user-name-url', encodeHTML(encodeURL(this.getUser().getName()))); postTemplate = postTemplate.replaceAll('$user-name', encodeHTML(this.getUser().getName())); postTemplate = postTemplate.replaceAll('$user-id-url', encodeHTML(encodeURL(this.getUser().getID()))); @@ -132,35 +166,35 @@ class Post { postTemplate = postTemplate.replaceAll('$user-id', encodeHTML(this.getUser().getID())); postTemplate = postTemplate.replaceAll('$user-image', encodeHTML(this.getUser().getIcon())); postTemplate = postTemplate.replaceAll('$content', encodeHTML(this.getContent())); - postTemplate = postTemplate.replaceAll('$date-relative', timeSince(this.getPostDate()) + ' ago'); + postTemplate = postTemplate.replaceAll('$date-relative', timeSince(this.getPostDate(), device) + (device === 'desktop' ? ' ago' : '')); postTemplate = postTemplate.replaceAll('$date', this.getPostDate().toLocaleString()); - + return postTemplate; } - + setUser(user) { this.user = user; } - + getUser() { return this.user; } - + setContent(content) { this.content = content; } - + getContent() { return this.content; } - + setPostDate(date) { // unix timestamp input if(date instanceof Date) this.date = date; else this.date = new Date(date * 1000); } - + getPostDate() { return this.date; } diff --git a/onionr/static-data/www/ui/src/js/timeline.js b/onionr/static-data/www/ui/src/js/timeline.js index 834568ca..6acd819d 100644 --- a/onionr/static-data/www/ui/src/js/timeline.js +++ b/onionr/static-data/www/ui/src/js/timeline.js @@ -1,20 +1,96 @@ /* write a random post to the page, for testing */ + +var verbs = +[ + ["go to", "goes to", "going to", "went to", "gone to"], + ["look at", "looks at", "looking at", "looked at", "looked at"], + ["choose", "chooses", "choosing", "chose", "chosen"], + ["torrent", "downloads", "downloading", "torrented", "downloaded"], + ["detonate", "detonates", "detonating", "detonated", "detonated"], + ["run", "runs", "running", "ran", "running"], + ["program", "programs", "programming", "coded", "programmed"], + ["start", "starts", "starting", "started", "started"] +]; +var tenses = +[ + {name:"Present", singular:1, plural:0, format:"%subject %verb %complement"}, + {name:"Present", singular:1, plural:0, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Past", singular:3, plural:3, format:"%subject %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement"}, + {name:"Past", singular:3, plural:3, format:"%subject just %verb %complement lol"}, + {name:"Present Continues", singular:2, plural:2, format:"%subject %be %verb %complement"} +]; +var subjects = +[ + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"I", be:"am", singular:0}, + {name:"My cat", be:"is", singular:0}, + {name:"My cat", be:"is", singular:0}, + {name:"My dog", be:"is", singular:0}, + {name:"My dog", be:"is", singular:0}, + {name:"My mom", be:"is", singular:0}, + {name:"My dad", be:"is", singular:0}, + {name:"You", be:"are", singular:0}, + {name:"He", be:"is", singular:1} +]; +var complementsForVerbs = +[ + ["the cinema", "Egypt", "the house", "the concert"], + ["for a map", "them", "the stars", "the lake"], + ["a book for reading", "a dvd for tonight"], + ["the virus", "the malware", "that 0day", "Onionr"], + ["a bomb", "a nuke", "some C4", "some ammonium nitrate"], + ["the race", "towards someone", "to the stars", "on top of your roof"], + ["Onionr", "the malware", "some software", "Onionr"], + ["Onionr", "Onionr", "the race", "the timer"], +] + +Array.prototype.random = function(){return this[Math.floor(Math.random() * this.length)];}; + +function generate(){ + var index = Math.floor(verbs.length * Math.random()); + var tense = tenses.random(); + var subject = subjects.random(); + var verb = verbs[index]; + var complement = complementsForVerbs[index]; + var str = tense.format; + str = str.replace("%subject", subject.name).replace("%be", subject.be); + str = str.replace("%verb", verb[subject.singular ? tense.singular : tense.plural]); + str = str.replace("%complement", complement.random()); + return str; +} + +var curDate = new Date() function addRandomPost() { var post = new Post(); var user = new User(); var items = ['arinerron', 'beardog108', 'samyk', 'snowden', 'aaronswartz']; user.setName(items[Math.floor(Math.random()*items.length)]); user.setID('i-eat-waffles-often-its-actually-crazy-like-i-dont-know-wow'); - post.setContent('spammm ' + btoa(Math.random() + ' wow')); + post.setContent(generate()); post.setUser(user); - post.setPostDate(new Date(new Date() - (Math.random() * 1000000))); - + + curDate = new Date(curDate - (Math.random() * 1000000)); + post.setPostDate(curDate); + document.getElementById('onionr-timeline-posts').innerHTML += post.getHTML(); } for(var i = 0; i < Math.round(50 * Math.random()); i++) addRandomPost(); - + function viewProfile(id, name) { document.getElementById("onionr-profile-username").innerHTML = encodeHTML(decodeURIComponent(name)); }