var ThreadSettingsForm = FormView.extend({ el: "#thread_settings", events: { "click": "hide", "click .inner": "stopPropagation", "click .thread_delete": "deleteThread", "click .file_delete": "deleteFile", "click .close_link": "hide", "change [name=color]": "changeColor", "change [name=privacy]": "toggleAllowed", "change [name=sort]": "changeSort", "click #allowed_names [type=checkbox]": "removeAllowed", "keydown [name=allowed_field]": "keydownAllowed", "blur [name=allowed_field]": "updateAllowed", "click [name=file_id]": "toggleFile", "click tr.file": "toggleFileRow", "click #move_files": "moveFiles", "click .files_delete": "deleteFiles", }, action: "", method: 'put', 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 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.$("[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) => { 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]') 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") 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) }) $("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)) }, loadThreads: function(threads){ // update the dropdown list of threads var $thread_select = this.$('[name=thread]') if (!threads || !threads.length) { $thread_select.parent().hide() return } $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) }) // 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() }, 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) => sanitizeHTML(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() if (! title || ! title.length) { errors.push("Please enter a title.") } return errors.length ? errors : null }, serialize: function(){ var data = { 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, sort: $("[name=sort]").val() }, } return JSON.stringify(data) }, success: function(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") }, 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() }, 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) }, toggleFile: function(e){ // e.preventDefault() 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) }, 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) }, toggleFileChecked: function($tr, $input, value){ $tr.toggleClass('checked', value) if ($input) $input.prop('checked', value) }, 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 => { return new Promise((resolve, reject) => { $.ajax({ method: "GET", 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) }, error: function(){ console.log('error moving', file_id) reject() } }) }) }) Promise.all(promises).then( () => { window.location.href = '/details/' + thread_id }).catch(() =>{ console.error('whaaaaa') alert('there was a problem moving the files...') }) }, deleteFiles: function(){ var thread_files = this.options.parent.data.files var files_view = this.options.parent.files window.hoe = files_view window.before = this.options.parent.data.files var inputs_checked = toArray(this.el.querySelectorAll("[name=file_id]:checked")) var file_names = inputs_checked.map(input => input.parentElement.nextElementSibling.firstElementChild.innerHTML) if (! inputs_checked.length) return var msg = "Are you sure you want to delete these files? \n\n" + file_names.join("\n") var should_remove = confirm(msg) if (should_remove) { var promises = inputs_checked.map(node => { return new Promise((resolve, reject) => { $.ajax({ method: "DELETE", url: "/api/file/" + node.value, headers: { "csrf-token": $("[name=_csrf]").attr("value") }, data: JSON.stringify({ csrf: csrf() }), dataType: "json", success: function(data){ $(node).closest('.file').remove() thread_files.splice( thread_files.findIndex( (file) => { return (file.id === parseInt(node.value)) } ),1)[0] resolve() }, error: function(){ console.log('error deleting file', node.value) reject() } }) }) }) Promise.all(promises).then( () => { files_view.load() }).catch((error) =>{ console.log(error) console.error('whaaaaa') alert('there was a problem deleting 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) if (should_remove) { $.ajax({ method: "DELETE", url: "/api/thread/" + id, headers: { "csrf-token": $("[name=_csrf]").attr("value") }, data: JSON.stringify({ csrf: csrf() }), dataType: "json", success: function(){ window.location.href = "/" } }) } }, deleteFile: function(e){ e.preventDefault() e.stopPropagation() var data = this.options.parent.data var files_view = this.options.parent.files var $el = $(e.currentTarget) var $parent = $el.closest('.file') var file_id = $el.data('id') if (! file_id) return 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", url: "/api/file/" + file_id, headers: { "csrf-token": $("[name=_csrf]").attr("value") }, data: JSON.stringify({ csrf: csrf() }), dataType: "json", success: function(resp){ data.files.splice(data.files.findIndex((f) => parseInt(f.id) === file_id ), 1) $parent.remove() files_view.load() } }) } }, })