// $.context from http://knol.google.com/k/jason/jquery-callback-contexts/1fk6f8qeqtvqa/2#
jQuery.extend({
	context: function(context, callback) {
		var co =
		{
			callback: function(method) {
				if (typeof method == 'string') method = context[method];
				var cb = function() { return method.apply(context, arguments); }
				return cb;
			}
		};
		if(callback != null)
		{
			return co.callback(callback);
		}
		return co;
	}
}); 

/*
jQuery Url Plugin
    * Version 1.0
    * 2009-03-22 19:30:05
    * URL: http://ajaxcssblog.com/jquery/url-read-get-variables/
    * Description: jQuery Url Plugin gives the ability to read GET parameters from the actual URL
    * Author: Matthias Jäggli
    * Copyright: Copyright (c) 2009 Matthias Jäggli under dual MIT/GPL license.
		* Modified by John Beech 2009-11-18 to return false instead of an empty string ""
		* Usage: $.url.param("name") 
*/
(function ($) {
    $.url = {};
    $.extend($.url, {
        _params: {},
        init: function(){
            var paramsRaw = "";
            try {
                paramsRaw = 
                    (document.location.href.split("?", 2)[1] || "").split("#")[0].split("&") || [];
                for(var i = 0; i< paramsRaw.length; i++){
                    var single = paramsRaw[i].split("=");
                    if(single[0])
                        this._params[single[0]] = unescape(single[1]);
                }
            }
            catch(e){
                alert(e);
            }
        },
        param: function(name){
            return this._params[name] || false;
        },
        paramAll: function(){
            return this._params;
        }
    });
    $.url.init();
})(jQuery);

/**
 * OpenComment providers a HTML/Javascript interface to a PHP webservice that authorises users using OpenID and allows the display and submission of comments.
 * The system is designed to run on static pages from any location on a website using a simple JavaScript include.
 * The URL of the page, discounting ?get=variables, is used as the ID for all comments, making it simple and easy to deploy.
 * @param SERVICE_BASE		(required) specifies the base URL of the PHP service to communicate with
 * @param IGNORE_PAGE_VARIABLES		(optional) specifies whether to match the basic url, or advanced urls with a ? and URL encoded variables
 * @param PAGE_URL		(optional) set this to specify an identifier or URL different to the page hosting the javascript.
 */
function OpenComment(SERVICE_BASE, IGNORE_PAGE_VARIABLES, PAGE_URL)
{
	this.SERVICE_BASE = (SERVICE_BASE != null) ? SERVICE_BASE : false;	
	this.IGNORE_PAGE_VARIABLES = (IGNORE_PAGE_VARIABLES != null) ? IGNORE_PAGE_VARIABLES : true; 
	this.PAGE_URL = (PAGE_URL != null) ? PAGE_URL : false; 
	
	var script_url = String(window.location);
	if(script_url.indexOf('?') > 0)
		script_url = script_url.substring(0,script_url.indexOf('?'));
	
	this.SERVICE_URL = (SERVICE_BASE != null) ? SERVICE_BASE + 'service.php' : false;
	this.SERVICE_LOGIN = (SERVICE_BASE != null) ? SERVICE_BASE : false; // defaults to index.php
	this.IS_SERVICE_LOGIN = (script_url == SERVICE_BASE) ? true : false;

	if(this.SERVICE_URL == false)
	{
		throw "OpenComment Service URL must be set.";
	}

	this.PROVIDER_URL = false;
	this.COMMENT_TYPE = 'comment'; // 'comment', 'feedback', 'todo', 'bug', 'suggestion'
		
	this.$container = false;
	this.$messagebox = false;
	this.$form = false;
	this.$comments = false;
	this.$iframe = false;
	this.$openid_form = false;
	
	this.REDIRECT = $.url.param("redirect");
	
	this.embedHere();
	$(document).ready($.context(this, this.setup));
}

OpenComment.prototype.embedHere = function()
{
	document.writeln('<div id="opencomment_container"></div>');
}

OpenComment.prototype.setup = function()
{
	this.createJQueryObjects();
	this.createChildren();
	this.attachActions(this.$container);
	
	if(this.PAGE_URL == false)
	{
		if($.URLDecode) this.PAGE_URL = $.URLDecode(String(window.location));
		else this.PAGE_URL = String(window.location);
	
		if(this.IGNORE_PAGE_VARIABLES == true)
		{
			if(this.PAGE_URL.indexOf('?') > 0)
			{
				this.PAGE_URL = this.PAGE_URL.substring(0, this.PAGE_URL.indexOf('?'));
			}
		}
	}

	this.DisplayForm();
	this.DisplayComments();
}

/**
 * Create new HTML elements as JQuery objects; (and create lists of objects if necessary).
 */
OpenComment.prototype.createJQueryObjects = function()
{
	this.$container = $('#opencomment_container');
	this.$messagebox = $('<div id="opencomment_messages"></div>');
	this.$form = $('<div id="opencomment_form"></div>');
	this.$comments = $('<div id="opencomment_comments"></div>');
	this.$iframe = $('<iframe id="target" style="display: none;"></iframe>');
}

/**
 * Append HTML elements to the DOM.
 */
OpenComment.prototype.createChildren = function()
{
	if(this.REDIRECT && this.IS_SERVICE_LOGIN)
	{
		this.$container.append(this.$messagebox);
		this.$container.append(this.$form);
		this.$container.append(this.$iframe);
	}
	else
	{
		this.$container.append(this.$comments);
		this.$container.append(this.$messagebox);
		this.$container.append(this.$form);
		this.$container.append(this.$iframe);
	}
}

/**
 * Find anchors classed as actions and add javascript hover and down states.
 */
OpenComment.prototype.attachActions = function($container)
{
	var actions = $(".action", $container);
	actions.mouseover(function() { $(this).addClass('hover'); });
	actions.mouseout(function() { $(this).removeClass('hover').removeClass('down'); });
	actions.mousedown(function() { $(this).addClass('down'); });
	actions.mouseup(function() { $(this).removeClass('down'); });
}

/**
 * Rewrites links in a form and handle submission 
 */
OpenComment.prototype.rewriteFormLinks = function()
{
	var actions = $(".action", this.$form);
	var oc = this;
	
	actions.click(function() {
		var href = $(this).attr("href");
		if(href != null && href != "")
		{
			url = oc.PAGE_URL;
			$.get(href, { page_url: url }, $.context(oc, 'handleFormAction'));
		}
		return false;
	});
	
	// Rewrite login form
	$openid_form = $('#openid_form', this.$form);
	$openid_form.submit($.context(this, 'handleLoginFormSubmit'));
	
	$openid_login = $('#openid_login', this.$form);
	$openid_login.click($.context(this, 'handleLoginFormSubmit'));
	
	// Rewrite comment form
	$openid_comment_form = $('#openid_comment_form', this.$form);
	$openid_comment_form.submit($.context(this, 'handleCommentFormSubmit'));
	
	$openid_comment_submit = $('#openid_comment_submit', this.$form);
	$openid_comment_submit.click($.context(this, 'handleCommentFormSubmit'));
}

OpenComment.prototype.handleLoginFormSubmit = function()
{
	this.Login();
	return false;
}

OpenComment.prototype.handleCommentFormSubmit = function()
{
	this.SaveComment();
	return false;
}

OpenComment.prototype.handleFormAction = function(data)
{
	this.DisplayFormContent(data);
	this.DisplayComments();
}

/**
 * Displays messages to the user and adds CSS styles based on the message contents, e.g. Error, Warning, Success.
 */
OpenComment.prototype.DisplayMessageContent = function(html, src)
{
	if(src != null)
		alert("DisplayMessageContent: "+src);

	this.displayContent(this.$messagebox, html);
	this.attachActions(this.$messagebox);	
	
	$paragraphs = $('p', this.$messagebox);
	
	var $form = this.$form;
	$paragraphs.each(function(i) {
		var $p = $(this);
		if($p.html().toLowerCase().indexOf('error') >= 0)
		{
			$p.addClass('message');
			$p.addClass('error');
			$form.show();
		}
		else
		if($p.html().toLowerCase().indexOf('warning') >= 0)
		{
			$p.addClass('message');
			$p.addClass('warning');
			$form.show();
		}
		else
		if($p.html().toLowerCase().indexOf('success') >= 0)
		{
			$p.addClass('message');
			$p.addClass('success');
		}
	});
}

/**
 * Displays form content and rewrites action anchors such as Submit and Logout.
 */
OpenComment.prototype.DisplayFormContent = function(html, src)
{
	if(src != null)
		alert("DisplayFormContent: "+src);

	this.displayContent(this.$form, html);
	this.attachActions(this.$form);
	this.rewriteFormLinks();
}

/**
 * Displays comments and rewrites action anchors such as Delete and Edit.
 */
OpenComment.prototype.DisplayCommentsContent = function(html, src)
{
	if(src != null)
		alert("DisplayCommentsContent: "+src);

	this.displayContent(this.$comments, html);
	this.attachActions(this.$comments);
	
	var $actions = $(".action", this.$comments);
	var oc = this;
	
	$actions.each(function(i)
	{
		var $a = $(this);
		this.oc = oc;
		
		if($a.html().toLowerCase().indexOf("delete") >= 0)
		{
			$a.click(function() { this.oc.DeleteComment($(this)); return false; });
		}
		else
		if($a.html().toLowerCase().indexOf("edit") >= 0)
		{
			$a.click(function() { this.oc.EditComment($(this)); return false; });
		}
		else
		{
			$a.click(function() { this.oc.ActionComment($(this)); return false; });
		}
	});
}

/**
 * Remote call to request to edit a comment, with javascript scroll to bring the form into view.
 */ 
OpenComment.prototype.EditComment = function($a)
{
	var href = $a.attr("href");
	window.scrollTo(0, this.$form.offset().top);
	
	this.DisplayMessageContent('');
	if(href != null && href != "")
	{
		$.get(href, { }, $.context(this, 'handleEditComment'));
	}
}

OpenComment.prototype.handleEditComment = function(data)
{
	this.DisplayFormContent(data);
}

/**
 * Remote call to delete a comment, with javascript confirmation from the user.
 */ 
OpenComment.prototype.DeleteComment = function($a)
{
	var href = $a.attr("href");
	if(!confirm("Are you sure you want to delete this entry?"))
	{
		return false;
	}
	else
	{
		if(href != null && href != "")
		{
			$.get(href, { }, $.context(this, 'handleDeleteComment'));
		}
	}
}

OpenComment.prototype.handleDeleteComment = function(data)
{
	this.DisplayMessageContent(data);
	this.DisplayComments();
}

/**
 * Remote call to a general case action specified by the href of the anchor.
 */ 
OpenComment.prototype.ActionComment = function($a)
{
		var href = $a.attr("href");
		if(href != null && href != "")
		{
			$.get(href, { }, $.context(this, 'handleActionComment'));
		}
}

OpenComment.prototype.handleActionComment = function(data)
{
	this.DisplayFormContent(data);
}

/**
 * Shows the specified container and adds replace the inner html contents.
 */
OpenComment.prototype.displayContent = function($container, html)
{
	$container.show();
	$container.html(html);
}

/**
 * Remote call to login the user by reading the OpenID proiver url from the current form.
 */
OpenComment.prototype.Login = function()
{
	var condition = $("input[@name='openid_type']:checked", this.$form).val();
	var providerUrl;
	
	if(condition != 'openid_url')
	{
		providerUrl = condition;
	}
	else
	{
		providerUrl = document.getElementById('openid_url').value;
	}

	if(providerUrl == null || providerUrl == '')
	{
		this.DisplayMessageContent('<p class="message warning">No provider set- your provider is the URL of an OpenID service you trust to confirm your identity.</p>');
	}
	else
	{
		this.$iframe.attr("src", this.SERVICE_URL + "?action=DisplayLoginIframe&openid.immediate=true&openid.id=" + providerUrl);
		this.DisplayMessageContent('<p class="message warning">Requesting authentication...</p>');
		this.$form.hide();
	}
	
	this.PROVIDER_URL = providerUrl;
}

/**
 * Logout is handled by a URL specified by the server.
 * If you want to add a manual Logout button, uncomment the code below and hook up the method.
 */
OpenComment.prototype.Logout = function()
{
	alert("OpenComment Logout - unused method stub - see rewriteFormLinks and handleFormAction");
	DisplayFormContent('<p class="message warning">Destroying session...</p>');
	$.get(SERVICE_URL, { action: "Logout" }, $.context(this, 'handleLogout'));
}

OpenComment.prototype.handleLogout = function(data)
{
	this.DisplayFormContent(data);
}

/**
 * Remote call to save a comment based on the data in openid_comment_form.
 * Validates and warns user before sending.
 */
OpenComment.prototype.SaveComment = function()
{	
	var form = document.getElementById('openid_comment_form');
	
	var id = $("input[name=comment_id]", form).val();
	var url = this.PAGE_URL;
	var type = this.COMMENT_TYPE;
	var author = $("input[name=comment_author]", form).val();
	var comment = $("textarea[name=comment_comment]", form).val();
	var website = $("input[name=comment_website]", form).val();

	var errors = 0;
	var error = '';
	
	if(author == null || author == '')
	{
		errors++;
		error += '<p>Warning: Please specify a string value for display name (i.e. author- like, your name?), or post as Anonymous.';
		
		author = 'Anonymous';
		$("input[name=comment_author]", form).val(author);
	}
	
	if(comment == null || comment == '' || comment.length < 5)
	{
		errors++;
		error += '<p>Warning: Please write something, your comment is too short.';
	}
	
	if(website != null && website != '' && website.indexOf('http') != 0)
	{
		website = 'http://' + website;
		$("input[name=comment_website]", form).val(website);
	}
	
	var urltest = new RegExp(); 
	urltest.compile("^[A-Za-z]+://[A-Za-z0-9-_]+\\.[A-Za-z0-9-_%&\?\/.=]+$"); 
	if(website != null && website != '' && !urltest.test(website))
	{
		errors++;
		error += '<p>Warning: (' + website + ') is not a valid URL.';
	}

	if(errors == 0)
	{
		$.get(this.SERVICE_URL, { action: "SaveComment",
			comment_id: id,
			comment_url: url,
			comment_type: type,
			comment_author: author,
			comment_comment: comment,
			comment_website: website },
			
			$.context(this, 'handleSaveComment')
		);
		
		this.DisplayMessageContent('<p class="message warning">Saving comment...</p>');
		this.$form.hide();
	}
	else
	{
		this.DisplayMessageContent(error);
	}
	return false;
}

OpenComment.prototype.handleSaveComment = function(data)
{
	if(data.toLowerCase().indexOf('warning') >= 0 || data.toLowerCase().indexOf('error') >= 0)
	{
		this.DisplayMessageContent(data);
	}
	else
	{
		this.DisplayComments();
		this.DisplayMessageContent(data);
	}
}

/**
 * Remote call to display an appropriate form to the user based on the permissions held by the server,
 * e.g. OpenID Login, Add Comment
 */
OpenComment.prototype.DisplayForm = function()
{
	$.get(this.SERVICE_URL, { action: "DisplayForm", page_url: this.PAGE_URL, redirect: this.REDIRECT }, $.context(this, 'handleDisplayForm'));
}

OpenComment.prototype.handleDisplayForm = function(data)
{
	this.DisplayFormContent(data);
	this.DisplayComments();
}

/**
 * Remote call to display comments for the current page
 */
OpenComment.prototype.DisplayComments = function()
{
	var url = (this.REDIRECT) ? this.REDIRECT : this.PAGE_URL;
	$.get(this.SERVICE_URL, { action: "DisplayComments", page_url: url }, $.context(this, 'handleDisplayComments'));
}

OpenComment.prototype.handleDisplayComments = function(data)
{
	this.DisplayCommentsContent(data);
}

/* IFrame callback methods - produced by PHP responses in order to call JavaScript methods */
function openid_doSubmit()
{
	var setupUrl = oc.SERVICE_URL + "?action=LoginRedirect&openid.id=" + oc.PROVIDER_URL + '&return.url=' + oc.PAGE_URL;
	openid_doRequestSetup(setupUrl, 'Provider is requesting setup... continue?');
}

function openid_doError(msg)
{
	oc.DisplayMessageContent('<p class="message error">' + msg + '</p>');
}

function openid_doSuccess(identity)
{
	var message = '<p class="message success">Authenticated as ' + identity + '</p>';
	
	if(oc.redirect)
	{
		window.location = oc.redirect;
		message += '<p class="message success">Redirecting you back to where you came from</p>';
	}
	else
	{
		oc.DisplayForm();		
	}
	
	oc.DisplayMessageContent(message);
}

function openid_doRequestSetup(setupUrl, msg)
{
	$response = $('<p class="message success">' + msg + '</p>');
	$link = $('<a href="' + setupUrl + '"> (click here)</a>');
	$response.append($link);
	
	oc.DisplayFormContent($response);
}

/* Example setup method for OpenComment */
// var oc = new OpenComment('http://localhost/opencomments/services/');