| <!-- |
| 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> |