implement comment editing
This commit is contained in:
parent
6242d243c9
commit
0b8cac7f18
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
21
isso/js/app/fancy.js
Normal file
21
isso/js/app/fancy.js
Normal file
@ -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
|
||||
};
|
||||
});
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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('<span class="votes">' + value + '</span>'));
|
||||
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) {
|
||||
|
@ -54,7 +54,7 @@
|
||||
<a class="downvote" href="#"><i>{{ svg-arrow-down}}</i></a>
|
||||
|
||||
<a class="reply" href="#">{{ i18n-comment-reply }}</a>
|
||||
<!--<a class="edit" href="#">Bearbeiten</a>-->
|
||||
<a class="edit" href="#">{{ i18n-comment-edit }}</a>
|
||||
<a class="delete" href="#">{{ i18n-comment-delete }}</a>
|
||||
</footer>
|
||||
<div class="isso-follow-up">
|
||||
|
@ -28,6 +28,10 @@ require(["ready", "app/api", "app/isso", "app/count", "app/dom", "app/markup"],
|
||||
}
|
||||
}).fail(function(err) {
|
||||
console.log(err);
|
||||
}).done(function() {
|
||||
if (window.location.hash.length > 0) {
|
||||
$(window.location.hash).scrollIntoView();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
@ -85,7 +85,7 @@ def new(app, environ, request, uri):
|
||||
|
||||
# save checksum of text into cookie, so mallory can't modify/delete a comment, if
|
||||
# he add a comment, then removed it but not the signed cookie.
|
||||
checksum = hashlib.md5(rv["text"]).hexdigest()
|
||||
checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
|
||||
|
||||
rv["text"] = app.markdown(rv["text"])
|
||||
rv["hash"] = pbkdf2(rv.get('email') or rv['remote_addr'], app.SALT, 1000, 6)
|
||||
@ -126,7 +126,7 @@ def single(app, environ, request, id):
|
||||
abort(403)
|
||||
|
||||
# verify checksum, mallory might skip cookie deletion when he deletes a comment
|
||||
if rv[1] != hashlib.md5(app.db.comments.get(id)["text"]).hexdigest():
|
||||
if rv[1] != hashlib.md5(app.db.comments.get(id)["text"].encode('utf-8')).hexdigest():
|
||||
abort(403)
|
||||
|
||||
if request.method == 'PUT':
|
||||
@ -152,7 +152,7 @@ def single(app, environ, request, id):
|
||||
for key in set(rv.keys()) - FIELDS:
|
||||
rv.pop(key)
|
||||
|
||||
checksum = hashlib.md5(rv["text"]).hexdigest()
|
||||
checksum = hashlib.md5(rv["text"].encode('utf-8')).hexdigest()
|
||||
rv["text"] = app.markdown(rv["text"])
|
||||
|
||||
resp = Response(json.dumps(rv), 200, content_type='application/json')
|
||||
|
Loading…
Reference in New Issue
Block a user