aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/comments/frontend/comments.js
diff options
context:
space:
mode:
Diffstat (limited to 'bootstrap/comments/frontend/comments.js')
-rw-r--r--bootstrap/comments/frontend/comments.js400
1 files changed, 400 insertions, 0 deletions
diff --git a/bootstrap/comments/frontend/comments.js b/bootstrap/comments/frontend/comments.js
new file mode 100644
index 0000000..d8bace6
--- /dev/null
+++ b/bootstrap/comments/frontend/comments.js
@@ -0,0 +1,400 @@
+// Collection of comment parsing functions (comments.js)
+HashOverConstructor.prototype.comments = {
+ collapsedCount: 0,
+ codeOpenRegex: /<code>/i,
+ codeTagRegex: /(<code>)([\s\S]*?)(<\/code>)/ig,
+ preOpenRegex: /<pre>/i,
+ preTagRegex: /(<pre>)([\s\S]*?)(<\/pre>)/ig,
+ lineRegex: /(?:\r\n|\r|\n)/g,
+ codeTagMarkerRegex: /CODE_TAG\[([0-9]+)\]/g,
+ preTagMarkerRegex: /PRE_TAG\[([0-9]+)\]/g,
+
+ // Tags that will have their innerHTML trimmed
+ trimTagRegexes: {
+ blockquote: {
+ test: /<blockquote>/,
+ replace: /(<blockquote>)([\s\S]*?)(<\/blockquote>)/ig
+ },
+
+ ul: {
+ test: /<ul>/,
+ replace: /(<ul>)([\s\S]*?)(<\/ul>)/ig
+ },
+
+ ol: {
+ test: /<ol>/,
+ replace: /(<ol>)([\s\S]*?)(<\/ol>)/ig
+ }
+ },
+
+ // Add comment content to HTML template
+ parse: function (comment, parent, collapse, sort, method, popular)
+ {
+ parent = parent || null;
+ collapse = collapse || false;
+ sort = sort || false;
+ method = method || 'ascending';
+ popular = popular || false;
+
+ // Reference to the parent object
+ var hashover = this.parent;
+
+ var commentKey = comment.permalink;
+ var permalink = 'hashover-' + commentKey;
+ var nameClass = 'hashover-name-plain';
+ var template = { permalink: commentKey };
+ var isReply = (parent !== null);
+ var commentDate = comment.date;
+ var codeTagCount = 0;
+ var codeTags = [];
+ var preTagCount = 0;
+ var preTags = [];
+ var classes = '';
+ var replies = '';
+
+ // Text for avatar image alt attribute
+ var permatext = commentKey.slice (1);
+ permatext = permatext.split ('r');
+ permatext = permatext.pop ();
+
+ // Trims whitespace from an HTML tag's inner HTML
+ function tagTrimmer (fullTag, openTag, innerHTML, closeTag)
+ {
+ return openTag + hashover.EOLTrim (innerHTML) + closeTag;
+ }
+
+ // Get parent comment via permalink
+ if (isReply === false && commentKey.indexOf ('r') > -1) {
+ // Get the parent comment permalink
+ var parentPermalink = hashover.permalinks.getParent (commentKey);
+
+ // Get the parent comment by its permalink
+ parent = hashover.permalinks.getComment (parentPermalink, hashover.instance.comments.primary);
+ isReply = (parent !== null);
+ }
+
+ // Check if this comment is a popular comment
+ if (popular === true) {
+ // Remove "-pop" from text for avatar
+ permatext = permatext.replace ('-pop', '');
+ } else {
+ // Check if comment is a reply
+ if (isReply === true) {
+ // Check that comments are being sorted
+ if (!sort || method === 'ascending') {
+ // Append class to indicate comment is a reply
+ classes += ' hashover-reply';
+ // Append class to indicate odd or even reply for CSS styling
+ if ((commentKey.split("r").length & 1) == 0) {
+ classes += ' odd';
+ } else {
+ classes += ' even';
+ }
+ }
+ }
+
+ // Check if comments are being collapsed
+ if (hashover.setup['collapses-comments'] !== false) {
+ // If so, append class to indicate collapsed comment
+ if (hashover.instance['total-count'] > 0) {
+ if (collapse === true && this.collapsedCount >= hashover.setup['collapse-limit']) {
+ classes += ' hashover-hidden';
+ } else {
+ this.collapsedCount++;
+ }
+ }
+ }
+ }
+
+ // Add avatar image to template
+ template.avatar = hashover.strings.parseTemplate (hashover.ui['user-avatar'], {
+ src: comment.avatar,
+ href: permalink,
+ text: permatext
+ });
+
+ if (comment.notice === undefined) {
+ var name = comment.name || hashover.setup['default-name'];
+ var website = comment.website;
+ var isTwitter = false;
+
+ // Check if user's name is a Twitter handle
+ if (name.charAt (0) === '@') {
+ name = name.slice (1);
+ nameClass = 'hashover-name-twitter';
+ isTwitter = true;
+ var nameLength = name.length;
+
+ // Check if Twitter handle is valid length
+ if (nameLength > 1 && nameLength <= 30) {
+ // Set website to Twitter profile if a specific website wasn't given
+ if (website === undefined) {
+ website = 'http://twitter.com/' + name;
+ }
+ }
+ }
+
+ // Check whether user gave a website
+ if (website !== undefined) {
+ if (isTwitter === false) {
+ nameClass = 'hashover-name-website';
+ }
+
+ // If so, display name as a hyperlink
+ var nameElement = hashover.strings.parseTemplate (hashover.ui['name-link'], {
+ href: website,
+ permalink: commentKey,
+ name: name
+ });
+ } else {
+ // If not, display name as plain text
+ var nameElement = hashover.strings.parseTemplate (hashover.ui['name-span'], {
+ permalink: commentKey,
+ name: name
+ });
+ }
+
+ // Construct thread link
+ if ((comment.url && comment.title) !== undefined) {
+ template['thread-link'] = hashover.strings.parseTemplate (hashover.ui['thread-link'], {
+ url: comment.url,
+ title: comment.title
+ });
+ }
+
+ // Construct parent thread hyperlink
+ if (isReply === true) {
+ var parentThread = 'hashover-' + parent.permalink;
+ var parentName = parent.name || hashover.setup['default-name'];
+
+ // Add thread parent hyperlink to template
+ template['parent-link'] = hashover.strings.parseTemplate (hashover.ui['parent-link'], {
+ parent: parentThread,
+ permalink: commentKey,
+ name: parentName
+ });
+ }
+
+ // Check if the logged in user owns the comment
+ if (comment['user-owned'] !== undefined) {
+ // If so, append class to indicate comment is from logged in user
+ classes += ' hashover-user-owned';
+
+ // Define "Reply" link with original poster title
+ var replyTitle = hashover.locale['commenter-tip'];
+ var replyClass = 'hashover-no-email';
+ } else {
+ // Check if commenter is subscribed
+ if (comment.subscribed === true) {
+ // If so, set subscribed title
+ var replyTitle = name + ' ' + hashover.locale['subscribed-tip'];
+ var replyClass = 'hashover-has-email';
+ } else{
+ // If not, set unsubscribed title
+ var replyTitle = name + ' ' + hashover.locale['unsubscribed-tip'];
+ var replyClass = 'hashover-no-email';
+ }
+ }
+
+ // Check if the comment is editable for the user
+ if (comment['editable'] !== undefined) {
+ // If so, add "Edit" hyperlink to template
+ template['edit-link'] = hashover.strings.parseTemplate (hashover.ui['edit-link'], {
+ href: comment.url || hashover.instance['file-path'],
+ permalink: commentKey
+ });
+ }
+
+ // Add like link and count to template if likes are enabled
+ if (hashover.setup['allows-likes'] !== false) {
+ hashover.optionalMethod ('addRatings', [
+ comment, template, 'like', commentKey
+ ], 'comments');
+ }
+
+ // Add dislike link and count to template if dislikes are enabled
+ if (hashover.setup['allows-dislikes'] !== false) {
+ hashover.optionalMethod ('addRatings', [
+ comment, template, 'dislike', commentKey
+ ], 'comments');
+ }
+
+ // Add name HTML to template
+ template.name = hashover.strings.parseTemplate (hashover.ui['name-wrapper'], {
+ class: nameClass,
+ link: nameElement
+ });
+
+ // Check if user timezones is enabled
+ if (hashover.setup['uses-user-timezone'] !== false) {
+ // If so, get local comment post date
+ var postDate = new Date (comment['sort-date'] * 1000);
+
+ // Check if short date format is enabled
+ if (hashover.setup['uses-short-dates'] !== false) {
+ // If so, get local date
+ var localDate = new Date ();
+
+ // Local comment post date to remove time from
+ var postDateCopy = new Date (postDate.getTime ());
+
+ // And format local time if the comment was posted today
+ if (postDateCopy.setHours (0, 0, 0, 0) === localDate.setHours (0, 0, 0, 0)) {
+ commentDate = hashover.strings.sprintf (hashover.locale['today'], [
+ hashover.dateTime.format (hashover.setup['time-format'], postDate)
+ ]);
+ }
+ } else {
+ // If not, format a long local date/time
+ commentDate = hashover.dateTime.format (hashover.locale['date-time'], postDate);
+ }
+ }
+
+ // Append status text to date
+ if (comment['status-text'] !== undefined) {
+ commentDate += ' (' + comment['status-text'] + ')';
+ }
+
+ // Add date from comment as permalink hyperlink to template
+ template.date = hashover.strings.parseTemplate (hashover.ui['date-link'], {
+ href: comment.url || hashover.instance['file-path'],
+ permalink: permalink,
+ date: commentDate
+ });
+
+ // Add "Reply" hyperlink to template
+ template['reply-link'] = hashover.strings.parseTemplate (hashover.ui['reply-link'], {
+ href: comment.url || hashover.instance['file-path'],
+ permalink: commentKey,
+ class: replyClass,
+ title: replyTitle
+ });
+
+ // Add reply count to template
+ if (comment.replies !== undefined) {
+ template['reply-count'] = comment.replies.length;
+
+ if (template['reply-count'] > 0) {
+ if (template['reply-count'] !== 1) {
+ template['reply-count'] += ' ' + hashover.locale['replies'];
+ } else {
+ template['reply-count'] += ' ' + hashover.locale['reply'];
+ }
+ }
+ }
+
+ // Add HTML anchor tag to URLs
+ var body = comment.body.replace (hashover.regex.links, '<a href="$1" rel="noopener noreferrer" target="_blank">$1</a>');
+
+ // Replace [img] tags with external image placeholder if enabled
+ body = body.replace (hashover.regex.imageTags, function (fullURL, url) {
+ // Check if embedded images are enabled
+ if (hashover.setup['allows-images'] !== false) {
+ return hashover.optionalMethod ('embedImage', [ url ], 'comments');
+ }
+
+ // Convert image URL into an anchor tag
+ return '<a href="' + url + '" rel="noopener noreferrer" target="_blank">' + url + '</a>';
+ });
+
+ // Parse markdown in comment if enabled
+ if (hashover.markdown !== undefined) {
+ body = hashover.markdown.parse (body);
+ }
+
+ // Check for code tags
+ if (this.codeOpenRegex.test (body) === true) {
+ // Replace code tags with marker text
+ body = body.replace (this.codeTagRegex, function (fullTag, openTag, innerHTML, closeTag) {
+ var codeMarker = openTag + 'CODE_TAG[' + codeTagCount + ']' + closeTag;
+
+ codeTags[codeTagCount] = hashover.EOLTrim (innerHTML);
+ codeTagCount++;
+
+ return codeMarker;
+ });
+ }
+
+ // Check for pre tags
+ if (this.preOpenRegex.test (body) === true) {
+ // Replace pre tags with marker text
+ body = body.replace (this.preTagRegex, function (fullTag, openTag, innerHTML, closeTag) {
+ var preMarker = openTag + 'PRE_TAG[' + preTagCount + ']' + closeTag;
+
+ preTags[preTagCount] = hashover.EOLTrim (innerHTML);
+ preTagCount++;
+
+ return preMarker;
+ });
+ }
+
+ // Check for various multi-line tags
+ for (var trimTag in this.trimTagRegexes) {
+ if (this.trimTagRegexes.hasOwnProperty (trimTag) === true
+ && this.trimTagRegexes[trimTag]['test'].test (body) === true)
+ {
+ // Trim whitespace
+ body = body.replace (this.trimTagRegexes[trimTag]['replace'], tagTrimmer);
+ }
+ }
+
+ // Break comment into paragraphs
+ var paragraphs = body.split (hashover.regex.paragraphs);
+ var pdComment = '';
+
+ // Wrap comment in paragraph tag
+ // Replace single line breaks with break tags
+ for (var i = 0, il = paragraphs.length; i < il; i++) {
+ pdComment += '<p>' + paragraphs[i].replace (this.lineRegex, '<br>') + '</p>' + hashover.setup['server-eol'];
+ }
+
+ // Replace code tag markers with original code tag HTML
+ if (codeTagCount > 0) {
+ pdComment = pdComment.replace (this.codeTagMarkerRegex, function (marker, number) {
+ return codeTags[number];
+ });
+ }
+
+ // Replace pre tag markers with original pre tag HTML
+ if (preTagCount > 0) {
+ pdComment = pdComment.replace (this.preTagMarkerRegex, function (marker, number) {
+ return preTags[number];
+ });
+ }
+
+ // Add comment data to template
+ template.comment = pdComment;
+ } else {
+ // Append notice class
+ classes += ' hashover-notice ' + comment['notice-class'];
+
+ // Add notice to template
+ template.comment = comment.notice;
+
+ // Add name HTML to template
+ template.name = hashover.strings.parseTemplate (hashover.ui['name-wrapper'], {
+ class: nameClass,
+ link: comment.title
+ });
+ }
+
+ // Comment HTML template
+ var html = hashover.strings.parseTemplate (hashover.ui['theme'], template);
+
+ // Recursively parse replies
+ if (comment.replies !== undefined) {
+ for (var reply = 0, total = comment.replies.length; reply < total; reply++) {
+ replies += this.parse (comment.replies[reply], comment, collapse);
+ }
+ }
+
+ // Wrap comment HTML
+ var wrapper = hashover.strings.parseTemplate (hashover.ui['comment-wrapper'], {
+ permalink: permalink,
+ class: classes,
+ html: html + replies
+ });
+
+ return wrapper;
+ }
+};