blob: f76ae862de26b76a5b8f04ca68372d49899e74b4 [file] [log] [blame]
<!--
The common.js file must be included before this file.
This in an HTML Import-able file that contains the definition
of the following elements:
<comments-sk>
To use this file import it:
<link href="/res/imp/comments-sk.html" rel="import" />
Usage:
<comments-sk
comments="[[myComments]]"
add_comment_url="/json/comments">
</comments-sk>
Properties:
add_comment_url - Where to send POST and DELETE requests for adding and
deleting comments.
allow_add_comment - Whether or not to allow the addition of comments.
allow_empty_comments - Whether or not to allow empty comments.
allow_delete_comment - Whether or not to allow deletion of comments.
collapsible - Whether or not long lists of comments may be collapsed and
display a "more" link.
collapse_threshold - If 'collapsible' is true and there are more than this
many comments, the comment list will be collapsed.
comments - List of comment objects to display. Each comment is expected to
have 'id', 'user', 'time', and 'message' properties.
extra_fields - Additional fields which are part of a comment. Array of
objects with attributes 'name', 'label', and 'type'. Valid values of
'type' are 'bool' and 'text'.
Methods:
None.
Events:
submit - When a comment is added, the "submit" event is triggered. New
comments do not appear in the list because it is not possible to
guarantee consistency between the UI and the database until the comment
has been inserted. Therefore, the "submit" event may be used to reload
the comments from the database.
-->
<link rel="stylesheet" href="/res/common/css/md.css">
<link rel="import" href="human-date-sk.html">
<link rel="import" href="linkify-sk.html">
<link rel="import" href="/res/imp/bower_components/polymer/polymer.html">
<link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html">
<link rel="import" href="/res/imp/bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html">
<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html">
<dom-module id="comments-sk">
<style>
.viewlink {
font-size: 0.7em;
}
table {
width: 100%;
@apply(--comments-sk-style);
}
td {
padding: 5px;
white-space: nowrap;
width: 10px;
}
td.commentMessage {
white-space: normal;
width: 100%;
}
th {
text-align: left;
white-space: nowrap;
}
</style>
<template>
<table id="table">
<template is="dom-if" if="[[_showHeader(comments)]]">
<tr>
<th>Time</th>
<th>User</th>
<th>Message</th>
<template is="dom-repeat" items="[[extra_fields]]" as="field">
<th>[[field.label]]</th>
</template>
</tr>
</template>
<template is="dom-if" if="[[_canCollapse]]">
<template is="dom-if" if="[[_collapsed]]">
<td colspan="3" class="viewlink">
<a href="#" onclick="return false;" on-click="_uncollapse">... view <span>[[_numMoreComments(collapse_threshold, comments)]]</span> more comments</a>
</td>
</template>
<template is="dom-if" if="[[_not(_collapsed)]]">
<td colspan="3" class="viewlink">
<a href="#" onclick="return false;" on-click="_collapse">... view fewer comments</a>
</td>
</template>
</template>
<template is="dom-repeat" items="[[comments]]" as="comment" index-as="index">
<template is="dom-if" if="[[_showThisComment(collapse_threshold, _collapsed, comments, index)]]">
<tr class="comment">
<td><human-date-sk date="[[comment.time]]" diff seconds></human-date-sk> ago</td>
<td>[[comment.user]]</td>
<td class="commentMessage"><linkify-sk text="[[comment.message]]"></linkify-sk></td>
<template is="dom-repeat" items="[[extra_fields]]" as="field">
<td>
<template is="dom-if" if="[[_fieldIsBool(field)]]">
<template is="dom-if" if="[[_get(comment, field)]]">
<iron-icon icon="icons:check"></iron-icon>
</template>
<template is="dom-if" if="[[!_get(comment, field)]]">
<iron-icon icon="icons:check-box-outline-blank"></iron-icon>
</template>
</template>
<template is="dom-if" if="[[_fieldIsText(field)]]">
<span>[[_get(comment, field)]]</span>
</template>
</td>
</template>
<template is="dom-if" if="[[_canDeleteComment]]">
<td>
<paper-icon-button class="clickable" icon="icons:clear" x-data="[[comment.id]]" on-click="_deleteComment"></paper-icon-button>
</td>
</template>
</tr>
</template>
</template>
<template is="dom-if" if="[[_canAddComment]]">
<tr>
<td colspan="3">
<paper-input id="commentBox" label="Comment" value="{{_commentText}}" style="width: 100%">
</paper-input>
</td>
<template is="dom-repeat" items="[[extra_fields]]" as="field">
<td>
<template is="dom-if" if="[[_fieldIsBool(field)]]">
<paper-checkbox id="[[_computeId(field)]]">
[[field.label]]
</paper-checkbox>
</template>
<template is="dom-if" if="[[_fieldIsText(field)]]">
<paper-input id="[[_computeId(field)]]" label="{{field.label}}" no-label-float>
</paper-input>
</template>
</td>
</template>
<td>
<button class="clickable" on-click="_addComment">submit</button>
</td>
</tr>
</template>
</table>
</template>
<script>
(function() {
Polymer({
is: "comments-sk",
properties: {
/**
* Where to send POST and DELETE requests for adding and
* deleting comments.
*/
add_comment_url: {
type: String,
value: "",
},
/**
* Whether or not to allow the addition of comments.
*/
allow_add_comment: {
type: Boolean,
value: false,
},
/**
* Whether or not to allow empty comments.
*/
allow_empty_comments: {
type: Boolean,
value: false,
},
/**
* Whether or not to allow deletion of comments.
*/
allow_delete_comment: {
type: Boolean,
value: false,
},
/**
* Whether or not long lists of comments may be collapsed and
* display a "more" link.
*/
collapsible: {
type: Boolean,
value: false,
},
/**
* If 'collapsible' is true and there are more than this
* many comments, the comment list will be collapsed.
*/
collapse_threshold: {
type: Number,
value: 3,
},
/**
* List of comment objects to display. Each comment is expected to
* have 'id', 'user', 'time', and 'message' properties.
*/
comments: {
type: Array,
value: function() { return []; },
},
/**
* Additional fields which are part of a comment. Array of
* objects with attributes 'name', 'label', and 'type'. Valid values of
* 'type' are 'bool' and 'text'.
*/
extra_fields: {
type: Array,
value: function() { return []; },
},
_canAddComment: {
type: Boolean,
computed: "_computeCanAddComment(_editRights, allow_add_comment)",
},
_canCollapse: {
type: Boolean,
computed: "_computeCanCollapse(collapsible, collapse_threshold, comments)",
},
_canDeleteComment: {
type: Boolean,
computed: "_computeCanDeleteComment(_editRights, allow_delete_comment)",
},
_collapsed: {
type: Boolean,
computed: "_computeCollapsed(_canCollapse, _wantCollapsed)",
},
_editRights: {
type: Boolean,
value: false,
},
_wantCollapsed: {
type: Boolean,
value: true,
},
},
ready: function() {
sk.Login.then(function (status) {
if (status.IsEditor) {
this._editRights = true;
} else {
if (status["Email"] && status["Email"].endsWith("@google.com")) {
this._editRights = true;
}
}
}.bind(this));
},
_collapse: function() {
this._wantCollapsed = true;
},
_uncollapse: function() {
this._wantCollapsed = false;
},
_addComment: function() {
if (!this.allow_empty_comments && !this._commentText) {
sk.errorMessage("Empty comments are not allowed.");
return;
}
var params = {};
for (var i = 0; i < this.extra_fields.length; i++) {
var field = this.extra_fields[i];
var valElem = this.$$("#extraField_" + field.name);
var value = null;
if (field.type == "bool") {
value = valElem.checked;
} else {
value = valElem.value;
}
params[field.name] = value;
}
params.comment = this._commentText;
this._disableUI();
sk.post(this.add_comment_url, JSON.stringify(params)).then(function (resp) {
this._commentText = "";
for (var i = 0; i < this.extra_fields.length; i++) {
var field = this.extra_fields[i];
var valElem = this.$$("#extraField_" + field.name);
if (field.type == "bool") {
valElem.checked = false;
} else {
valElem.value = "";
}
}
this._enableUI();
this.dispatchEvent(new CustomEvent("submit", null));
}.bind(this), function (err) {
this._enableUI();
sk.errorMessage(err);
}.bind(this));
},
_deleteComment: function(e, d, t) {
var commentId = e.model.comment.id;
this._disableUI();
sk.delete(this.add_comment_url + "/" + commentId).then(function (resp) {
this._enableUI();
this.dispatchEvent(new CustomEvent("submit", null));
}.bind(this), function (err) {
this._enableUI();
sk.errorMessage(err);
}.bind(this));
},
_disableUI: function() {
var elements = this.$.table.getElementsByClassName("clickable");
for (var i = 0; i< elements.length; i++) {
elements[i].disabled = true;
}
},
_enableUI: function() {
var elements = this.$.table.getElementsByClassName("clickable");
for (var i = 0; i< elements.length; i++) {
elements[i].disabled = false;
}
},
_not: function(val) {
return !val;
},
_computeCanCollapse: function(collapsible, collapse_threshold, comments) {
return collapsible && comments.length > collapse_threshold;
},
_computeCanAddComment: function(editRights, allow_add_comment) {
return editRights && allow_add_comment;
},
_showHeader: function(comments) {
return comments.length > 0;
},
_showThisComment: function(collapse_threshold, collapsed, comments, index) {
if (index >= comments.length) {
// For some reason, this happens when elements are deleted.
return false;
}
return (!collapsed || index >= comments.length - collapse_threshold) && (comments[index].time && comments[index].user);
},
_computeCanDeleteComment: function(editRights, allow_delete_comment) {
return editRights && allow_delete_comment;
},
_computeCollapsed: function(canCollapse, wantCollapse) {
return canCollapse && wantCollapse;
},
_fieldIsBool: function(field) {
return field.type == "bool";
},
_fieldIsText: function(field) {
return field.type == "text";
},
_get: function(comment, field) {
return comment[field.name];
},
_computeId: function(field) {
return "extraField_" + field.name;
},
_numMoreComments: function(collapse_threshold, comments) {
return comments.length - collapse_threshold;
},
});
})();
</script>
</dom-module>