aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/comments/api
diff options
context:
space:
mode:
authorThedro Neely <thedroneely@gmail.com>2018-08-30 04:40:53 -0400
committerThedro Neely <thedroneely@gmail.com>2018-08-30 04:40:53 -0400
commit2659205908bd5cab508f4ff817123673e078ab74 (patch)
treee455f36b124e9b98f7005e6d4310b71881734623 /bootstrap/comments/api
downloadedwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.tar.gz
edwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.tar.bz2
edwinmattiacci.com-2659205908bd5cab508f4ff817123673e078ab74.zip
Initialize Repo: First Commit
Diffstat (limited to 'bootstrap/comments/api')
-rw-r--r--bootstrap/comments/api/backend/count-link-ajax.php76
-rw-r--r--bootstrap/comments/api/backend/latest-ajax.php207
-rw-r--r--bootstrap/comments/api/count-link.php95
-rw-r--r--bootstrap/comments/api/frontends/count-link/constructor.js43
-rw-r--r--bootstrap/comments/api/frontends/count-link/getcommentcount.js19
-rw-r--r--bootstrap/comments/api/frontends/count-link/instantiate.js4
-rw-r--r--bootstrap/comments/api/frontends/latest/addcontrols.js22
-rw-r--r--bootstrap/comments/api/frontends/latest/addratings.js22
-rw-r--r--bootstrap/comments/api/frontends/latest/constructor.js79
-rw-r--r--bootstrap/comments/api/frontends/latest/init.js37
-rw-r--r--bootstrap/comments/api/frontends/latest/instantiate.js4
-rw-r--r--bootstrap/comments/api/json.php76
-rw-r--r--bootstrap/comments/api/latest.php154
-rw-r--r--bootstrap/comments/api/rss.php327
14 files changed, 1165 insertions, 0 deletions
diff --git a/bootstrap/comments/api/backend/count-link-ajax.php b/bootstrap/comments/api/backend/count-link-ajax.php
new file mode 100644
index 0000000..bf53312
--- /dev/null
+++ b/bootstrap/comments/api/backend/count-link-ajax.php
@@ -0,0 +1,76 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Change to the HashOver directory
+chdir (realpath ('../../'));
+
+// Check if request is for JSONP
+if (isset ($_GET['jsonp'])) {
+ // If so, setup HashOver for JavaScript
+ require ('backend/javascript-setup.php');
+} else {
+ // If not, setup HashOver for JSON
+ require ('backend/json-setup.php');
+}
+
+try {
+ // Instantiate HashOver class
+ $hashover = new \HashOver ('json', 'api');
+ $hashover->setup->setPageURL ('request');
+ $hashover->initiate ();
+ $hashover->finalize ();
+
+ // Throw exception if the "Latest Comments" API is disabled
+ if ($hashover->setup->apiStatus ('count-link') === 'disabled') {
+ throw new \Exception ('This API is not enabled.');
+ }
+
+ // Count response array
+ $data = array (
+ 'primary-count' => $hashover->thread->primaryCount - 1,
+ 'total-count' => $hashover->thread->totalCount - 1
+ );
+
+ // Check if there are any comments
+ if ($hashover->thread->totalCount > 1) {
+ // If so, set the count link text to the comment count
+ $data['link-text'] = $hashover->getCommentCount ();
+ } else {
+ // If not, set the count link text to "Post Comment"
+ $data['link-text'] = $hashover->locale->text['post-button'];
+ }
+
+ // Generate statistics
+ $hashover->statistics->executionEnd ();
+
+ // HashOver statistics
+ $data['statistics'] = array (
+ 'execution-time' => $hashover->statistics->executionTime,
+ 'script-memory' => $hashover->statistics->scriptMemory,
+ 'system-memory' => $hashover->statistics->systemMemory
+ );
+
+ // Encode JSON data
+ echo $hashover->misc->jsonData ($data);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('json');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/api/backend/latest-ajax.php b/bootstrap/comments/api/backend/latest-ajax.php
new file mode 100644
index 0000000..964fe34
--- /dev/null
+++ b/bootstrap/comments/api/backend/latest-ajax.php
@@ -0,0 +1,207 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Change to the HashOver directory
+chdir (realpath ('../../'));
+
+// Check if request is for JSONP
+if (isset ($_GET['jsonp'])) {
+ // If so, setup HashOver for JavaScript
+ require ('backend/javascript-setup.php');
+} else {
+ // If not, setup HashOver for JSON
+ require ('backend/json-setup.php');
+}
+
+try {
+ // Instantiate HashOver class
+ $hashover = new \HashOver ('json', 'api');
+ $hashover->setup->setThreadName ('request');
+ $hashover->initiate ();
+ $hashover->finalize ();
+
+ // Throw exception if the "Latest Comments" API is disabled
+ if ($hashover->setup->apiStatus ('latest') === 'disabled') {
+ throw new \Exception ('This API is not enabled.');
+ }
+
+ // Comments and statistics response array
+ $data = array ();
+
+ // Add locales to data
+ $data['locale'] = array (
+ 'date-time' => $hashover->locale->text['date-time'],
+ 'dislike' => $hashover->locale->text['dislike'],
+ 'external-image-tip' => $hashover->locale->text['external-image-tip'],
+ 'like' => $hashover->locale->text['like'],
+ 'today' => $hashover->locale->text['date-today'],
+ 'commenter-tip' => $hashover->locale->text['commenter-tip'],
+ 'subscribed-tip' => $hashover->locale->text['subscribed-tip'],
+ 'unsubscribed-tip' => $hashover->locale->text['unsubscribed-tip'],
+ 'replies' => $hashover->locale->text['replies'],
+ 'reply' => $hashover->locale->text['reply'],
+ 'loading' => $hashover->locale->text['loading'],
+ 'click-to-close' => $hashover->locale->text['click-to-close'],
+ 'day-names' => $hashover->locale->text['date-day-names'],
+ 'month-names' => $hashover->locale->text['date-month-names']
+ );
+
+ // Add setup information to data
+ $data['setup'] = array (
+ 'server-eol' => PHP_EOL,
+ 'default-name' => $hashover->setup->defaultName,
+ 'user-is-logged-in' => $hashover->login->userIsLoggedIn,
+ 'time-format' => $hashover->setup->timeFormat,
+ 'image-extensions' => $hashover->setup->imageTypes,
+ 'image-placeholder' => $hashover->setup->getImagePath ('place-holder'),
+ 'theme-css' => $hashover->setup->getThemePath ('latest.css'),
+ 'device-type' => ($hashover->setup->isMobile === true) ? 'mobile' : 'desktop',
+ 'uses-user-timezone' => $hashover->setup->usesUserTimezone,
+ 'uses-short-dates' => $hashover->setup->usesShortDates,
+ 'allows-images' => $hashover->setup->allowsImages,
+ 'uses-markdown' => $hashover->setup->usesMarkdown
+ );
+
+ // Add UI HTML to data
+ $data['ui'] = array (
+ 'user-avatar' => $hashover->ui->userAvatar (),
+ 'name-link' => $hashover->ui->nameElement ('a'),
+ 'name-span' => $hashover->ui->nameElement ('span'),
+ 'thread-link' => $hashover->ui->threadLink (),
+ 'reply-link' => $hashover->ui->formLink ('{{href}}', 'reply'),
+ 'like-count' => $hashover->ui->likeCount ('likes'),
+ 'dislike-count' => $hashover->ui->likeCount ('dislikes'),
+ 'name-wrapper' => $hashover->ui->nameWrapper (),
+ 'date-link' => $hashover->ui->dateLink (),
+ 'comment-wrapper' => $hashover->ui->commentWrapper (),
+ 'theme' => $hashover->templater->parseTheme ('latest.html')
+ );
+
+ // Attempt to get comment thread from GET/POST data
+ $get_thread = $hashover->setup->getRequest ('thread', 'auto');
+
+ // Check if we're getting metadata for a specific thread
+ if ($get_thread !== 'auto') {
+ // If so, attempt to read thread-specific latest comments metadata
+ $latest = $hashover->thread->data->readMeta ('latest-comments', $get_thread);
+ } else {
+ // If not, attempt to read global latest comments metadata
+ $latest = $hashover->thread->data->readMeta ('latest-comments', 'auto', true);
+ }
+
+ // Check if the latest comments read successfully
+ if ($latest !== false) {
+ // If so, reduce number of latest comments to configured limit
+ $latest = array_slice ($latest, 0, $hashover->setup->latestMax);
+ } else {
+ // If not, set to empty array
+ $latest = array ();
+ }
+
+ // Latest comments
+ $comments = array ();
+
+ // Run through the latest comments
+ foreach ($latest as $item) {
+ // Get comment key
+ $key = basename ($item);
+ $key_parts = explode ('-', $key);
+
+ // Decide proper thread
+ $thread = ($get_thread === 'auto') ? dirname ($item) : $get_thread;
+
+ // Attempt to read page information metadata
+ $page_info = $hashover->thread->data->readMeta ('page-info', $thread);
+
+ // Attempt to read comment
+ $raw = $hashover->thread->data->read ($key, $thread);
+
+ // Skip failed or unapproved comments or missing metadata
+ if ((!empty ($raw['status']) and $raw['status'] !== 'approved')
+ or ($raw and $page_info) === false)
+ {
+ continue;
+ }
+
+ // Parse comment
+ $comment = $hashover->commentParser->parse ($raw, $key, $key_parts);
+
+ // Merge comment with page metadata
+ $comment = array_merge ($page_info, $comment);
+
+ // Trim comment body to configurable length
+ if ($hashover->setup->latestTrimWidth > 0) {
+ // Instantiate WriteComments
+ //
+ // TODO: Split WriteComments into multiple classes so we
+ // can instantiate only the functionality that we need
+ //
+ $write_comments = new WriteComments (
+ $hashover->setup,
+ $hashover->thread
+ );
+
+ // Shorthands
+ $trim_length = $hashover->setup->latestTrimWidth;
+ $close_tags = $write_comments->closeTags;
+
+ // Add <code> to list of tags to close
+ $write_comments->closeTags[] = 'code';
+
+ // Trim the comment to configurable length
+ $body = rtrim (mb_strimwidth ($comment['body'], 0, $trim_length, '...'));
+
+ // Close any tags that may have had their endings trimmed off
+ $body = $write_comments->tagCloser ($close_tags, $body);
+
+ // Escape any HTML tags that may have been trimmed in half
+ $body = $write_comments->htmlSelectiveEscape ($body);
+
+ // Update the comment
+ $comment['body'] = $body;
+ }
+
+ // Add comment to response array
+ $comments[] = $comment;
+ }
+
+ // HashOver instance information
+ $data['instance'] = array (
+ 'comments' => array ('primary' => $comments),
+ 'total-count' => count ($comments)
+ );
+
+ // Generate statistics
+ $hashover->statistics->executionEnd ();
+
+ // HashOver statistics
+ $data['statistics'] = array (
+ 'execution-time' => $hashover->statistics->executionTime,
+ 'script-memory' => $hashover->statistics->scriptMemory,
+ 'system-memory' => $hashover->statistics->systemMemory
+ );
+
+ // Encode JSON data
+ echo $hashover->misc->jsonData ($data);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('json');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/api/count-link.php b/bootstrap/comments/api/count-link.php
new file mode 100644
index 0000000..f3da768
--- /dev/null
+++ b/bootstrap/comments/api/count-link.php
@@ -0,0 +1,95 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Change to the HashOver directory
+chdir (realpath ('../'));
+
+// Setup HashOver for JavaScript
+require ('backend/javascript-setup.php');
+
+try {
+ // Instantiate general setup class
+ $setup = new Setup (array (
+ 'mode' => 'javascript',
+ 'context' => 'api'
+ ));
+
+ // Throw exception if the "Latest Comments" API is disabled
+ if ($setup->apiStatus ('count-link') === 'disabled') {
+ throw new \Exception ('This API is not enabled.');
+ }
+
+ // Instantiate HashOver statistics class
+ $statistics = new Statistics ('javascript');
+
+ // Start execution timer
+ $statistics->executionStart ();
+
+ // Instantiate JavaScript build class
+ $javascript = new JavaScriptBuild ('api/frontends/count-link');
+
+ // Register initial constructor
+ $javascript->registerFile ('constructor.js');
+
+ // Register comment count AJAX request getter method
+ $javascript->registerFile ('getcommentcount.js');
+
+ // Change to standard frontend directory
+ $javascript->changeDirectory ('frontend');
+
+ // Register HashOver script tag getter method
+ $javascript->registerFile ('script.js');
+
+ // Register backend path setter
+ $javascript->registerFile ('backendpath.js');
+
+ // Register HashOver ready state detection method
+ $javascript->registerFile ('onready.js');
+
+ // Register element creation methods
+ $javascript->registerFile ('elements.js');
+
+ // Register AJAX-related methods
+ $javascript->registerFile ('ajax.js');
+
+ // Change back to count link frontend directory
+ $javascript->changeDirectory ('api/frontends/count-link');
+
+ // Register automatic instantiation code
+ $javascript->registerFile ('instantiate.js', array (
+ 'include' => !isset ($_GET['nodefault'])
+ ));
+
+ // JavaScript build process output
+ $output = $javascript->build (
+ $setup->minifiesJavascript,
+ $setup->minifyLevel
+ );
+
+ // Display JavaScript build process output
+ echo $output, PHP_EOL;
+
+ // Display statistics
+ echo $statistics->executionEnd ();
+
+} catch (\Exception $error) {
+ $misc = new Misc ('javascript');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/api/frontends/count-link/constructor.js b/bootstrap/comments/api/frontends/count-link/constructor.js
new file mode 100644
index 0000000..7473788
--- /dev/null
+++ b/bootstrap/comments/api/frontends/count-link/constructor.js
@@ -0,0 +1,43 @@
+// @licstart The following is the entire license notice for the
+// JavaScript code in this page.
+//
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+//
+// @licend The above is the entire license notice for the
+// JavaScript code in this page.
+
+"use strict";
+
+// Count link API frontend constructor (constructor.js)
+function HashOverCountLink ()
+{
+ // Get count link class elements
+ var countLinks = document.getElementsByClassName ('hashover-count-link');
+
+ // Run through the count links
+ for (var i = 0, il = countLinks.length; i < il; i++) {
+ var link = countLinks[i];
+
+ // Instantiate new count link object
+ this.getCommentCount (link, {
+ url: link.href
+ });
+ }
+};
+
+// Constructor to add HashOver methods to
+var HashOverConstructor = HashOverCountLink;
diff --git a/bootstrap/comments/api/frontends/count-link/getcommentcount.js b/bootstrap/comments/api/frontends/count-link/getcommentcount.js
new file mode 100644
index 0000000..536824c
--- /dev/null
+++ b/bootstrap/comments/api/frontends/count-link/getcommentcount.js
@@ -0,0 +1,19 @@
+// Send AJAX request to backend for a comment count (getcommentcount.js)
+HashOverCountLink.prototype.getCommentCount = function (link, options)
+{
+ // Reference to this HashOver object
+ var hashover = this;
+
+ // Get backend queries
+ var queries = ['url=' + options.url];
+
+ // Backend request path
+ var requestPath = '/countlinkajax';
+
+ // Handle backend request
+ this.ajax ('POST', requestPath, queries, function (json) {
+ if (json['link-text'] !== undefined) {
+ link.textContent = json['link-text'];
+ }
+ }, true);
+};
diff --git a/bootstrap/comments/api/frontends/count-link/instantiate.js b/bootstrap/comments/api/frontends/count-link/instantiate.js
new file mode 100644
index 0000000..9329ca6
--- /dev/null
+++ b/bootstrap/comments/api/frontends/count-link/instantiate.js
@@ -0,0 +1,4 @@
+// Instantiate after the DOM is parsed (instantiate.js)
+HashOverCountLink.onReady (function () {
+ window.hashoverCountLink = new HashOverCountLink ();
+});
diff --git a/bootstrap/comments/api/frontends/latest/addcontrols.js b/bootstrap/comments/api/frontends/latest/addcontrols.js
new file mode 100644
index 0000000..b5642f1
--- /dev/null
+++ b/bootstrap/comments/api/frontends/latest/addcontrols.js
@@ -0,0 +1,22 @@
+// Add various events to various elements in each comment (addcontrols.js)
+HashOverLatest.prototype.addControls = function (json, popular)
+{
+ // Reference to this object
+ var hashover = this;
+
+ // Get permalink from JSON object
+ var permalink = json.permalink;
+
+ // Set onclick functions for external images
+ if (this.setup['allows-images'] !== false) {
+ // Get embedded image elements
+ var embeddedImgs = document.getElementsByClassName ('hashover-embedded-image');
+
+ for (var i = 0, il = embeddedImgs.length; i < il; i++) {
+ embeddedImgs[i].onclick = function ()
+ {
+ hashover.openEmbeddedImage (this);
+ };
+ }
+ }
+};
diff --git a/bootstrap/comments/api/frontends/latest/addratings.js b/bootstrap/comments/api/frontends/latest/addratings.js
new file mode 100644
index 0000000..b4d7c8e
--- /dev/null
+++ b/bootstrap/comments/api/frontends/latest/addratings.js
@@ -0,0 +1,22 @@
+// Add Like/Dislike link and count to template (addratings.js)
+HashOverLatest.prototype.comments.addRatings = function (comment, template, action, commentKey)
+{
+ // Reference to the parent object
+ var hashover = this.parent;
+
+ // Check if the comment has been likes/dislikes
+ if (comment[action + 's'] !== undefined) {
+ // Add likes/dislikes to HTML template
+ template[action + 's'] = comment[action + 's'];
+
+ // Get "X Like/Dislike(s)" locale
+ var plural = (comment[action + 's'] === 1 ? 0 : 1);
+ var count = comment[action + 's'] + ' ' + hashover.locale[action][plural];
+ }
+
+ // Add like count to HTML template
+ template[action + '-count'] = hashover.strings.parseTemplate (hashover.ui[action + '-count'], {
+ permalink: commentKey,
+ text: count || ''
+ });
+};
diff --git a/bootstrap/comments/api/frontends/latest/constructor.js b/bootstrap/comments/api/frontends/latest/constructor.js
new file mode 100644
index 0000000..32c1880
--- /dev/null
+++ b/bootstrap/comments/api/frontends/latest/constructor.js
@@ -0,0 +1,79 @@
+// @licstart The following is the entire license notice for the
+// JavaScript code in this page.
+//
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+//
+// @licend The above is the entire license notice for the
+// JavaScript code in this page.
+
+"use strict";
+
+// Latest comments API frontend constructor (constructor.js)
+function HashOverLatest (options)
+{
+ // Reference to this HashOver object
+ var hashover = this;
+
+ // Backend queries
+ if (options && options.thread !== undefined) {
+ var queries = ['thread=' + encodeURIComponent (options.thread)];
+ } else {
+ var queries = [];
+ }
+
+ // Backend request path
+ var requestPath = '/latestajax';
+
+ // Handle backend request
+ this.ajax ('POST', requestPath, queries, function (json) {
+ // Handle error messages
+ if (json.message !== undefined) {
+ hashover.displayError (json, 'hashover-widget');
+ return;
+ }
+
+ // Locales from HashOver backend
+ HashOverLatest.prototype.locale = json.locale;
+
+ // Setup information from HashOver back-end
+ HashOverLatest.prototype.setup = json.setup;
+
+ // UI HTML from HashOver back-end
+ HashOverLatest.prototype.ui = json.ui;
+
+ // Thread information from HashOver back-end
+ hashover.instance = json.instance;
+
+ // Backend execution time and memory usage statistics
+ hashover.statistics = json.statistics;
+
+ // Initiate HashOver latest comments
+ hashover.init ();
+ }, true);
+
+ // Add parent proterty to all prototype objects
+ for (var name in this) {
+ var value = this[name];
+
+ if (value && value.constructor === Object) {
+ value.parent = this;
+ }
+ }
+};
+
+// Constructor to add HashOver methods to
+var HashOverConstructor = HashOverLatest;
diff --git a/bootstrap/comments/api/frontends/latest/init.js b/bootstrap/comments/api/frontends/latest/init.js
new file mode 100644
index 0000000..77185cd
--- /dev/null
+++ b/bootstrap/comments/api/frontends/latest/init.js
@@ -0,0 +1,37 @@
+// HashOver latest comments UI initialization process (init.js)
+HashOverLatest.prototype.init = function ()
+{
+ // Shorthand
+ var comments = this.instance.comments.primary;
+
+ // Initial comments HTML
+ var html = '';
+
+ // Append theme CSS if enabled
+ this.optionalMethod ('appendCSS', [ 'hashover-widget' ]);
+
+ // Add main HashOver element to this HashOver instance
+ this.instance['main-element'] = this.getMainElement ('hashover-widget');
+
+ // Templatify UI HTML strings
+ for (var element in this.ui) {
+ this.ui[element] = this.strings.templatify (this.ui[element]);
+ }
+
+ // Parse every comment
+ for (var i = 0, il = comments.length; i < il; i++) {
+ html += this.comments.parse (comments[i]);
+ }
+
+ // Add comments to element's innerHTML
+ if ('insertAdjacentHTML' in this.instance['main-element']) {
+ this.instance['main-element'].insertAdjacentHTML ('beforeend', html);
+ } else {
+ this.instance['main-element'].innerHTML = html;
+ }
+
+ // Add control events
+ for (var i = 0, il = comments.length; i < il; i++) {
+ this.addControls (comments[i]);
+ }
+};
diff --git a/bootstrap/comments/api/frontends/latest/instantiate.js b/bootstrap/comments/api/frontends/latest/instantiate.js
new file mode 100644
index 0000000..82ff971
--- /dev/null
+++ b/bootstrap/comments/api/frontends/latest/instantiate.js
@@ -0,0 +1,4 @@
+// Instantiate after the DOM is parsed
+HashOverLatest.onReady (function () {
+ window.hashoverLatest = new HashOverLatest ();
+});
diff --git a/bootstrap/comments/api/json.php b/bootstrap/comments/api/json.php
new file mode 100644
index 0000000..2904960
--- /dev/null
+++ b/bootstrap/comments/api/json.php
@@ -0,0 +1,76 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2010-2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Change to the HashOver directory
+chdir (realpath ('../'));
+
+// Setup HashOver for JSON
+require ('backend/json-setup.php');
+
+try {
+ // Instantiate HashOver class
+ $hashover = new \HashOver ('json', 'api');
+
+ // Display error if the API is disabled
+ if ($hashover->setup->apiStatus ('json') === 'disabled') {
+ throw new \Exception ('<b>HashOver</b>: This API is not enabled.');
+ }
+
+ // Configure HashOver and load comments
+ $hashover->setup->setPageURL ('request');
+ $hashover->setup->collapsesComments = false;
+ $hashover->initiate ();
+
+ // Comments and statistics response array
+ $data = array ();
+
+ // Setup where to start reading comments
+ $start = $hashover->setup->getRequest ('start', 0);
+
+ // Check for comments
+ if ($hashover->thread->totalCount > 1) {
+ // Parse comments; TODO: Use starting point
+ $hashover->parsePrimary ();
+ $hashover->parsePopular ();
+
+ // Display as JSON data
+ $data['comments'] = $hashover->comments;
+ } else {
+ // Return no comments message
+ $data = array ('No comments.');
+ }
+
+ // Generate statistics
+ $hashover->statistics->executionEnd ();
+
+ // HashOver statistics
+ $data['statistics'] = array (
+ 'execution-time' => $hashover->statistics->executionTime,
+ 'script-memory' => $hashover->statistics->scriptMemory,
+ 'system-memory' => $hashover->statistics->systemMemory
+ );
+
+ // Return JSON or JSONP function call
+ echo $hashover->misc->jsonData ($data);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('json');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/api/latest.php b/bootstrap/comments/api/latest.php
new file mode 100644
index 0000000..0582260
--- /dev/null
+++ b/bootstrap/comments/api/latest.php
@@ -0,0 +1,154 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Change to the HashOver directory
+chdir (realpath ('../'));
+
+// Setup HashOver for JavaScript
+require ('backend/javascript-setup.php');
+
+try {
+ // Instantiate general setup class
+ $setup = new Setup (array (
+ 'mode' => 'javascript',
+ 'context' => 'api'
+ ));
+
+ // Throw exception if the "Latest Comments" API is disabled
+ if ($setup->apiStatus ('latest') === 'disabled') {
+ throw new \Exception ('This API is not enabled.');
+ }
+
+ // Instantiate HashOver statistics class
+ $statistics = new Statistics ('javascript');
+
+ // Start execution timer
+ $statistics->executionStart ();
+
+ // Instantiate JavaScript build class
+ $javascript = new JavaScriptBuild ('api/frontends/latest');
+
+ // Register initial constructor
+ $javascript->registerFile ('constructor.js');
+
+ // Change to standard frontend directory
+ $javascript->changeDirectory ('frontend');
+
+ // Register HashOver script tag getter method
+ $javascript->registerFile ('script.js');
+
+ // Register backend path setter
+ $javascript->registerFile ('backendpath.js');
+
+ // Register HashOver ready state detection method
+ $javascript->registerFile ('onready.js');
+
+ // Register element creation methods
+ $javascript->registerFile ('elements.js');
+
+ // Register main HashOver element getter method
+ $javascript->registerFile ('getmainelement.js');
+
+ // Register error message handler method
+ $javascript->registerFile ('displayerror.js');
+
+ // Register AJAX-related methods
+ $javascript->registerFile ('ajax.js');
+
+ // Register pre-compiled regular expressions
+ $javascript->registerFile ('regex.js');
+
+ // Register end-of-line trimmer method
+ $javascript->registerFile ('eoltrim.js');
+
+ // Register parent permalink getter method
+ $javascript->registerFile ('permalinks.js');
+
+ // Register markdown methods
+ $javascript->registerFile ('markdown.js', array (
+ 'include' => $setup->usesMarkdown
+ ));
+
+ // Register date/time methods
+ $javascript->registerFile ('datetime.js', array (
+ 'include' => $setup->usesUserTimezone
+ ));
+
+ // Register search and replace methods
+ $javascript->registerFile ('strings.js');
+
+ // Register optional method handler method
+ $javascript->registerFile ('optionalmethod.js');
+
+ // Register comment parsing methods
+ $javascript->registerFile ('comments.js');
+
+ // Register embedded image method
+ $javascript->registerFile ('embedimage.js', array (
+ 'include' => $setup->allowsImages,
+
+ 'dependencies' => array (
+ 'openembeddedimage.js'
+ )
+ ));
+
+ // Register classList polyfill methods
+ $javascript->registerFile ('classes.js');
+
+ // Register theme stylesheet appender method
+ $javascript->registerFile ('appendcss.js', array (
+ 'include' => $setup->appendsCss
+ ));
+
+ // Change back to latest frontend directory
+ $javascript->changeDirectory ('api/frontends/latest');
+
+ // Register Like/Dislike methods
+ $javascript->registerFile ('addratings.js', array (
+ 'include' => ($setup->allowsLikes or $setup->allowsDislikes)
+ ));
+
+ // Register control event handler attacher method
+ $javascript->registerFile ('addcontrols.js');
+
+ // Register initialization method
+ $javascript->registerFile ('init.js');
+
+ // Register automatic instantiation code
+ $javascript->registerFile ('instantiate.js', array (
+ 'include' => !isset ($_GET['nodefault'])
+ ));
+
+ // JavaScript build process output
+ $output = $javascript->build (
+ $setup->minifiesJavascript,
+ $setup->minifyLevel
+ );
+
+ // Display JavaScript build process output
+ echo $output, PHP_EOL;
+
+ // Display statistics
+ echo $statistics->executionEnd ();
+
+} catch (\Exception $error) {
+ $misc = new Misc ('javascript');
+ $message = $error->getMessage ();
+ $misc->displayError ($message);
+}
diff --git a/bootstrap/comments/api/rss.php b/bootstrap/comments/api/rss.php
new file mode 100644
index 0000000..d1cf020
--- /dev/null
+++ b/bootstrap/comments/api/rss.php
@@ -0,0 +1,327 @@
+<?php namespace HashOver;
+
+// Copyright (C) 2010-2018 Jacob Barkdull
+// This file is part of HashOver.
+//
+// HashOver is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// HashOver is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with HashOver. If not, see <http://www.gnu.org/licenses/>.
+
+
+// Tell browser this is XML/RSS
+header ('Content-Type: application/xml; charset=utf-8');
+
+// Change to the HashOver directory
+chdir (realpath ('../'));
+
+// Do some standard HashOver setup work
+require ('backend/nocache-headers.php');
+require ('backend/standard-setup.php');
+
+// Autoload class files
+spl_autoload_register (function ($uri) {
+ $uri = str_replace ('\\', '/', strtolower ($uri));
+ $class_name = basename ($uri);
+ $error = '"' . $class_name . '.php" file could not be included!';
+
+ if (!@include ('backend/classes/' . $class_name . '.php')) {
+ echo '<?xml version="1.0" encoding="UTF-8"?>', PHP_EOL;
+ echo '<error>', $error, '</error>';
+ exit;
+ }
+});
+
+function create_rss (&$hashover)
+{
+ // Shorter variable name
+ $thread = $hashover->setup->threadName;
+
+ // Attempt to read page information metadata
+ $metadata = $hashover->thread->data->readMeta ('page-info', $thread);
+
+ // Check if metadata read successfully
+ if ($metadata !== false) {
+ // If so, set page URL blank if it's missing from the metadata
+ if (!isset ($metadata['url'])) {
+ $metadata['url'] = '';
+ }
+
+ // And set page title to "Untitled" if it's missing from the metadata
+ if (!isset ($metadata['title'])) {
+ $metadata['title'] = $hashover->locale->text['untitled'];
+ }
+ } else {
+ // If not, set default metadata information
+ $metadata = array (
+ 'url' => '',
+ 'title' => $hashover->locale->text['untitled']
+ );
+ }
+
+ // Create new DOM document.
+ $xml = new \DOMDocument ('1.0', 'UTF-8');
+ $xml->preserveWhiteSpace = false;
+ $xml->formatOutput = true;
+
+ // Create main RSS element
+ $rss = $xml->createElement ('rss');
+ $rss->setAttribute ('version', '2.0');
+ $rss->setAttribute ('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+ $rss->setAttribute ('xmlns:content', 'http://purl.org/rss/1.0/modules/content/');
+ $rss->setAttribute ('xmlns:atom', 'http://www.w3.org/2005/Atom');
+
+ // Display error if the API is disabled
+ if ($hashover->setup->apiStatus ('rss') === 'disabled') {
+ $title = $xml->createElement ('title');
+ $title_value = $xml->createTextNode ('HashOver: RSS API is not enabled.');
+ $title->appendChild ($title_value);
+ $rss->appendChild ($title);
+
+ $description = $xml->createElement ('description');
+ $description_value = $xml->createTextNode ('Error!');
+ $description->appendChild ($description_value);
+ $rss->appendChild ($description);
+
+ // Add main RSS element to XML
+ $xml->appendChild ($rss);
+
+ // Return RSS XML
+ exit (str_replace (' ', "\t", $xml->saveXML ()));
+ }
+
+ // Create channel element
+ $channel = $xml->createElement ('channel');
+
+ // Create channel title element
+ $title = $xml->createElement ('title');
+ $title_value = $xml->createTextNode (html_entity_decode ($metadata['title'], ENT_COMPAT, 'UTF-8'));
+ $title->appendChild ($title_value);
+
+ // Add channel title to channel element
+ $channel->appendChild ($title);
+
+ // Create channel link element
+ $link = $xml->createElement ('link');
+ $link_value = $xml->createTextNode (html_entity_decode ($metadata['url'], ENT_COMPAT, 'UTF-8'));
+ $link->appendChild ($link_value);
+
+ // Add channel link to channel element
+ $channel->appendChild ($link);
+
+ // Create channel description element
+ $description = $xml->createElement ('description');
+ $count_plural = ($hashover->thread->totalCount !== 1);
+ $showing_comments_locale = $hashover->locale->text['showing-comments'][$count_plural];
+ $count_locale = sprintf ($showing_comments_locale, $hashover->thread->totalCount - 1);
+ $description_value = $xml->createTextNode ($count_locale);
+ $description->appendChild ($description_value);
+
+ // Add channel description to channel element
+ $channel->appendChild ($description);
+
+ // Create channel atom link element
+ $atom_link = $xml->createElement ('atom:link');
+ $atom_link->setAttribute ('href', 'http://' . $hashover->setup->domain . $_SERVER['PHP_SELF'] . '?url=' . $metadata['url']);
+ $atom_link->setAttribute ('rel', 'self');
+
+ // Add channel atom link to channel element
+ $channel->appendChild ($atom_link);
+
+ // Create channel language element
+ $language = $xml->createElement ('language');
+ $language_value = $xml->createTextNode ('en-us');
+ $language->appendChild ($language_value);
+
+ // Add channel language to channel element
+ $channel->appendChild ($language);
+
+ // Create channel ttl element
+ $ttl = $xml->createElement ('ttl');
+ $ttl_value = $xml->createTextNode ('40');
+ $ttl->appendChild ($ttl_value);
+
+ // Add channel ttl to channel element
+ $channel->appendChild ($ttl);
+
+ // Add channel element to main RSS element
+ $rss->appendChild ($channel);
+
+ // Parse comments
+ function parse_comments (&$metadata, &$comment, &$rss, &$xml, &$hashover)
+ {
+ // Skip deleted/unmoderated comments
+ if (isset ($comment['notice'])) {
+ return;
+ }
+
+ // Encode HTML entities
+ $comment['body'] = htmlentities ($comment['body'], ENT_COMPAT, 'UTF-8', true);
+
+ // Decode HTML entities
+ $comment['body'] = html_entity_decode ($comment['body'], ENT_COMPAT, 'UTF-8');
+
+ // Remove [img] tags
+ $comment['body'] = preg_replace ('/\[(img|\/img)\]/iS', '', $comment['body']);
+
+ // Parse comment as markdown
+ $comment['body'] = $hashover->markdown->parseMarkdown ($comment['body']);
+
+ // Convert <code> tags to <pre> tags
+ $comment['body'] = preg_replace ('/(<|<\/)code>/iS', '\\1pre>', $comment['body']);
+
+ // Get name from comment or use configured default
+ $name = !empty ($comment['name']) ? $comment['name'] : $hashover->setup->defaultName;
+
+ // Create item element
+ $item = $xml->createElement ('item');
+
+ // Generate comment summary item title
+ $title = $name . ' : ';
+ $single_comment = str_replace (PHP_EOL, ' ', strip_tags ($comment['body']));
+
+ if (mb_strlen ($single_comment) > 40) {
+ $title .= mb_substr ($single_comment, 0, 40) . '...';
+ } else {
+ $title .= $single_comment;
+ }
+
+ // Create item title element
+ $item_title = $xml->createElement ('title');
+ $item_title_value = $xml->createTextNode (html_entity_decode ($title, ENT_COMPAT, 'UTF-8'));
+ $item_title->appendChild ($item_title_value);
+
+ // Add item title element to item element
+ $item->appendChild ($item_title);
+
+ // Create item name element
+ $item_name = $xml->createElement ('name');
+ $item_name_value = $xml->createTextNode (html_entity_decode ($name, ENT_COMPAT, 'UTF-8'));
+ $item_name->appendChild ($item_name_value);
+
+ // Add item name element to item element
+ $item->appendChild ($item_name);
+
+ // Add HTML anchor tag to URLs (hyperlinks)
+ $comment['body'] = preg_replace ('/((ftp|http|https):\/\/[a-z0-9-@:%_\+.~#?&\/=]+) {0,}/iS', '<a href="\\1" target="_blank">\\1</a>', $comment['body']);
+
+ // Replace newlines with break tags
+ $comment['body'] = str_replace (PHP_EOL, '<br>', $comment['body']);
+
+ // Create item description element
+ $item_description = $xml->createElement ('description');
+ $item_description_value = $xml->createTextNode ($comment['body']);
+ $item_description->appendChild ($item_description_value);
+
+ // Add item description element to item element
+ $item->appendChild ($item_description);
+
+ // Create item avatar element
+ $item_avatar = $xml->createElement ('avatar');
+ $web_root = 'http://' . $hashover->setup->domain . $hashover->setup->httpRoot;
+ $item_avatar_value = $xml->createTextNode ($web_root . $comment['avatar']);
+ $item_avatar->appendChild ($item_avatar_value);
+
+ // Add item avatar element to item element
+ $item->appendChild ($item_avatar);
+
+ if (!empty ($comment['likes'])) {
+ // Create item likes element
+ $item_likes = $xml->createElement ('likes');
+ $item_likes_value = $xml->createTextNode ($comment['likes']);
+ $item_likes->appendChild ($item_likes_value);
+
+ // Add item likes element to item element
+ $item->appendChild ($item_likes);
+ }
+
+ if ($hashover->setup->allowsDislikes === true) {
+ if (!empty ($comment['dislikes'])) {
+ // Create item dislikes element
+ $item_dislikes = $xml->createElement ('dislikes');
+ $item_dislikes_value = $xml->createTextNode ($comment['dislikes']);
+ $item_dislikes->appendChild ($item_dislikes_value);
+
+ // Add item dislikes element to item element
+ $item->appendChild ($item_dislikes);
+ }
+ }
+
+ // Create item publication date element
+ $item_pubDate = $xml->createElement ('pubDate');
+ $item_pubDate_value = $xml->createTextNode (date ('D, d M Y H:i:s O', $comment['sort-date']));
+ $item_pubDate->appendChild ($item_pubDate_value);
+
+ // Add item pubDate element to item element
+ $item->appendChild ($item_pubDate);
+
+ // URL to comment for item guide and link elements
+ $item_permalink_url = $metadata['url'] . '#' . $comment['permalink'];
+
+ // Create item guide element
+ $item_guid = $xml->createElement ('guid');
+ $item_guid_value = $xml->createTextNode ($item_permalink_url);
+ $item_guid->appendChild ($item_guid_value);
+
+ // Add item guide element to item element
+ $item->appendChild ($item_guid);
+
+ // Create item link element
+ $item_link = $xml->createElement ('link');
+ $item_link_value = $xml->createTextNode ($item_permalink_url);
+ $item_link->appendChild ($item_link_value);
+
+ // Add item link element to item element
+ $item->appendChild ($item_link);
+
+ // Add item element to main RSS element
+ $rss->appendChild ($item);
+
+ // Recursively parse replies
+ if (!empty ($comment['replies'])) {
+ foreach ($comment['replies'] as $reply) {
+ parse_comments ($metadata, $reply, $rss, $xml, $hashover);
+ }
+ }
+ }
+
+ // Add item element to main RSS element
+ foreach ($hashover->comments['primary'] as $comment) {
+ parse_comments ($metadata, $comment, $rss, $xml, $hashover);
+ }
+
+ // Add main RSS element to XML
+ $xml->appendChild ($rss);
+
+ // Return RSS XML
+ echo preg_replace_callback ('/^(\s+)/m', function ($spaces) {
+ return str_repeat ("\t", strlen ($spaces[1]) / 2);
+ }, $xml->saveXML ());
+
+ // Return statistics
+ echo $hashover->statistics->executionEnd ();
+}
+
+try {
+ // Instantiate HashOver class
+ $hashover = new \HashOver ('php', 'api');
+ $hashover->setup->setPageURL ('request');
+ $hashover->setup->collapsesComments = false;
+ $hashover->initiate ();
+ $hashover->parsePrimary ();
+
+ // Create RSS feed
+ create_rss ($hashover);
+
+} catch (\Exception $error) {
+ $misc = new Misc ('rss');
+ $misc->displayError ($error->getMessage ());
+}