/* search form */

function SearchForm(id) {
	var controlIsPressed = false;
	
	var tagFilterControl = null;
	var attributeFilterControl = null;
	
	jQuery('*').keydown(function(event) {
		if (event.keyCode == 17) {
			controlIsPressed = true;
		}
	});
	jQuery('*').keyup(function(event) {
		if (event.keyCode == 17) {
			controlIsPressed = false;
		}
	});
	
	this.submit = function() {
		jQuery('#' + id).submit();
	};
	var submitMethod = this.submit;
	
	this.initMainSearchInput = function(inputId, autoCompletionResultsDivId, buttonId) {
		// add autocompletion for main search input
	    var dataSource = new YAHOO.widget.DS_XHR("/functions/findTag", ["resultSet.hits","tag"]);
		dataSource.responseType = YAHOO.widget.DS_XHR.TYPE_JSON;
	    var autoComp = new YAHOO.widget.AutoComplete(inputId, autoCompletionResultsDivId , dataSource, {
	        animSpeed: 0.05,
	        useShadow: false,
			autoHighlight : false,
			maxResultsDisplayed : 20,
			delimChar: [" "]
	    });
		
		jQuery('#' + inputId).keyup(function(event){
			if (event.keyCode == 13) {
				submitMethod();
			}
		});
		
		jQuery('#' + buttonId).click(function() {
			submitMethod();
		});
	};
	
	this.bindTagDragAndDropEvents = function() {
		makeTagsDraggable(jQuery('#' + id + ' .tag.draggable'));
		makeTagsDroppable(jQuery('#' + id + ' .tag'));
		
	};
	
	this.setTagFilterControl = function(control) {
		tagFilterControl = control;
	};
	
	this.setAttributeFilterControl = function(control) {
		attributeFilterControl = control;
	};
	
	this.makeTagsClickable = function(jQueryObjectSet) {
		jQueryObjectSet.click(function(event) { 
			if (jQuery(this).hasClass('noclick')) {
				jQuery(this).removeClass('noclick');
			}
			else if (tagFilterControl != null) {
				if (controlIsPressed) {
					tagFilterControl.addTagMustNot(jQuery(this).text());
				}
				else {
					tagFilterControl.addTagMust(jQuery(this).text());
				}
				event.stopPropagation();
				event.preventDefault();
			}
		});
	};
	
}

/* tag based search result table */

function updateTableCell(cellId, newValue, oldValue, finishedHandler) {
	if (newValue != oldValue) {
		var oldTags = oldValue.split(',');
		var newTags = newValue.split(',');
		removeTagsRecursively(getAssetIdFromCellId(cellId), getColumnNameFromCellId(cellId), oldTags, function() {
			addTagsRecursively(getAssetIdFromCellId(cellId), getColumnNameFromCellId(cellId), newTags, function() {
				finishedHandler();
			});
		});
	}
	else {
		finishedHandler();
	}
}

function undoServerSideIdEscaping(id) {
	return id.replace('_', '/');
}
	
function removeTagsRecursively(assetId, prefix, remainingValues, finishedHandler) {
	if (remainingValues.length > 0) {
		var currentValue = remainingValues.shift();
		
		if (jQuery.trim(currentValue) == "") {
			removeTagsRecursively(assetId, prefix, remainingValues, finishedHandler);
		}
		else {
			jQuery.ajax({
				type: "POST",
				url: "/functions/removeTagFromAsset",
				data: {
					"assetId": undoServerSideIdEscaping(assetId),
					"tag": (prefix != null ? prefix + ":" : "" ) + jQuery.trim(currentValue)
				},
				success: function(msg){
					if (msg == "success") {
						removeTagsRecursively(assetId, prefix, remainingValues, finishedHandler);
					}
					else {
						alert('tag could not be removed');
					}
				},
				error: function(XMLHttpRequest, textStatus, errorThrown){
					alert('The server is unavailable. A tag could not be removed.');
				}
			});
		}
	}
	else {
		finishedHandler();
	}
}

function addTagsRecursively(assetId, prefix, remainingValues, finishedHandler) {		
	if (remainingValues.length > 0) {
		var currentValue = remainingValues.shift();
		
		if (jQuery.trim(currentValue) == "") {
			addTagsRecursively(assetId, prefix, remainingValues, finishedHandler);
		}
		else {		
			jQuery.ajax({
				type: "POST",
				url: "/functions/addTagToAsset",
				data: {
					"assetId": undoServerSideIdEscaping(assetId),
					"tag": (prefix != null ? prefix + ":" : "" ) + jQuery.trim(currentValue)
				},
				success: function(msg){
					if (msg == "success") {
						addTagsRecursively(assetId, prefix, remainingValues, finishedHandler);
					}
					else {
						alert('tag could not be added');
					}
				},
				error: function(XMLHttpRequest, textStatus, errorThrown){
					alert('The server is unavailable. A tag could not be added.');
				}
			});
		}
	}
	else {
		finishedHandler();
	}
}

function getAssetIdFromCellId(cellId) {
	var firstDashPosition = cellId.indexOf('-');
	var secondPart = cellId.substring(firstDashPosition+1);
	return secondPart.substring(0, secondPart.indexOf('-'));
}

function getColumnNameFromCellId(cellId) {
	var firstDashPosition = cellId.indexOf('-');
	var secondPart = cellId.substring(firstDashPosition+1);
	return secondPart.substring(secondPart.indexOf('-') + 1);
}
	

function TagEditControl(id, assetId) {

	var lastAddedTag = null;

	var dataSource = new YAHOO.widget.DS_XHR("/functions/findTag", ["resultSet.hits","tag"]);
	dataSource.responseType = YAHOO.widget.DS_XHR.TYPE_JSON;
    var autoComp = new YAHOO.widget.AutoComplete("tagInput" + assetId, "tagAutoCompletionResults" + assetId, dataSource, {
        animSpeed: 0.05,
        useShadow: false,
		autoHighlight : false,
		maxResultsDisplayed : 20
    });
	
	autoComp.itemSelectEvent.subscribe(function(sType, aArgs) {
		var tagName = aArgs[2][0];
		if (lastAddedTag != tagName) { // prevent submitting twice
			lastAddedTag = tagName;
			addTagsRecursively(assetId, null, [tagName], function(){
				jQuery("#tagInput" + id).val('');
				reloadTagsMethod(null);
			});
		}
	});
	
	this.reloadTags = function(finishedHandler) {
		jQuery("#actualTagsSpan" + assetId).html('');
		jQuery('#loadingTagsAnimation' + assetId).removeClass('hidden'); 
		jQuery.ajax({
		    type: "POST",
		    url: "/functions/getTagsForAsset",
		    data: {
				"assetId": undoServerSideIdEscaping(assetId)
			},
			dataType: "json",
		    success: function(msg){
				jQuery("#actualTagsSpan" + assetId).append(msg.tagsHtml);
				jQuery("#actualTagsSpan" + assetId + " > .tag").addClass('deletable');
				//makeTagsDraggable(jQuery("#actualTagsSpan" + assetId + " > .tag.draggable"));	
				//makeTagsDroppable(jQuery("#actualTagsSpan" + assetId + " > .tag"));	
				setEventsForTagsMethod();
				
				if (finishedHandler != null) {
					finishedHandler();
				}
				jQuery('#loadingTagsAnimation' + assetId).addClass('hidden'); 
		    },
			error: function (XMLHttpRequest, textStatus, errorThrown) {
			    alert('The server is unavailable. Tags could not be reloaded');
			    jQuery('#loadingTagsAnimation' + assetId).addClass('hidden');
			}
		});
	};
	var reloadTagsMethod = this.reloadTags;
	
	
	this.openTagEditToolbar = function() {
		jQuery("#tagEditToolbar" + id).css('display', 'block');
		jQuery("#tagInput" + id).focus();
		jQuery("#tagInput" + id).val('');
		jQuery("#actualTagsSpan" + id + " > .tag").addClass('deletable');
		jQuery("#tagEditButton" + id).css('display', 'none');
		setEventsForTagsMethod();
	};
	var openTagEditToolbarMethod = this.openTagEditToolbar;
	
	this.closeTagEditToolbar = function() {
		jQuery("#tagEditToolbar" + id).css('display', 'none');
		jQuery("#actualTagsSpan" + id + " > .tag").removeClass('deletable');
		jQuery("#tagEditButton" + id).css('display', 'inline');
		setEventsForTagsMethod();
	};
	var closeTagEditToolbarMethod = this.closeTagEditToolbar;
	
	this.setEventsForTags = function() {
		jQuery("#actualTagsSpan" + id + " > .tag").unbind('click');
		jQuery("#actualTagsSpan" + id + " > .tag.deletable").click(function (event) {
			var tagName = jQuery(this).text();
			removeTagsRecursively(assetId, null, [tagName], function() {
				reloadTagsMethod(null);
			});
			lastAddedTag = null;
			event.preventDefault();
		});
	};
	var setEventsForTagsMethod = this.setEventsForTags;
	
	this.containsTag = function(tagName) {
		var result = false;
		jQuery("#actualTagsSpan" + id + " .tag").each(function () {
			if (jQuery(this).html() == tagName) {
				result = true;
			}
		});
		return result;
	};
	var containsTagMethod = this.containsTag;

	// register events
	jQuery("#tagEditButton" + id).click(function(event) {
		openTagEditToolbarMethod();
		event.preventDefault();
	});
	
	jQuery("#hideTagToolbarButton" + id).click(function(event) {	
		if (jQuery("#tagInput" + id).val() == "") {
			closeTagEditToolbarMethod();
		}
		else {
			var tagName = jQuery("#tagInput" + assetId).val();
			addTagsRecursively(assetId, null, [tagName], function() {
				jQuery("#tagInput" + id).val('');
				reloadTagsMethod(closeTagEditToolbarMethod);
			});
		}	
		event.preventDefault();
	});
	
	jQuery('#addTagButton' + id).click(function(event) {
		if (jQuery("#tagInput" + id).val() != "") {
			var tagName = jQuery("#tagInput" + assetId).val();
			addTagsRecursively(assetId, null, [tagName], function() {
				jQuery("#tagInput" + id).val('');
				reloadTagsMethod();
			});
		}	
	});
	
	/* needed in safari to prevent form submit */
	jQuery("#tagInput" + id).keypress(function(event) {
		if (event.keyCode == 13) {
			event.preventDefault();
		}
	});
	
	jQuery("#tagInput" + id).keypress(function(event){
		if (event.keyCode == 27) {
			closeTagEditToolbarMethod();
		}
		else if (event.keyCode == 13) {
			// if container is open, we should get an itemSelectEvent			
			if (jQuery("#tagInput" + id).val() == "") {
				closeTagEditToolbarMethod();
			}
			else {
				var tagName = jQuery("#tagInput" + assetId).val();

				if (lastAddedTag != tagName) {
					lastAddedTag = tagName;
					addTagsRecursively(assetId, null, [tagName], function(){
						jQuery("#tagInput" + id).val('');
						reloadTagsMethod(null);
					});
				}
			}
		}
	});
	
	if (jQuery('#tagContainer' + assetId).hasClass('alwaysEdit')) {
		openTagEditToolbarMethod();
	};
	
}

/* ++ tags general ++++++++++++++++++++++++++++++ */

function makeTagsDraggable(jQueryObjectSet) {
	jQueryObjectSet.draggable({
		revert: "invalid",
		start: function(event, ui) {
			jQuery(this).addClass('noclick');
		}
	});
}

function makeTagsDroppable(jQueryObjectSet) {
	jQueryObjectSet.droppable({
		accept: '.tag.draggable',
		hoverClass: 'droppableHover',
		greedy: true,
		drop: function(event, ui) {
			var answer = confirm('Assign the tag"' + jQuery(this).text() + '" to all items having the tag "' + ui.draggable.text() + '"?');
			if (answer) {
				jQuery.ajax({
				    type: "POST",
				    url: "/functions/unifyTags",
					dataType: "json",
				    data: {
						"parentFolderTagName": jQuery(this).text(),
						"childFolderTagName": ui.draggable.text()
					},
				    success: function(msg){
						if (!msg.success) {
							alert('Tag unification operation failed. Do you have sufficient rights?');
						}
						else {
							alert('tag unification operation succeeded! ' + msg.numberOfAddedTags + ' tags were added.');
							jQuery('#detailedSearch').submit();
						}
				    },
					error: function (XMLHttpRequest, textStatus, errorThrown) {
					    alert('The server is unavailable. Tag unification operation could not be performed.' 
							+ ' textStatus: ' + textStatus + ' errorThrown: ' + errorThrown);
					}
				});
			}
		}
	});
}

/* ++++++++++++++++++++++++++++++++++++++++++++++ */

jQuery(document).ready(function(){
	jQuery('#roleSelect').change(function() {
		var id = jQuery(this).val();
		var href = "/changeRole?groupId=" + id + "&location=" + window.location.href;
        window.location = href;
	});
    jQuery('a.toro-delete-link').click(function(){
		jQuery(this).find(".modalContent").modal( { onShow:function(dialog) {
			dialog.container.find('input.toro-delete-link-submit').focus();
		} });
    });
	jQuery(".tricia-in-place-editing-link").hide();
	jQuery('.tricia-in-place-editing').hover( 
		function() {
			jQuery(this).find(".tricia-in-place-editing-link").show();
		}, 
		function() {
			jQuery(this).find(".tricia-in-place-editing-link").hide();
		}
	);
    jQuery('.tricia-in-place-editing-link').click(function() {
		tinyMCEAsOverlay(jQuery(this).attr("href"));
		return false;
	});
    jQuery('.tricia-in-place-editing').dblclick(function() {
		tinyMCEAsOverlay(jQuery(this).find('.tricia-in-place-editing-link').eq(0).attr("href"));
		return false;
	});
	registerChangeValue();
});

function tinyMCEAsOverlay (href) {
	var overlayDiv = jQuery("#toro-overlay");
	if(overlayDiv.length == 0) {
		overlayDiv = jQuery('<div id="toro-overlay"></div>');
		jQuery("body").append(overlayDiv);
	} else {
		overlayDiv.empty();
	}
	jQuery.ajax( {
		dataType: "json",
		url: href,
		success: function(data) {
			if (data.code == "success") {
				overlayDiv.append(data.html);
				registerChangeValue();
				var dialog = overlayDiv.dialog({
					width: 810,
					modal: true,
					close: function(event, ui) {
						goToUrl(window.location.href);
					},
					title: data.title
				});
				jQuery("body").data("dialog", dialog);
				if(tinyMCE != null) {
					var id = overlayDiv.find("textarea").eq(0).attr("id");
					tinymce.execCommand('mceFocus', false, id);
				}
			} else {
				goToUrl(data.href);
			}
		}
	} );
	return false;
}

function getColor(elem, attr) {
	var color;
	do {
		color = elem.css(attr);
		// Keep going until we find an element that has color, or we hit the body
		if ( color != '' && color != 'transparent' || elem.nodeName == 'body' )
			break; 
	} while ( elem = elem.parent() );
	return color;
};

function registerChangeValue() {
    jQuery("div.toro-edit-manyRoleControl").each(function(){
        handleManyRoleLabel(jQuery(this));
    });
	jQuery("div.toro-edit-oneRoleControl").each(function() {
		handleOneRoleControl(jQuery(this));
	});
	jQuery('form.toro-edit-form input.toro-edit-input').change(function() {
		changeValue(jQuery(this));
	});
	jQuery('form.toro-edit-form textarea.toro-edit-input').change(function() {
		changeValue(jQuery(this));
	});
	jQuery('form.toro-edit-form select.toro-edit-input').change(function() {
		changeValue(jQuery(this));
	});
	jQuery('form.toro-edit-form input.toro-edit-input').keyup(function(e) {
		if(e.keyCode == 13) {
			changeValue(jQuery(this));
			return false;
		}
	});
	jQuery('form.toro-edit-form input.toro-edit-additional-choice').change(function() {
		changeControlValue(jQuery(this));
	});
}

function handleOneRoleControl(container) {
	var uid = container.attr("uid");
	var noReplaceButton = container.attr("noReplaceButton") == "true";
	if(uid == null) {
		addOneRoleSetBy(container, false, false, noReplaceButton);
	} else {
		addOneRoleReplaceBy(container, uid, container.attr("title"), container.html(), false, false, noReplaceButton);
	}
}

function addOneRoleSetBy(container, doChangeValueNow, neverChangeValue, noReplaceButton) {
	container.empty();
	var input = getJQueryAutoCompleteInput();
	if(!neverChangeValue && doChangeValueNow) {
		changeOneRoleValue(container, "");
	}
    container.append(input);
	
	input.autocomplete({
		minLength: 2,
		menuWidth: 300,
		source: function(request, response) {
			jQuery.ajax({
				url: "/functions/findAsset2",
				dataType: "json",
				data: {
					query: request.term,
					otherKind: container.attr("otherKind")
				},
				success: function( data ) {
					response( data );
				}
			})
		},
		focus: function(event, ui) {
			return false;
		},
		select: function(event, ui) {
			addOneRoleReplaceBy(container, ui.item.uid, ui.item.label, ui.item.imageTag, true, neverChangeValue, noReplaceButton);
		}
	}).data( "autocomplete" )._renderItem = autoCompleteRenderItemfunction;	
}

function addOneRoleReplaceBy(container, uid, title, imageTag, doChangeValueNow, neverChangeValue, noReplaceButton) {
	var div = jQuery("<div class='toro-one-role-asset'>" + imageTag + "&nbsp;<label>" + title + "</label></div>");
    div.hover(function(){
        jQuery(this).addClass("toro-hoverAssociation");
        jQuery(this).attr("hover", "true");
    }, function(){
        jQuery(this).removeClass("toro-hoverAssociation");
        jQuery(this).attr("hover", "false");
    });
    div.click(function(){
        if ("true" == jQuery(this).attr("hover")) {
			container.empty();
			container.removeAttr("uid");
			changeOneRoleValue(container, "");
			handleOneRoleControl(container);
        }
		return false;
    });
	div.attr("uid", uid);
	container.empty();
	if(doChangeValueNow && !neverChangeValue) {
		changeOneRoleValue(container, uid);
	}
	container.append(div);
}

function changeOneRoleValue(container, uid){
	var input = container.parent().find("input.toro-edit-parameter-submit");
    changeValue(input.get());
	input.val(uid);
}

function getJQueryAutoCompleteInput() {
	return jQuery("<input type='text' size='35'/>");
}

function handleManyRoleLabel(container){
	var divClass = "toro-edit-handled-many-role";
	if(container.find("." + divClass).size() == 0) {
		var input = getJQueryAutoCompleteInput();
	    container.append(input);
		
		input.autocomplete({
			minLength: 2,
			menuWidth: 300,
			source: function(request, response) {
				jQuery.ajax({
					url: "/functions/findAsset2",
					dataType: "json",
					data: {
						query: request.term,
						otherKind: container.attr("otherKind")
					},
					success: function( data ) {
						response( data );
					}
				})
			},
			focus: function(event, ui) {
				return false;
			},
			select: function(event, ui) {
			    var uid = ui.item.uid;
			    if (!contains(container, uid)) {
			        var newLi = jQuery("<li>" + ui.item.imageTag + "&nbsp;<label></label></li>");
			        newLi.attr("uid", uid);
			        var label = newLi.find("label");
			        label.text(ui.item.label);
					var uls = container.find("ul.toro-associations");
			        var ul;
					if(uls.length == 0) {
						ul = jQuery('<ul class="toro-associations"></ul>')
						container.append(ul);
					} else {
			        	ul = container.find("ul").eq(0);
					}
					var overrideReaders = container.find("input.toro-edit-override-readers-overrides");
					if(overrideReaders.length > 0) {
						overrideReaders.eq(0).attr("checked", true);
						changeValue(overrideReaders);
					}
			        ul.append(newLi);
			        handleLi(label);
			        input.remove();
			        changeManyRoleValue(container);
					handleManyRoleLabel(container);
			    }
			}
		}).data( "autocomplete" )._renderItem = autoCompleteRenderItemfunction;	
		
	    container.find("ul > li > label").each(function(){
	        handleLi(jQuery(this));
	    });
	}
}

function handleLi(label){
    label.hover(function(){
        jQuery(this).addClass("toro-hoverAssociation");
        jQuery(this).attr("hover", "true");
    }, function(){
        jQuery(this).removeClass("toro-hoverAssociation");
        jQuery(this).attr("hover", "false");
    });
    label.click(function(){
        if ("true" == jQuery(this).attr("hover")) {
			var container = label.parents("div.toro-edit-manyRoleControl").eq(0);
            label.parent("li").remove();
            changeManyRoleValue(container);
            var overrideReaders = container.find("input.toro-edit-override-readers-overrides");
            if (overrideReaders.length > 0) {
				if(container.find("ul.toro-associations").find("li").length == 0) {
					overrideReadersInherits = container.parents(".toro-edit-control").find("input.toro-edit-override-readers-inherits");
	                overrideReadersInherits.eq(0).attr("checked", true);
	                changeValue(overrideReadersInherits);
				}
            }
			return false;
        }
		return false;
    });
}

function contains(container, uid){
    var result = false;
    container.find("li").each(function(){
        if (uid == jQuery(this).attr("uid")) {
            result = true;
        }
    });
    return result;
}

function changeManyRoleValue(container){
	var listenerName = container.attr("listener");
	if(listenerName != null) {
		eval(listenerName + "()");
	}
	var input = container.find("input.toro-edit-parameter-submit");
    changeValue(input.get());
	var value = "";
	container.find("ul > li").each(function() {
		value += jQuery(this).attr("uid") + " ";
	});
	input.val(value);
}

function changeControlValue(control) {
	var controlWrapper = jQuery(control).parents(".toro-edit-control");
	if(controlWrapper.length == 0) {
		alert("Cannot change control value!");
	}
	var manyRoleContainer = controlWrapper.find(".toro-edit-manyRoleControl");
	if(manyRoleContainer.length == 1) {
		changeManyRoleValue(manyRoleContainer);
	} else {
		changeValue(controlWrapper.find(".toro-edit-input"));
	}
}

function autoCompleteRenderItemfunction( ul, item ) {
	jQuery( "<li></li>" )
		.data( "item.autocomplete", item )
		.append( item.html )
		.appendTo( ul )
		.find(".toro-autocomplete-details").each(function() {
			var a = jQuery(this);
            a.tooltip({
                content: function(response){
                    jQuery.get("/functions/showDetails?url=" + a.attr("url"), response);
                    return "Loading...";
                },
                position: {
                    my: "center bottom",
                    at: "center top",
                    offset: "0 -5"
                }
            });
	});
}

// --------------------------------------------------------

function debugJQueryElement(e) {
	var result = "";
	jQuery.each(e, function() {
		var e = jQuery(this);
		result += "html: " + e.eq(0).get().nodeName + "\n";
		result += getCssProperty(e, "visibility");
		result += getCssProperty(e, "z-index");
		result += getCssProperty(e, "position");
		result += getCssProperty(e, "display");
		//e.css("visibility", "visible");
		e.css("z-index", "0");
	});
	return result;
}

function getCssProperty(e, name) {
	return name + ": " + e.css(name) + "\n";
}

function setToNulls(clearInput) {
	var length = clearInput.val().length;
	var nulls = "";
	for(var i=0; i<length; i++) {
		nulls = nulls + "0";
	}
	clearInput.val(nulls);
}

function changeValue(input, parameterName){
	var jInput = jQuery(input);
	if(jInput.length > 0) {
		changeValueNonCompoundValue(jInput, parameterName);
		var parents = jInput.parents("table.toro-show-compoundValues");
		if(parents.size() > 0) {
			parents.parent().find("> input").each(function() {
				changeValueNonCompoundValue(jQuery(this));
			});
		}
	}
}

function changeValueNonCompoundValue(input, parameterName) {
	jQuery(".changes-hidden").each(function() {
		var changesField = jQuery(this);
	    var val = changesField.val();
		if(parameterName == null) {
	    	parameterName = input.attr("name");
		}
		if(parameterName.indexOf("-") >= 0) {
			parameterName = parameterName.substring(0, parameterName.indexOf("-"));
		}
	    if (val.indexOf(parameterName) < 0) {
	        changesField.val(val += "," + parameterName);
	    }
	});
}

// ---------------------------------------------------

function goToUrl(url) {
	window.location = url;
}

function appendToUrl(query) {
	var url = window.location.href;
	if(url.indexOf('?') > 0) {
		url = url + "&" + query;
	} else {
		url = url + "?" + query;
	}
	return url;
}

function bugReport() {
	window.open("/feedback/createIssue?location=" + window.location.href, "Feedback", "menubar=no,width=660,height=500,toolbar=no");
}


// ----------- link control ----------------------------------------

function linkOrImageControl() {
	if(window.location.href.indexOf("/linkControl/") > 0) {
		return "linkControl";
	} else {
		return "imageControl";
	}
}

function directoryInput(selectedData) {
	jQuery("#toro-linkControl-directory").load("/" + linkOrImageControl() + "/directoryView?dirUid=" + selectedData.uid, directoryLoaded);
}

function directoryLoaded() {
	jQuery('div#toro-linkControl-directory a').click(function(e) {
		if(jQuery(this).hasClass('toro-popup-go-into')) {
			var href = jQuery(this).attr("href");
			jQuery("#toro-linkControl-directory").load("/" + linkOrImageControl() + "/directoryView?url=" + href, directoryLoaded);
		} else {
			jQuery('#document-form').css("visibility", "visible");
			var nameInput = jQuery('#document-name');
			if(nameInput) {
				var name = jQuery(this).attr("name");
				if(name == null || name.length == 0) {
					name = jQuery(this).text();
				}
				nameInput.val(name);
			}
			var url = jQuery(this).attr('href');
			if(url.length > 7 && url.substring(0, 7) == "http://") {
				url = url.substring(8, url.length);
				var index = url.indexOf('/');
				url = url.substring(index, url.length);					
			}
			if (url.indexOf('?details=true') > 0) {
				url = url.substring(0, url.length - '?details=true'.length);
			}
			jQuery('#document-url').val(url);
			return false;
		}
		return false;
	});
}

function handleJQueryAutoCompletion(container, onSelect, value) {
	var input = getJQueryAutoCompleteInput();
	container.empty();
	container.append(input);

	input.autocomplete({
		minLength: 2,
		menuWidth: 300,
		source: function(request, response) {
			jQuery.ajax({
				url: "/functions/findAsset2",
				dataType: "json",
				data: {
					query: request.term,
					otherKind: container.attr("otherKind")
				},
				success: function( data ) {
					response( data );
				}
			})
		},
		focus: function(event, ui) {
			return false;
		},
		select: function(event, ui) {
			onSelect(ui.item);
		}
	}).data( "autocomplete" )._renderItem = autoCompleteRenderItemfunction;
}

function createNewWikiPage() {
	var wikiUrl = jQuery('.toro-choose-wiki').eq(0).attr('wikiUrl');
	var name = jQuery('.toro-choose-wikiPage').find('input').val();
    if (name && name.length > 2) {
        jQuery.getJSON("/wikiPage/createStub", {
            name: name,
            wikiUrl: wikiUrl
        }, function(json){
			if(json.invalid != null) {
				alert("There already exists a page with this name.");
			} else {
				insertLink(json.url, name);
			}
        });
    } else {
		alert("Provide a name with at least 2 characters.");
	}
}

function loadCss(cssUrl) {
	jQuery('head').each(function() {
		jQuery(this).append("<link rel='stylesheet' type='text/css' href='" + cssUrl + "' />");
	});
}


function loadCaptchaForm(submitForm){
    jQuery('div#toro-overlay').empty();
    jQuery('div#toro-overlay').load("/captcha/new", function() {
		initCaptchaForm(submitForm);
	});
	jQuery.modal(jQuery('div#toro-overlay'), {close: false});
}

function initCaptchaForm(submitForm) {
	registerChangeValue();
	jQuery('#toro-captcha-edit .toro-cancel').click(function() {
		jQuery.modal.close();
	});
    jQuery('#toro-captcha-edit').submit(function(){
        jQuery(this).ajaxSubmit({
            dataType: "json",
            success: function(json){
                if (json.isValid) {
					jQuery.modal.close();
					jQuery(submitForm).unbind('submit').submit();
                } else {
                    // is not valid
                    jQuery('div#toro-overlay').empty();
                    jQuery('div#toro-overlay').load("/captcha/edit?captchaId=" + json.captchaId + "&showValidationMessage", function() {
						initCaptchaForm(submitForm);
					});
                }
            }
        });
        // return false to prevent normal browser submit and page navigation
        return false;
    });
}

// ---------------------------------------------------

// Return back to the last visited page
function returnBack(){
    var name = navigator.appName
    if (name == "Microsoft Internet Explorer") 
        location.href = "javascript:history.back()";
    else 
        location.href = "javascript:window.back()";
}

function randomString() {
	var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
	var string_length = 8;
	var randomstring = '';
	for (var i=0; i<string_length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		randomstring += chars.substring(rnum,rnum+1);
	}
	return randomstring;
}

// ------------------- foldable table rows ------------------------------

jQuery(function(){
	jQuery("tr.tricia-heading-row-with-collapsing").each(function() {
		var tr = jQuery(this);
		if(isCollapsable(tr)) {
			tr.addClass("tricia-heading-row-closed");
			tr.next("tr").each(function() {
				hideIfNoHeadingRow(jQuery(this));
			});
			tr.click(function() {
				tr.toggleClass("tricia-heading-row-closed");
				tr.toggleClass("tricia-heading-row-open");
				tr.next("tr").each(function() {
					toggleIfNoHeadingRow(jQuery(this));
				});
			});
		}
	});
});

function isCollapsable(tr) {
	var nextTr = tr.next("tr");
	if(nextTr.length == 0) {
		return false;
	} else {
		return collapse(nextTr);
	}
}

function collapse(tr) {
	return !tr.hasClass("tricia-heading-row-with-collapsing") && !tr.hasClass("tricia-heading-row-stop-collapsing") && !tr.hasClass("tricia-heading-row-without-collapsing");
}

function hideIfNoHeadingRow(tr){
	if(collapse(tr)) {
		tr.hide();
		tr.next("tr").each(function() {
			hideIfNoHeadingRow(jQuery(this));
		});
	}
}

function toggleIfNoHeadingRow(tr){
	if (!tr.hasClass("tricia-heading-row-with-collapsing")) {
		if (tr.filter(":hidden").size() > 0) {
			showIfNoHeadingRow(tr);
		} else {
			hideIfNoHeadingRow(tr);
		}
	}
}

function showIfNoHeadingRow(tr){
	if(!tr.hasClass("tricia-heading-row-with-collapsing")) {
		tr.show();
		tr.next("tr").each(function() {
			showIfNoHeadingRow(jQuery(this));
		});
	}
}


//-- MD5 Algorithm

/*
 *  md5.jvs 1.0b 27/06/96
 *
 * Javascript implementation of the RSA Data Security, Inc. MD5
 * Message-Digest Algorithm.
 *
 * Copyright (c) 1996 Henri Torgemane. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for any purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies.
 *
 * Of course, this soft is provided "as is" without express or implied
 * warranty of any kind.
 *
 * $Id: md5.js,v 1.1.1.1 2000/04/17 16:40:07 kk Exp $
 *
 */
function array(n){
    for (i = 0; i < n; i++) 
        this[i] = 0;
    this.length = n;
}

/* Some basic logical functions had to be rewritten because of a bug in
 * Javascript.. Just try to compute 0xffffffff >> 4 with it..
 * Of course, these functions are slower than the original would be, but
 * at least, they work!
 */
function integer(n){
    return n % (0xffffffff + 1);
}

function shr(a, b){
    a = integer(a);
    b = integer(b);
    if (a - 0x80000000 >= 0) {
        a = a % 0x80000000;
        a >>= b;
        a += 0x40000000 >> (b - 1);
    }
    else 
        a >>= b;
    return a;
}

function shl1(a){
    a = a % 0x80000000;
    if (a & 0x40000000 == 0x40000000) {
        a -= 0x40000000;
        a *= 2;
        a += 0x80000000;
    }
    else 
        a *= 2;
    return a;
}

function shl(a, b){
    a = integer(a);
    b = integer(b);
    for (var i = 0; i < b; i++) 
        a = shl1(a);
    return a;
}

function and(a, b){
    a = integer(a);
    b = integer(b);
    var t1 = (a - 0x80000000);
    var t2 = (b - 0x80000000);
    if (t1 >= 0) 
        if (t2 >= 0) 
            return ((t1 & t2) + 0x80000000);
        else 
            return (t1 & b);
    else 
        if (t2 >= 0) 
            return (a & t2);
        else 
            return (a & b);
}

function or(a, b){
    a = integer(a);
    b = integer(b);
    var t1 = (a - 0x80000000);
    var t2 = (b - 0x80000000);
    if (t1 >= 0) 
        if (t2 >= 0) 
            return ((t1 | t2) + 0x80000000);
        else 
            return ((t1 | b) + 0x80000000);
    else 
        if (t2 >= 0) 
            return ((a | t2) + 0x80000000);
        else 
            return (a | b);
}

function xor(a, b){
    a = integer(a);
    b = integer(b);
    var t1 = (a - 0x80000000);
    var t2 = (b - 0x80000000);
    if (t1 >= 0) 
        if (t2 >= 0) 
            return (t1 ^ t2);
        else 
            return ((t1 ^ b) + 0x80000000);
    else 
        if (t2 >= 0) 
            return ((a ^ t2) + 0x80000000);
        else 
            return (a ^ b);
}

function not(a){
    a = integer(a);
    return (0xffffffff - a);
}

// Here begin the real algorithm

var state = new array(4);
var count = new array(2);
var buffer = new array(64);
var transformBuffer = new array(16);
var digestBits = new array(16);

var S11 = 7;
var S12 = 12;
var S13 = 17;
var S14 = 22;
var S21 = 5;
var S22 = 9;
var S23 = 14;
var S24 = 20;
var S31 = 4;
var S32 = 11;
var S33 = 16;
var S34 = 23;
var S41 = 6;
var S42 = 10;
var S43 = 15;
var S44 = 21;

function F(x, y, z){
    return or(and(x, y), and(not(x), z));
}

function G(x, y, z){
    return or(and(x, z), and(y, not(z)));
}

function H(x, y, z){
    return xor(xor(x, y), z);
}

function I(x, y, z){
    return xor(y, or(x, not(z)));
}

function rotateLeft(a, n){
    return or(shl(a, n), (shr(a, (32 - n))));
}

function FF(a, b, c, d, x, s, ac){
    a = a + F(b, c, d) + x + ac;
    a = rotateLeft(a, s);
    a = a + b;
    return a;
}

function GG(a, b, c, d, x, s, ac){
    a = a + G(b, c, d) + x + ac;
    a = rotateLeft(a, s);
    a = a + b;
    return a;
}

function HH(a, b, c, d, x, s, ac){
    a = a + H(b, c, d) + x + ac;
    a = rotateLeft(a, s);
    a = a + b;
    return a;
}

function II(a, b, c, d, x, s, ac){
    a = a + I(b, c, d) + x + ac;
    a = rotateLeft(a, s);
    a = a + b;
    return a;
}

function transform(buf, offset){
    var a = 0, b = 0, c = 0, d = 0;
    var x = transformBuffer;
    
    a = state[0];
    b = state[1];
    c = state[2];
    d = state[3];
    
    for (i = 0; i < 16; i++) {
        x[i] = and(buf[i * 4 + offset], 0xff);
        for (j = 1; j < 4; j++) {
            x[i] += shl(and(buf[i * 4 + j + offset], 0xff), j * 8);
        }
    }
    
    // Round 1
    a = FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
    d = FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
    c = FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
    b = FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
    a = FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
    d = FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
    c = FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
    b = FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
    a = FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
    d = FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
    c = FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
    b = FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
    a = FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
    d = FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
    c = FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
    b = FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
    // Round 2
    a = GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
    d = GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
    c = GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
    b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
    a = GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
    d = GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
    c = GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
    b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
    a = GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
    d = GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
    c = GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
    b = GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
    a = GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
    d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
    c = GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
    b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
    // Round 3
    a = HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
    d = HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
    c = HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
    b = HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
    a = HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
    d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
    c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
    b = HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
    a = HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
    d = HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
    c = HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
    b = HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
    a = HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
    d = HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
    c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
    b = HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
    // Round 4
    a = II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
    d = II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
    c = II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
    b = II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
    a = II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
    d = II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
    c = II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
    b = II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
    a = II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
    d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
    c = II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
    b = II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
    a = II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
    d = II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
    c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
    b = II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
    
}

function init(){
    count[0] = count[1] = 0;
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
    for (i = 0; i < digestBits.length; i++) 
        digestBits[i] = 0;
}

function update(b){
    var index, i;
    
    index = and(shr(count[0], 3), 0x3f);
    if (count[0] < 0xffffffff - 7) 
        count[0] += 8;
    else {
        count[1]++;
        count[0] -= 0xffffffff + 1;
        count[0] += 8;
    }
    buffer[index] = and(b, 0xff);
    if (index >= 63) {
        transform(buffer, 0);
    }
}

function finish(){
    var bits = new array(8);
    var padding;
    var i = 0, index = 0, padLen = 0;
    
    for (i = 0; i < 4; i++) {
        bits[i] = and(shr(count[0], (i * 8)), 0xff);
    }
    for (i = 0; i < 4; i++) {
        bits[i + 4] = and(shr(count[1], (i * 8)), 0xff);
    }
    index = and(shr(count[0], 3), 0x3f);
    padLen = (index < 56) ? (56 - index) : (120 - index);
    padding = new array(64);
    padding[0] = 0x80;
    for (i = 0; i < padLen; i++) 
        update(padding[i]);
    for (i = 0; i < 8; i++) 
        update(bits[i]);
    
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            digestBits[i * 4 + j] = and(shr(state[i], (j * 8)), 0xff);
        }
    }
}

function hexa(n){
    var hexa_h = "0123456789abcdef";
    var hexa_c = "";
    var hexa_m = n;
    for (hexa_i = 0; hexa_i < 8; hexa_i++) {
        hexa_c = hexa_h.charAt(Math.abs(hexa_m) % 16) + hexa_c;
        hexa_m = Math.floor(hexa_m / 16);
    }
    return hexa_c;
}


var ascii = "01234567890123456789012345678901" +
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

function MD5(entree){
    var l, s, k, ka, kb, kc, kd;
    
    init();
    for (k = 0; k < entree.length; k++) {
        l = entree.charAt(k);
        update(ascii.lastIndexOf(l));
    }
    finish();
    ka = kb = kc = kd = 0;
    for (i = 0; i < 4; i++) 
        ka += shl(digestBits[15 - i], (i * 8));
    for (i = 4; i < 8; i++) 
        kb += shl(digestBits[15 - i], ((i - 4) * 8));
    for (i = 8; i < 12; i++) 
        kc += shl(digestBits[15 - i], ((i - 8) * 8));
    for (i = 12; i < 16; i++) 
        kd += shl(digestBits[15 - i], ((i - 12) * 8));
    s = hexa(kd) + hexa(kc) + hexa(kb) + hexa(ka);
    return s;
}

// End of the MD5 algorithm

/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD4 Message
 * Digest Algorithm, as defined in RFC 1320.
 * Version 2.1 Copyright (C) Jerrad Pierce, Paul Johnston 1999 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = "-"; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 16;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 */
function hex_md4(s){ return binl2hex(core_md4(str2binl(s), s.length * chrsz));}
function b64_md4(s){ return binl2b64(core_md4(str2binl(s), s.length * chrsz));}
function str_md4(s){ return binl2str(core_md4(str2binl(s), s.length * chrsz));}
function hex_hmac_md4(key, data) { return binl2hex(core_hmac_md4(key, data)); }
function b64_hmac_md4(key, data) { return binl2b64(core_hmac_md4(key, data)); }
function str_hmac_md4(key, data) { return binl2str(core_hmac_md4(key, data)); }

/* 
 * Perform a simple self-test to see if the VM is working 
 */
function md4_vm_test()
{
  return hex_md4("abc") == "a448017aaf21d8525fc10ae87aa6729d";
}

/*
 * Calculate the MD4 of an array of little-endian words, and a bit length
 */
function core_md4(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << (len % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;
  
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md4_ff(a, b, c, d, x[i+ 0], 3 );
    d = md4_ff(d, a, b, c, x[i+ 1], 7 );
    c = md4_ff(c, d, a, b, x[i+ 2], 11);
    b = md4_ff(b, c, d, a, x[i+ 3], 19);
    a = md4_ff(a, b, c, d, x[i+ 4], 3 );
    d = md4_ff(d, a, b, c, x[i+ 5], 7 );
    c = md4_ff(c, d, a, b, x[i+ 6], 11);
    b = md4_ff(b, c, d, a, x[i+ 7], 19);
    a = md4_ff(a, b, c, d, x[i+ 8], 3 );
    d = md4_ff(d, a, b, c, x[i+ 9], 7 );
    c = md4_ff(c, d, a, b, x[i+10], 11);
    b = md4_ff(b, c, d, a, x[i+11], 19);
    a = md4_ff(a, b, c, d, x[i+12], 3 );
    d = md4_ff(d, a, b, c, x[i+13], 7 );
    c = md4_ff(c, d, a, b, x[i+14], 11);
    b = md4_ff(b, c, d, a, x[i+15], 19);

    a = md4_gg(a, b, c, d, x[i+ 0], 3 );
    d = md4_gg(d, a, b, c, x[i+ 4], 5 );
    c = md4_gg(c, d, a, b, x[i+ 8], 9 );
    b = md4_gg(b, c, d, a, x[i+12], 13);
    a = md4_gg(a, b, c, d, x[i+ 1], 3 );
    d = md4_gg(d, a, b, c, x[i+ 5], 5 );
    c = md4_gg(c, d, a, b, x[i+ 9], 9 );
    b = md4_gg(b, c, d, a, x[i+13], 13);
    a = md4_gg(a, b, c, d, x[i+ 2], 3 );
    d = md4_gg(d, a, b, c, x[i+ 6], 5 );
    c = md4_gg(c, d, a, b, x[i+10], 9 );
    b = md4_gg(b, c, d, a, x[i+14], 13);
    a = md4_gg(a, b, c, d, x[i+ 3], 3 );
    d = md4_gg(d, a, b, c, x[i+ 7], 5 );
    c = md4_gg(c, d, a, b, x[i+11], 9 );
    b = md4_gg(b, c, d, a, x[i+15], 13);

    a = md4_hh(a, b, c, d, x[i+ 0], 3 );
    d = md4_hh(d, a, b, c, x[i+ 8], 9 );
    c = md4_hh(c, d, a, b, x[i+ 4], 11);
    b = md4_hh(b, c, d, a, x[i+12], 15);
    a = md4_hh(a, b, c, d, x[i+ 2], 3 );
    d = md4_hh(d, a, b, c, x[i+10], 9 );
    c = md4_hh(c, d, a, b, x[i+ 6], 11);
    b = md4_hh(b, c, d, a, x[i+14], 15);
    a = md4_hh(a, b, c, d, x[i+ 1], 3 );
    d = md4_hh(d, a, b, c, x[i+ 9], 9 );
    c = md4_hh(c, d, a, b, x[i+ 5], 11);
    b = md4_hh(b, c, d, a, x[i+13], 15);
    a = md4_hh(a, b, c, d, x[i+ 3], 3 );
    d = md4_hh(d, a, b, c, x[i+11], 9 );
    c = md4_hh(c, d, a, b, x[i+ 7], 11);
    b = md4_hh(b, c, d, a, x[i+15], 15);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);

  }
  return Array(a, b, c, d);

}

/*
 * These functions implement the basic operation for each round of the
 * algorithm.
 */
function md4_cmn(q, a, b, x, s, t)
{
  return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
}
function md4_ff(a, b, c, d, x, s)
{
  return md4_cmn((b & c) | ((~b) & d), a, 0, x, s, 0);
}
function md4_gg(a, b, c, d, x, s)
{
  return md4_cmn((b & c) | (b & d) | (c & d), a, 0, x, s, 1518500249);
}
function md4_hh(a, b, c, d, x, s)
{
  return md4_cmn(b ^ c ^ d, a, 0, x, s, 1859775393);
}

/*
 * Calculate the HMAC-MD4, of a key and some data
 */
function core_hmac_md4(key, data)
{
  var bkey = str2binl(key);
  if(bkey.length > 16) bkey = core_md4(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++) 
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_md4(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
  return core_md4(opad.concat(hash), 512 + 128);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert a string to an array of little-endian words
 * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
 */
function str2binl(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
  return bin;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
  return str;
}

/*
 * Convert an array of little-endian words to a hex string.
 */
function binl2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of little-endian words to a base-64 string
 */
function binl2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}
