summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bucky/app/site.js4
-rw-r--r--bucky/bin/build-search.js8
-rw-r--r--bucky/search/bdb.js106
-rw-r--r--bucky/search/lexicon.js268
-rw-r--r--bucky/search/middleware.js160
-rw-r--r--bucky/search/parse_term.js3
-rw-r--r--bucky/search/search.js9
-rw-r--r--bucky/search/snippet.js57
-rw-r--r--bucky/search/stopwords.js32
-rw-r--r--fortune/mail-verbs2
-rw-r--r--package.json4
-rw-r--r--public/assets/js/lib/views/details/settings.js501
-rw-r--r--public/assets/js/lib/views/index/hootbox.js1
-rw-r--r--public/assets/js/lib/views/mail/mailbox.js126
-rw-r--r--public/assets/js/lib/views/mail/message.js85
-rw-r--r--public/assets/js/util/format.js3
-rw-r--r--views/pages/mailbox.ejs2
-rw-r--r--views/pages/message.ejs21
-rw-r--r--views/partials/message.ejs20
-rw-r--r--yarn.lock136
20 files changed, 822 insertions, 726 deletions
diff --git a/bucky/app/site.js b/bucky/app/site.js
index 3627bac..de42155 100644
--- a/bucky/app/site.js
+++ b/bucky/app/site.js
@@ -19,6 +19,7 @@ var RedisStore = require("connect-redis")(session);
var redisClient = redis.createClient();
var upload = require("../util/upload");
+var lexicon = require("../search/lexicon");
var app, server;
@@ -83,7 +84,7 @@ site.init = function () {
server = http.createServer(app).listen(process.env.PORT || 5000, function () {
console.log(
"Bucky listening at http://" + process.env.HOST_NAME + ":%s",
- server.address().port
+ server.address().port,
);
});
@@ -101,6 +102,7 @@ site.init = function () {
if (process.env.NODE_ENV === "production") {
require("../bin/build-scripts");
}
+ lexicon.watch();
};
site.api = require("./api");
site.pages = require("./pages");
diff --git a/bucky/bin/build-search.js b/bucky/bin/build-search.js
index 23657b3..cb12c23 100644
--- a/bucky/bin/build-search.js
+++ b/bucky/bin/build-search.js
@@ -1,4 +1,6 @@
-var lexicon = require('../search/lexicon')
-
-lexicon.build().then(() => process.exit())
+var lexicon = require("../search/lexicon");
+lexicon.build().then(() => {
+ lexicon.save();
+ process.exit();
+});
diff --git a/bucky/search/bdb.js b/bucky/search/bdb.js
index 0495666..e62f59e 100644
--- a/bucky/search/bdb.js
+++ b/bucky/search/bdb.js
@@ -1,68 +1,48 @@
var fs = require("fs");
-function berkeleydb(fn) {
- var db;
- var bdb_lib = require("berkeleydb");
- var dbenv = new bdb_lib.DbEnv();
- var bdb_status = dbenv.open("./search/db/env");
- if (bdb_status) {
- console.log("open dbenv failed:", bdb_status);
- process.exit();
- }
-
- fn = "./" + fn + ".db";
-
- function exitHandler(options, err) {
- if (db) db.close();
- // if (options.cleanup) console.log('clean');
- if (err) console.log(err.stack);
- if (options.exit) process.exit();
- }
-
- // do something when app is closing
- process.on("exit", exitHandler.bind(null, { cleanup: true }));
-
- // catches ctrl+c event
- process.on("SIGINT", exitHandler.bind(null, { exit: true }));
-
- // catches "kill pid" (for example: nodemon restart)
- process.on("SIGUSR1", exitHandler.bind(null, { exit: true }));
- process.on("SIGUSR2", exitHandler.bind(null, { exit: true }));
+var databases = {};
- //catches uncaught exceptions
- process.on("uncaughtException", exitHandler.bind(null, { exit: true }));
-
- function open(fn) {
- if (db) db.close();
- var _db = new bdb_lib.Db(dbenv);
- var bdb_status = _db.open(fn);
- if (bdb_status) {
- console.log("openĀ " + fn + " failed:", bdb_status);
- process.exit();
- }
- db = _db;
+function jsondb(dbName) {
+ if (databases[dbName]) {
+ return databases[dbName];
}
- open(fn);
+ let db = {};
+ let filename = "./" + dbName + ".db";
- return {
+ // Store context for this database
+ var controller = {
+ load: function () {
+ if (fs.existsSync(filename)) {
+ try {
+ db = JSON.parse(fs.readFileSync(filename));
+ } catch (err) {
+ console.error("couldn't read " + filename);
+ process.exit();
+ }
+ } else {
+ db = {};
+ }
+ },
+ save: function () {
+ fs.writeFileSync(filename, JSON.stringify(db, false, 0));
+ },
+ reset: function () {
+ db = {};
+ },
put: function (term, serialized) {
- db.put(term, serialized);
+ db[term] = serialized;
},
get: function (term) {
- return db.get(term);
+ return db[term];
},
};
-}
-function jsondb(fn) {
- let db;
-
- fn = "./" + fn + ".db";
+ databases[dbName] = controller;
function exitHandler(options, err) {
- if (db) {
- fs.writeFileSync(fn, JSON.stringify(db, false, 0));
- }
+ // if (db) {
+ // fs.writeFileSync(fn, JSON.stringify(db, false, 0));
+ // }
// if (options.cleanup) console.log('clean');
if (err) console.log(err.stack);
if (options.exit) process.exit();
@@ -81,24 +61,8 @@ function jsondb(fn) {
//catches uncaught exceptions
process.on("uncaughtException", exitHandler.bind(null, { exit: true }));
- if (fs.existsSync(fn)) {
- try {
- db = JSON.parse(fs.readFileSync(fn));
- } catch (err) {
- console.error("couldn't read " + fn);
- process.exit();
- }
- } else {
- db = {};
- }
+ controller.load();
- return {
- put: function (term, serialized) {
- db[term] = serialized;
- },
- get: function (term) {
- return db[term];
- },
- };
+ return controller;
}
-module.exports = process.env.USE_BDB === "true" ? berkeleydb : jsondb;
+module.exports = jsondb;
diff --git a/bucky/search/lexicon.js b/bucky/search/lexicon.js
index dc1d7ab..d725777 100644
--- a/bucky/search/lexicon.js
+++ b/bucky/search/lexicon.js
@@ -1,129 +1,185 @@
-require('dotenv').load();
+require("dotenv").load();
-var STOPWORDS = require('./stopwords')
-var bdb = require('./bdb')
-var db = require('../db')
+var STOPWORDS = require("./stopwords");
+var bdb = require("./bdb");
+var db = require("../db");
+var parse_term = require("./parse_term");
-var search_db = bdb('search')
+var search_db = bdb("search");
-var lexicon = {}
-var lex_counts = {}
-var total = 0
+var lexicon = new Map();
+var lex_counts = new Map();
+var total = 0;
-module.exports = { build: build_index }
+module.exports = {
+ build: build_index,
+ watch: watch_index,
+ save: () => search_db.save(),
+};
+
+var BUILD_DELAY = 1000 * 60 * 60 * 24;
+function watch_index() {
+ build_index();
+ console.log(
+ "rebuilding search index every",
+ BUILD_DELAY / (60 * 60 * 1000),
+ "hours",
+ );
+ var interval = setInterval(build_index, BUILD_DELAY);
+}
function build_index(cb) {
- console.log("building index")
+ console.log("building search index");
+ lexicon = new Map();
+ lex_counts = new Map();
+ total = 0;
return parse_threads()
.then(parse_comments)
.then(parse_files)
- .then( () => {
- var unique = Object.keys(lexicon).length
- console.log( "--- WORD COUNT: ", total );
- console.log( "--- UNIQUE WORDS: ", unique );
+ .then(() => {
+ var unique = lexicon.size;
+ console.log("--- WORD COUNT: ", total);
+ console.log("--- UNIQUE WORDS: ", unique);
lexicon_store();
- console.log( "Done!")
- return { total, unique }
- })
+ console.log("Done!");
+ return { total, unique };
+ });
}
function parse_threads() {
- return db.Thread.where('id', '>', 1).fetchAll().then( (threads) => {
- console.log('got threads', threads.length)
- threads.forEach( (thread) => {
- total += parse_terms({
- string: thread.get('title'),
- thread: thread.get('id'),
- })
- })
- })
+ return db.Thread.where("id", ">", 1)
+ .fetchAll()
+ .then((threads) => {
+ console.log("got threads", threads.length);
+ for (const thread of threads) {
+ total += parse_terms({
+ string: thread.get("title"),
+ thread: thread.get("id"),
+ });
+ }
+ });
}
function parse_comments() {
- return db.Comment.where('thread', '>', 1).fetchAll().then( (comments) => {
- console.log('got comments', comments.length)
- comments.forEach( (comment) => {
- total += parse_terms({
- string: comment.get('comment').toString(),
- thread: comment.get('thread'),
- comment: comment.get('id'),
- })
- })
- })
+ return db.Comment.where("thread", ">", 1)
+ .fetchAll()
+ .then((comments) => {
+ console.log("got comments", comments.length);
+ for (const comment of comments) {
+ total += parse_terms({
+ string: comment.get("comment").toString(),
+ thread: comment.get("thread"),
+ comment: comment.get("id"),
+ });
+ }
+ });
}
function parse_files() {
- return db.File.fetchAll().then( (files) => {
- console.log('got files', files.length)
- files.forEach( (file) => {
+ return db.File.fetchAll().then((files) => {
+ console.log("got files", files.length);
+ for (const file of files) {
total += parse_terms({
- string: file.get('filename'),
- thread: file.get('thread'),
- file: file.get('id'),
- })
- })
- })
+ string: file.get("filename"),
+ thread: file.get("thread"),
+ file: file.get("id"),
+ });
+ }
+ });
}
-var underscoreRegexp = new RegExp('_', 'g')
-var spaceRegexp = new RegExp('[^a-zA-Z0-9]+', 'g')
+var underscoreRegexp = new RegExp("_", "g");
+var spaceRegexp = new RegExp("[^a-zA-Z0-9]+", "g");
-function parse_terms (opt) {
- var thread = opt.thread
- var comment = opt.comment || 0
- var file = opt.file || 0
- var string = opt.string
- if (!string || !thread) return 0
- var count = 0
- var terms = string
- .replace(underscoreRegexp, ' ')
- .split(spaceRegexp)
- .forEach((term) => {
- var t = term.toLowerCase()
- var lookup = lexicon[t] = lexicon[t] || {}
- var res = lookup[thread] = lookup[thread] || { strength: 0 }
- res.thread = res.thread || thread
- res.comment = res.comment || comment
- res.file = res.file || file
- // prioritize threads
- if (!comment && !file) {
- res.strength += 2
- }
- else {
- res.strength += 1
- }
- count += 1
- lex_counts[term] = lex_counts[term] || 0
- lex_counts[term] += 1
- })
- return count || 0
+/**
+ * For each term, create mappings:
+ * - lexicon[term][thread] => {thread, comment, file, strength}
+ * - lex_counts[term] => document frequency
+ * - total terms ++
+ */
+function parse_terms(opt) {
+ var thread = opt.thread;
+ var comment = opt.comment || 0;
+ var file = opt.file || 0;
+ var string = opt.string;
+ if (!string || !thread) {
+ return 0;
+ }
+ var term_count = 0;
+ var terms = string.replace(underscoreRegexp, " ").split(spaceRegexp);
+ for (const term of terms) {
+ var parsedTerm = parse_term(term);
+ if (STOPWORDS.has(parsedTerm)) {
+ continue;
+ }
+ if (!term || !parsedTerm) {
+ continue;
+ }
+ if (!lexicon.has(parsedTerm)) {
+ lexicon.set(parsedTerm, {});
+ }
+ var lookup = lexicon.get(parsedTerm);
+ lookup[thread] = lookup[thread] || { strength: 1 };
+
+ var res = lookup[thread];
+ res.thread = res.thread || thread;
+ res.comment = res.comment || comment;
+ res.file = res.file || file;
+
+ // prioritize threads
+ if (!comment && !file) {
+ res.strength += 100;
+ } else {
+ res.strength += 1;
+ }
+ term_count += 1;
+
+ if (!lex_counts.has(parsedTerm)) {
+ lex_counts.set(parsedTerm, new Set());
+ }
+ const lex_count = lex_counts.get(parsedTerm);
+
+ try {
+ lex_count.add(res.thread);
+ } catch (error) {
+ console.error(error);
+ console.log(term, terms, lex_count);
+ }
+ }
+ return term_count || 0;
}
-var put_total = 0
-function lexicon_store () {
- console.log('writing db...')
- Object.keys(lexicon).forEach( (term) => {
- if (STOPWORDS.has(term)) return
- var serialized = serialize_matches(term);
- if (! serialized) return;
- if ((put_total % 5000) === 0) console.log(put_total + '...')
- put_total += 1
- // if (put_total > 10) return
- // console.log(term)
- search_db.put(term, serialized)
- })
+var put_total = 0;
+function lexicon_store() {
+ console.log("writing db...");
+ // console.log(Object.keys(lexicon));
+ search_db.reset();
+ for (const term of lexicon.keys()) {
+ var serialized = serialize_matches(term);
+ if (!serialized) return;
+ if (put_total % 5000 === 0) console.log(put_total + "...");
+ put_total += 1;
+ // if (put_total > 10) return
+ // console.log(term)
+ search_db.put(term, serialized);
+ }
+ // search_db.save();
+}
+function serialize_matches(term) {
+ var matches = lexicon.get(term);
+ var lex_count = lex_counts.get(term)?.size || 0;
+ if (!lex_count) {
+ return null;
+ }
+ var idf = Math.log(total / lex_count);
+ var serialized_matches = [];
+ Object.values(matches).forEach((match) => {
+ if (!match) return;
+ var s = [
+ match.thread,
+ match.comment,
+ match.file,
+ Number((match.strength * idf).toFixed(2)),
+ ];
+ if (s) serialized_matches.push(s);
+ });
+ if (!serialized_matches.length) return;
+ return serialized_matches;
}
-function serialize_matches (term) {
- var matches = lexicon[term]
- var idf = Math.log(total / lex_counts[term])
- var serialized_matches = [];
- Object.values(matches).forEach( (match) => {
- if (!match) return
- var s = [
- match.thread,
- match.comment,
- match.file,
- match.strength * idf
- ].join(' ')
- if (s) serialized_matches.push(s)
- })
- if (!serialized_matches.length) return
- return serialized_matches.join(',')
-} \ No newline at end of file
diff --git a/bucky/search/middleware.js b/bucky/search/middleware.js
index 0cca05c..a93ee7f 100644
--- a/bucky/search/middleware.js
+++ b/bucky/search/middleware.js
@@ -1,111 +1,121 @@
-var db = require('../db')
+var db = require("../db");
-var search = require('./search')
-var snippet = require('./snippet')
-var lexicon = require('./lexicon')
+var search = require("./search");
+var snippet = require("./snippet");
+var lexicon = require("./lexicon");
module.exports = {
-
search: function (req, res, next) {
- res.search = search.search(req.query.query, req.query.start, req.query.limit)
- if (! res.search) {
- res.sendStatus(400)
- return
+ res.search = search.search(
+ req.query.query,
+ req.query.start,
+ req.query.limit,
+ );
+ if (!res.search) {
+ res.sendStatus(400);
+ return;
}
- next()
+ next();
},
-
- getThreads: function (req, res, next){
+
+ getThreads: function (req, res, next) {
var thread_ids = res.search.thread_ids;
- if (! thread_ids || ! thread_ids.length) {
- res.search.threads = []
- return next()
+ if (!thread_ids || !thread_ids.length) {
+ res.search.threads = [];
+ return next();
}
- db.getThreadsById(thread_ids).then(function(threads){
+ db.getThreadsById(thread_ids).then(function (threads) {
threads.forEach((thread) => {
- var flag_id = thread.get('flagged')
+ var flag_id = thread.get("flagged");
if (flag_id) {
- res.search.file_ids.push(flag_id)
+ res.search.file_ids.push(flag_id);
}
- })
- res.search.threads = threads
- next()
- })
+ });
+ res.search.threads = threads;
+ next();
+ });
},
-
- getComments: function (req, res, next){
+
+ getComments: function (req, res, next) {
var comment_ids = res.search.comment_ids;
- if (! comment_ids || ! comment_ids.length) {
- res.search.comments = []
- return next()
+ if (!comment_ids || !comment_ids.length) {
+ res.search.comments = [];
+ return next();
}
- db.getCommentsById(comment_ids).then(function(comments){
- var terms = res.search.meta.terms
- comments.forEach(function(comment){
- const snip = snippet(comment.get('comment').toString(), terms)
- comment.set('comment', snip)
- })
- res.search.comments = comments
- next()
- })
+ db.getCommentsById(comment_ids).then(function (comments) {
+ var terms = res.search.meta.terms;
+ comments.forEach(function (comment) {
+ const snip = snippet(comment.get("comment").toString(), terms);
+ comment.set("comment", snip);
+ });
+ res.search.comments = comments;
+ next();
+ });
},
-
- getFiles: function (req, res, next){
- var file_ids = res.search.file_ids
- if (! file_ids || ! file_ids.length) {
- res.search.files = []
- return next()
+
+ getFiles: function (req, res, next) {
+ var file_ids = res.search.file_ids;
+ if (!file_ids || !file_ids.length) {
+ res.search.files = [];
+ return next();
}
- db.getFilesById(file_ids).then(function(files){
- res.search.files = files
- next()
- })
+ db.getFilesById(file_ids).then(function (files) {
+ res.search.files = files;
+ next();
+ });
},
- logQuery: function(req, res, next) {
+ logQuery: function (req, res, next) {
// req.search.query, req.search.count
- next()
+ next();
},
- success: function(req, res, next){
- var terms = res.search.meta.terms
- var threads = {}, comments = {}, files = {}
- res.search.threads.forEach((t) => { threads[t.id] = t })
- res.search.comments.forEach((t) => { comments[t.id] = t })
- res.search.files.forEach((t) => { files[t.id] = t })
+ success: function (req, res, next) {
+ var terms = res.search.meta.terms;
+ var threads = {},
+ comments = {},
+ files = {};
+ res.search.threads.forEach((t) => {
+ threads[t.id] = t;
+ });
+ res.search.comments.forEach((t) => {
+ comments[t.id] = t;
+ });
+ res.search.files.forEach((t) => {
+ files[t.id] = t;
+ });
var results = res.search.results.map((r) => {
- var m = {}
- m.thread = threads[r.thread]
- m.comment = comments[r.comment]
- m.file = files[r.file]
- m.count = r.count
- m.strength = r.strength
+ var m = {};
+ m.thread = threads[r.thread];
+ m.comment = comments[r.comment];
+ m.file = files[r.file];
+ m.count = r.count;
+ m.strength = r.strength;
if (m.thread) {
- var flagged = m.thread.get('flagged')
+ var flagged = m.thread.get("flagged");
if (flagged) {
- m.thread.set('flagged', files[flagged])
+ m.thread.set("flagged", files[flagged]);
}
- var allowed = m.thread.get('allowed')
+ var allowed = m.thread.get("allowed");
if (allowed) {
- m.thread.set('allowed', allowed.toString().split(" "))
+ m.thread.set("allowed", allowed.toString().split(" "));
}
- var display = m.thread.get('display')
+ var display = m.thread.get("display");
if (display) {
- m.thread.set('display', display.toString().split(" "))
+ m.thread.set("display", display.toString().split(" "));
}
}
- return m
- })
+ return m;
+ });
res.json({
meta: res.search.meta,
results: results,
- })
+ });
},
- rebuild: function(req, res, next){
- lexicon.build().then( (data) => {
- res.json(data)
- })
+ rebuild: function (req, res, next) {
+ lexicon.build().then((data) => {
+ res.json(data);
+ });
},
-
-}
+};
diff --git a/bucky/search/parse_term.js b/bucky/search/parse_term.js
new file mode 100644
index 0000000..9cac238
--- /dev/null
+++ b/bucky/search/parse_term.js
@@ -0,0 +1,3 @@
+module.exports = function parse_term(term) {
+ return term ? String(term).toLowerCase().replace(/s?$/, "") : "";
+};
diff --git a/bucky/search/search.js b/bucky/search/search.js
index fb3bb2d..8924b1f 100644
--- a/bucky/search/search.js
+++ b/bucky/search/search.js
@@ -1,12 +1,14 @@
var db = require("../db");
var bdb = require("./bdb")("search");
var STOPWORDS = require("./stopwords");
+var parse_term = require("./parse_term");
var wordRegexp = new RegExp("[^a-z0-9]+", "g");
function parse_terms(s) {
return s
.toLowerCase()
.split(wordRegexp)
+ .map(parse_term)
.filter((term) => !!term);
}
function cmp(a, b) {
@@ -16,12 +18,11 @@ function cmp(a, b) {
function find_term(term) {
var row = bdb.get(term);
if (!row) return [];
- var res = row.toString();
+ var res = row;
// console.log(res)
if (!res.length) return [];
- var matches = res.split(",").map((s) => {
- if (!s.length) return;
- var partz = s.split(" ");
+ var matches = res.map((partz) => {
+ if (!partz.length) return;
return {
thread: parseInt(partz[0]),
comment: parseInt(partz[1]),
diff --git a/bucky/search/snippet.js b/bucky/search/snippet.js
index 17988d2..787a53f 100644
--- a/bucky/search/snippet.js
+++ b/bucky/search/snippet.js
@@ -1,35 +1,36 @@
-var util = require('../util/util')
-var STOPWORDS = require('./stopwords')
+var util = require("../util/util");
+var STOPWORDS = require("./stopwords");
+var parse_term = require("./parse_term");
function snippet(s, terms) {
- s = util.sanitize(s)
- var term_set = new Set(terms)
-
- var words = s.split(/[^a-zA-Z0-9]+/)
- var snippet = "";
-
+ s = util.sanitize(s);
+ var term_set = new Set(terms);
+
+ var words = s.split(/[^a-zA-Z0-9]+/);
+ var snippet = "";
+
// deduper for matching @words indexes, so we don't add a word twice
- var index_matches = {}
+ var index_matches = {};
// words in the eventual snippet
- var words_matched = []
+ var words_matched = [];
// counter for aggregating context after a match
- var aggr = 0;
+ var aggr = 0;
// amount of context to show, in number of words surrounding a match
var pad = 10;
// loop over each of the words in the string
- var word
for (var i = 0, len = words.length; i < len; i++) {
- word = words[i]
+ var word = words[i];
+ var term = parse_term(word);
- // if the word matches...
- if (term_set.has(word.toLowerCase()) && ! STOPWORDS.has(word.toLowerCase())) {
+ // if the word matches...
+ if (term && term_set.has(term) && !STOPWORDS.has(term.toLowerCase())) {
// if we aren't already aggregating, add an ellipsis
- if (! aggr) {
- words_matched.push("...")
+ if (!aggr) {
+ words_matched.push("...");
}
// look backward $pad words
@@ -44,38 +45,38 @@ function snippet(s, terms) {
if (index_matches[idx]) continue INNER;
// checks out, save this word
- words_matched.push(words[idx])
+ words_matched.push(words[idx]);
// note the matching index in our deduper
index_matches[idx] = 1;
- }
+ }
// enter aggregate mode -- add the next (pad) words
aggr = pad;
- }
+ }
// have we been told to aggregate?
else if (aggr) {
// save this word
- words_matched.push(word)
+ words_matched.push(word);
// add index to the deduper
index_matches[i] = 1;
// one less word to aggregate
aggr--;
- }
+ }
// keep snippets to a modest length
- if (words_matched.length > 30) break
- }
+ if (words_matched.length > 30) break;
+ }
// add a trailing ellipsis
- words_matched.push("...")
+ words_matched.push("...");
// create the snippet from the saved context words
- snippet = words_matched.join(" ")
+ snippet = words_matched.join(" ");
- return snippet
+ return snippet;
}
-module.exports = snippet \ No newline at end of file
+module.exports = snippet;
diff --git a/bucky/search/stopwords.js b/bucky/search/stopwords.js
index ceffe14..735e94d 100644
--- a/bucky/search/stopwords.js
+++ b/bucky/search/stopwords.js
@@ -1,18 +1,18 @@
module.exports = new Set(
- "a about above across adj after again against all almost alone along also " +
- "although always am among an and another any anybody anyone anything anywhere " +
- "apart are around as aside at away be because been before behind being below " +
- "besides between beyond both but by can cannot could did do does doing done " +
- "down downwards during each either else enough etc even ever every everybody " +
- "everyone except far few for forth from get gets got had hardly has have having " +
- "her here herself him himself his how however i if in indeed instead into inward " +
- "is it its itself just kept many maybe might mine more most mostly much must " +
- "myself near neither next no nobody none nor not nothing nowhere of off often on " +
- "only onto or other others ought our ours out outside over own p per please plus " +
- "pp quite rather really said seem self selves several shall she should since so " +
- "some somebody somewhat still such than that the their theirs them themselves " +
- "then there therefore these they this thorough thoroughly those through thus to " +
- "together too toward towards under until up upon v very was well were what " +
- "whatever when whenever where whether which while who whom whose will with" +
- "within without would yet young your yourself s".split(" ")
+ "a adj " +
+ "am an and " +
+ "are as at be been " +
+ "but by can could did do does doing done " +
+ "down " +
+ "far few for forth from get gets got had hardly has have having " +
+ "her here herself him himself his how i if in into " +
+ "is it its itself just kept many maybe might mine more much must " +
+ "myself near neither next no none nor not of off often on " +
+ "only onto or other others ought our ours out over own p per please plus " +
+ "pp quite rather really said seem self selves several shall she should since so " +
+ "some somebody somewhat still such than that the their theirs them themselves " +
+ "then there therefore these they this thorough thoroughly those through thus to " +
+ "together too toward towards under until up upon v very was well were what " +
+ "whatever when whenever where whether which while who whom whose will with" +
+ "within without would yet young your yourself s".split(" "),
);
diff --git a/fortune/mail-verbs b/fortune/mail-verbs
index 2ac2d6d..ab37cb5 100644
--- a/fortune/mail-verbs
+++ b/fortune/mail-verbs
@@ -96,7 +96,7 @@ flexed that cosmic wit
said it... in a song
lassoed a cold one
passed out on your lawn
-crossed picket lines to say
+stood on picket lines to say
minged up the div
forwarded this spam
accidentally rapped
diff --git a/package.json b/package.json
index 351c15c..018f7ec 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"build:scripts": "node bucky/bin/build-scripts",
"export:keyword": "node bucky/bin/export-keyword",
"test": "echo \"Error: no test specified\" && exit 1",
- "restart": "git pull; pm2 restart bucky; yarn build:scripts"
+ "restart": "git pull && node bucky/bin/build-scripts && node index"
},
"repository": {
"type": "git",
@@ -37,7 +37,7 @@
"mongodb": "^2.2.36",
"multer": "^1.4.2",
"multiparty": "^4.2.1",
- "mysql2": "^0.15.8",
+ "mysql2": "^3.14.3",
"node-fetch": "^1.7.3",
"node-uuid": "^1.4.8",
"passport": "^0.3.0",
diff --git a/public/assets/js/lib/views/details/settings.js b/public/assets/js/lib/views/details/settings.js
index c2ff078..fd23c46 100644
--- a/public/assets/js/lib/views/details/settings.js
+++ b/public/assets/js/lib/views/details/settings.js
@@ -1,9 +1,8 @@
var ThreadSettingsForm = FormView.extend({
-
el: "#thread_settings",
events: {
- "click": "hide",
+ // click: "hide",
"click .inner": "stopPropagation",
"click .thread_delete": "deleteThread",
"click .file_delete": "deleteFile",
@@ -20,152 +19,174 @@ var ThreadSettingsForm = FormView.extend({
},
action: "",
- method: 'put',
+ method: "put",
- initialize: function(){
- this.__super__.initialize.call(this)
- this.template = this.$(".template").html()
- this.allowedTemplate = this.$(".allowedTemplate").html()
- this.filesTemplate = this.$(".settingsFilesTemplate").html()
+ initialize: function () {
+ this.__super__.initialize.call(this);
+ this.template = this.$(".template").html();
+ this.allowedTemplate = this.$(".allowedTemplate").html();
+ this.filesTemplate = this.$(".settingsFilesTemplate").html();
},
- populate: function(){
- var data = this.options.parent.data
- var keywords = data.keywords
- var keyword = data.keyword
- var thread = data.thread
- var comments = data.comments
- var files = data.files
- var settings = thread.settings
- var display = thread.display
+ populate: function () {
+ var data = this.options.parent.data;
+ var keywords = data.keywords;
+ var keyword = data.keyword;
+ var thread = data.thread;
+ var comments = data.comments;
+ var files = data.files;
+ var settings = thread.settings;
+ var display = thread.display;
- this.thread = thread
- this.files = data.files
- this.action = "/api/thread/" + thread.id
- this.allowed = (this.thread.allowed || "").split(" ").map(s => s.trim()).filter(s => !! s)
+ this.thread = thread;
+ this.files = data.files;
+ this.action = "/api/thread/" + thread.id;
+ this.allowed = (this.thread.allowed || "")
+ .split(" ")
+ .map((s) => s.trim())
+ .filter((s) => !!s);
- this.$(".close_link").attr("href", "/details/" + thread.id)
- this.$(".metadata").html(metadata(thread))
- this.$("[name=title]").val(thread.title)
+ this.$(".close_link").attr("href", "/details/" + thread.id);
+ this.$(".metadata").html(metadata(thread));
+ this.$("[name=title]").val(thread.title);
- this.$("[name=hootbox]").prop("checked", !!thread.settings.hootbox)
- this.$("[name=shorturls]").prop("checked", !!thread.settings.shorturls)
- this.$("[name=noupload]").prop("checked", !!thread.settings.noupload)
- this.$("[name=privacy]").prop("checked", !!thread.privacy)
+ this.$("[name=hootbox]").prop("checked", !!thread.settings.hootbox);
+ this.$("[name=shorturls]").prop("checked", !!thread.settings.shorturls);
+ this.$("[name=noupload]").prop("checked", !!thread.settings.noupload);
+ this.$("[name=privacy]").prop("checked", !!thread.privacy);
- var $color = this.$('[name=color]')
+ var $color = this.$("[name=color]");
Object.keys(COLORS).forEach((color) => {
- var option = document.createElement('option')
- option.value = color
- option.innerHTML = color
- $color.append(option)
- })
- $color.val(thread && thread.color? thread.color: keyword? keyword.color: "")
+ var option = document.createElement("option");
+ option.value = color;
+ option.innerHTML = color;
+ $color.append(option);
+ });
+ $color.val(
+ thread && thread.color ? thread.color : keyword ? keyword.color : "",
+ );
- var $sort = this.$('[name=sort]')
+ var $sort = this.$("[name=sort]");
FILE_SORTS.forEach((sort) => {
- var option = document.createElement('option')
- option.value = sort.key
- option.innerHTML = sort.label
- $sort.append(option)
- })
- $sort.val(thread.settings.sort || "name_asc")
+ var option = document.createElement("option");
+ option.value = sort.key;
+ option.innerHTML = sort.label;
+ $sort.append(option);
+ });
+ $sort.val(thread.settings.sort || "name_asc");
- this.toggleAllowed()
- this.fetchKeywords()
+ this.toggleAllowed();
+ this.fetchKeywords();
- var $files = this.$(".files")
- $files.empty()
- files.sort((a,b) => cmp(a.filename, b.filename))
- .forEach(file => {
- var size = hush_size(file.size)
- var datetime = verbose_date(file.date, true)
- var date_class = carbon_date(file.date)
- var link = make_link(file)
- var t = this.filesTemplate.replace(/{{username}}/g, file.username)
- .replace(/{{link}}/g, link)
- .replace(/{{filename}}/g, file.filename)
- .replace(/{{date_class}}/g, date_class)
- .replace(/{{date}}/g, datetime[0])
- .replace(/{{time}}/g, datetime[1])
- .replace(/{{size_class}}/g, size[0])
- .replace(/{{size}}/g, size[1])
- .replace(/{{id}}/g, file.id)
- var $t = $(t)
- $files.append($t)
- })
+ var $files = this.$(".files");
+ $files.empty();
+ files
+ .sort((a, b) => cmp(a.filename, b.filename))
+ .forEach((file) => {
+ var size = hush_size(file.size);
+ var datetime = verbose_date(file.date, true);
+ var date_class = carbon_date(file.date);
+ var link = make_link(file);
+ var t = this.filesTemplate
+ .replace(/{{username}}/g, file.username)
+ .replace(/{{link}}/g, link)
+ .replace(/{{filename}}/g, file.filename)
+ .replace(/{{date_class}}/g, date_class)
+ .replace(/{{date}}/g, datetime[0])
+ .replace(/{{time}}/g, datetime[1])
+ .replace(/{{size_class}}/g, size[0])
+ .replace(/{{size}}/g, size[1])
+ .replace(/{{id}}/g, file.id);
+ var $t = $(t);
+ $files.append($t);
+ });
- $("body").removeClass("loading")
+ $("body").removeClass("loading");
},
- fetchKeywords: function(thread){
- $.get('/api/keywords', function(data){
- var $keyword = this.$('[name=keyword]')
- data.keywords
- .map( (a) => a.keyword)
- .sort( (a,b) => a < b ? -1 : a === b ? 0 : 1 )
- .forEach((keyword) => {
- var option = document.createElement('option')
- option.value = keyword
- option.innerHTML = keyword
- $keyword.append(option)
- })
- $keyword.val(this.thread.keyword)
- }.bind(this))
+ fetchKeywords: function (thread) {
+ $.get(
+ "/api/keywords",
+ function (data) {
+ var $keyword = this.$("[name=keyword]");
+ data.keywords
+ .map((a) => a.keyword)
+ .sort((a, b) => (a < b ? -1 : a === b ? 0 : 1))
+ .forEach((keyword) => {
+ var option = document.createElement("option");
+ option.value = keyword;
+ option.innerHTML = keyword;
+ $keyword.append(option);
+ });
+ $keyword.val(this.thread.keyword);
+ }.bind(this),
+ );
},
- loadThreads: function(threads){
+ loadThreads: function (threads) {
// update the dropdown list of threads
- var $thread_select = this.$('[name=thread]')
+ var $thread_select = this.$("[name=thread]");
if (!threads || !threads.length) {
- $thread_select.parent().hide()
- return
+ $thread_select.parent().hide();
+ return;
}
- $thread_select.parent().show()
+ $thread_select.parent().show();
threads
- .map( (a) => [a.title.toLowerCase(), a])
- .sort( (a,b) => a[0].localeCompare(b[0]) )
- .forEach((pair) => {
- const thread = pair[1]
- var option = document.createElement('option')
- option.value = thread.id
- // console.log(thread, get_revision(thread))
- option.innerHTML = '[' + thread.id + get_revision(thread) + '] ' + sanitize(thread.title)
- $thread_select.append(option)
- })
+ .map((a) => [a.title.toLowerCase(), a])
+ .sort((a, b) => a[0].localeCompare(b[0]))
+ .forEach((pair) => {
+ const thread = pair[1];
+ var option = document.createElement("option");
+ option.value = thread.id;
+ // console.log(thread, get_revision(thread))
+ option.innerHTML =
+ "[" +
+ thread.id +
+ get_revision(thread) +
+ "] " +
+ sanitize(thread.title);
+ $thread_select.append(option);
+ });
// console.log(threads)
},
- toggleAllowed: function(e){
- var checked = this.$('[name=privacy]').prop('checked')
- this.$(".allowed_field_container").toggle(checked)
- this.$(".allowed_names").toggle(checked)
- if (! checked) return
- this.$("[name=allowed_field]").focus()
- this.displayAllowed()
+ toggleAllowed: function (e) {
+ var checked = this.$("[name=privacy]").prop("checked");
+ this.$(".allowed_field_container").toggle(checked);
+ this.$(".allowed_names").toggle(checked);
+ if (!checked) return;
+ this.$("[name=allowed_field]").focus();
+ this.displayAllowed();
},
- displayAllowed: function(e){
- var $allowedNames = this.$(".allowed_names").empty()
- this.allowed.forEach(username => {
- var t = this.allowedTemplate.replace(/{{username}}/g, username)
- $allowedNames.append(t)
- })
+ displayAllowed: function (e) {
+ var $allowedNames = this.$(".allowed_names").empty();
+ this.allowed.forEach((username) => {
+ var t = this.allowedTemplate.replace(/{{username}}/g, username);
+ $allowedNames.append(t);
+ });
},
- keydownAllowed: function(e){
- if (e.keyCode === 13) { // enter
- e.preventDefault()
- e.stopPropagation()
- this.updateAllowed()
+ keydownAllowed: function (e) {
+ if (e.keyCode === 13) {
+ // enter
+ e.preventDefault();
+ e.stopPropagation();
+ this.updateAllowed();
}
},
- updateAllowed: function(){
- var usernames = this.$('[name=allowed_field]').val().replace(/,/g, ' ').split(' ').map(s => s.trim()).filter(s => !! s)
- this.$('[name=allowed_field]').val('')
- usernames = usernames.filter( (name) => this.allowed.indexOf(name) === -1 )
- .map( (name) => sanitizeHTML(name) )
+ updateAllowed: function () {
+ var usernames = this.$("[name=allowed_field]")
+ .val()
+ .replace(/,/g, " ")
+ .split(" ")
+ .map((s) => s.trim())
+ .filter((s) => !!s);
+ this.$("[name=allowed_field]").val("");
+ usernames = usernames
+ .filter((name) => this.allowed.indexOf(name) === -1)
+ .map((name) => sanitizeHTML(name));
$.ajax({
method: "PUT",
url: "/api/checkUsernames",
@@ -173,31 +194,33 @@ var ThreadSettingsForm = FormView.extend({
data: JSON.stringify({ csrf: csrf(), usernames: usernames }),
contentType: "application/json",
dataType: "json",
- success: function(data){
- if (! data.usernames || ! data.usernames.length) return
- this.allowed = this.allowed.concat(data.usernames)
- this.displayAllowed()
+ success: function (data) {
+ if (!data.usernames || !data.usernames.length) return;
+ this.allowed = this.allowed.concat(data.usernames);
+ this.displayAllowed();
}.bind(this),
- })
+ });
},
- removeAllowed: function(){
- this.allowed = this.$("#allowed_names input[type=checkbox]:checked").map(function(){
- return $(this).val()
- })
- this.displayAllowed()
+ removeAllowed: function () {
+ this.allowed = this.$("#allowed_names input[type=checkbox]:checked").map(
+ function () {
+ return $(this).val();
+ },
+ );
+ this.displayAllowed();
},
- validate: function(){
- var errors = []
- var title = $("[name=title]").val()
- if (! title || ! title.length) {
- errors.push("Please enter a title.")
+ validate: function () {
+ var errors = [];
+ var title = $("[name=title]").val();
+ if (!title || !title.length) {
+ errors.push("Please enter a title.");
}
- return errors.length ? errors : null
+ return errors.length ? errors : null;
},
- serialize: function(){
+ serialize: function () {
var data = {
title: $("[name=title]").val(),
keyword: $("[name=keyword]").val(),
@@ -208,115 +231,127 @@ var ThreadSettingsForm = FormView.extend({
hootbox: $("[name=hootbox]:checked").val() ? true : false,
shorturls: $("[name=shorturls]:checked").val() ? true : false,
noupload: $("[name=noupload]:checked").val() ? true : false,
- sort: $("[name=sort]").val()
+ sort: $("[name=sort]").val(),
},
- }
- return JSON.stringify(data)
+ };
+ return JSON.stringify(data);
},
- success: function(data){
- console.log(data)
- window.location.href = "/details/" + this.options.parent.data.thread.id
+ success: function (data) {
+ console.log(data);
+ window.location.href = "/details/" + this.options.parent.data.thread.id;
},
visible: false,
- show: function(){
- this.visible = true
- app.typing = true
- this.populate()
- this.$el.addClass('visible')
- app.router.pushState("/details/" + this.options.parent.data.thread.id + "/settings")
+ show: function () {
+ this.visible = true;
+ app.typing = true;
+ this.populate();
+ this.$el.addClass("visible");
+ app.router.pushState(
+ "/details/" + this.options.parent.data.thread.id + "/settings",
+ );
},
- hide: function(e){
- e && e.preventDefault()
- this.visible = false
- app.typing = false
- this.$el.removeClass('visible')
- app.router.pushState("/details/" + this.options.parent.data.thread.id)
+ hide: function (e) {
+ e && e.preventDefault();
+ this.visible = false;
+ app.typing = false;
+ this.$el.removeClass("visible");
+ app.router.pushState("/details/" + this.options.parent.data.thread.id);
},
- toggle: function(){
- if (this.visible) this.hide()
- else this.show()
+ toggle: function () {
+ if (this.visible) this.hide();
+ else this.show();
},
- changeColor: function(){
- var color_name = this.$("[name=color]").val()
- set_background_color(color_name)
+ changeColor: function () {
+ var color_name = this.$("[name=color]").val();
+ set_background_color(color_name);
},
- changeSort: function(){
- var sort_name = this.$("[name=sort]").val()
- console.log(">", sort_name)
- app.view.files.resort(sort_name)
+ changeSort: function () {
+ var sort_name = this.$("[name=sort]").val();
+ console.log(">", sort_name);
+ app.view.files.resort(sort_name);
},
- toggleFile: function(e){
+ toggleFile: function (e) {
// e.preventDefault()
- e.stopPropagation()
- const $input = $(e.currentTarget)
- const $tr = $input.closest('tr.file')
- const value = e.currentTarget.checked
+ e.stopPropagation();
+ const $input = $(e.currentTarget);
+ const $tr = $input.closest("tr.file");
+ const value = e.currentTarget.checked;
// $(e.currentTarget).prop('checked', value)
// console.log('check', $input, value)
- this.toggleFileChecked($tr, null, value)
+ this.toggleFileChecked($tr, null, value);
},
- toggleFileRow: function(e){
+ toggleFileRow: function (e) {
// e.preventDefault()
- e.stopPropagation()
- const $tr = $(e.currentTarget)
- const $input = $tr.find('input[type="checkbox"]')
- const value = ! $input.prop('checked')
- this.toggleFileChecked($tr, $input, value)
+ e.stopPropagation();
+ const $tr = $(e.currentTarget);
+ const $input = $tr.find('input[type="checkbox"]');
+ const value = !$input.prop("checked");
+ this.toggleFileChecked($tr, $input, value);
},
- toggleFileChecked: function($tr, $input, value){
- $tr.toggleClass('checked', value)
- if ($input) $input.prop('checked', value)
+ toggleFileChecked: function ($tr, $input, value) {
+ $tr.toggleClass("checked", value);
+ if ($input) $input.prop("checked", value);
},
- moveFiles: function(){
- var thread_id = this.$("[name=thread]").val()
+ moveFiles: function () {
+ var thread_id = this.$("[name=thread]").val();
// if (!thread_id) return alert("Please choose a thread")
- var file_ids = toArray(this.el.querySelectorAll("[name=file_id]:checked")).map(input => input.value)
- console.log("thread:", thread_id)
- console.log("files:", file_ids)
- var promises = file_ids.map(file_id => {
+ var file_ids = toArray(
+ this.el.querySelectorAll("[name=file_id]:checked"),
+ ).map((input) => input.value);
+ console.log("thread:", thread_id);
+ console.log("files:", file_ids);
+ var promises = file_ids.map((file_id) => {
return new Promise((resolve, reject) => {
$.ajax({
method: "GET",
- url: '/api/file/' + file_id + '/move/' + thread_id,
+ url: "/api/file/" + file_id + "/move/" + thread_id,
headers: { "csrf-token": $("[name=_csrf]").attr("value") },
dataType: "json",
- success: function(data){
- console.log('moved', file_id)
- resolve(data)
+ success: function (data) {
+ console.log("moved", file_id);
+ resolve(data);
},
- error: function(){
- console.log('error moving', file_id)
- reject()
- }
- })
+ error: function () {
+ console.log("error moving", file_id);
+ reject();
+ },
+ });
+ });
+ });
+ Promise.all(promises)
+ .then(() => {
+ window.location.href = "/details/" + thread_id;
})
- })
- Promise.all(promises).then( () => {
- window.location.href = '/details/' + thread_id
- }).catch(() =>{
- console.error('whaaaaa')
- alert('there was a problem moving the files...')
- })
+ .catch(() => {
+ console.error("whaaaaa");
+ alert("there was a problem moving the files...");
+ });
},
- deleteThread: function(e){
- var data = this.options.parent.data
- var id = data.thread.id
- var comment_count = (data.comments || []).length
- var file_count = (data.files || []).length
- var msg = "Are you sure you want to delete this thread?\n\n#" + id + ' "' + sanitizeHTML(data.thread.title) + '"'
- msg += " + " + comment_count + " comment" + courtesy_s(comment_count)
- if ( file_count) msg += " + " + file_count + " file" + courtesy_s(file_count)
- var should_remove = confirm(msg)
+ deleteThread: function (e) {
+ var data = this.options.parent.data;
+ var id = data.thread.id;
+ var comment_count = (data.comments || []).length;
+ var file_count = (data.files || []).length;
+ var msg =
+ "Are you sure you want to delete this thread?\n\n#" +
+ id +
+ ' "' +
+ sanitizeHTML(data.thread.title) +
+ '"';
+ msg += " + " + comment_count + " comment" + courtesy_s(comment_count);
+ if (file_count)
+ msg += " + " + file_count + " file" + courtesy_s(file_count);
+ var should_remove = confirm(msg);
if (should_remove) {
$.ajax({
method: "DELETE",
@@ -324,25 +359,30 @@ var ThreadSettingsForm = FormView.extend({
headers: { "csrf-token": $("[name=_csrf]").attr("value") },
data: JSON.stringify({ csrf: csrf() }),
dataType: "json",
- success: function(){
- window.location.href = "/"
- }
- })
+ success: function () {
+ window.location.href = "/";
+ },
+ });
}
},
- deleteFile: function(e){
- e.preventDefault()
- e.stopPropagation()
- var $el = $(e.currentTarget)
- var $parent = $el.closest('.file')
- var file_id = $el.data('id')
- if (! file_id) return
- var data = this.options.parent.data
- var file = data.files.find(f => f.id === file_id)
- if (! file) return
- var msg = "Are you sure you want to delete this file?\n\n#" + file_id + ' "' + sanitizeHTML(file.filename) + '"'
- var should_remove = confirm(msg)
+ deleteFile: function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ var $el = $(e.currentTarget);
+ var $parent = $el.closest(".file");
+ var file_id = $el.data("id");
+ if (!file_id) return;
+ var data = this.options.parent.data;
+ var file = data.files.find((f) => f.id === file_id);
+ if (!file) return;
+ var msg =
+ "Are you sure you want to delete this file?\n\n#" +
+ file_id +
+ ' "' +
+ sanitizeHTML(file.filename) +
+ '"';
+ var should_remove = confirm(msg);
if (should_remove) {
$.ajax({
method: "DELETE",
@@ -350,12 +390,11 @@ var ThreadSettingsForm = FormView.extend({
headers: { "csrf-token": $("[name=_csrf]").attr("value") },
data: JSON.stringify({ csrf: csrf() }),
dataType: "json",
- success: function(data){
- console.log(data)
- $parent.remove()
- }
- })
+ success: function (data) {
+ console.log(data);
+ $parent.remove();
+ },
+ });
}
},
-
-})
+});
diff --git a/public/assets/js/lib/views/index/hootbox.js b/public/assets/js/lib/views/index/hootbox.js
index c874e74..a5d2270 100644
--- a/public/assets/js/lib/views/index/hootbox.js
+++ b/public/assets/js/lib/views/index/hootbox.js
@@ -29,7 +29,6 @@ var HootBox = FormView.extend({
},
parse: function (comment) {
- console.log(comment);
var t = this.template
.replace(/{{image}}/g, profile_image(comment.username))
.replace(/{{username}}/g, comment.username)
diff --git a/public/assets/js/lib/views/mail/mailbox.js b/public/assets/js/lib/views/mail/mailbox.js
index c48d948..cae87f0 100644
--- a/public/assets/js/lib/views/mail/mailbox.js
+++ b/public/assets/js/lib/views/mail/mailbox.js
@@ -2,91 +2,95 @@ var MailboxView = View.extend({
el: "#messages",
events: {
- 'click .discard_link': 'discard',
+ "click .discard_link": "discard",
},
action: "/api/mailbox/",
- initialize: function(){
- this.__super__.initialize.call(this)
- this.template = this.$(".template").html()
- this.boxlist = new BoxList ()
+ initialize: function () {
+ this.__super__.initialize.call(this);
+ this.template = this.$(".template").html();
+ this.boxlist = new BoxList();
+ this.message = new MessageView();
},
- load: function(name){
- name = sanitizeHTML(name) || "inbox"
- $("h1").html(name)
- var query = window.location.search.substr(1)
- $.get(this.action + name, query, this.populate.bind(this))
+ load: function (name) {
+ name = sanitizeHTML(name) || "inbox";
+ $("h1").html(name);
+ var query = window.location.search.substr(1);
+ $.get(this.action + name, query, this.populate.bind(this));
},
- populate: function(data){
+ populate: function (data) {
if (data.boxes) {
- this.boxlist.load(data.boxes)
+ this.boxlist.load(data.boxes);
- var user = data.user
- var max = data.messages.length-1
+ var user = data.user;
+ var max = data.messages.length - 1;
if (data.messages.length) {
- var limit = data.query.limit || 50
- var offset = data.query.offset + data.messages.length
+ var limit = data.query.limit || 50;
+ var offset = data.query.offset + data.messages.length;
if (limit > data.messages.length) {
- $(".next_page").hide()
+ $(".next_page").hide();
+ } else {
+ var query = { limit, offset };
+ $(".next_page a").attr("href", "?" + querystring(query));
}
- else {
- var query = { limit, offset }
- $(".next_page a").attr("href", "?" + querystring(query))
- }
- }
- else {
- $("#no_messages").show()
- $(".next_page").hide()
+ } else {
+ $("#no_messages").show();
+ $(".next_page").hide();
}
- data.messages.forEach(function(message, i){
- var $row = this.parse(message, user)
- if (i === 0) $row.addClass("first")
- if (i === max) $row.addClass("last")
- this.$el.append($row)
- }.bind(this))
+ data.messages.forEach(
+ function (message, i) {
+ var $row = this.parse(message, user);
+ if (i === 0) $row.addClass("first");
+ if (i === max) $row.addClass("last");
+ this.$el.append($row);
+ }.bind(this),
+ );
}
- $("body").removeClass('loading')
+ $("body").removeClass("loading");
},
- parse: function(message, user){
- var datetime = verbose_date(message.date)
- var size = hush_size(message.size)
- var id = message.id
+ parse: function (message, user) {
+ var datetime = verbose_date(message.date);
+ var size = hush_size(message.size);
+ var id = message.id;
- var is_sender = message.sender === user.username
+ var is_sender = message.sender === user.username;
var t = this.template
- .replace(/{{id}}/g, message.id)
- .replace(/{{to}}/g, is_sender ? "to " : "")
- .replace(/{{unread}}/g, message.unread ? "unread" : "")
- .replace(/{{username}}/g, is_sender ? message.recipient : message.sender)
- .replace(/{{subject}}/g, message.subject)
- .replace(/{{date}}/g, datetime[0])
- .replace(/{{time}}/g, datetime[1])
- .replace(/{{date_class}}/g, carbon_date(message.lastmodified) )
- .replace(/{{size}}/g, size[1] )
- .replace(/{{size_class}}/g, size[0] )
- var $t = $(t)
+ .replace(/{{id}}/g, message.id)
+ .replace(/{{to}}/g, is_sender ? "to " : "")
+ .replace(/{{unread}}/g, message.unread ? "unread" : "")
+ .replace(/{{username}}/g, is_sender ? message.recipient : message.sender)
+ .replace(/{{subject}}/g, message.subject)
+ .replace(/{{date}}/g, datetime[0])
+ .replace(/{{time}}/g, datetime[1])
+ .replace(/{{date_class}}/g, carbon_date(message.lastmodified))
+ .replace(/{{size}}/g, size[1])
+ .replace(/{{size_class}}/g, size[0]);
+ var $t = $(t);
if (is_sender) {
- $t.find('.reply_link').remove()
+ $t.find(".reply_link").remove();
}
- return $t
+ return $t;
},
- discard: function(e){
- var id = $(e.target).data('id')
- var ok = confirm("Really delete this message?")
- if (! ok) return
+ discard: function (e) {
+ var id = $(e.target).data("id");
+ var ok = confirm("Really delete this message?");
+ if (!ok) return;
$.ajax({
- method: 'delete',
- url: '/api/message/' + id,
+ method: "delete",
+ url: "/api/message/" + id,
headers: { "csrf-token": csrf() },
data: { _csrf: csrf() },
- success: function(){ window.location.reload() },
- error: function(){ window.location.reload() },
- })
+ success: function () {
+ window.location.reload();
+ },
+ error: function () {
+ window.location.reload();
+ },
+ });
},
-
-})
+});
diff --git a/public/assets/js/lib/views/mail/message.js b/public/assets/js/lib/views/mail/message.js
index 6fa3d78..6ea2274 100644
--- a/public/assets/js/lib/views/mail/message.js
+++ b/public/assets/js/lib/views/mail/message.js
@@ -1,67 +1,70 @@
var MessageView = View.extend({
-
el: "#message",
events: {
- 'click .discard_link': 'discard',
+ "click .discard_link": "discard",
},
- action: '/api/message/',
+ action: "/api/message/",
- initialize: function(){
- this.template = this.$(".template").html()
+ initialize: function () {
+ this.template = this.$(".template").html();
+ this.$el.empty().hide();
},
- load: function(name){
- name = sanitizeHTML(name) || "inbox"
+ load: function (name) {
+ name = sanitizeHTML(name) || "inbox";
$.ajax({
url: this.action + name,
- method: 'get',
+ method: "get",
success: this.populate.bind(this),
- error: app.router.error404
- })
+ error: app.router.error404,
+ });
},
- populate: function(data){
- this.parse(data)
- $("body").removeClass('loading')
+ populate: function (data) {
+ this.parse(data);
+ $("body").removeClass("loading");
},
- parse: function(data){
- var message = data.message
-// var user = data.user
+ parse: function (data) {
+ var message = data.message;
+ // var user = data.user
- $("h1").html(message.subject)
- var datetime = verbose_date(message.date)
- var id = message.id
- var is_sender = message.sender === auth.user.username
+ $("h1").html(message.subject);
+ var datetime = verbose_date(message.date);
+ var id = message.id;
+ var is_sender = message.sender === auth.user.username;
var t = this.template
- .replace(/{{id}}/g, message.id)
- .replace(/{{sender}}/g, message.sender)
- .replace(/{{avatar}}/g, profile_image(message.sender))
- .replace(/{{subject}}/g, message.subject)
- .replace(/{{date}}/g, datetime[0])
- .replace(/{{time}}/g, datetime[1])
- .replace(/{{body}}/g, tidy_urls(message.body) )
- var $t = $(t)
+ .replace(/{{id}}/g, message.id)
+ .replace(/{{sender}}/g, message.sender)
+ .replace(/{{avatar}}/g, profile_image(message.sender))
+ .replace(/{{subject}}/g, message.subject)
+ .replace(/{{date}}/g, datetime[0])
+ .replace(/{{time}}/g, datetime[1])
+ .replace(/{{body}}/g, tidy_urls(message.body));
+ var $t = $(t);
if (is_sender) {
- $t.find('.reply_link').remove()
+ $t.find(".reply_link").remove();
}
- this.$el.empty().append($t)
+ this.$el.empty().append($t).show();
},
- discard: function(e){
- var id = $(e.target).data('id')
- var ok = confirm("Really delete this message?")
- if (! ok) return
+ discard: function (e) {
+ var id = $(e.target).data("id");
+ var ok = confirm("Really delete this message?");
+ if (!ok) return;
$.ajax({
- method: 'delete',
- url: '/api/message/' + id,
+ method: "delete",
+ url: "/api/message/" + id,
headers: { "csrf-token": csrf() },
data: { _csrf: csrf() },
- success: function(){ window.location.href = "/mail" },
- error: function(){ window.location.href = "/mail" },
- })
+ success: function () {
+ window.location.href = "/mail";
+ },
+ error: function () {
+ window.location.href = "/mail";
+ },
+ });
},
-
-})
+});
diff --git a/public/assets/js/util/format.js b/public/assets/js/util/format.js
index 8920a38..a594bdd 100644
--- a/public/assets/js/util/format.js
+++ b/public/assets/js/util/format.js
@@ -20,7 +20,8 @@ function csrf() {
function bold_terms(s, terms) {
s = sanitizeHTML(s);
terms.forEach((term) => {
- s = s.replace(new RegExp(term, "ig"), "<b>" + term + "</b>");
+ const sanitized_term = term.replace(/[^a-zA-Z0-9]/g, "");
+ s = s.replace(new RegExp(`(${sanitized_term}\\w*)`, "ig"), "<b>$1</b>");
});
return s;
}
diff --git a/views/pages/mailbox.ejs b/views/pages/mailbox.ejs
index 756cb21..b7f55c8 100644
--- a/views/pages/mailbox.ejs
+++ b/views/pages/mailbox.ejs
@@ -64,4 +64,6 @@
</div>
</div>
+<% include ../partials/message %>
+
<% include ../partials/footer %>
diff --git a/views/pages/message.ejs b/views/pages/message.ejs
index 93987b4..8a8868f 100644
--- a/views/pages/message.ejs
+++ b/views/pages/message.ejs
@@ -5,25 +5,6 @@
<a href='/mail/'>Inbox</a>
</div>
-<div class="bluebox" id="message">
- <script class="template" type="text/html">
- <a href="/profile/{{sender}}" class="av" style="background-image:url({{avatar}});"></a>
- <span class="subject">{{subject}}</span>
- <span class="sender">
- sent by
- <a href="/profile/{{sender}}">{{sender}}</a>
- on {{date}} {{time}}
- <span class='reply_link'>
- &middot;
- <a href="/mail/reply/{{id}}">reply</a>
- </span>
- &middot;
- <a href='#' class='discard_link'>discard</a>
- </span>
- <div class="body">
- <span class="contents">{{body}}</span>
- </div>
- </script>
-</div>
+<% include ../partials/message %>
<% include ../partials/footer %>
diff --git a/views/partials/message.ejs b/views/partials/message.ejs
new file mode 100644
index 0000000..b48b7b4
--- /dev/null
+++ b/views/partials/message.ejs
@@ -0,0 +1,20 @@
+<div class="bluebox" id="message">
+ <script class="template" type="text/html">
+ <a href="/profile/{{sender}}" class="av" style="background-image:url({{avatar}});"></a>
+ <span class="subject">{{subject}}</span>
+ <span class="sender">
+ sent by
+ <a href="/profile/{{sender}}">{{sender}}</a>
+ on {{date}} {{time}}
+ <span class='reply_link'>
+ &middot;
+ <a href="/mail/reply/{{id}}">reply</a>
+ </span>
+ &middot;
+ <a href='#' class='discard_link'>discard</a>
+ </span>
+ <div class="body">
+ <span class="contents">{{body}}</span>
+ </div>
+ </script>
+</div>
diff --git a/yarn.lock b/yarn.lock
index 3bd8828..bb575df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -144,11 +144,6 @@ ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
-ansicolors@~0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef"
- integrity sha512-tOIuy1/SK/dr94ZA0ckDohKXNeBNqZ4us6PjMVLs5h1w2GBB6uPtOknp2+VF4F/zcy9LI70W+Z+pE2Soajky1w==
-
anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
@@ -215,6 +210,11 @@ async@^3.2.0, async@~3.2.0:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+aws-ssl-profiles@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz#157dd77e9f19b1d123678e93f120e6f193022641"
+ integrity sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==
+
axios@^0.21.0:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
@@ -242,11 +242,6 @@ bluebird@^3.7.2:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-bn.js@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.0.0.tgz#825c4107f7fa789378ee1b0d86be0503d7ac743b"
- integrity sha512-NmOLApC80+n+P28y06yHgwGlOCkq/X4jRh5s590959FZXSrM+I/61h0xxuIaYsg0mD44mEAZYG/rnclWuRoz+A==
-
bodec@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/bodec/-/bodec-0.1.0.tgz#bc851555430f23c9f7650a75ef64c6a94c3418cc"
@@ -352,14 +347,6 @@ call-bind@^1.0.0:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
-cardinal@0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-0.4.4.tgz#ca5bb68a5b511b90fe93b9acea49bdee5c32bfe2"
- integrity sha512-3MxV0o9wOpQcobrcSrRpaSxlYkohCcZu0ytOjJUww/Yo/223q4Ecloo7odT+M0SI5kPgb1JhvSaF4EEuVXOLAQ==
- dependencies:
- ansicolors "~0.2.1"
- redeyed "~0.4.0"
-
chalk@3.0.0, chalk@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
@@ -604,6 +591,11 @@ denque@^1.4.1:
resolved "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz"
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
+denque@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
+ integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
+
depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz"
@@ -642,11 +634,6 @@ dotenv@^1.2.0:
resolved "https://registry.npmjs.org/dotenv/-/dotenv-1.2.0.tgz"
integrity sha1-fNc+FuB/BXyAchR6W8OoZ38KtcY=
-double-ended-queue@2.0.0-0:
- version "2.0.0-0"
- resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.0.0-0.tgz#7847fda1c00fb722245aff83643a4887670efd2c"
- integrity sha512-t5ouWOpItmHrm0J0+bX/cFrIjBFWnJkk5LbIJq6bbU/M4aLX2c3LrM4QYsBptwvlPe3WzdpQefQ0v1pe/A5wjg==
-
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
@@ -725,11 +712,6 @@ esprima@^4.0.0, esprima@^4.0.1:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-esprima@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad"
- integrity sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==
-
estraverse@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
@@ -945,6 +927,13 @@ function-bind@^1.1.1:
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+generate-function@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
+ integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
+ dependencies:
+ is-property "^1.0.2"
+
get-intrinsic@^1.0.2:
version "1.1.3"
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz"
@@ -1114,6 +1103,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+iconv-lite@^0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
inflection@^1.12.0:
version "1.13.4"
resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.13.4.tgz#65aa696c4e2da6225b148d7a154c449366633a32"
@@ -1193,6 +1189,11 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+is-property@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+ integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==
+
is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz"
@@ -1318,10 +1319,10 @@ log-driver@^1.2.7:
resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8"
integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==
-lru-cache@2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.5.0.tgz#d82388ae9c960becbea0c73bb9eb79b6c6ce9aeb"
- integrity sha512-dVmQmXPBlTgFw77hm60ud//l2bCuDKkqC2on1EBoM7s9Urm9IQDrnujwZ93NFnAq0dVZ0HBXTS7PwEG+YE7+EQ==
+long@^5.2.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83"
+ integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==
lru-cache@^5.1.1:
version "5.1.1"
@@ -1337,6 +1338,16 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
+lru-cache@^7.14.1:
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
+ integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
+
+lru.min@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/lru.min/-/lru.min-1.1.2.tgz#01ce1d72cc50c7faf8bd1f809ebf05d4331021eb"
+ integrity sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==
+
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
@@ -1479,23 +1490,27 @@ mute-stream@~0.0.4:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
-mysql2@^0.15.8:
- version "0.15.8"
- resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-0.15.8.tgz#f16650b6f7c5bb568b34511e21bafe36d5a89ef1"
- integrity sha512-3x5o6C20bfwJYPSoT74MOoad7/chJoq4qXHDL5VAuRBBrIyErovLoj04Dz/5EY9X2kTxWSGNiTegtxpROTd2YQ==
+mysql2@^3.14.3:
+ version "3.14.3"
+ resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.14.3.tgz#52aa6266b416d8b629bf398ba2be9840492b9b08"
+ integrity sha512-fD6MLV8XJ1KiNFIF0bS7Msl8eZyhlTDCDl75ajU5SJtpdx9ZPEACulJcqJWr1Y8OYyxsFc4j3+nflpmhxCU5aQ==
dependencies:
- bn.js "2.0.0"
- cardinal "0.4.4"
- double-ended-queue "2.0.0-0"
- named-placeholders "0.1.3"
- readable-stream "1.0.33"
+ aws-ssl-profiles "^1.1.1"
+ denque "^2.1.0"
+ generate-function "^2.3.1"
+ iconv-lite "^0.6.3"
+ long "^5.2.1"
+ lru.min "^1.0.0"
+ named-placeholders "^1.1.3"
+ seq-queue "^0.0.5"
+ sqlstring "^2.3.2"
-named-placeholders@0.1.3:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-0.1.3.tgz#353776ee259ad105227e13852eef4215ac631e84"
- integrity sha512-Mt79RtxZ6MYTIEemPGv/YDKpbuavcAyGHb0r37xB2mnE5jej3uBzc4+nzOeoZ4nZiii1M32URKt9IjkSTZAmTA==
+named-placeholders@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351"
+ integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==
dependencies:
- lru-cache "2.5.0"
+ lru-cache "^7.14.1"
needle@2.4.0:
version "2.4.0"
@@ -1871,16 +1886,6 @@ read@^1.0.4:
dependencies:
mute-stream "~0.0.4"
-readable-stream@1.0.33:
- version "1.0.33"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.33.tgz#3a360dd66c1b1d7fd4705389860eda1d0f61126c"
- integrity sha512-72KxhcKi8bAvHP/cyyWSP+ODS5ef0DIRs0OzrhGXw31q41f19aoELCbvd42FjhpyEDxQMRiiC5rq9rfE5PzTqg==
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "0.0.1"
- string_decoder "~0.10.x"
-
readable-stream@1.1.x:
version "1.1.14"
resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz"
@@ -1918,13 +1923,6 @@ rechoir@^0.8.0:
dependencies:
resolve "^1.20.0"
-redeyed@~0.4.0:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-0.4.4.tgz#37e990a6f2b21b2a11c2e6a48fd4135698cba97f"
- integrity sha512-pnk1vsaNLu1UAAClKsImKz9HjBvg9i8cbRqTRzJbiCjGF0fZSMqpdcA5W3juO3c4etFvTrabECkq9wjC45ZyxA==
- dependencies:
- esprima "~1.0.4"
-
redis-commands@^1.5.0:
version "1.5.0"
resolved "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz"
@@ -2025,7 +2023,7 @@ safe-buffer@5.2.1, safe-buffer@^5.2.1:
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-"safer-buffer@>= 2.1.2 < 3":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -2081,6 +2079,11 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
+seq-queue@^0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e"
+ integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==
+
serve-favicon@^2.5.0:
version "2.5.0"
resolved "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz"
@@ -2223,6 +2226,11 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+sqlstring@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c"
+ integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==
+
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"