diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2017-12-15 05:36:50 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2017-12-15 05:36:50 +0100 |
| commit | 7ad469291c015b33a2d20587db26b9621ed82d00 (patch) | |
| tree | 83e2a56822033a638d03ff7ddf4bfee3181631e6 | |
| parent | cc585396a85e3107bb7b4298098b84b738919c8f (diff) | |
sort file list by name or date, updates audio player
| -rw-r--r-- | bucky/app/bucky.js | 59 | ||||
| -rw-r--r-- | bucky/app/router.js | 29 | ||||
| -rw-r--r-- | bucky/db/index.js | 15 | ||||
| -rw-r--r-- | public/assets/css/bucky.css | 31 | ||||
| -rw-r--r-- | public/assets/js/lib/views/details/audio.js | 42 | ||||
| -rw-r--r-- | public/assets/js/lib/views/details/files.js | 71 | ||||
| -rw-r--r-- | public/assets/js/lib/views/details/index.js | 2 | ||||
| -rw-r--r-- | public/assets/js/lib/views/details/settings.js | 94 | ||||
| -rw-r--r-- | public/assets/js/lib/views/index/threadbox.js | 2 | ||||
| -rw-r--r-- | public/assets/js/util/format.js | 2 | ||||
| -rw-r--r-- | public/assets/js/vendor/util.js | 1 | ||||
| -rw-r--r-- | views/partials/files.ejs | 11 | ||||
| -rw-r--r-- | views/partials/settings.ejs | 16 |
13 files changed, 281 insertions, 94 deletions
diff --git a/bucky/app/bucky.js b/bucky/app/bucky.js index ec0ab8c..25de991 100644 --- a/bucky/app/bucky.js +++ b/bucky/app/bucky.js @@ -184,6 +184,8 @@ var bucky = module.exports = { return res.sendStatus(500) } var keyword = util.sanitize(req.body.keyword || "") + var privacy = parseInt(req.body.privacy) || 0 + var allowed = util.sanitize(req.body.allowed || "") var settings if (typeof req.body.settings === 'object') { try { @@ -194,29 +196,48 @@ var bucky = module.exports = { if (! settings) { return res.sendStatus(500) } +console.log(privacy) res.thread.set('title', title) res.thread.set('keyword', keyword) res.thread.set('color', util.sanitize(req.body.color || 'blue')) res.thread.set('revision', res.thread.get('revision')+1) res.thread.set('settings', settings) - res.thread.save().then( () => next() ) - }, - ensureInterestedUsers: function(req, res, next){ - // given a thread, find people who might be interested in it - // - other people who have been in threads with you - // - other people who have posted on the keyword - // for now though, just show the last 20 people who have logged in.. - db.getLastlog(21).then( (users) => { - res.interestedUsers = users - next() - }).catch( () => { - res.interestedUsers = [] + res.thread.set('privacy', privacy) + res.thread.set('allowed', allowed) + res.thread.save() + .then( () => next() ) + .catch(err => { + console.error(err) next() }) }, - ensureThreadUsers: function(req, res, next) { - db.getThreadUsers(res.thread.get('id')).then(thread_users => { - res.thread_users = thread_users +// ensureInterestedUsers: function(req, res, next){ +// // given a thread, find people who might be interested in it +// // - other people who have been in threads with you +// // - other people who have posted on the keyword +// // for now though, just show the last 20 people who have logged in.. +// db.getLastlog(21).then( (users) => { +// res.interestedUsers = users +// next() +// }).catch( () => { +// res.interestedUsers = [] +// next() +// }) +// }, +// ensureThreadUsers: function(req, res, next) { +// db.getThreadUsers(res.thread.get('id')).then(thread_users => { +// res.thread_users = thread_users +// next() +// }) +// }, + checkUsernames: function(req, res, next) { + if (! req.body.usernames) return res.sendStatus(500) + db.checkUsernames(req.body.usernames).then( (users) => { + res.usernames = users.map(user => user.username) + next() + }).catch((err) => { + console.log(err) + res.usernames = [] next() }) }, @@ -370,6 +391,7 @@ var bucky = module.exports = { reject(err) }, success: function(url){ + console.log("file >", url) var data = { thread: res.thread.get('id'), username: req.user.get('username'), @@ -436,7 +458,7 @@ var bucky = module.exports = { res.sendStatus({ error: 'Problem uploading avatar.' }) }, success: function(url){ - console.log(">", url) + console.log("avatar >", url) res.user.set('avatar', url) next() } @@ -461,7 +483,7 @@ var bucky = module.exports = { next() }, checkThreadPrivacy: function(req, res, next) { - if (res.thread.checkPrivacy(req.user)) { + if (! res.thread.checkPrivacy(req.user)) { return res.sendStatus(500) } next() @@ -479,10 +501,11 @@ var bucky = module.exports = { } next() }, - checkThreadsPrivacy: function(req, res, next) { + filterPrivateThreads: function(req, res, next) { res.threads = res.threads.filter(thread => { return thread.checkPrivacy(req.user) }) + next() }, /* MAIL */ diff --git a/bucky/app/router.js b/bucky/app/router.js index 106c65e..e24253a 100644 --- a/bucky/app/router.js +++ b/bucky/app/router.js @@ -76,8 +76,7 @@ module.exports = function(app){ bucky.sanitizeUser, function(req, res) { res.json(res.user) - } - ) + }) app.post("/api/user/:username", middleware.ensureAuthenticated, bucky.ensureUser, @@ -90,6 +89,13 @@ module.exports = function(app){ function(req, res){ res.json(util.sanitizeUser(res.user)) }) + app.put("/api/checkUsernames", + middleware.ensureAuthenticated, + bucky.checkUsernames, + function(req, res){ + res.send({ usernames: res.usernames }) + }) + /* threads */ @@ -97,7 +103,7 @@ module.exports = function(app){ bucky.ensureLastlog, middleware.ensureAuthenticated, bucky.ensureLatestThreads, - bucky.ensureThreadsPrivacy, + bucky.filterPrivateThreads, bucky.ensureCommentCountsForThreads, bucky.ensureFileCountsForThreads, bucky.ensureKeywordsForThreads, @@ -114,7 +120,7 @@ module.exports = function(app){ bucky.ensureLastlog, middleware.ensureAuthenticated, bucky.ensureThreadsForKeyword, - bucky.ensureThreadsPrivacy, + bucky.filterPrivateThreads, bucky.ensureCommentCountsForThreads, bucky.ensureFileCountsForThreads, bucky.ensureKeywordsForThreads, @@ -127,22 +133,11 @@ module.exports = function(app){ lastlog: res.lastlog, }) }) - // app.get("/api/thread/:id/interested", - // middleware.ensureAuthenticated, - // bucky.ensureThread, - // bucky.ensureThreadPrivacy, - // bucky.ensureInterestedUsers, - // // bucky.ensureThreadUsers, - // function(req, res){ - // res.json({ - // interestedUsers: res.interestedUsers, - // }) - // }) app.get("/api/thread/:id", middleware.ensureAuthenticated, bucky.ensureThread, + bucky.checkThreadPrivacy, bucky.bumpViewCount, - bucky.ensureThreadPrivacy, bucky.ensureKeywordForThread, bucky.ensureCommentsForThread, bucky.ensureFilesForThread, @@ -152,7 +147,6 @@ module.exports = function(app){ function(req, res){ res.json({ thread: res.thread, - thread_users: res.thread, comments: res.comments, files: res.files, keyword: res.keyword, @@ -261,6 +255,7 @@ module.exports = function(app){ middleware.ensureAuthenticated, bucky.ensureKeyword, bucky.ensureThreadsForKeyword, + bucky.filterPrivateThreads, bucky.ensureCommentCountsForThreads, bucky.ensureFileCountsForThreads, bucky.ensureKeywordsForThreads, diff --git a/bucky/db/index.js b/bucky/db/index.js index 652f723..f92ba2f 100644 --- a/bucky/db/index.js +++ b/bucky/db/index.js @@ -18,8 +18,9 @@ var Thread = db.Thread = bookshelf.Model.extend({ if (this.get('privacy') === 0) return true let username = user.get('username') if (this.get('username') === username) return true - let allowed = this.get('allowed').split(',') - if (allowed.findIndex(username) !== -1) return true + let allowed = (this.get('allowed') || '').split(',') + if (allowed.indexOf(username) !== -1) return true + return false } }) var ThreadUser = db.ThreadUser = bookshelf.Model.extend({ @@ -65,13 +66,19 @@ db.getUsersById = function(ids){ return User.where("id", "in", ids).fetchAll() } db.getUsernamesById = function(ids){ - return User.column("id").column("username").where("id", "in", ids).fetchAll() + return knex.column("id").column("username") + .select().from('users').where("id", "in", ids) +} +db.checkUsernames = function(usernames){ + return knex.column("username") + .select().distinct().from('users').where("username", "in", usernames) } db.getUserByUsername = function(username) { return new User({'username': username}).fetch() } db.getLastlog = function(limit){ - return knex.column('id').column('username').column('lastseen').select().from('users').orderBy('lastseen', 'desc').limit(limit || 10) + return knex.column('id').column('username').column('lastseen') + .select().from('users').orderBy('lastseen', 'desc').limit(limit || 10) } /* THREADS */ diff --git a/public/assets/css/bucky.css b/public/assets/css/bucky.css index 03cb81d..e915ded 100644 --- a/public/assets/css/bucky.css +++ b/public/assets/css/bucky.css @@ -556,6 +556,8 @@ pre br { display: block; } +/* FILES */ + #files, #files tr { margin: 0; padding: 0; @@ -599,6 +601,20 @@ pre br { #files tr.total td:first-child { padding: 5px; } +.bold { + font-weight: bold; +} +.italic { + font-style: italic; +} +.total div { + display: flex; + flex-direction: row; + justify-content: space-between; +} +#files .total td a { + display: inline; +} #gallery { width: 100%; @@ -688,6 +704,21 @@ pre br { .desktop button.thread_delete:hover { background: #ff8288; } +.allowed_field_container, +.allowed_names { + display: none; +} +#thread_controls .allowed_names div { + display: inline-block; + white-space: nowrap; + margin-right: 10px; +} +#thread_controls .allowed_names label { + min-width: auto; + padding-right: 2px; + width: auto; + display: inline-block; +} /* SEARCH */ diff --git a/public/assets/js/lib/views/details/audio.js b/public/assets/js/lib/views/details/audio.js index 42f5376..e328a07 100644 --- a/public/assets/js/lib/views/details/audio.js +++ b/public/assets/js/lib/views/details/audio.js @@ -3,24 +3,35 @@ var audio = (function(){ var el, music = [], current_index = -1 var links, comment, parent + var selected = false var playing = false audio.init = function () { - links = document.querySelectorAll("a") comment = document.querySelector("#comment") parent = document.querySelector("#audio") - - Array.prototype.slice.apply(links).forEach(function(url){ - if (! url.href.match(/(mp3|wav|ogg)/)) return - url.dataset.index = music.length - music.push(url) - }) + + audio.index() audio.build() } + audio.index = function () { + music = [] + var links = document.querySelectorAll("a") + Array.prototype.slice.apply(links).forEach(function(link){ + if (! link.href.match(/(mp3|wav|ogg)/)) return + link.dataset.index = music.length + music.push(link) + if (playing && link.href === el.src) { + current_index = parseInt(link.dataset.index) + } + }) + if (playing) { + audio.set_cursor() + } + } audio.build = function () { el = document.createElement("audio") el.setAttribute("controls", true) - el.addEventListener("loadeddata", () => { if (playing) el.play() }) + el.addEventListener("loadeddata", () => { if (selected) el.play() }) el.addEventListener("ended", audio.next) el.src = music[0] parent.appendChild(el) @@ -33,10 +44,14 @@ var audio = (function(){ document.body.removeEventListener("keydown", audio.keydown) } audio.play = function (index) { + playing = true current_index = (parseInt(index) + music.length) % music.length el.src = music[current_index].href - playing = document.querySelector(".playing") - if (playing) playing.classList.remove("playing") + audio.set_cursor() + } + audio.set_cursor = function () { + selected = document.querySelector(".playing") + if (selected) selected.classList.remove("playing") music[current_index].classList.add("playing") } audio.prev = function (){ @@ -50,6 +65,13 @@ var audio = (function(){ else el.pause() } audio.keydown = function(e){ + function element_is_text_input(el) { + var tagName = el.tagName.toLowerCase() + return (tagName == 'input' && el.type == 'text' || tagName == 'textarea') + } + if (element_is_text_input(document.activeElement)) { + return + } if (app.typing || e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) return switch (e.keyCode) { case 37: // left diff --git a/public/assets/js/lib/views/details/files.js b/public/assets/js/lib/views/details/files.js index e8832a7..b68d42c 100644 --- a/public/assets/js/lib/views/details/files.js +++ b/public/assets/js/lib/views/details/files.js @@ -4,6 +4,8 @@ var FilesView = FormView.extend({ events: { "click .file": "pick", + "click #sortByName": "sortByName", + "click #sortByDate": "sortByDate", }, initialize: function(){ @@ -17,23 +19,61 @@ var FilesView = FormView.extend({ this.$el.hide() } var total = 0, has_music = false + this.files = files files.forEach(function(file){ if (is_image(file.filename)) { // return } - this.appendFile(file) total += file.size has_music = has_music || file.filename.match(/(mp3|wav|ogg)/i) }.bind(this)) - var size = hush_size(total) - var t = this.templateTotal.replace(/{{size_class}}/g, size[0]) - .replace(/{{size}}/g, size[1]) - this.$el.append(t) + this.total = total + this.has_music = has_music - if (has_music) { + if (this.has_music) { audio.init() } + + this.sortByName() + }, + + files: [], + sortedFiles: [], + currentSort: 'name', + reverse: false, + + sortByName: function(e){ + e && e.preventDefault() + if (this.currentSort !== 'name') { + this.currentSort = 'name' + this.reverse = false + } else { + this.reverse = !this.reverse + } + this.sort((a,b) => cmp(a.filename, b.filename)) + }, + sortByDate: function(e){ + e && e.preventDefault() + if (this.currentSort !== 'date') { + this.currentSort = 'date' + this.reverse = true + } else { + this.reverse = !this.reverse + } + this.sort((a,b) => cmp(a.date, b.date)) + }, + + sort: function(f){ + this.$el.empty() + this.sortedFiles = this.reverse ? this.files.sort(f) : this.files.sort(f).reverse() + this.sortedFiles.forEach(file => { + this.appendFile(file) + }) + this.appendTotal() + if (this.has_music) { + audio.index() + } }, parse: function(file){ @@ -64,11 +104,28 @@ var FilesView = FormView.extend({ this.$el.append($el) }, + appendTotal: function(){ + var size = hush_size(this.total) + var nameClass = this.sort === 'name' ? 'bold' : '' + if (nameClass && this.reverse) { + nameClass += ' italic' + } + var dateClass = this.sort === 'date' ? 'bold' : '' + if (dateClass && this.reverse) { + dateClass += ' italic' + } + var t = this.templateTotal.replace(/{{size_class}}/g, size[0]) + .replace(/{{size}}/g, size[1]) + .replace(/{{nameClass}}/g, nameClass) + .replace(/{{dateClass}}/g, dateClass) + this.$el.append(t) + }, + pick: function(e){ if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) return if (! e.target.href || ! e.target.href.match(/(mp3|wav|ogg)/i)) return e.preventDefault() - audio.play( e.target.dataset.index ) + audio.play( e.target.dataset.index ) // index set in audio.js }, })
\ No newline at end of file diff --git a/public/assets/js/lib/views/details/index.js b/public/assets/js/lib/views/details/index.js index df2aeff..1b67b92 100644 --- a/public/assets/js/lib/views/details/index.js +++ b/public/assets/js/lib/views/details/index.js @@ -28,7 +28,7 @@ var DetailsView = View.extend({ populate: function(data){ this.data = data - console.log(data) + // console.log(data) set_background_color(data.thread.color || (data.keyword ? data.keyword.color : 'plain')) $("body").removeClass('loading') var thread = data.thread diff --git a/public/assets/js/lib/views/details/settings.js b/public/assets/js/lib/views/details/settings.js index 1124826..0a77774 100644 --- a/public/assets/js/lib/views/details/settings.js +++ b/public/assets/js/lib/views/details/settings.js @@ -8,6 +8,10 @@ var ThreadSettingsForm = FormView.extend({ "click .thread_delete": "deleteThread", "click .close_link": "hide", "change [name=color]": "changeColor", + "change [name=privacy]": "toggleAllowed", + "click #allowed_names [type=checkbox]": "removeAllowed", + "keydown [name=allowed_field]": "keydownAllowed", + "blur [name=allowed_field]": "updateAllowed", }, action: "", @@ -29,7 +33,9 @@ var ThreadSettingsForm = FormView.extend({ var settings = thread.settings var display = thread.display + this.thread = thread 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)) @@ -38,6 +44,7 @@ var ThreadSettingsForm = FormView.extend({ 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]') Object.keys(COLORS).forEach((color) => { @@ -48,13 +55,13 @@ var ThreadSettingsForm = FormView.extend({ }) $color.val(thread.color || keyword ? keyword.color : "") + this.toggleAllowed() this.fetchKeywords() -// this.fetchAllowedUsers(thread) $("body").removeClass("loading") }, - fetchKeywords: function(){ + fetchKeywords: function(thread){ $.get('/api/keywords', function(data){ var $keyword = this.$('[name=keyword]') data.keywords @@ -66,33 +73,62 @@ var ThreadSettingsForm = FormView.extend({ option.innerHTML = keyword $keyword.append(option) }) - $keyword.val(thread.keyword) + $keyword.val(this.thread.keyword) }.bind(this)) }, -// fetchAllowedUsers: function(thread){ -// var usernameRegexp = new RegExp('{{username}}', g) -// $.get('/api/thread/' + thread.id + '/interested', function(data){ -// var $allowed = this.$(".allowed") -// var tmpl = this.allowedTemplate -// // make a lookup of existing users -// var allowed = {} -// thread.allowed.split(" ").forEach((username) => { -// -// }) -// // build the ui -// data.interestedUsers -// .map( (a) => a.username) -// .sort( (a,b) => a < b ? -1 : a === b ? 0 : 1 ) -// .forEach((username) => { -// var t = tmpl.replace(usernameRegexp, "") -// .replace('{{checked}}', -// $keyword.append(option) -// }) -// $keyword.val(thread.keyword) -// }.bind(this)) -// }, - + 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) + }) + }, + + 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) => sanitize(name) ) + $.ajax({ + method: "PUT", + url: "/api/checkUsernames", + headers: { "csrf-token": $("[name=_csrf]").attr("value") }, + 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() + }.bind(this), + }) + }, + + 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() @@ -107,11 +143,13 @@ var ThreadSettingsForm = FormView.extend({ title: $("[name=title]").val(), keyword: $("[name=keyword]").val(), color: $("[name=color]").val(), + privacy: $("[name=privacy]:checked").val() ? 1 : 0, + allowed: this.allowed.join(","), settings: { hootbox: $("[name=hootbox]:checked").val() ? true : false, shorturls: $("[name=shorturls]:checked").val() ? true : false, noupload: $("[name=noupload]:checked").val() ? true : false, - } + }, } return JSON.stringify(data) }, @@ -125,6 +163,7 @@ var ThreadSettingsForm = FormView.extend({ 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") @@ -133,6 +172,7 @@ var ThreadSettingsForm = FormView.extend({ 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) }, diff --git a/public/assets/js/lib/views/index/threadbox.js b/public/assets/js/lib/views/index/threadbox.js index 65ad945..4ecb919 100644 --- a/public/assets/js/lib/views/index/threadbox.js +++ b/public/assets/js/lib/views/index/threadbox.js @@ -53,7 +53,7 @@ var ThreadBox = View.extend({ var size = hush_size(thread.size) var comments = hush_null(thread.comment_count, "c") var files = hush_null(thread.file_count, "f") - var dot = privacy_dot(thread.private) + var dot = privacy_dot(thread.privacy) var datetime = verbose_date(thread.lastmodified) var age = get_age(thread.lastmodified) var id = thread.id + get_revision(thread) diff --git a/public/assets/js/util/format.js b/public/assets/js/util/format.js index 339e4f6..e58928e 100644 --- a/public/assets/js/util/format.js +++ b/public/assets/js/util/format.js @@ -28,7 +28,7 @@ function commatize (n) { function privacy_dot (p) { if (! p) return "·" - else return "·:" + else return ".:" } var short_months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ") diff --git a/public/assets/js/vendor/util.js b/public/assets/js/vendor/util.js index 34ef45f..b08a914 100644 --- a/public/assets/js/vendor/util.js +++ b/public/assets/js/vendor/util.js @@ -71,6 +71,7 @@ function mod(n,m){ return n-(m * floor(n/m)) } function dist(x0,y0,x1,y1){ return sqrt(pow(x1-x0,2)+pow(y1-y0,2)) } function angle(x0,y0,x1,y1){ return atan2(y1-y0,x1-x0) } function avg(m,n,a){ return (m*(a-1)+n)/a } +function cmp (a,b){ return (a<b)?-1:(a===b)?0:1 } function noop(){} function pixel(x,y){ return 4*(mod(y,actual_h)*actual_w+mod(x,actual_w)) } diff --git a/views/partials/files.ejs b/views/partials/files.ejs index 5b55f4b..456fe17 100644 --- a/views/partials/files.ejs +++ b/views/partials/files.ejs @@ -21,7 +21,16 @@ <script class="templateTotal" type="text/html"> <tr class="total"> <td colspan="5"> - total size: <span class="{{size_class}}">{{size}}</span> + <div> + <span> + <i>sort by</i> + <a href="#" class="{{nameClass}}" id="sortByName">name</a> + <a href="#" class="{{dateClass}}" id="sortByDate">date</a> + </span> + <span> + total size: <span class="{{size_class}}">{{size}}</span> + </span> + </div> </td> </tr> </script> diff --git a/views/partials/settings.ejs b/views/partials/settings.ejs index 3964429..d3833e1 100644 --- a/views/partials/settings.ejs +++ b/views/partials/settings.ejs @@ -30,28 +30,30 @@ </div> <div> <label for="thread_hootbox">hoot style</label> - <input id="thread_hootbox" name="hootbox" value="0" type="hidden"> <input id="thread_hootbox" name="hootbox" value="1" type="checkbox"> </div> <div> <label for="thread_noupload">disable uploads</label> - <input id="thread_noupload" name="noupload" value="0" type="hidden"> <input id="thread_noupload" name="noupload" value="1" type="checkbox"> </div> <div> <label for="thread_shorturls">shorten urls</label> - <input id="thread_shorturls" name="shorturls" value="0" type="hidden"> <input id="thread_shorturls" name="shorturls" value="1" type="checkbox"> </div> <div> <label for="thread_privacy">private?</label> - <input id="thread_privacy" name="privacy" value="0" type="hidden"> <input id="thread_privacy" name="privacy" value="1" type="checkbox"> </div> - <div class="allowed"> + <div class="allowed_field_container"> + <label for="allowed_field">allow names:</label> + <input id="allowed_field" name="allowed_field" type="text" /> + </div> + <div class="allowed_names"> <script type="text/html" class="allowedTemplate"> - <label for="user_{{username}}">{{username}}</label> - <input id="user_{{username}}" name="allowed" value="{{username}}" type="checkbox"> + <div> + <label for="user_{{username}}">{{username}}</label> + <input id="user_{{username}}" name="allowed" value="{{username}}" type="checkbox" checked> + </div> </script> </div> <div> |
