// @licstart  The following is the entire license notice for the
//  JavaScript code in this page.
//
// Copyright (C) 2018-2021 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 (settings)
{
	// Get count link class elements
	var countLinks = document.getElementsByClassName ('hashover-count-link');

	// Ensure settings parameter is an object
	if (!settings || settings.constructor !== Object) {
		settings = {};
	}

	// Send request for comment count for each hyperlink
	for (var i = 0, il = countLinks.length; i < il; i++) {
		this.getCommentCount (countLinks[i], settings);
	}
};

// Constructor to add shared methods to (constructor.js)
var HashOverConstructor = HashOverCountLink;

// Converts an object in a series of URL queries (cfgqueries.js)
HashOverConstructor.cfgQueries = function (value, name, queries)
{
	// Current URL query matrix
	name = name || [];

	// All settings URL queries to return
	queries = queries || [];

	// Check if value is an object
	if (typeof (value) !== 'object') {
		// If so, get query matrix as string
		var matrix = '[' + name.join ('][') + ']';

		// Encode current URL query value
		var value = encodeURIComponent (value);

		// Add current URL query to return array
		queries.push ('cfg' + matrix + '=' + value);

		// And do nothing else
		return;
	}

	// Otherwise, descend in setting object
	for (var key in value) {
		HashOverConstructor.cfgQueries (value[key], name.concat (key), queries);
	}

	// And return settings URL queries
	return queries;
};

// Send AJAX request to backend for a comment count (getcommentcount.js)
HashOverCountLink.prototype.getCommentCount = function (link, settings)
{
	// Reference to this HashOver object
	var hashover = this;

	// Get backend queries
	var queries = ['url=' + encodeURIComponent (link.href)];

	// Backend request path
	var requestPath = HashOverCountLink.backendPath + '/count-link-ajax.php';

	// Get cfg URL queries array
	var cfgQueries = HashOverCountLink.cfgQueries (settings);

	// And add cfg queries
	queries = queries.concat (cfgQueries);

	// Handle backend request
	this.ajax ('POST', requestPath, queries, function (json) {
		if (json['link-text'] !== undefined) {
			link.textContent = json['link-text'];
		}
	}, true);
};

// Get the current HashOver script tag (script.js)
HashOverConstructor.script = (function () {
	// Get various scripts
	var loaderScript = document.getElementById ('hashover-loader');
	var scripts = document.getElementsByTagName ('script');

	// Use either the current script or an identified loader script
	var currentScript = document.currentScript || loaderScript;

	// Otherwise, fallback to the last script encountered
	return currentScript || scripts[scripts.length - 1];
}) ();

// Returns root path (rootpath.js)
HashOverConstructor.getRootPath = function (removeApi)
{
	// Get the HashOver script source URL
	var scriptSrc = HashOverConstructor.script.getAttribute ('src');

	// Get HashOver root path
	var root = scriptSrc.replace (/\/[^\/]*\/?$/, '');

	// Remove API directory from root path if told to
	if (removeApi === true) {
		root = root.replace (/\/api/, '');
	}

	// And return HashOver root path
	return root;
};

// Root path (rootpath.js)
HashOverConstructor.rootPath = HashOverConstructor.getRootPath ();

// Returns backend path (backendpath.js)
HashOverConstructor.getBackendPath = function (removeApi)
{
	return HashOverConstructor.getRootPath (removeApi) + '/backend';
};

// Backend path (backendpath.js)
HashOverConstructor.backendPath = HashOverConstructor.getBackendPath ();

// Array of JSONP callbacks, starting with default error handler (ajax.js)
HashOverConstructor.jsonp = [
	function (json) { alert (json.message); }
];

// Send HTTP requests using JSONP as a fallback (ajax.js)
HashOverConstructor.prototype.jsonp = function (method, path, data, callback, async)
{
	// Get constructor name
	var source = this.constructor.toString ();
	var constructor = source.match (/function (\w+)/)[1];

	// Push callback into JSONP array
	this.constructor.jsonp.push (callback);

	// Add JSONP callback index and constructor to request data
	data.push ('jsonp=' + (this.constructor.jsonp.length - 1));
	data.push ('jsonp_object=' + constructor || 'HashOver');

	// Create request script
	var script = document.createElement ('script');

	// Set request script path
	script.src = path + '?' + data.join ('&');

	// Set request script to load type
	script.async = async;

	// Append request script to page
	document.body.appendChild (script);
};

// Send HTTP requests using either XMLHttpRequest or JSONP (ajax.js)
HashOverConstructor.prototype.ajax = function (method, path, data, callback, async)
{
	// Reference to this object
	var hashover = this;

	// Arguments to this method
	var args = arguments;

	// Successful request handler
	var onSuccess = function ()
	{
		// Parse JSON response
		var json = JSON.parse (this.responseText);

		// And execute callback
		callback.apply (this, [ json ]);
	};

	// CORS error handler
	var onError = function ()
	{
		// Call JSONP fallback
		hashover.jsonp.apply (hashover, args);

		// And set AJAX to use JSONP
		hashover.ajax = hashover.jsonp;
	};

	// Check for XHR with credentials support
	if ('withCredentials' in new XMLHttpRequest ()) {
		// If supported, create XHR request
		var xhr = new XMLHttpRequest ();

		// Set ready state change handler
		xhr.onreadystatechange = function ()
		{
			// Do nothing if request isn't ready
			if (this.readyState !== 4) {
				return;
			}

			// Handle successful request response
			if (this.status === 200) {
				return onSuccess.apply (this);
			}

			// Handle failed request response, likely CORS error
			if (this.status === 0) {
				return onError ();
			}
		};

		// Open XHR request
		xhr.open (method, path, async);

		// Set request headers
		xhr.setRequestHeader ('Content-type', 'application/x-www-form-urlencoded');

		// Set request to include credentials, mostly cookies
		xhr.withCredentials = true;

		// Send XHR request
		xhr.send (data.join ('&'));

		// And do nothing else
		return;
	}

	// Try to fallback to XDomainRequest if supported
	if (typeof (XDomainRequest) !== 'undefined') {
		// If so, create XDR request
		var xdr = new XDomainRequest ();

		// Open request
		xdr.open (method, path);

		// Set successful request response handler
		xdr.onload = onSuccess;

		// Set failed request response handler
		xdr.onerror = onError;

		// Send XDR request
		setTimeout (xdr.send, 0);

		// And do nothing else
		return;
	}

	// If all else fails fallback to JSONP
	onError ();
};

// Execute a callback when the page HTML is parsed and ready (onready.js)
HashOverConstructor.onReady = function (callback)
{
	// Ready state
	var state = document.readyState;

	// Check if document HTML has been parsed
	if (state === 'interactive' || state === 'complete') {
		// If so, execute callback immediately
		callback ();
	} else {
		// If not, execute callback after the DOM is parsed
		document.addEventListener ('DOMContentLoaded', function () {
			callback ();
		}, false);
	}
};

// Instantiate after the DOM is parsed (instantiate.js)
HashOverCountLink.onReady (function () {
	window.hashoverCountLink = new HashOverCountLink ();
});

/*

	HashOver Statistics

	Execution Time     : 7.69901 ms
	Script Memory Peak : 0.55 MiB
	System Memory Peak : 2 MiB

*/