<commit_before>function health_table_build() {
    var template_source = $("#template-health-table").html();
    var template = Handlebars.compile(template_source);

    var html = template({attributes: health_attributes_list});
    $("#health-table").html(html);
}

function attribute_table_build() {
    var template_source = $("#template-attributes-table").html();
    var template = Handlebars.compile(template_source);
    var html = template({attributes: attributes_list});
    $("#attributes-table").html(html);
}

function build_attribute_button_handler(prefix) {
    function btn_handler() {
        var button = $(this);
        var attribute = $(prefix + "-" + button.data("type"));
        var value = attribute.data("value") | 0;
        var is_decrem = button.find(".glyphicon-minus").length

        value = is_decrem ? value - 1 : value + 1;

        attribute.data("value", value);
        attribute.text(value).trigger("change");
    }

    return btn_handler;
}

function connect_attribute_buttons(base_prefix, attr_prefix) {
    var buttons = $(base_prefix + " button");

    buttons.click(build_attribute_button_handler(attr_prefix));
}

function connect_attribute_display(prefix, lst) {
    for (var i = 0; i < lst.length; i++) {
        var attribute = $(prefix + "-" + lst[i].type);
        if (attribute) {
            attribute.text(attribute.data("value"));
        }

        attribute.on("change", on_attribute_change);
    }
}

function local_save_data() {
    var save_data = {};

    data_sets.forEach(function(data_set) {
        var data = window[data_set.name];
        save_data[data_set.name] = {};

        for (var i = 0; i < data.length; i++) {
            var attr = data[i].type;
            save_data[data_set.name][attr] = $(data_set.prefix + "-" + attr).data("value");
        }
    });

    window.localStorage.setItem("rpg-data", JSON.stringify(save_data));
}

function local_load_data() {
    var raw_data = window.localStorage.getItem("rpg-data");

    if (!raw_data) {
        return;
    }

    var parsed_data = JSON.parse(raw_data);

    data_sets.forEach(function(data_set) {
        var data = window[data_set.name];

        for (var i = 0; i < data.length; i++) {
            var attr = data[i].type;
            var span_attr = $(data_set.prefix + "-" + attr);
            var value = parsed_data[data_set.name][attr];

            span_attr.data("value", value).text(value).trigger("change");
        }
    });
}

function on_attribute_change(event) {
    var span = $(this);
    console.log(span);
}

var attributes_list = [
    { name: "Erő", type: "strength" },
    { name: "Ügyesség", type: "dexterity" },
    { name: "Gyorsaság", type: "speed" },
    { name: "Állóképesség", type: "stanima" },
    { name: "Egészség", type: "health" },
    { name: "Szépség", type: "beauty" },
    { name: "Intelligencia", type: "intelligence" },
    { name: "Akaraterő", type: "will-power" },
    { name: "Asztrál", type: "astral" }
];

var health_attributes_list = [
    { name: "Alap ÉP", type: "base-hp" },
    { name: "Max ÉP", type: "max-hp" },
    { name: "Akt. ÉP:", type: "current-hp" },
];

var data_sets = [
    { name: "attributes_list", prefix: "#attribute" },
    { name: "health_attributes_list", prefix: "#health" },
];

$(document).ready(function() {
    attribute_table_build();
    connect_attribute_display("#attribute", attributes_list);
    connect_attribute_buttons("table.attributes", "#attribute");

    health_table_build();
    connect_attribute_display("#health", health_attributes_list);
    connect_attribute_buttons("table.health", "#health");

    $("#button-load-data").click(local_load_data);
    $("#button-save-data").click(local_save_data);
});
<commit_msg>Add a simple connected attribute calculation way<commit_after>function health_table_build() {
    var template_source = $("#template-health-table").html();
    var template = Handlebars.compile(template_source);

    var html = template({attributes: health_attributes_list});
    $("#health-table").html(html);
}

function attribute_table_build() {
    var template_source = $("#template-attributes-table").html();
    var template = Handlebars.compile(template_source);
    var html = template({attributes: attributes_list});
    $("#attributes-table").html(html);
}

function build_attribute_button_handler(prefix) {
    function btn_handler() {
        var button = $(this);
        var attribute = $(prefix + "-" + button.data("type"));
        var value = attribute.data("value") | 0;
        var is_decrem = button.find(".glyphicon-minus").length

        value = is_decrem ? value - 1 : value + 1;

        attribute.data("value", value);
        attribute.text(value).trigger("change");
    }

    return btn_handler;
}

function connect_attribute_buttons(base_prefix, attr_prefix) {
    var buttons = $(base_prefix + " button");

    buttons.click(build_attribute_button_handler(attr_prefix));
}

function connect_attribute_display(prefix, lst) {
    for (var i = 0; i < lst.length; i++) {
        var attribute = $(prefix + "-" + lst[i].type);
        if (attribute) {
            attribute.text(attribute.data("value"));
        }

        attribute.on("change", on_attribute_change);
    }
}

function local_save_data() {
    var save_data = {};

    data_sets.forEach(function(data_set) {
        var data = window[data_set.name];
        save_data[data_set.name] = {};

        for (var i = 0; i < data.length; i++) {
            var attr = data[i].type;
            save_data[data_set.name][attr] = $(data_set.prefix + "-" + attr).data("value");
        }
    });

    window.localStorage.setItem("rpg-data", JSON.stringify(save_data));
}

function local_load_data() {
    var raw_data = window.localStorage.getItem("rpg-data");

    if (!raw_data) {
        return;
    }

    var parsed_data = JSON.parse(raw_data);

    data_sets.forEach(function(data_set) {
        var data = window[data_set.name];

        for (var i = 0; i < data.length; i++) {
            var attr = data[i].type;
            var span_attr = $(data_set.prefix + "-" + attr);
            var value = parsed_data[data_set.name][attr];

            span_attr.data("value", value).text(value).trigger("change");
        }
    });
}

function on_attribute_change(event) {
    var span = $(this);
    console.log(span);
}

var attributes_list = [
    // has 'attribute-' prefix
    { name: "Erő", type: "strength" },
    { name: "Ügyesség", type: "dexterity" },
    { name: "Gyorsaság", type: "speed" },
    { name: "Állóképesség", type: "stanima" },
    { name: "Egészség", type: "health" },
    { name: "Szépség", type: "beauty" },
    { name: "Intelligencia", type: "intelligence" },
    { name: "Akaraterő", type: "will-power" },
    { name: "Asztrál", type: "astral" }
];

var health_attributes_list = [
    // has 'health-' prefix
    { name: "Alap ÉP", type: "base-hp" },
    { name: "Max ÉP", type: "max-hp" },
    { name: "Akt. ÉP:", type: "current-hp" },
];

var data_sets = [
    { name: "attributes_list", prefix: "#attribute" },
    { name: "health_attributes_list", prefix: "#health" },
];

var data_connections = [
    { name: "on-health-change",
      on: "#attribute-health",
      update: "#health-max-hp",
      handler: function (on, update) {
        var health_value = $("#attribute-health").data("value") | 0;
        var value = $("#health-base-hp").data("value") + (health_value > 10 ? health_value - 10 : 0);
        $(update).data("value", value).text(value).trigger("chage");
      }
    },
    { name: "on-base-hp-change",
      on: "#health-base-hp",
      update: "#health-max-hp",
      handler: "on-health-change"    // defer the change operation to the other connection
    },
];

function connect_data_connections() {
    for (var i = 0; i < data_connections.length; i++) {
        var connection = data_connections[i];
        var attribute = $(connection.on);

        if (typeof(connection.handler) === "string") {
            // We found a handler which defers its call to another connection's handler
            // select that handler and modify the current connection.
            for (var j = 0; j < data_connections.length; i++) {
                if (data_connections[j].name == connection.handler) {
                    connection.handler = data_connections[j].handler;
                    break;
                }
            }
        }

        attribute.on("change", connection, on_connected_update);
    }
}

function on_connected_update(e) {
    e.data.handler(e.data.on, e.data.update);
}

$(document).ready(function() {
    attribute_table_build();
    connect_attribute_display("#attribute", attributes_list);
    connect_attribute_buttons("table.attributes", "#attribute");

    health_table_build();
    connect_attribute_display("#health", health_attributes_list);
    connect_attribute_buttons("table.health", "#health");

    connect_data_connections();
    $("#button-load-data").click(local_load_data);
    $("#button-save-data").click(local_save_data);
});
<|endoftext|>


<commit_before>$(document).ready(function()
{
		setInterval(waiting,1000);
		executeSteps(StepCounter.step);
		$(document).on('click','a',clearLinks);
});

var advanceStep = function(step)
{
	return ++step;
}

var backupFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['backup_files']));
	$.get("controller.php","action=BackupFiles").then(function(data)
	{
		clearWaiting();
		if(data.success)
		{
			  $("#info").append(sprintf('
{0}
',message['backup_success']));
				StepCounter.incrementStep(1);
				executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['backup_failed']));
		}
	}, failed);
}

var checkForBackups = function()
{
	$("#info").append(sprintf('
{0}
',message['check_for_backups']));
	$.get("controller.php","action=CheckForBackups").then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['backups_found']));
			$("#info").append(sprintf('
{0}
',message['prompt_update_backup']));
			$("#info").append(sprintf('
{0} >{1}
',message['prompt_update_btn'],message['prompt_restore_btn']));
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['backups_not_found']));
			StepCounter.step = Step.CheckVersionFileExists;
			executeSteps(StepCounter.step);
		}
	},failed);
}

var checkForScripts = function()
{
	$("#info").append(sprintf('
{0}
',message['check_for_scripts']));
	$.get("controller.php","action=CheckForScripts").then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			  $("#info").append(sprintf('
{0}
',message['scripts_exists']));
				StepCounter.incrementStep(1);
				executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['no_scripts_exists']));
			StepCounter.incrementStep(2);
			 executeSteps(StepCounter.step);
		}
	}, failed);
}

var checkVersion = function()
{
	var current = false;
	$.get("controller.php","action=VersionIsCurrent").then(function(data)
	{
			clearWaiting();
		  current = data.current;
			if(current)
			{
				$("#info").append(sprintf('
'+message['version_up_to_date']+'
',data.current_version));
			}
			else
			{
				$("#info").append(sprintf('
'+message['version_out_of_date']+'
',data.current_version, data.update_version));
				StepCounter.incrementStep(1);
				$("#info").append(sprintf('
{0}
',message['update_btn']));
			}
	},
	failed);
}

var checkVersionFileExists = function()
{
	$("#info").append(sprintf('
{0} ',message['check_version_file_exists']));
	$.get('controller.php','action=CheckUpdateFileExists').then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['update_file_exists']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf(sprintf('
{0}
',message['update_file_does_not_exist']),data.url));
		}
	},failed);

}

var checkRemoteFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['check_remote_files_exist']));
	$.get("controller.php","action=CheckRemoteFilesExist").then(function(data){
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['remote_files_exist']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['remote_files_dont_exist']));
		}
	});
}

var checkWritablilty = function()
{
	$("#info").append(sprintf('
{0} ',message['check_files_are_writable']));
	$.get("controller.php","action=CheckFilesAreWritable").then(function(data)
	{
		 if(data.writable)
		 {
			 $("#info").append(sprintf('
{0}
',message['files_are_writable']));
			 StepCounter.incrementStep(1);
			 executeSteps(StepCounter.step);
		 }
		 else
		 {
		   $("#info").append(sprintf('
{0}
',message['files_are_not_writable']));
		 }
		 clearWaiting();
	},failed);
}

var clearLinks = function()
{
	$("#info a").remove();
}

var clearWaiting = function()
{
		$(".waiting").removeClass("waiting");
}

var executeScripts = function()
{
	$("#info").append(sprintf('
{0} ',message['running_scripts']));
	$.get("controller.php","action=ExecuteScripts").then(function(data)
	{
		$("#info").append(sprintf('
{0} ',message['scripts_finished']));
		StepCounter.incrementStep(1);
		executeSteps(StepCounter.step);
	},failed);
}

var executeSteps = function(step)
{
	switch(step)
	{
		case Step.CheckForBackups:
			checkForBackups();
			break;
		case Step.BackupFiles:
			backupFiles();
			break;
		case Step.CheckVersionFileExists:
			checkVersionFileExists();
			break;
		case Step.CheckVersion:
			checkVersion();
			break;
		case Step.CheckWritability:
			 checkWritablilty();
			 break;
		case Step.CheckRemoteFilesExist:
			checkRemoteFiles();
			break;
		case Step.InstallFiles:
			installFiles();
			break;
	 	case Step.UpdateVersion:
			updateVersion();
			break;
		case Step.CheckForScripts:
			checkForScripts();
			break;
		case Step.ExecuteScripts:
			executeScripts();
			break;
		case Step.Finished:
			finished();
			break;
		default:
		  stepNotFound(step);
			break;
	}
}

var failed = function(xhr,status,error)
{
	clearWaiting();
	$("#info").append(sprintf('
{0}
',message['update_failed']));
	$("#info").append(sprintf('
Status: {0}
',status));
	$("#info").append(sprintf('
Error:  {0}
',error));
}

var finished = function()
{
	$("#info").append(sprintf('
{0}
',message['update_finished']));
}

var installFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['installing_files']));
	$.get("controller.php","action=InstallFiles").then(function(data){
			clearWaiting();
			$("#info").append(sprintf('
{0}
',message['files_installed']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
	},failed);
}

var sprintf = function()
{
	 var message = arguments[0];
	 for(var i = 1; i < arguments.length; ++i)
	 {
		 message = message.replace('{'+(i-1)+'}',arguments[i]);
	 }
	 return message;
}

var Step = {
	CheckForBackups:	0,
	BackupsExist: 1,
	ChooseBackupFile: 2,
	RestoreBackup: 3,
	RestorationComplete: 4,
	CheckVersionFileExists : 5,
	CheckVersion: 6,
	CheckWritability: 7,
	CheckRemoteFilesExist: 8,
	BackupFiles: 9,
	InstallFiles: 10,
	CheckForScripts: 11,
	ExecuteScripts: 12,
	UpdateVersion: 13,
	Finished: 14
}

var StepCounter = {
	 step: 0,
	 incrementStep: function(incBy)
	 {
		 this.step += incBy;
	 },

	 incrementAndExecuteStep: function(incBy)
	 {
		 this.step += incBy;
		 executeSteps(this.step);
	 },

	 setStepAndExecute: function(step)
	 {
		 this.step = step;
		 executeSteps(this.step);
	 }
}

var stepNotFound = function(step)
{
	$("#info").append(sprintf(sprintf('
{0}
',message['step_not_found']),step));
}

var updateVersion = function()
{
	$("#info").append(sprintf('
{0}
',message['updating_version']));
	$.get("controller.php","action=UpdateVersion").then(function(data){
			clearWaiting();
			$("#info").append(sprintf('
{0}
',message['version_file_updated']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
	},failed);
}

var waiting = function()
{
		$(".waiting").each(function()
    {
    		var dots = $(this).text();
        dots += ".";
        if(dots.length > 3)
        {
          dots = "";
        }
        $(this).text(dots);
    });

}
<commit_msg>Fix check for update btn<commit_after>$(document).ready(function()
{
		setInterval(waiting,1000);
		executeSteps(StepCounter.step);
		$(document).on('click','a',clearLinks);
});

var advanceStep = function(step)
{
	return ++step;
}

var backupFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['backup_files']));
	$.get("controller.php","action=BackupFiles").then(function(data)
	{
		clearWaiting();
		if(data.success)
		{
			  $("#info").append(sprintf('
{0}
',message['backup_success']));
				StepCounter.incrementStep(1);
				executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['backup_failed']));
		}
	}, failed);
}

var checkForBackups = function()
{
	$("#info").append(sprintf('
{0}
',message['check_for_backups']));
	$.get("controller.php","action=CheckForBackups").then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['backups_found']));
			$("#info").append(sprintf('
{0}
',message['prompt_update_backup']));
			$("#info").append(sprintf('
{0} {1}
',message['prompt_update_btn'],message['prompt_restore_btn']));
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['backups_not_found']));
			StepCounter.step = Step.CheckVersionFileExists;
			executeSteps(StepCounter.step);
		}
	},failed);
}

var checkForScripts = function()
{
	$("#info").append(sprintf('
{0}
',message['check_for_scripts']));
	$.get("controller.php","action=CheckForScripts").then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			  $("#info").append(sprintf('
{0}
',message['scripts_exists']));
				StepCounter.incrementStep(1);
				executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['no_scripts_exists']));
			StepCounter.incrementStep(2);
			 executeSteps(StepCounter.step);
		}
	}, failed);
}

var checkVersion = function()
{
	var current = false;
	$.get("controller.php","action=VersionIsCurrent").then(function(data)
	{
			clearWaiting();
		  current = data.current;
			if(current)
			{
				$("#info").append(sprintf('
'+message['version_up_to_date']+'
',data.current_version));
			}
			else
			{
				$("#info").append(sprintf('
'+message['version_out_of_date']+'
',data.current_version, data.update_version));
				StepCounter.incrementStep(1);
				$("#info").append(sprintf('
{0}
',message['update_btn']));
			}
	},
	failed);
}

var checkVersionFileExists = function()
{
	$("#info").append(sprintf('
{0} ',message['check_version_file_exists']));
	$.get('controller.php','action=CheckUpdateFileExists').then(function(data)
	{
		clearWaiting();
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['update_file_exists']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf(sprintf('
{0}
',message['update_file_does_not_exist']),data.url));
		}
	},failed);

}

var checkRemoteFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['check_remote_files_exist']));
	$.get("controller.php","action=CheckRemoteFilesExist").then(function(data){
		if(data.exists)
		{
			$("#info").append(sprintf('
{0}
',message['remote_files_exist']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
		}
		else
		{
			$("#info").append(sprintf('
{0}
',message['remote_files_dont_exist']));
		}
	});
}

var checkWritablilty = function()
{
	$("#info").append(sprintf('
{0} ',message['check_files_are_writable']));
	$.get("controller.php","action=CheckFilesAreWritable").then(function(data)
	{
		 if(data.writable)
		 {
			 $("#info").append(sprintf('
{0}
',message['files_are_writable']));
			 StepCounter.incrementStep(1);
			 executeSteps(StepCounter.step);
		 }
		 else
		 {
		   $("#info").append(sprintf('
{0}
',message['files_are_not_writable']));
		 }
		 clearWaiting();
	},failed);
}

var clearLinks = function()
{
	$("#info a").remove();
}

var clearWaiting = function()
{
		$(".waiting").removeClass("waiting");
}

var executeScripts = function()
{
	$("#info").append(sprintf('
{0} ',message['running_scripts']));
	$.get("controller.php","action=ExecuteScripts").then(function(data)
	{
		$("#info").append(sprintf('
{0} ',message['scripts_finished']));
		StepCounter.incrementStep(1);
		executeSteps(StepCounter.step);
	},failed);
}

var executeSteps = function(step)
{
	switch(step)
	{
		case Step.CheckForBackups:
			checkForBackups();
			break;
		case Step.BackupFiles:
			backupFiles();
			break;
		case Step.CheckVersionFileExists:
			checkVersionFileExists();
			break;
		case Step.CheckVersion:
			checkVersion();
			break;
		case Step.CheckWritability:
			 checkWritablilty();
			 break;
		case Step.CheckRemoteFilesExist:
			checkRemoteFiles();
			break;
		case Step.InstallFiles:
			installFiles();
			break;
	 	case Step.UpdateVersion:
			updateVersion();
			break;
		case Step.CheckForScripts:
			checkForScripts();
			break;
		case Step.ExecuteScripts:
			executeScripts();
			break;
		case Step.Finished:
			finished();
			break;
		default:
		  stepNotFound(step);
			break;
	}
}

var failed = function(xhr,status,error)
{
	clearWaiting();
	$("#info").append(sprintf('
{0}
',message['update_failed']));
	$("#info").append(sprintf('
Status: {0}
',status));
	$("#info").append(sprintf('
Error:  {0}
',error));
}

var finished = function()
{
	$("#info").append(sprintf('
{0}
',message['update_finished']));
}

var installFiles = function()
{
	$("#info").append(sprintf('
{0}
',message['installing_files']));
	$.get("controller.php","action=InstallFiles").then(function(data){
			clearWaiting();
			$("#info").append(sprintf('
{0}
',message['files_installed']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
	},failed);
}

var sprintf = function()
{
	 var message = arguments[0];
	 for(var i = 1; i < arguments.length; ++i)
	 {
		 message = message.replace('{'+(i-1)+'}',arguments[i]);
	 }
	 return message;
}

var Step = {
	CheckForBackups:	0,
	BackupsExist: 1,
	ChooseBackupFile: 2,
	RestoreBackup: 3,
	RestorationComplete: 4,
	CheckVersionFileExists : 5,
	CheckVersion: 6,
	CheckWritability: 7,
	CheckRemoteFilesExist: 8,
	BackupFiles: 9,
	InstallFiles: 10,
	CheckForScripts: 11,
	ExecuteScripts: 12,
	UpdateVersion: 13,
	Finished: 14
}

var StepCounter = {
	 step: 0,
	 incrementStep: function(incBy)
	 {
		 this.step += incBy;
	 },

	 incrementAndExecuteStep: function(incBy)
	 {
		 this.step += incBy;
		 executeSteps(this.step);
	 },

	 setStepAndExecute: function(step)
	 {
		 this.step = step;
		 executeSteps(this.step);
	 }
}

var stepNotFound = function(step)
{
	$("#info").append(sprintf(sprintf('
{0}
',message['step_not_found']),step));
}

var updateVersion = function()
{
	$("#info").append(sprintf('
{0}
',message['updating_version']));
	$.get("controller.php","action=UpdateVersion").then(function(data){
			clearWaiting();
			$("#info").append(sprintf('
{0}
',message['version_file_updated']));
			StepCounter.incrementStep(1);
			executeSteps(StepCounter.step);
	},failed);
}

var waiting = function()
{
		$(".waiting").each(function()
    {
    		var dots = $(this).text();
        dots += ".";
        if(dots.length > 3)
        {
          dots = "";
        }
        $(this).text(dots);
    });

}
<|endoftext|>


<commit_before>/*
    While I'm not trying to enforce how creators present their themes in self posts,
    I will require consistency for my own sanity.

    For self-posts to pass the checks, they will require the following syntax:
    [Download](direct link to download file or page)
        Example: [Preview](http://download.site/theme.zip)
        If this is not available, no quick download link will be provided.
    [Preview](direct link to desired preview image)
        Example: [Preview](http://imgur.com/myimage.jpg)
        If this is not provided, Reddit's fallback will be used if available otherwise the post will be skipped.
        URLs are not verified for being available, thus non-existant images will be displayed accordingly.

        For security, the only allowed image domain is Imgur.
        Imgur album support for the first image may eventually be implemented, but it is not currently.
    None of these are case-sensitive.

*/

var VitaThemes = {
    cache: [],
    config: {
        'ignoreInvalid': true,
        'target': 'r/vitathemes', //Read target to fetch info. Should also support multireddits (r/sub1+sub2)
        'page': {
            running: false, //Probably bad way to do this. Tracking if we're currently processing the page load.
            buffer: 150,    //Amount of pixels within range of the bottom of the screen to load the next page.
            size: 20,       //Amount of results to cap at per page. Doesn't guarantee amount that will pass processing checks.
            after: "",      //Current page.
        }
    },
    saveConfig: function(time, sort, search) {
        //Store search options in URL hash. Array and join?
        window.location.hash = (time && sort && search) ? [VitaThemes.config.target, time, sort, search].join("/") : (this.config.target !== 'r/vitathemes') ? this.config.target : '';
    },
    loadConfig: function() {
        //Load initial search options based on URL hash. Currently unoptimized and prone to huge failure!
        var temp = window.location.hash.split('/'); // ["#r", subreddit(s), time, sort, search]

        if (temp.length < 2) return false;

        this.config.target = "r/"+temp[1]; //Life is simply unfair.
        $("#subreddit_link").attr({title: this.config.target, href: '//reddit.com/'+this.config.target});
        if (temp[2]) $("select#t").find("[value^="+temp[2]+"]").attr("selected", "selected");
        if (temp[3]) $("select#sort").find("[value^="+temp[3]+"]").attr("selected", "selected");
        if (temp[4]) $("#search").val(decodeURIComponent(temp[4]));
    },
    init: function() {
        console.log("init called, it's a miracle");

        this.loadConfig();
        this.bind();

        this.doScroll();
    },
    bind: function() {
        var that = this;

        //Searching
        $("#run_search").click(function(e) {
            e.preventDefault();
            that.search();
        });

        $("#search").keyup(function(e){
            if(e.keyCode == 13) { that.search(); }
        });

        $(document).on('click', '.author', function() {
            $("#search").val("author:"+$(this).text());
            that.search();
        });

        //Scrolling
        $(window).scroll(function() {
           //Eventually add thumbnail lazy loading.
           that.doScroll();
        });
    },
    doScroll: function(skip) {
        if (VitaThemes.config.page.after !== null && this.canScroll()) {
            console.log("bottom / scrollin' scrollin' scrollin'");
            this.config.page.running = true; //Prevent spamming.
            this.fetch.reddit(false, true); //Do thing. No callback (false), noSave (true).
        }
    },
    canScroll: function() {
        var buffer = $(document).height() - this.config.page.buffer
            bar = $("#infinite_scroll").offset().top,
            max = $(window).scrollTop() + $(window).height();

        return (!this.config.page.running && (bar <= max) /*|| (max >= buffer)*/);
    },
    search: function() {
        //Empty cache, clear page, reset pagination.
        this.config.page.running = true;
        this.cache = [];
        $('#items').html("");
        this.config.page.after = "";

        this.fetch.reddit();
    },
    run: function() {
        var that = this,
            start = ($(".item") && $(".item").last().index() + 1 || 0);

        $.each(this.cache.slice(start), function(index, value) {
            $('#items').append(that.generate.item(value));
        });

        this.doScroll();
    },
    fetch: {
        parse: function(info) {
            //Determine if we can get the info we need

            //info.self, info.is_self
            /*  Attempt to detect the link to the preview image in the selftext, otherwise attempt to use Reddit's fallback.
                Currently doesn't care about what's at the other end of the URL. The policing should be done via the subreddit.
            */

            var preview;

            //Check if the provided preview link is imgur.
            if (info.selftext && /\[preview\]\((.+?)\)/im.test(info.selftext)) {
                var imgur = /^(?:https?:\/\/)?(?:(?:i|www)\.)?imgur\.com\/(?!gallery)([a-z0-9]{3,})/i, //Overkill.
                    temp = info.selftext.match(/\[preview\]\((.+?)\)/im).pop();

                //If imgur, sanitize the URL and proceed. Otherwise set to false.
                preview = (imgur.test(temp)) ? temp : false;
            };
            if (!preview) { preview = (info.preview && info.preview.images[0].source.url || false); }
            if (!preview && VitaThemes.config.ignoreInvalid == true) {
                console.warn('No preview.', info); //Log problematic items.
                return false;
            }

            //Check if request
            if (/^\[?request/i.test(info.title)) {
                console.warn('Request detected.', info);
                return false;
            }

            if (location.protocol !== 'file:')
                preview = preview.replace(/https?:/, ""); //Remove instances of http: and https: to inherit protocol.
            var download = (info.selftext && /\[download\]\((.+?)\)/im.test(info.selftext)) ? info.selftext.match(/\[download\]\((.+?)\)/im).pop() : false;

            var info = {
                'name': info.title.replace(/\[(Theme|Release)\]/ig, ""),
                'author': info.author,
                'url': "//reddit.com"+info.permalink,
                'downloadUrl': download,
                'previewUrl': preview,
                'nsfw': info.over_18,
                'stats': {
                    'comments': info.num_comments,
                    'votes': {
                        'up': info.ups,
                        'down': info.downs
                    }
                }
            };

            return info;
        },
        process: function (info) {
            var temp = this.parse(info);

            if (temp !== false)
                VitaThemes.cache.push(temp);
        },
        reddit: function(callback, noSave) {
            var that = this;

            if (VitaThemes.config.page.after == null) { //If this is null, we've hit the end of the results.
                VitaThemes.config.page.running = false;
                (callback || VitaThemes.run());
                return false;
            }

            $("#infinite_scroll #pending").show();
            $("#infinite_scroll #chickens").hide();

            //Obtain search box for options
            var search = encodeURIComponent($("#search").val()),
                sort = $("select#sort").val(),
                t = $("select#t").val(),
                scope = "sort="+sort+"&t="+t,
                pagination = "after=" + (VitaThemes.config.page.after || "") + "&limit=" + VitaThemes.config.page.size,
                out = (search.length > 0) ? "search.json?restrict_sr=on&"+scope+"&"+pagination+"&q="+search : ".json?"+scope+"&"+pagination;

            if (!noSave) VitaThemes.saveConfig(t, sort, search); //Store search options in URL hash.

            $.getJSON("https://www.reddit.com/"+VitaThemes.config.target+"/"+out, function(data) {
                var after = data.data.after,
                    before = data.data.before;
                //console.log(after, before, data);

                $.each(data.data.children, function(index, content) {
                    that.process(content.data);
                });

                VitaThemes.config.page.after = data.data.after; //Store for pagination.

                VitaThemes.config.page.running = false;
                $("#infinite_scroll #pending").hide();
                $("#infinite_scroll #chickens").show();
                (callback || VitaThemes.run());
            });
        }
    },
    generate: {
        item: function(info) {
            //info.name, info.url, info.votes?, img.preview, etc.
            var item = $("<div />", {class: 'item'});

            //Populate
            $("<img />", {class: 'preview ' + ((info.nsfw)?'nsfw':''), src: info.previewUrl}).appendTo(item); //Append the preview image.
            $("<div />", {class: 'name', text: info.name}).appendTo(item); //Append the name.
            this.quicklinks(info).appendTo(item); //Append quicklinks.

            //item = $("<div />", {class: 'col-sm-3'}).append(item); //Wrap up for Bootstrap

            return item;
        },
        quicklinks: function(info) {
            //Overly complicated yet future-proof HTML generation. Don't judge me.
            var quicklinks = $("<div />", {class: 'quicklinks'}),
                fa_global = 'fa fa-fw ';

            if (info.downloadUrl !== false) {
                $("<a />", {href: info.downloadUrl, target: '_blank'}).append($("<i />", {class:fa_global+'fa-download'})).appendTo(quicklinks);
            }

            var discuss = $("<a />", {class: 'discuss', href: info.url, target: '_blank', title: 'Discussion'})
            $("<div />", {class: 'comments', text: (info.stats.comments > 99) ? '99+' : info.stats.comments}).appendTo(discuss);
            $("<i />", {class: fa_global+'fa-comments'}).appendTo(discuss);
            discuss.appendTo(quicklinks);

            //Stats
            var icon = $("<i />", {class:fa_global+'fa-bar-chart'})[0].outerHTML,
                icon_comment = $("<span />", {class: fa_global+'fa-comment'})[0].outerHTML,
                icon_author = $("<span />", {class: fa_global+'fa-pencil'})[0].outerHTML,
                up = $("<span />", {class: 'up boats', title: 'Upvotes', text: +info.stats.votes.up})[0].outerHTML,
                down = $("<span />", {class: 'down boats', title: 'Downvotes', text: info.stats.votes.down})[0].outerHTML,

                author = $("<span />", {class: 'author', title: 'Search by author', html: icon_author + info.author})[0].outerHTML;

            $("<div />", {class: 'stats', html: icon + " " + up + " / " + down + "<br>" + author}).appendTo(quicklinks);

            return quicklinks;
        }
    }
}

$(document).ready(function() { VitaThemes.init() });<commit_msg>Minor<commit_after>/*
    While I'm not trying to enforce how creators present their themes in self posts,
    I will require consistency for my own sanity.

    For self-posts to pass the checks, they will require the following syntax:
    [Download](direct link to download file or page)
        Example: [Preview](http://download.site/theme.zip)
        If this is not available, no quick download link will be provided.
    [Preview](direct link to desired preview image)
        Example: [Preview](http://imgur.com/myimage.jpg)
        If this is not provided, Reddit's fallback will be used if available otherwise the post will be skipped.
        URLs are not verified for being available, thus non-existant images will be displayed accordingly.

        For security, the only allowed image domain is Imgur.
        Imgur album support for the first image may eventually be implemented, but it is not currently.
    None of these are case-sensitive.

*/

var VitaThemes = {
    cache: [],
    config: {
        'ignoreInvalid': true,
        'target': 'r/vitathemes', //Read target to fetch info. Should also support multireddits (r/sub1+sub2)
        'page': {
            running: false, //Probably bad way to do this. Tracking if we're currently processing the page load.
            buffer: 150,    //Amount of pixels within range of the bottom of the screen to load the next page.
            size: 20,       //Amount of results to cap at per page. Doesn't guarantee amount that will pass processing checks.
            after: "",      //Current page.
        }
    },
    saveConfig: function(time, sort, search) {
        //Store search options in URL hash. Array and join?
        window.location.hash = (time && sort && search) ? [VitaThemes.config.target, time, sort, search].join("/") : (this.config.target !== 'r/vitathemes') ? this.config.target : '';
    },
    loadConfig: function() {
        //Load initial search options based on URL hash. Currently unoptimized and prone to huge failure!
        var temp = window.location.hash.split('/'); // ["#r", subreddit(s), time, sort, search]

        if (temp.length < 2) return false;

        this.config.target = "r/"+temp[1]; //Life is simply unfair.
        $("#subreddit_link").attr({title: this.config.target, href: '//reddit.com/'+this.config.target});
        if (temp[2]) $("select#t").find("[value^="+temp[2]+"]").attr("selected", "selected");
        if (temp[3]) $("select#sort").find("[value^="+temp[3]+"]").attr("selected", "selected");
        if (temp[4]) $("#search").val(decodeURIComponent(temp[4]));
    },
    init: function() {
        console.log("init called, it's a miracle");

        this.loadConfig();
        this.bind();

        this.doScroll();
    },
    bind: function() {
        var that = this;

        //Searching
        $("#run_search").click(function(e) {
            e.preventDefault();
            that.search();
        });

        $("#search").keyup(function(e){
            if(e.keyCode == 13) { that.search(); }
        });

        $(document).on('click', '.author', function() {
            $("#search").val("author:"+$(this).text());
            that.search();
        });

        //Scrolling
        $(window).scroll(function() {
           //Eventually add thumbnail lazy loading.
           that.doScroll();
        });
    },
    doScroll: function(skip) {
        if (VitaThemes.config.page.after !== null && this.canScroll()) {
            console.log("bottom / scrollin' scrollin' scrollin'");
            this.config.page.running = true; //Prevent spamming.
            this.fetch.reddit(false, true); //Do thing. No callback (false), noSave (true).
        }
    },
    canScroll: function() {
        var buffer = $(document).height() - this.config.page.buffer
            bar = $("#infinite_scroll").offset().top,
            max = $(window).scrollTop() + $(window).height();

        return (!this.config.page.running && (bar <= max) /*|| (max >= buffer)*/);
    },
    search: function() {
        //Empty cache, clear page, reset pagination.
        this.config.page.running = true;
        this.cache = [];
        $('#items').html("");
        this.config.page.after = "";

        this.fetch.reddit();
    },
    run: function() {
        var that = this,
            start = ($(".item") && $(".item").last().index() + 1 || 0);

        $.each(this.cache.slice(start), function(index, value) {
            $('#items').append(that.generate.item(value));
        });

        this.doScroll();
    },
    fetch: {
        parse: function(info) {
            //Determine if we can get the info we need

            //info.self, info.is_self
            /*  Attempt to detect the link to the preview image in the selftext, otherwise attempt to use Reddit's fallback.
                Currently doesn't care about what's at the other end of the URL. The policing should be done via the subreddit.
            */

            var preview;

            //Check if the provided preview link is imgur.
            if (info.selftext && /\[preview\]\((.+?)\)/im.test(info.selftext)) {
                var imgur = /^(?:https?:\/\/)?(?:(?:i|www)\.)?imgur\.com\/(?!gallery)([a-z0-9]{3,})/i, //Overkill.
                    temp = info.selftext.match(/\[preview\]\((.+?)\)/im).pop();

                //If imgur, sanitize the URL and proceed. Otherwise set to false.
                preview = (imgur.test(temp)) ? temp : false;
            };
            if (!preview) { preview = (info.preview && info.preview.images[0].source.url || false); }
            if (!preview && VitaThemes.config.ignoreInvalid == true) {
                console.warn('No preview.', info); //Log problematic items.
                return false;
            }

            //Check if request
            if (/(^\[?request|tutorial)/i.test(info.title)) {
                console.warn('Potentially irrelevant post detected. (based on title)', info);
                return false;
            }

            if (location.protocol !== 'file:')
                preview = preview.replace(/https?:/, ""); //Remove instances of http: and https: to inherit protocol.
            var download = (info.selftext && /\[download\]\((.+?)\)/im.test(info.selftext)) ? info.selftext.match(/\[download\]\((.+?)\)/im).pop() : false;

            var info = {
                'name': info.title.replace(/\[(Theme|Release)\]/ig, ""),
                'author': info.author,
                'url': info.url, //Whatever the post may link to (otherwise, to the thread)
                'permalink': "//reddit.com"+info.permalink, //Permalink to the thread
                'downloadUrl': download,
                'previewUrl': preview,
                'nsfw': info.over_18,
                'stats': {
                    'comments': info.num_comments,
                    'votes': {
                        'up': info.ups,
                        'down': info.downs
                    }
                }
            };

            return info;
        },
        process: function (info) {
            var temp = this.parse(info);

            if (temp !== false)
                VitaThemes.cache.push(temp);
        },
        reddit: function(callback, noSave) {
            var that = this;

            if (VitaThemes.config.page.after == null) { //If this is null, we've hit the end of the results.
                VitaThemes.config.page.running = false;
                (callback || VitaThemes.run());
                return false;
            }

            $("#infinite_scroll #pending").show();
            $("#infinite_scroll #chickens").hide();

            //Obtain search box for options
            var search = encodeURIComponent($("#search").val()),
                sort = $("select#sort").val(),
                t = $("select#t").val(),
                scope = "sort="+sort+"&t="+t,
                pagination = "after=" + (VitaThemes.config.page.after || "") + "&limit=" + VitaThemes.config.page.size,
                out = (search.length > 0) ? "search.json?restrict_sr=on&"+scope+"&"+pagination+"&q="+search : ".json?"+scope+"&"+pagination;

            if (!noSave) VitaThemes.saveConfig(t, sort, search); //Store search options in URL hash.

            $.getJSON("https://www.reddit.com/"+VitaThemes.config.target+"/"+out, function(data) {
                var after = data.data.after,
                    before = data.data.before;
                //console.log(after, before, data);

                $.each(data.data.children, function(index, content) {
                    that.process(content.data);
                });

                VitaThemes.config.page.after = data.data.after; //Store for pagination.

                VitaThemes.config.page.running = false;
                $("#infinite_scroll #pending").hide();
                $("#infinite_scroll #chickens").show();
                (callback || VitaThemes.run());
            });
        }
    },
    generate: {
        item: function(info) {
            //info.name, info.url, info.votes?, img.preview, etc.
            var item = $("<div />", {class: 'item'});

            //Populate
            $("<a />", {href: info.url, target: '_blank'}).append(
                $("<img />", {class: 'preview ' + ((info.nsfw)?'nsfw':''), src: info.previewUrl}) //Append the preview image.
            ).appendTo(item);
            $("<div />", {class: 'name', text: info.name}).appendTo(item); //Append the name.
            this.quicklinks(info).appendTo(item); //Append quicklinks.

            //item = $("<div />", {class: 'col-sm-3'}).append(item); //Wrap up for Bootstrap

            return item;
        },
        quicklinks: function(info) {
            //Overly complicated yet future-proof HTML generation. Don't judge me.
            var quicklinks = $("<div />", {class: 'quicklinks'}),
                fa_global = 'fa fa-fw ';

            if (info.downloadUrl !== false) {
                $("<a />", {href: info.downloadUrl, target: '_blank'}).append($("<i />", {class:fa_global+'fa-download'})).appendTo(quicklinks);
            }

            var discuss = $("<a />", {class: 'discuss', href: info.permalink, target: '_blank', title: 'Discussion'})
            $("<div />", {class: 'comments', text: (info.stats.comments > 99) ? '99+' : info.stats.comments}).appendTo(discuss);
            $("<i />", {class: fa_global+'fa-comments'}).appendTo(discuss);
            discuss.appendTo(quicklinks);

            //Stats
            var icon = $("<i />", {class:fa_global+'fa-bar-chart'})[0].outerHTML,
                icon_comment = $("<span />", {class: fa_global+'fa-comment'})[0].outerHTML,
                icon_author = $("<span />", {class: fa_global+'fa-pencil'})[0].outerHTML,
                up = $("<span />", {class: 'up boats', title: 'Upvotes', text: +info.stats.votes.up})[0].outerHTML,
                down = $("<span />", {class: 'down boats', title: 'Downvotes', text: info.stats.votes.down})[0].outerHTML,

                author = $("<span />", {class: 'author', title: 'Search by author', html: icon_author + info.author})[0].outerHTML;

            $("<div />", {class: 'stats', html: icon + " " + up + " / " + down + "<br>" + author}).appendTo(quicklinks);

            return quicklinks;
        }
    }
}

$(document).ready(function() { VitaThemes.init() });<|endoftext|>


<commit_before>
$.extend($.easing,
{
    def: 'easeOutQuad',
    easeInOutExpo: function (x, t, b, c, d) {
        if (t==0) return b;
        if (t==d) return b+c;
        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
    }
});

(function( $ ) {

    var settings;
    var disableScrollFn = false;
    var navItems;
    var navs = {}, sections = {};

    $.fn.navScroller = function(options) {
        settings = $.extend({
            scrollToOffset: 170,
            scrollSpeed: 800,
            activateParentNode: true,
        }, options );
        navItems = this;

        //attatch click listeners
    	navItems.on('click', function(event){
    		event.preventDefault();
            var navID = $(this).attr("href").substring(1);
            disableScrollFn = true;
            activateNav(navID);
            populateDestinations(); //recalculate these!
        	$('html,body').animate({scrollTop: sections[navID] - settings.scrollToOffset},
                settings.scrollSpeed, "easeInOutExpo", function(){
                    disableScrollFn = false;
                }
            );
    	});

        //populate lookup of clicable elements and destination sections
        populateDestinations(); //should also be run on browser resize, btw

        // setup scroll listener
        $(document).scroll(function(){
            if (disableScrollFn) { return; }
            var page_height = $(window).height();
            var pos = $(this).scrollTop();
            for (i in sections) {
                if ((pos + settings.scrollToOffset >= sections[i]) && sections[i] < pos + page_height){
                    activateNav(i);
                }
            }
        });
    };

    function populateDestinations() {
        navItems.each(function(){
            var scrollID = $(this).attr('href').substring(1);
            navs[scrollID] = (settings.activateParentNode)? this.parentNode : this;
            sections[scrollID] = $(document.getElementById(scrollID)).offset().top;
        });
    }

    function activateNav(navID) {
        for (nav in navs) { $(navs[nav]).removeClass('active'); }
        $(navs[navID]).addClass('active');
    }
})( jQuery );

$(document).ready(function (){

    $(' nav .navscroller li a').navScroller();

    //section divider icon click gently scrolls to reveal the section
	$(".sectiondivider").on('click', function(event) {
    	$('html,body').animate({scrollTop: $(event.target.parentNode).offset().top - 50}, 400, "linear");
	});

    //links going to other sections nicely scroll
	$(".container a").each(function(){
        if ($(this).attr("href").charAt(0) == '#'){
            $(this).on('click', function(event) {
        		event.preventDefault();
                var target = $(event.target).closest("a");
                var targetHight =  $(target.attr("href")).offset().top
            	$('html,body').animate({scrollTop: targetHight - 170}, 800, "easeInOutExpo");
            });
        }
	});

});

<commit_msg>Update site.js<commit_after>
$.extend($.easing,
{
    def: 'easeOutQuad',
    easeInOutExpo: function (x, t, b, c, d) {
        if (t==0) return b;
        if (t==d) return b+c;
        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
    }
});

(function( $ ) {

    var settings;
    var disableScrollFn = false;
    var navItems;
    var navs = {}, sections = {};

    $.fn.navScroller = function(options) {
        settings = $.extend({
            scrollToOffset: 170,
            scrollSpeed: 800,
            activateParentNode: true,
        }, options );
        navItems = this;

        //attatch click listeners
    	navItems.on('click', function(event){
    		event.preventDefault();
            var navID = $(this).attr("href").substring(1);
            disableScrollFn = true;
            activateNav(navID);
            populateDestinations(); //recalculate these!
        	$('html,body').animate({scrollTop: sections[navID] - settings.scrollToOffset},
                settings.scrollSpeed, "easeInOutExpo", function(){
                    disableScrollFn = false;
                }
            );
    	});

        //populate lookup of clicable elements and destination sections
        populateDestinations(); //should also be run on browser resize, btw

        // setup scroll listener
        $(document).scroll(function(){
            if (disableScrollFn) { return; }
            var page_height = $(window).height();
            var pos = $(this).scrollTop();
            for (i in sections) {
                if ((pos + settings.scrollToOffset >= sections[i]) && sections[i] < pos + page_height){
                    activateNav(i);
                }
            }
        });
    };

    function populateDestinations() {
        navItems.each(function(){
            var scrollID = $(this).attr('href').substring(1);
            navs[scrollID] = (settings.activateParentNode)? this.parentNode : this;
            sections[scrollID] = $(document.getElementById(scrollID)).offset().top;
        });
    }

    function activateNav(navID) {
        for (nav in navs) { $(navs[nav]).removeClass('active'); }
        $(navs[navID]).addClass('active');
    }
})( jQuery );

$(document).ready(function (){

    $(".navscroller").("li a").navScroller();

    //section divider icon click gently scrolls to reveal the section
	$(".sectiondivider").on('click', function(event) {
    	$('html,body').animate({scrollTop: $(event.target.parentNode).offset().top - 50}, 400, "linear");
	});

    //links going to other sections nicely scroll
	$(".container a").each(function(){
        if ($(this).attr("href").charAt(0) == '#'){
            $(this).on('click', function(event) {
        		event.preventDefault();
                var target = $(event.target).closest("a");
                var targetHight =  $(target.attr("href")).offset().top
            	$('html,body').animate({scrollTop: targetHight - 170}, 800, "easeInOutExpo");
            });
        }
	});

});

<|endoftext|>


<commit_before>if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/serviceWorker.js', {
    scope: '/js/'
  });
}
<commit_msg>Update site.js<commit_after>if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/js/serviceWorker.js', {
    scope: '/js/'
  });
}
<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:foldmethod=marker:foldmarker={{{,}}}:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

let {equiv:equiv} = require('./test/equiv');
let Util = require('util');

function merge(ctor, obj) {
  for (let [k,v] in Iterator(obj))
    ctor[k] = v;
}

const TAPProducer = function TAPProducer() {
  this.outputStream = require('system').stdout;

  var depth;
  Object.defineProperty(this, "paddDepth", {

    getter: function() { return depth },
    setter: function(d) {
      d = new Number(d).valueOf();
      if (isNaN(d))
        throw TypeError("paddDepth must be a number");
      depth = d;
      this.padding = this.getPadd();
    }
  });

  this.paddDepth = 0;
}

merge(TAPProducer.prototype, {
  paddStr: '  ',

  colourize: true,

  green: function green() {
    var a = Array.slice(arguments);
    if (a.length == 0 && a[0] instanceof Array)
      a = a[0];

    a = a.join(' ');
    if (!this.colourize || !a.length)
      return a;
    return '\x1b[32m' + a + '\x1b[0m';
  },

  red: function red() {
    var a = Array.slice(arguments);
    if (a.length == 1 && a[0] instanceof Array)
      a = a[0];

    a = a.join(' ');
    if (!this.colourize || !a.length)
      return a;
    return '\x1b[31m' + a + '\x1b[0m';
  },

  // Handle/print the assert
  do_assert: function do_assert(a) {

    a.num = ++this._state.currentCase.numAsserts;

    let data = [a.num];
    data.push(a.ok ? this.green('ok') : this.red('not ok'));

    if (a.message !== undefined)
      data.push('-', a.message)
    else if ('defaultMsg' in a)
      data.push('-', a.defaultMsg)

    if (!a.ok)
      this._state.currentCase.assertsFailed.push(a);

    this.print(data);

    if (a.diag)
      this.diag(a.diag);
    if (a.errorDiag)
      this.diag(a.errorDiag, this.red);
    return a.ok;
  },

  print: function print() {
    var msg;
    if (arguments[0] instanceof Array)
      msg = arguments[0].join(" ");
    else
      msg = Array.join(arguments, " ");

    this.outputStream.print(this.padding + msg)
  },

  diag: function diag(msg,filter) {
    var self = this,
        lines = String(msg).split(/\n/)

    lines = lines.map(function(l) {
      return self.padding + ("# " + l)
    });

    if (filter)
      lines = lines.map(function(x) { return filter.call(self,x) });
    lines = lines.join("\n");

    this.outputStream.print(lines);
  },

  getPadd: function getPadd() {
    var i = 0;
    return [this.paddStr for (i in Util.Range(0, this.paddDepth))].join('');
  }
});

const Suite = exports.Suite = function Suite(cases) {
  this.__proto__.__proto__.constructor.call(this);
  this._state = {
    cases: [],
    assertsFailed: [],
    numAsserts: 0,
    currentCase: undefined
  }
  this.nested = false;

  if (!cases) {
    return;
  }

  // TODO: setup and teardown methods
  for (let [k,t] in Iterator(cases)) {

    if (/^test/.test(k) == false)
      continue;

    let testCase = {
      name: k.replace(/^test[_ ]?/, ""),
      index: this._state.cases.length,
      numAsserts: 0,
      assertsFailed: []
    };

    // Not a function - must be a sub-test
    if (typeof t != "function") {
      let s = testCase.suite = new Suite(t);
      s.nested = true;
      s.name = k;
    }
    else testCase.func = t;

    this._state.cases.push(testCase);
  }

  this._state.plan = this._state.cases.length;
}

Suite.prototype.__proto__ = TAPProducer.prototype;

merge(Suite.prototype, {

  run: function run() {
    if (!this._state.cases.length) {
      throw new Error("Cannot run a Test.Suite without any test cases!");
    }

    var oldCurrent = exports.__currentSuite__;
    try {
      exports.__currentSuite__ = this;
      for (let [name,test] in Iterator(this._state.cases)) {
        this.runCase( test);
      }
    }
    finally {
      exports.__currentSuite__ = oldCurrent;
    }
    this.finalize();
  },

  runCase: function runCase(theCase) {
    var asserts = exports.asserts,
        depth = this.paddDepth,
        error;

    try {
      this.diag(theCase.name + "...");
      this.paddDepth = depth + 1;
      try {
        this._state.currentCase = theCase;
        if (theCase.suite) {
          var nested = theCase.suite;
          nested.paddDepth = this.paddDepth;

          nested.run();
          theCase = nested._state;
        }
        else {
          theCase.func.call(asserts, this);
        }
      }
      finally {
        this.printPlan(theCase);
        this.paddDepth = depth;
      }
    }
    catch (e) {
      error = e;
    }
    this.finishCase(theCase, error)
  },

  finishCase: function finishCase(theCase, error) {
    var failed = error || theCase.assertsFailed.length > 0;
    this._state.currentCase = this._state;
    if (error) {

      var diag = "case terminated with exception:\n"
               + error.toString();

      if (error.fileName) {
        diag += "\n  " +  error.stack.replace(/\n/g, "\n  ");
      }

      this.do_assert( {
        type: 'case',
        when: new Date(),
        errorDiag: diag,
        ok: !failed,
        message: theCase.name
      });
      return;
    }

    this.do_assert( {
      type: 'case',
      when: new Date(),
      ok: !failed,
      message: theCase.name
    });
    delete this._state.currentCase;

  },

  printPlan: function printPlan(theCase) {
    if (theCase.planned)
      return;

    if ("plan" in theCase == false)
      theCase.plan = theCase.numAsserts;

    this.print("1.." + theCase.plan);
    theCase.planned = true;
  },

  finalize: function finalize() {
    if (this.nested)
      return;
    if (this._state.assertsFailed.length)
      this.print(this.red("Failed", this._state.assertsFailed.length, "tests"));
    else
      this.print(this.green("All tests successful"));
  }
})

exports.runner = function runner() {
  var suite;
  var args = Array.slice(arguments);
  if (args.length == 1 && args[0] instanceof Suite) {
    builder = args[0];
  }
  else if (args.every(function(x) { return x instanceof Suite })) {
    suite = new Suite();
    args.forEach(suite.addSubSuite, builder);
  }
  else if (args[0] instanceof Object) {
    suite = new Suite(args[0]);
  }
  else {
    throw new TypeError("Invalid arguments to test.runner");
  }

  return suite.run();
};

// The general test runner for 'run this dir of tests'
exports.prove = function prove() {
  const fs = require('filesystem-base');

  var i = 0;
  var options = {};

  if (typeof arguments[0] == "object") {
    i++;
    options = arguments[0];
  }

  var test_files = [];

  for (; i < arguments.length; ++i) {
    var x = arguments[i].replace(/^file:\/\//, '');

    if (!fs.exists(x))
      throw new TypeError("Cannot determine test source for " + uneval(x));

    if (fs.isDirectory(x))
      findTests(fs.list(x));
    else
      findTests([x]);
  }

  var test = loadTests(test_files);
  var suite = new Suite(test);
  suite.run();

  var failed = suite._state.assertsFailed.length;
  quit( failed <= 255 ? failed : 255 );

  function findTests(files) {
    for ([,x] in Iterator(files) ) {
      // Ingore "." dot-files
      if (x.match(/(?:^|[\/\\])\.[^\/]*$/))
        continue;

      if (fs.isDirectory(x)) {
        if (options.recurse)
          findTests(fs.list(x));
        continue;
      }

      if (x.match(/\.t\.js$/))
        test_files.push(x);
    }
  }

  function loadTests(files) {
    var filename;
    var testObject = {};
    for ([,filename] in Iterator(files) ) {
      var id  = fs.canonical(filename);

      var test = require('file://' + id);
      testObject['test ' + id] = test;
    }

    return testObject;
  }
}

var currentSuite;
Object.defineProperty( exports, "__currentSuite__", {
  getter: function() { return currentSuite },
  setter: function(x) {
    if (x === undefined || x instanceof Suite)
      return currentSuite = x;
    throw new TypeError("__currentSuite__ must be an instanceof test.Suite");
  },
  configurable: false,
  enumerable: false
});

exports.asserts = {
  same: function(got, expected, message) {

    var ok = !!equiv(got, expected);

    var a = {
      type: 'same',
      when: new Date(),
      ok: ok,
      message: message,
      defaultMsg: "arguments are the same"
    };

    if (!ok) {
      a.diag = "   Got: " + (got === undefined
                             ? "undefined"
                             : uneval(got))
             + "\nWanted: " + (expected === undefined
                             ? "undefined"
                             : uneval(expected));
    }
    return exports.__currentSuite__.do_assert(a);
  },

  instanceOf: function(obj, type, msg) {
    var test = obj instanceof type;
    return exports.__currentSuite__.do_assert( {
      type: 'instanceof',
      ok: !!test,
      when: new Date(),
      message: msg,
      defaultMsg: "is instanceof "
                  + (type.name || "correct type")
    });
  },

  ok: function ok(test, msg) {
    if (arguments.length == 0) {
      test = true; // If nothing is passed in, then assume its successful
    }
    return exports.__currentSuite__.do_assert( {
      type: 'ok',
      ok: !!test,
      when: new Date(),
      message: msg
    } );
  },

  matches: function matches(value, re, msg) {
    if (re instanceof RegExp == false) {
      re = new RegExp(re);
    }
    var ok = !!re(value);

    var a = {
      type: 'matches',
      ok: ok,
      when: new Date(),
      message: msg,
      defaultMsg: "matches " + re,
    };

    if (!ok) {
      a.diag = (value === undefined
                ? "undefined"
                : uneval(value))
             + "\nDidn't Match: " + re
    }

    return exports.__currentSuite__.do_assert(a);
  },

  diag: function diag() {
    var suite = exports.__currentSuite__;
    suite.diag.apply(suite, arguments);
  },

  throwsOk: function( testcase, expected, message ) {
    if ( message == undefined ) {
      message = expected;
      expected = undefined;
    }
    var suite = exports.__currentSuite__;

    var a = {
      type: "throwsOk",
      ok: 0,
      message: message,
      defaultMsg: "throws error ok",
    };

    try {
      testcase();
      a.diag = "No error thrown";
      suite.do_assert( a );
    }
    catch ( e ) {
      if ( expected === undefined || expected == e.toString() ) {
        a.ok = true;
        suite.do_assert( a );
      }
      else {
        a.diag = "   Got: " + e.toString() + "\n"
                 "Wanted: " + expected;
        suite.do_assert( a );
      }
    }
  }

};

/*
t = new TestHarness;
t.test_expect_prop = function() {
  with(this) {
    ok(true, "foo")
  }
}
t.test_expect_prop.expect = 6;

t.test_die = function() {
  throw new Error("foo\nbar");
}

t.test_noplan = function() {
  this.ok("no plan");
  this.same(1,new Number("1"), "numbers are the same");
}

t.test_gt_ten = function () {
  this.expect(12);
  [this.ok(1, i) for (i in Util.Range(0,12) )];
}

t.test_gt = function() {
  this.expect(1);
  this.ok();
  this.ok();
  this.ok();
}

t.go();
*/
<commit_msg>Fix test.js for change to fs.list behaviour.<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:foldmethod=marker:foldmarker={{{,}}}:
/*
The MIT License

Copyright (c) 2008, 2009 Flusspferd contributors (see "CONTRIBUTORS" or
                                       http://flusspferd.org/contributors.txt)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

let {equiv:equiv} = require('./test/equiv');
let Util = require('util');

function merge(ctor, obj) {
  for (let [k,v] in Iterator(obj))
    ctor[k] = v;
}

const TAPProducer = function TAPProducer() {
  this.outputStream = require('system').stdout;

  var depth;
  Object.defineProperty(this, "paddDepth", {

    getter: function() { return depth },
    setter: function(d) {
      d = new Number(d).valueOf();
      if (isNaN(d))
        throw TypeError("paddDepth must be a number");
      depth = d;
      this.padding = this.getPadd();
    }
  });

  this.paddDepth = 0;
}

merge(TAPProducer.prototype, {
  paddStr: '  ',

  colourize: true,

  green: function green() {
    var a = Array.slice(arguments);
    if (a.length == 0 && a[0] instanceof Array)
      a = a[0];

    a = a.join(' ');
    if (!this.colourize || !a.length)
      return a;
    return '\x1b[32m' + a + '\x1b[0m';
  },

  red: function red() {
    var a = Array.slice(arguments);
    if (a.length == 1 && a[0] instanceof Array)
      a = a[0];

    a = a.join(' ');
    if (!this.colourize || !a.length)
      return a;
    return '\x1b[31m' + a + '\x1b[0m';
  },

  // Handle/print the assert
  do_assert: function do_assert(a) {

    a.num = ++this._state.currentCase.numAsserts;

    let data = [a.num];
    data.push(a.ok ? this.green('ok') : this.red('not ok'));

    if (a.message !== undefined)
      data.push('-', a.message)
    else if ('defaultMsg' in a)
      data.push('-', a.defaultMsg)

    if (!a.ok)
      this._state.currentCase.assertsFailed.push(a);

    this.print(data);

    if (a.diag)
      this.diag(a.diag);
    if (a.errorDiag)
      this.diag(a.errorDiag, this.red);
    return a.ok;
  },

  print: function print() {
    var msg;
    if (arguments[0] instanceof Array)
      msg = arguments[0].join(" ");
    else
      msg = Array.join(arguments, " ");

    this.outputStream.print(this.padding + msg)
  },

  diag: function diag(msg,filter) {
    var self = this,
        lines = String(msg).split(/\n/)

    lines = lines.map(function(l) {
      return self.padding + ("# " + l)
    });

    if (filter)
      lines = lines.map(function(x) { return filter.call(self,x) });
    lines = lines.join("\n");

    this.outputStream.print(lines);
  },

  getPadd: function getPadd() {
    var i = 0;
    return [this.paddStr for (i in Util.Range(0, this.paddDepth))].join('');
  }
});

const Suite = exports.Suite = function Suite(cases) {
  this.__proto__.__proto__.constructor.call(this);
  this._state = {
    cases: [],
    assertsFailed: [],
    numAsserts: 0,
    currentCase: undefined
  }
  this.nested = false;

  if (!cases) {
    return;
  }

  // TODO: setup and teardown methods
  for (let [k,t] in Iterator(cases)) {

    if (/^test/.test(k) == false)
      continue;

    let testCase = {
      name: k.replace(/^test[_ ]?/, ""),
      index: this._state.cases.length,
      numAsserts: 0,
      assertsFailed: []
    };

    // Not a function - must be a sub-test
    if (typeof t != "function") {
      let s = testCase.suite = new Suite(t);
      s.nested = true;
      s.name = k;
    }
    else testCase.func = t;

    this._state.cases.push(testCase);
  }

  this._state.plan = this._state.cases.length;
}

Suite.prototype.__proto__ = TAPProducer.prototype;

merge(Suite.prototype, {

  run: function run() {
    if (!this._state.cases.length) {
      throw new Error("Cannot run a Test.Suite without any test cases!");
    }

    var oldCurrent = exports.__currentSuite__;
    try {
      exports.__currentSuite__ = this;
      for (let [name,test] in Iterator(this._state.cases)) {
        this.runCase( test);
      }
    }
    finally {
      exports.__currentSuite__ = oldCurrent;
    }
    this.finalize();
  },

  runCase: function runCase(theCase) {
    var asserts = exports.asserts,
        depth = this.paddDepth,
        error;

    try {
      this.diag(theCase.name + "...");
      this.paddDepth = depth + 1;
      try {
        this._state.currentCase = theCase;
        if (theCase.suite) {
          var nested = theCase.suite;
          nested.paddDepth = this.paddDepth;

          nested.run();
          theCase = nested._state;
        }
        else {
          theCase.func.call(asserts, this);
        }
      }
      finally {
        this.printPlan(theCase);
        this.paddDepth = depth;
      }
    }
    catch (e) {
      error = e;
    }
    this.finishCase(theCase, error)
  },

  finishCase: function finishCase(theCase, error) {
    var failed = error || theCase.assertsFailed.length > 0;
    this._state.currentCase = this._state;
    if (error) {

      var diag = "case terminated with exception:\n"
               + error.toString();

      if (error.fileName) {
        diag += "\n  " +  error.stack.replace(/\n/g, "\n  ");
      }

      this.do_assert( {
        type: 'case',
        when: new Date(),
        errorDiag: diag,
        ok: !failed,
        message: theCase.name
      });
      return;
    }

    this.do_assert( {
      type: 'case',
      when: new Date(),
      ok: !failed,
      message: theCase.name
    });
    delete this._state.currentCase;

  },

  printPlan: function printPlan(theCase) {
    if (theCase.planned)
      return;

    if ("plan" in theCase == false)
      theCase.plan = theCase.numAsserts;

    this.print("1.." + theCase.plan);
    theCase.planned = true;
  },

  finalize: function finalize() {
    if (this.nested)
      return;
    if (this._state.assertsFailed.length)
      this.print(this.red("Failed", this._state.assertsFailed.length, "tests"));
    else
      this.print(this.green("All tests successful"));
  }
})

exports.runner = function runner() {
  var suite;
  var args = Array.slice(arguments);
  if (args.length == 1 && args[0] instanceof Suite) {
    builder = args[0];
  }
  else if (args.every(function(x) { return x instanceof Suite })) {
    suite = new Suite();
    args.forEach(suite.addSubSuite, builder);
  }
  else if (args[0] instanceof Object) {
    suite = new Suite(args[0]);
  }
  else {
    throw new TypeError("Invalid arguments to test.runner");
  }

  return suite.run();
};

// The general test runner for 'run this dir of tests'
exports.prove = function prove() {
  const fs = require('filesystem-base');

  var i = 0;
  var options = {};

  if (typeof arguments[0] == "object") {
    i++;
    options = arguments[0];
  }

  var test_files = [];

  for (; i < arguments.length; ++i) {
    var x = arguments[i].replace(/^file:\/\//, '');

    if (!fs.exists(x))
      throw new TypeError("Cannot determine test source for " + uneval(x));

    if (fs.isDirectory(x))
      findTests( fs.list(x).map(function(f) x + "/" + f) );
    else
      findTests([x]);
  }

  var test = loadTests(test_files);
  var suite = new Suite(test);
  suite.run();

  var failed = suite._state.assertsFailed.length;
  quit( failed <= 255 ? failed : 255 );

  function findTests(files) {
    for ([,x] in Iterator(files) ) {
      // Ingore "." dot-files
      if (x.match(/(?:^|[\/\\])\.[^\/]*$/))
        continue;

      if (fs.isDirectory(x)) {
        if (options.recurse)
          findTests(fs.list(x));
        continue;
      }

      if (x.match(/\.t\.js$/))
        test_files.push(x);
    }
  }

  function loadTests(files) {
    var filename;
    var testObject = {};
    for ([,filename] in Iterator(files) ) {
      var id  = fs.canonical(filename);

      var test = require('file://' + id);
      testObject['test ' + id] = test;
    }

    return testObject;
  }
}

var currentSuite;
Object.defineProperty( exports, "__currentSuite__", {
  getter: function() { return currentSuite },
  setter: function(x) {
    if (x === undefined || x instanceof Suite)
      return currentSuite = x;
    throw new TypeError("__currentSuite__ must be an instanceof test.Suite");
  },
  configurable: false,
  enumerable: false
});

exports.asserts = {
  same: function(got, expected, message) {

    var ok = !!equiv(got, expected);

    var a = {
      type: 'same',
      when: new Date(),
      ok: ok,
      message: message,
      defaultMsg: "arguments are the same"
    };

    if (!ok) {
      a.diag = "   Got: " + (got === undefined
                             ? "undefined"
                             : uneval(got))
             + "\nWanted: " + (expected === undefined
                             ? "undefined"
                             : uneval(expected));
    }
    return exports.__currentSuite__.do_assert(a);
  },

  instanceOf: function(obj, type, msg) {
    var test = obj instanceof type;
    return exports.__currentSuite__.do_assert( {
      type: 'instanceof',
      ok: !!test,
      when: new Date(),
      message: msg,
      defaultMsg: "is instanceof "
                  + (type.name || "correct type")
    });
  },

  ok: function ok(test, msg) {
    if (arguments.length == 0) {
      test = true; // If nothing is passed in, then assume its successful
    }
    return exports.__currentSuite__.do_assert( {
      type: 'ok',
      ok: !!test,
      when: new Date(),
      message: msg
    } );
  },

  matches: function matches(value, re, msg) {
    if (re instanceof RegExp == false) {
      re = new RegExp(re);
    }
    var ok = !!re(value);

    var a = {
      type: 'matches',
      ok: ok,
      when: new Date(),
      message: msg,
      defaultMsg: "matches " + re,
    };

    if (!ok) {
      a.diag = (value === undefined
                ? "undefined"
                : uneval(value))
             + "\nDidn't Match: " + re
    }

    return exports.__currentSuite__.do_assert(a);
  },

  diag: function diag() {
    var suite = exports.__currentSuite__;
    suite.diag.apply(suite, arguments);
  },

  throwsOk: function( testcase, expected, message ) {
    if ( message == undefined ) {
      message = expected;
      expected = undefined;
    }
    var suite = exports.__currentSuite__;

    var a = {
      type: "throwsOk",
      ok: 0,
      message: message,
      defaultMsg: "throws error ok",
    };

    try {
      testcase();
      a.diag = "No error thrown";
      suite.do_assert( a );
    }
    catch ( e ) {
      if ( expected === undefined || expected == e.toString() ) {
        a.ok = true;
        suite.do_assert( a );
      }
      else {
        a.diag = "   Got: " + e.toString() + "\n"
                 "Wanted: " + expected;
        suite.do_assert( a );
      }
    }
  }

};

/*
t = new TestHarness;
t.test_expect_prop = function() {
  with(this) {
    ok(true, "foo")
  }
}
t.test_expect_prop.expect = 6;

t.test_die = function() {
  throw new Error("foo\nbar");
}

t.test_noplan = function() {
  this.ok("no plan");
  this.same(1,new Number("1"), "numbers are the same");
}

t.test_gt_ten = function () {
  this.expect(12);
  [this.ok(1, i) for (i in Util.Range(0,12) )];
}

t.test_gt = function() {
  this.expect(1);
  this.ok();
  this.ok();
  this.ok();
}

t.go();
*/
<|endoftext|>