
var webPath = "/nina-client/";

var myWindow; // Container for child windows
var myWindowReturn; // Returned value from child window
var characterCount = new Object();
var elementInFocus = null; // Element that is currently in focus
var w2widgetsBusy = false; // Flag is true when Ajax threads are not completed
var output = "idle"; // Current status of generation pipe
var validateOnSubmit = false; // validate fields on every submit event
var dp = null; // Dynamic preview updater object (Ajax.PeriodicalUpdater)
var dpEnabled = false; // Temporary measure to disable dynamic preview if true

/**
 * Determine if a string ends with a sub-string
 *
 * @param str - the sub-string to check
 *
 * @return if the sub-string has been found at the end of the string
 */
String.prototype.endsWith = function(str){
	return this.lastIndexOf(str) == this.length-str.length;
}

/**
 * Move the cursor caret to a specified position in this TEXTAREA field
 * This fixes an issue in Safari where the cursor does not default to end of field when selected
 * 
 * @param position - character position to place caret (use TextArea.value.length for end)
 */
Element.addMethods({
    moveCaret: function(elementId, position) {
        element = $(elementId);
        element.selectionStart = position;
        element.selectionEnd = position; 
        return element;
    }
});

/**
 * Return all element objects from page that implement a specified class
 * Written by Jonathan Snook, http://www.snook.ca/jonathan
 * Add-ons by Robert Nyman, http://www.robertnyman.com
 * 
 * @param className - the classname to check
 * @param tag - check only these nodes
 * @param elm - check from this root element
 * 
 * @return if the condition is true
 */
function getElementsByClassName(className, tag, elm){
	var testClass = new RegExp("(^|\\s)" + className + "(\\s|$)");
	var tag = tag || "*";
	var elm = elm || document;
	var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
	var returnElements = [];
	var current;
	var length = elements.length;
	for(var i=0; i<length; i++){
		current = elements[i];
		if(testClass.test(current.className)){
			returnElements.push(current);
		}	
	}
	return returnElements;
}

/**
 * Cross-Browser Split v0.1 - An ECMA-compliant, uniform cross-browser split method
 * MIT-style license, by Steven Levithan <http://stevenlevithan.com> 
 *
 * @param separator - string or regex pattern to use as delimiter
 * @param limit - stop after this many repetitions
 * 
 * @return the array produced
 */ 
String.prototype.split = function(separator, limit) {
    var flags = "";
    if (separator === undefined) { 
        // toString is used because the typeof this is object  
        return [this.toString()]; 
    } 
    else if (separator === null || separator.constructor !== RegExp) { 
        // Convert to a regex with escaped metacharacters  
        separator = new RegExp(String(separator).replace(/[.*+?^${}()|[\]\/\\]/g, "\\$&"), "g"); 
    } 
    else { 
        flags = separator.toString().replace(/^[\S\s]+\//, ""); 
        if (!separator.global) { 
            separator = new RegExp(separator.source, "g" + flags); 
        } 
    } 
    // Used for the IE non-participating capturing group fix  
    var separator2 = new RegExp("^" + separator.source + "$", flags); 
    if (limit === undefined || +limit < 0) { 
        limit = false; 
    }
    else {
        limit = Math.floor(+limit); 
        // NaN and 0 (the values which will trigger the condition here) are both falsy
        if (!limit) return [];
    } 
    var match, output = [], lastLastIndex = 0, i = 0; 
    while ((limit ? i++ <= limit : true) && (match = separator.exec(this))) { 
        // Fix IE's infinite-loop-resistant but incorrect RegExp.lastIndex
        if ((match[0].length === 0) && (separator.lastIndex > match.index)) { 
            separator.lastIndex--; 
        }
        if (separator.lastIndex > lastLastIndex) { 
            if (match.length > 1) { 
                match[0].replace(separator2, function() {
                    for (var j = 1; j < arguments.length - 2; j++){ 
                        if (arguments[j] === undefined) match[j] = undefined; 
                    }
                }); 
            } 
            output = output.concat(this.substring(lastLastIndex, match.index), (match.index === this.length ? [] : match.slice(1))); 
            lastLastIndex = separator.lastIndex; 
        } 
        if (match[0].length === 0) {
            separator.lastIndex++; 
        }
    }
    return (lastLastIndex === this.length) ? (separator.test("") ? output : output.concat("")) : (limit ? output : output.concat(this.substring(lastLastIndex)));
};

/**
 * Set the current element in focus
 * This allows events to be triggered on the widget, outside of element control
 *
 * @param elementId - ID of the widget that has gained focus
 */
function setFocusedElement(elementId) {
	elementInFocus = elementId;
	$(elementId).moveCaret($F(elementId).length);
    	element = document.getElementById(elementId);
}

/**
 * Submit text content to the pipe
 *
 * @param elementId - Element ID of widget
 */
function submitText(elementId) {
	Element.addClassName(elementId, "w2widgets_busy");
	var content = $F(elementId);
	submitWidget(elementId, content);
}

/**
 * Select an option in a closed field using multiple elements
 *
 * @param optionElementId - ID of element containing value to send to the parent
 * @param parentElementId - ID of element destination for option value (usually a hidden field)
 * @param value - Prefixed value to set for this option (checkboxes)
 */
function selectOption(optionElementId, parentElementId, value) {
	Element.addClassName(optionElementId, "w2widgets_busy");
	if ($(optionElementId).type == "checkbox") {
		if ($(optionElementId).checked) $(parentElementId).value = value;
		else $(parentElementId).value = "";
	}
	else
		$(parentElementId).value = $F(optionElementId);
	submitWidget(parentElementId, $F(parentElementId));
	Element.removeClassName(optionElementId, "w2widgets_busy");
}

/**
 * Upload a text file from the client filesystem
 *
 * @param elementId - Element ID of widget
 */
function uploadText(elementId) {
	// TODO: Add text upload mechanism
	alert("Text upload currently not implemented");
}

/**
 * Upload an image from the client filesystem
 *
 * @param elementId - Element ID of widget
 */
function uploadImage(elementId) {
	// Reset dialog return
	myWindowReturn = "";
	var widgetParts = elementId.split("_");
	Element.addClassName(elementId, "w2widgets_busy");
	// Open the file upload window
	myWindow = window.open(
		webPath + "scripts/UploadImage.php?widget=" + elementId + "&targetPipe=" + widgetParts[1] + "&targetField=" + widgetParts[2], 
		null, 
		"height=450px,width=650px,toolbar=no,directories=no,status=no,linemenubar=no,scrollbars=no,resizable=no,modal=yes");
	myWindow.focus();
	waitForSubmitDialogComplete(elementId);
}

function waitForSubmitDialogComplete(elementId) {
	if (myWindow.closed || myWindowReturn != "") {
		if (myWindowReturn != "") {
			submitWidget(elementId, myWindowReturn);
		}
	} 
	else {
		setTimeout("waitForSubmitDialogComplete('" + elementId + "')", 1000);
	}
}

/**
 * Select an image from the server-side image gallery
 *
 * @param elementId - Element ID of widget
 */
function selectGalleryImage(elementId) {
	// Reset dialog return
	myWindowReturn = "";
	var widgetParts = elementId.split("_");
	Element.addClassName(elementId, "w2widgets_busy");
	// Open the file upload window
	myWindow = window.open(
		webPath + "utilities/gallery/gallery.php?widget=" + elementId + "&targetPipe=" + widgetParts[1] + "&targetField=" + widgetParts[2], 
		null, 
		"height=400px,width=600px,toolbar=no,directories=no,status=no,linemenubar=no,scrollbars=no,resizable=no,modal=yes");
	myWindow.focus();
	waitForSubmitDialogComplete(elementId);
}

/**
 * Delete the content of a field (text or image)
 *
 * @param elementId - Element ID of widget
 */
function deleteField(elementId) {
	Element.addClassName(elementId, "w2widgets_busy");
	$(elementId).src = webPath + "images/noimage.png";
	submitWidget(elementId, "");
}

/**
 * Send content to the PHP submit script
 *
 * @param elementId - Element ID of widget
 * @param content - value to store
 */
function submitWidget(elementId, content) {
    w2widgetsBusy = true;
	// Submit the content, and handle response
	var opt = {
		asynchronous: false,
		encoding: 'UTF-8',
		contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
	    method: "post",
	    postBody: "value=" + encodeURIComponent(content),
	    onSuccess:
	    	function(t) {
				if (t.responseText != "nosession") {
					// Validate this field if config.xml has instructed it
					if (validateOnSubmit) 
						updateErrorMessages(elementId);
				}
				else {
					handleLostSession();
				}
				Element.removeClassName(elementId, "w2widgets_busy");
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/SubmitW2Widget.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert("Error " + t.status + ': ' + t.statusText);
		   	}
	}
	new Ajax.Request(webPath + "scripts/SubmitW2Widget.php?widget=" + elementId, opt);
	// Update the dynamic preview as soon as the content is submitted
	if (dpEnabled) updateDynamicPreview();
}

/**
 * Run the error validation
 *
 * @param elementId (optional) - check only this widget
 */
function updateErrorMessages(elementId) {
	w2widgetsBusy = true;
	// Build the request (if elementId is not set, then check all elements)
	var errorRequest = "checkall=true";
	if (arguments.length > 0) errorRequest = "widget=" + elementId;
	// Submit the content, and handle response
	var opt = {
		asynchronous: false,
	    method: "post",
	    postBody: errorRequest,
	    onSuccess:
	    	function(t) {
	    		if (t.responseText != "nosession") {
					if (elementId == null) {
						// Set all widgets as valid initially
						var elementArray = getElementsByClassName("w2widgets", "*", document);
						for (elementIndex in elementArray) {
							// Do not process the last element returned! it is a weird special element containing code for splicing
							if (elementIndex < elementArray.length && elementArray[elementIndex].id != "") {
								setFieldValidity(elementArray[elementIndex].id, true);
							}
						}
					}
					else {
						// Set submitting widget as valid initially
						setFieldValidity(elementId, true);
					}
					if (t.responseText != "" && t.responseText != null) {
						// Flag all the invalid widgets
						var invalid = t.responseText;
						var invalidArray = invalid.split(" ");
						for (invalidIndex = 0; invalidIndex < invalidArray.length - 1; invalidIndex++) 
							setFieldValidity(invalidArray[invalidIndex], false);
					}
					// Redraw the error widget
					redrawErrorMessages();
				}
				else {
					handleLostSession();
				}
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/GetInvalidWidgets.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert("Error " + t.status + ': ' + t.statusText);
		   	}
	}
	new Ajax.Request(webPath + "scripts/GetInvalidWidgets.php", opt);
}

/**
 * Redraw the error message widget
 */
function redrawErrorMessages() {
	// Update the error messages
	var opt = {
	    method: "post",
	    asynchronous: false,
	    onSuccess:
	    	function(t) {
	    		if (t.responseText == "nosession") {
					handleLostSession();
				}
				w2widgetsBusy = false;
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/UpdateW2ErrorWidget.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert("Error " + t.status + ': ' + t.statusText);
		   	}
	}
	// Send the request
	new Ajax.Updater("w2errorwidget_articlecontent_canvas", webPath + "scripts/UpdateW2ErrorWidget.php", opt);
}

/**
 * Change the style of a field to make it read-only
 *
 * @param fieldType - the type of field being processed
 * @param elementId - the element to make read-only
 * @param fieldName - the name of this field
 */
function setFieldReadonly(fieldType, elementId, fieldName) {
	Element.addClassName(elementId, "w2widgets_disabled");
	switch (fieldType) {
		case "image":
			// Disable image fields
			$(elementId).onclick = null;
			break;
		case "radio":
			// Traverse the radio buttons of given name and disable them. Effectively making field read-only
			$(elementId).disabled = true; 
			var elements = document.getElementsByName(fieldName); 
			for (elementIndex in elements) { 
				elements[elementIndex].disabled = true; 
				Element.addClassName(elements[elementIndex], "w2widgets_disabled");
			}
			break;
		default:
			// All other widgets follow this execution
			$(elementId).disabled = true;
			break;
	}
}

/**
 * Pre-select an option in closed fields
 *
 * @param fieldType - the type of field being processed
 * @param elementId - the element to make read-only
 * @param index - the index of the selected field
 */
function setFieldSelectedOption(fieldType, elementId, index) {
	switch (fieldType) {
		case "radio":
			var elements = document.getElementsByName(elementId);
			elements[index].checked = true;
			break;
		case "checkbox":
			var elements = document.getElementsByName(elementId);
			if (index > 0) elements[1].checked = true;
			else elements[1].checked = false;
			break;
		default:
			$(elementId).childNodes[index].selected = true;
			break;
	}
}

/**
 * Call up the crop tool for selected image widget
 *
 * @param elementId - Element ID of widget
 */
function cropImage(elementId) {
	// Reset dialog return
	myWindowReturn = "";
	var widgetParts = elementId.split("_");
	Element.addClassName(elementId, "w2widgets_busy");
	// Open the file upload window
	myWindow = window.open(
		webPath + "utilities/croptool/croptool.php?" + 
		      "widget=" + elementId + 
		      "&targetPipe=" + widgetParts[1] + 
		      "&targetField=" + widgetParts[2],
		null, 
		"height=408px,width=370px,toolbar=no,directories=no,status=no,linemenubar=no,scrollbars=no,resizable=no,modal=yes");
	myWindow.focus();
	waitForSubmitDialogComplete(elementId);
	
}

/**
 * Continue to the generation page if validation is successful
 *
 * @param targetPage - Navigate to this page on success
 * @param invalidMessage - Message to display if there are errors
 */
function gotoGenerateIfValid(targetPage, invalidMessage) {
	// Run validation on all widgets
	updateErrorMessages();
	waitForValidation(targetPage, invalidMessage);
}

function waitForValidation(targetPage, invalidMessage) {
	// Move to target page if validation is successful
	if (!w2widgetsBusy) {
		if ($("w2errorwidget_articlecontent").style.display == "none") {
			window.location = targetPage;
		}
		else {
			alert(invalidMessage);
		}
	}
	else {
		// Submission/validation currently in process, retry this event until complete
		setTimeout("waitForValidation('" + targetPage + "', '" + invalidMessage + "')", 1000);
	}
}

/**
 * Provide the current status indicator as returned by the generation pipe
 *
 * @return the status (see GeneratePipe for details)
 */
function getGenerateStatus() {
	var parentObject = this;
	var opt = {
	    method: "get",
	    onSuccess: 
	    	function(t) {
				parentObject.output = t.responseText;
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/GenerationStatus.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert('Error ' + t.status + ': ' + t.statusText);
		   	}
	}
	// Send the request
	new Ajax.Request(webPath + "scripts/GenerationStatus.php?data=status", opt);
	return output;
}

/**
 * Provide the current detailed generation message given by the server
 *
 * @param messageTarget - the target HTML element to place the message data
 *
 * @return the human-readable message returned by the server
 */
function getGenerateMessage(messageTarget) {
	var opt = {
	    method: "get",
		asynchronous: false,
	    frequency: 0.5
	}
	// Periodically make the Ajax call and place result in messageTarget
	new Ajax.PeriodicalUpdater($(messageTarget), webPath + "scripts/GenerationStatus.php?data=message", opt);
}

/**
 * Run events on field after key has been pressed
 *
 * @param keyEvent - event object passed by browser
 * @param elementId - ID of element to parse
 * @param bannedArray - array of keycodes banned from use (not just invalid) in this field
 */
function parseKeyEntry(keyEvent, elementId, bannedArray) {
	var allowKey = true;
	if(window.event) {
		// IE
		keynum = keyEvent.keyCode
	}
	else if(keyEvent.which) {
		// Netscape/Firefox/Opera
		keynum = keyEvent.which
	}
	for (charIndex = 0; charIndex < bannedArray.length; charIndex++) {
		if (keynum == bannedArray[charIndex]) allowKey = false;
	}
	return allowKey;
}

/**
 * Checks if field has character count values, and updates/actions on the value
 *
 * @param elementId - ID of element to update
 */
function updateCharacterCount(elementId) {
	var content = $F(elementId);
	// Set the character count value
	var out = "Count " + content.length;
	if (typeof characterCount[elementId + "_charCount"] != "undefined") {
		// This field has a restricted character count, check for required action
		var charCount = characterCount[elementId + "_charCount"];
		// Display the min/max count if requested
		if (charCount[2] && charCount[3]) {
			out += " (" + charCount[0] + "-" + charCount[1] + ")";
		}
		else if (charCount[2]) {
			out += " (" + charCount[0] + ")";
		}
		else if (charCount[3]) {
			out += "/" + charCount[1];
		}
		// Remove all classes related to count
		Element.removeClassName(elementId + "_charCount", "w2widgets_belowCount");
		Element.removeClassName(elementId + "_charCount", "w2widgets_aboveCount");
		Element.removeClassName(elementId + "_charCount", "w2widgets_withinCount");
		if (charCount[0] > 0 && content.length < charCount[0]) {
			// Field is below minimum character count
			Element.addClassName(elementId + "_charCount", "w2widgets_belowCount");
		}
		else if (charCount[1] > 0 && content.length > charCount[1]) {
			// Field is above maximum character count
			Element.addClassName(elementId + "_charCount", "w2widgets_aboveCount");
		}
		else {
			// Field is within character count
			Element.addClassName(elementId + "_charCount", "w2widgets_withinCount");
		}
	}
	// Render display
	$(elementId + "_charCount").innerHTML = out;
}

/**
 * Set the valid/invalid presentation for a given field
 * 
 * @param elementId - the element ID to modify
 * @param isValid - specify whether this widget is valid or not
 */
function setFieldValidity(elementId, isValid) {
	var widgetType = elementId.split("_")[1];
	if (widgetType == "image")	{
		// If this widget is an image, change the image src to the invalid png
		if (!isValid) {
			$(elementId).src = webPath + "images/noimage_invalid.png";
			Element.addClassName($(elementId + "_imagecontainer"), "w2widgets_invalid");
		}
		else {
			Element.removeClassName($(elementId + "_imagecontainer"), "w2widgets_invalid");				
		}
	}
	else if ($(elementId).type == "hidden") {
		var elements = document.getElementsByName($(elementId).name);
		// Traverse the widget elements
		for (index = 1; index < elements.length; index++) {
			elementIt = elements[index];
			if (!isValid) {
				Element.addClassName(elementIt, "w2widgets_invalid");
			}
			else {
				Element.removeClassName(elementIt, "w2widgets_invalid");
			}
		}
	}
	else {
		// All other widgets follow this behaviour
		if (!isValid) {
			Element.addClassName(elementId, "w2widgets_invalid");
		}
		else {
			Element.removeClassName(elementId, "w2widgets_invalid");
		}
	}
}

/**
 * Add an element to the restricted character count array
 *
 * @param elementId - ID of element to add
 * @param min - minimum allowed characters in this field
 * @param max - maximum allowed characters in this field
 * @param showMin - display the minimum count on the widget
 * @param showMax - display the maximum count on the widget
 */
function setCharacterCount(elementId, min, max, showMin, showMax) {
	characterCount[elementId] = new Array(min, max, showMin, showMax);
}

/**
 * Run the spell checker on the specified widget
 *
 * @param elementId - the element containing the widget value to spell check
 */
function spellCheckSingleField(elementId) {
	var speller = new spellChecker($(elementId));
	speller.openChecker();
}

/**
 * Run the spell checker on all widgets on this page
 *
 */
function spellCheckAllFields() {
	var speller = new spellChecker();
	speller.spellCheckAll();
}

/**
 * Poll the dynamic preview widget to update the image
 * 
 */
function pollDynamicPreview() {
	var opt = {
	    method: "get",
	    asynchronous: true,
	    frequency: 1,
	    onSuccess:
	    	function(t) {
	    		switch (t.responseText) {
					case "nosession":
						handleLostSession();
	    			case "failed":
	    				dp.stop();
	    			case "poll":
	    				break;
	    			default:
	    				dp.stop();
						$("w2dynamicpreviewwidget").src= t.responseText;
	    				break;
	    		}
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/UpdateDynamicPreview.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert("Error " + t.status + ': ' + t.statusText);
		   	}
	}
	// Send the request
	dp = new Ajax.PeriodicalUpdater("", webPath + "scripts/UpdateDynamicPreview.php?mode=poll", opt);
}

/**
 * Send a regeneration request to the dynamic preview widget
 * 
 */
function updateDynamicPreview() {
	var opt = {
	    method: "get",
	    asynchronous: true,
	    onSuccess:
	    	function(t) {
	    		// Stop then start the polling process
	    		if (dp != null) dp.stop();
				if (t.responseText == "complete") {
					pollDynamicPreview();
				}
				else if (t.responseText == "nosession") {
					handleLostSession();
				}		
	    	},
	    on404: 
	    	function(t) {
		        alert("Error 404: " + webPath + "scripts/UpdateDynamicPreview.php was not found.");
		    },
	    onFailure: 
	    	function(t) {
	        	alert("Error " + t.status + ': ' + t.statusText);
		   	}
	}
	// Send the request
	new Ajax.Request(webPath + "scripts/UpdateDynamicPreview.php?mode=update", opt);
}

/**
 * Toggle if validation should be processed after every field is submitted, or only on form submit
 * 
 * @param onSubmit - set this to true to trigger validation on every submit
 */
function setValidateOnSubmit(onSubmit) {
	validateOnSubmit = onSubmit;
}

/**
 * Set to true to enable the dynamic preview scripts
 *
 * @param enable - enable the dynamic preview mechanism
 */
function setDynamicPreviewEnable(enable) {
	dpEnabled = enable;
}

/**
 * Perform an action if the server session is lost
 *
 */
function handleLostSession() {
	alert("Session has been inactive for longer than allowed time limit. Redirecting you to the home page");
	window.location = "/index.php";
}
