/* linkchecker.js is part of Aloha Editor project http://aloha-editor.org
 *
 * Aloha Editor is a WYSIWYG HTML5 inline editing library and editor. 
 * Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
 * Contributors http://aloha-editor.org/contribution.php 
 * 
 * Aloha Editor is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * Aloha Editor is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 * As an additional permission to the GNU GPL version 2, you may distribute
 * non-source (e.g., minimized or compacted) forms of the Aloha-Editor
 * source code without the copy of the GNU GPL normally required,
 * provided you include this license notice and a URL through which
 * recipients can access the Corresponding Source.
 */
(function(window, undefined) {
	"use strict";

	var
		jQuery = window.alohaQuery || window.jQuery, $ = jQuery,
		GENTICS = window.GENTICS,
		Aloha = window.Aloha;

	Aloha.LinkChecker = new Aloha.Plugin('linkchecker');

	/**
	 * All error codes that have an explanation.
	 */
	Aloha.LinkChecker.errorCodes = [400, 401, 402, 403, 404, 405,
											406, 407, 408, 409, 410, 411,
											412, 413, 414, 415, 416, 417,
											418, 500, 501, 502, 503, 504,
											505, 506];
	/**
	 * This codes are asumed temporary errors.
	 */
	Aloha.LinkChecker.warningCodes = [404, 411, 412, 413, 500, 503,
											  504, 505];


	/**
	 * Initialize the plugin and set initialize flag on true
	 */
	Aloha.LinkChecker.init = function () {

		this.proxyUrl = null;

		if ( typeof Aloha.LinkChecker.settings.proxyUrl !== 'undefined' ) {
			this.proxyUrl = Aloha.LinkChecker.settings.proxyUrl;
		}

		// initialize the timer
		this.timer = {};

		// initialize the running requests
		this.xhr = {};

		// remember reference to this class for callback
		var that = this;

		// mark active Editable with a css class
		Aloha.EventRegistry.subscribe(
				Aloha,
				"editableActivated",
				function (jEvent, aEvent) {
					// find all link tags
					aEvent.editable.obj.find('a').each(function() {
						that.checkLink(this, jQuery(this).attr('href'), 0);
					});
				}
		);

		// remove active Editable ccs class
		Aloha.EventRegistry.subscribe(
				Aloha,
				"editableDeactivated",
				function (jEvent, aEvent) {
					// remove link marks
					that.makeClean(aEvent.editable.obj);
				}
		);

		// remove active Editable ccs class
		Aloha.EventRegistry.subscribe(
				Aloha,
				"hrefChanged",
				function (jEvent, aEvent) {
					that.checkLink(aEvent.obj, 'hrefChanged');
				}
		);

	};

	Aloha.LinkChecker.checkLink = function (obj, scope, delay, timeout) {
		var that = this,
			url, cleanUrl;

		// extract url from link object
		url = jQuery(obj).attr('href');
		cleanUrl = url;

		// i probably an internal link
		if ( typeof url == 'string' && !/^http/.test( url.toLowerCase() ) ) {
			this.makeCleanLink(obj);
			return;
		}

		if ( this.proxyUrl ) {
			url = this.proxyUrl + url;
		}

		// abort already running ajax requests for the scope
		if (this.xhr[scope]) {
			this.xhr[scope].abort();
			this.xhr[scope] = undefined;
		}

		this.timer[scope] = this.urlExists(
			url,
			// success
			function(xhr) {
				that.makeCleanLink(obj);
			},
			//failure
			function(xhr) {
				var e, o;

				if ( obj ) {
					if ( jQuery.inArray(xhr.status, that.errorCodes) >= 0 ) {
						e = xhr.status;
					} else {
						e = '0';
					}
					o = jQuery(obj);
					// when the link has a title and was not yet marked as being invalid, we store the title in 'data-title'
					if ( o.attr('title') && !o.attr('data-invalid') ) {
						o.attr('data-title', o.attr('title'));
					}
					// now we mark the link as being invalid
					o.attr('data-invalid', 'true');
					// and we set an error message to the title
					o.attr('title', cleanUrl+'. '+that.i18n('error.'+e));
					// set the link class
					if ( jQuery.inArray(xhr.status, that.warningCodes) >= 0 ) {
						o.addClass('aloha-link-warn');
					} else {
						o.addClass('aloha-link-error');
					}
				}
			},
			scope,
			timeout,
			delay
		);
	};

	Aloha.LinkChecker.urlExists = function (url, successFunc, failureFunc, scope, timeout, delay) {
		var that = this, newTimer;

		// abort timer for that request
		clearTimeout(this.timer[scope]);

		delay = (delay != null && typeof delay !== 'undefined' ) ? delay : 700;

		// start timer for delayed request
		newTimer = window.setTimeout( function() {

			// start request
			that.xhr[scope] = jQuery.ajax({
				url: url,
				timeout: timeout ? 10000 : timeout,
				type: 'HEAD',
				complete: function(xhr) {
					// abort timer for that request
					clearTimeout(newTimer);
					try {
						// if response HTTP status 200 link is ok
						// this implementation does NOT cover redirects!
						if (xhr.status < 400) {
							successFunc.call(this, xhr);
						} else {
							failureFunc.call(this, xhr);
						}
					} catch(e) {
						failureFunc.call(this, {'status':0});
					}
				}
			});

		}, delay);

		return newTimer;
	};

	Aloha.LinkChecker.makeCleanLink = function (obj) {
		if ( obj ) {
			var o = jQuery(obj);
			// restore the original title (if one existed)
			if ( o.attr('data-title') ) {
				o.attr('title', o.attr('data-title'));
			} else {
				// otherwise remove the title
				o.removeAttr('title');
			}
			// remove the temporary data
			o.removeAttr('data-title');
			o.removeAttr('data-invalid');
			// remove the classes
			o.removeClass('aloha-link-error');
			o.removeClass('aloha-link-warn');
		}
	};

	Aloha.LinkChecker.makeClean = function (editable) {
		var that = this;
		// find all link tags
		editable.find('a').each(function() {
			that.makeCleanLink(this);
		});
	};

	Aloha.LinkChecker.urlencode = function (str) {
		// URL-encodes string
		//
		// version: 1008.1718
		// discuss at: http://phpjs.org/functions/urlencode
		// +   original by: Philip Peterson
		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +	  input by: AJ
		// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +   improved by: Brett Zamir (http://brett-zamir.me)
		// +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +	  input by: travc
		// +	  input by: Brett Zamir (http://brett-zamir.me)
		// +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
		// +   improved by: Lars Fischer
		// +	  input by: Ratheous
		// +	  reimplemented by: Brett Zamir (http://brett-zamir.me)
		// +   bugfixed by: Joris
		// +	  reimplemented by: Brett Zamir (http://brett-zamir.me)
		// %		  note 1: This reflects PHP 5.3/6.0+ behavior
		// %		note 2: Please be aware that this function expects to encode into UTF-8 encoded strings, as found on
		// %		note 2: pages served as UTF-8
		// *	 example 1: urlencode('Kevin van Zonneveld!');
		// *	 returns 1: 'Kevin+van+Zonneveld%21'
		// *	 example 2: urlencode('http://kevin.vanzonneveld.net/');
		// *	 returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
		// *	 example 3: urlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
		// *	 returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'
		str = (str+'').toString();

		// Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
		// PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
		return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
																		replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
	};

})(window);