diff --git a/isso/css/isso.scss b/isso/css/isso.scss index 6816f03..d25c2a8 100644 --- a/isso/css/isso.scss +++ b/isso/css/isso.scss @@ -103,6 +103,12 @@ a { } } + > div.textarea-wrapper textarea { + @include fill-parent; + @include isso-shadow; + font: inherit; + } + > footer { font-family: "Helvetica", Arial, sans-serif; @@ -119,7 +125,7 @@ a { } } - a.reply, a.delete { + a.reply, a.edit, a.cancel, a.delete { padding-left: 1em; } @@ -130,7 +136,6 @@ a { .upvote svg, .downvote svg { position: relative; top: 0.2em; - //margin-bottom: -0.2em; } } } diff --git a/isso/js/app/api.js b/isso/js/app/api.js index 2dcd2f4..d9d5a22 100644 --- a/isso/js/app/api.js +++ b/isso/js/app/api.js @@ -73,7 +73,7 @@ define(["q"], function(Q) { var qs = function(params) { var rv = ""; for (var key in params) { - if (params.hasOwnProperty(key)) { + if (params.hasOwnProperty(key) && params[key]) { rv += key + "=" + encodeURIComponent(params[key]) + "&"; } } @@ -86,8 +86,9 @@ define(["q"], function(Q) { function (rv) { return JSON.parse(rv.body); }); }; - var modify = function(data) { - // ... + var modify = function(id, data) { + return curl("PUT", endpoint + "/id/" + id, JSON.stringify(data)).then( + function (rv) { return JSON.parse(rv.body); }); }; var remove = function(id) { @@ -100,9 +101,15 @@ define(["q"], function(Q) { }); }; - var fetch = function() { + var view = function(id, plain) { + return curl("GET", endpoint + "/id/" + id + "?" + qs({plain: plain}), null).then(function (rv) { + return JSON.parse(rv.body); + }); + }; - return curl("GET", endpoint + "/?" + qs({uri: location}), null).then(function (rv) { + var fetch = function(plain) { + + return curl("GET", endpoint + "/?" + qs({uri: location, plain: plain}), null).then(function (rv) { if (rv.status === 200) { return JSON.parse(rv.body); } else { @@ -139,7 +146,9 @@ define(["q"], function(Q) { salt: salt, create: create, + modify: modify, remove: remove, + view: view, fetch: fetch, count: count, like: like, diff --git a/isso/js/app/dom.js b/isso/js/app/dom.js index 9c9e54a..ee1fbe8 100644 --- a/isso/js/app/dom.js +++ b/isso/js/app/dom.js @@ -35,6 +35,35 @@ define(function() { }); }; + window.Element.prototype.toggle = function(type, on, off) { + + function Toggle(el, on, off) { + this.state = false; + this.el = el; + this.on = on; + this.off = off; + } + + Toggle.prototype.next = function next() { + if (! this.state) { + this.state = true; + this.on(this); + } else { + this.state = false; + this.off(this); + } + }; + + Toggle.prototype.wait = function wait() { + this.state = ! this.state; + }; + + var toggler = new Toggle(this, on, off); + this.on(type, function() { + toggler.next(); + }); + }; + window.Element.prototype.remove = function() { // Mimimi, I am IE and I am so retarded, mimimi. this.parentNode.removeChild(this); @@ -70,8 +99,21 @@ define(function() { return wrapper.firstChild; }; - DOM.new = function(tag) { - return document.createElement(tag); + DOM.new = function(tag, content) { + + var el = document.createElement(tag.split(".")[0]); + tag.split(".").slice(1).forEach(function(val) { el.classList.add(val); }); + + if (["A", "LINK"].indexOf(el.nodeName) > -1) { + el.href = "#"; + } + + if (["TEXTAREA", "INPUT"].indexOf(el.nodeName) > -1) { + el.value = content; + } else { + el.textContent = content || ""; + } + return el; }; DOM.each = function(tag, func) { diff --git a/isso/js/app/fancy.js b/isso/js/app/fancy.js new file mode 100644 index 0000000..8a6b9c7 --- /dev/null +++ b/isso/js/app/fancy.js @@ -0,0 +1,21 @@ +define(function() { + + "use strict"; + + // http://chuvash.eu/2011/12/14/the-cleanest-auto-resize-for-a-textarea/ + var autoresize = function(textarea, minheight) { + var offset= !window.opera ? (textarea.offsetHeight - textarea.clientHeight) : (textarea.offsetHeight + parseInt(window.getComputedStyle(textarea, null).getPropertyValue('border-top-width'))); + ["keyup", "focus"].forEach(function(event) { + textarea.on(event, function() { + if ((textarea.scrollHeight + offset ) > minheight) { + textarea.style.height = "auto"; + textarea.style.height = (textarea.scrollHeight + offset ) + 'px'; + } + }); + }); + }; + + return { + autoresize: autoresize + }; +}); \ No newline at end of file diff --git a/isso/js/app/i18n/de.js b/isso/js/app/i18n/de.js index 655719a..1e180ba 100644 --- a/isso/js/app/i18n/de.js +++ b/isso/js/app/i18n/de.js @@ -8,9 +8,12 @@ define({ "no-comments": "Keine Kommentare bis jetzt", "comment-reply": "Antworten", + "comment-edit": "Bearbeiten", + "comment-save": "Speichern", "comment-delete": "Löschen", "comment-confirm": "Bestätigen", "comment-close": "Schließen", + "comment-cancel": "Abbrechen", "comment-deleted": "Kommentar gelöscht.", "comment-queued": "Kommentar muss noch freigeschaltet werden.", "comment-anonymous": "Anonym", diff --git a/isso/js/app/i18n/en.js b/isso/js/app/i18n/en.js index 14edf94..0381a63 100644 --- a/isso/js/app/i18n/en.js +++ b/isso/js/app/i18n/en.js @@ -8,9 +8,12 @@ define({ "no-comments": "No Comments Yet", "comment-reply": "Reply", + "comment-edit": "Edit", + "comment-save": "Save", "comment-delete": "Delete", "comment-confirm": "Confirm", "comment-close": "Close", + "comment-cancel": "Cancel", "comment-deleted": "Comment deleted.", "comment-queued": "Comment in queue for moderation.", "comment-anonymous": "Anonymous", diff --git a/isso/js/app/isso.js b/isso/js/app/isso.js index 3ad22e3..1d70010 100644 --- a/isso/js/app/isso.js +++ b/isso/js/app/isso.js @@ -1,24 +1,12 @@ /* Isso – Ich schrei sonst! */ -define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/markup", "app/i18n", "app/lib"], - function(behave, templates, $, utils, api, Mark, i18n, lib) { +define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/markup", "app/i18n", "app/lib", "app/fancy"], + function(behave, templates, $, utils, api, Mark, i18n, lib, fancy) { "use strict"; var msgs = i18n[i18n.lang]; - var toggle = function(el, on, off) { - if (el.classList.contains("off") || ! el.classList.contains("on")) { - el.classList.remove("off"); - el.classList.add("on"); - on(el); - } else { - el.classList.remove("on"); - el.classList.add("off"); - off(el); - } - }; - var Postbox = function(parent) { var el = $.htmlify(Mark.up(templates["postbox"])); @@ -85,16 +73,8 @@ define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/marku }); }); - var textarea = $("textarea", el); - new behave({textarea: textarea}); - - var offset= !window.opera ? (textarea.offsetHeight - textarea.clientHeight) : (textarea.offsetHeight + parseInt(window.getComputedStyle(textarea, null).getPropertyValue('border-top-width'))); - $("textarea", el).on("keyup", function() { - if ((textarea.scrollHeight + offset ) > 48) { - textarea.style.height = "auto"; - textarea.style.height = (textarea.scrollHeight + offset ) + 'px'; - } - }); + fancy.autoresize($("textarea", el), 48); + new behave({textarea: $("textarea", el)}); return el; }; @@ -140,20 +120,17 @@ define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/marku text = $("#isso-" + comment.id + " > .text-wrapper > div.text"); var form = new Postbox(comment.id); - $("a.reply", footer).on("click", function() { - toggle( - $("a.reply", footer), - function(reply) { - footer.insertAfter(form); - $("textarea", form).focus(); - reply.textContent = msgs["comment-close"]; - }, - function(reply) { - form.remove(); - reply.textContent = msgs["comment-reply"]; - } - ); - }); + $("a.reply", footer).toggle("click", + function() { + footer.insertAfter(form); + $("textarea", form).focus(); + $("a.reply", footer).textContent = msgs["comment-close"]; + }, + function() { + form.remove(); + $("a.reply", footer).textContent = msgs["comment-reply"]; + } + ); if (comment.parent !== null) { $("a.parent", header).on("mouseover", function() { @@ -171,7 +148,7 @@ define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/marku span.remove(); return; } else { - footer.prepend($.htmlify('' + value + '')); + footer.prepend($.new("span.votes", value)); } } else { if (value === 0) { @@ -195,11 +172,52 @@ define(["behave", "app/text/html", "app/dom", "app/utils", "app/api", "app/marku }); if (! utils.cookie(comment.id)) { -// $("a.edit", footer).remove(); + $("a.edit", footer).remove(); $("a.delete", footer).remove(); return; } + $("a.edit", footer).toggle("click", + function(toggler) { + var edit = $("a.edit", footer); + + edit.textContent = msgs["comment-save"]; + edit.insertAfter($.new("a.cancel", msgs["comment-cancel"])).on("click", function() { + text.textContent = ""; + text.className = "text"; + text.append(comment.text); + toggler.next(); + }); + + api.view(comment.id, 1).then(function(rv) { + var textarea = $.new("textarea", rv.text); + new behave({textarea: textarea}); + fancy.autoresize(textarea, 48); + text.className = "textarea-wrapper"; + text.textContent = ""; + text.append(textarea); + textarea.focus(); + }); + }, + function(toggler) { + var textarea = $("textarea", text); + if (textarea && textarea.value.length < 3) { + textarea.focus(); + toggler.wait(); + return; + } else if (textarea) { + api.modify(comment.id, {"text": textarea.value}).then(function(rv) { + text.innerHTML = rv.text; + text.className = "text"; + comment.text = rv.text; + }); + } + + $("a.cancel", footer).remove(); + $("a.edit", footer).textContent = msgs["comment-edit"]; + } + ); + $("a.delete", footer).on("click", function() { if ($("a.delete", footer).textContent === msgs["comment-confirm"]) { api.remove(comment.id).then(function(rv) { diff --git a/isso/js/app/text/comment.html b/isso/js/app/text/comment.html index 38f6874..ae09f9a 100644 --- a/isso/js/app/text/comment.html +++ b/isso/js/app/text/comment.html @@ -54,7 +54,7 @@ {{ svg-arrow-down}} {{ i18n-comment-reply }} - + {{ i18n-comment-edit }} {{ i18n-comment-delete }}