/* $Id$ */

/*jslint 
 * white: true, browser: true, devel: true, windows: true, 
 * forin: true, onevar: true, undef: true, nomen: true, 
 * plusplus: true, regexp: true, newcap: true, immed: true 
 */

/* namespace */
function Webfm() {}

/*
 ** Global variables
 */
//Translation possible by changing the array values (DO NOT ALTER KEYS!)
Webfm.js_msg = [];
Webfm.js_msg.mkdir = Drupal.t("Create New Dir");
Webfm.js_msg.file = Drupal.t("file");
Webfm.js_msg.dir = Drupal.t("directory");
Webfm.js_msg.u_file = Drupal.t("File");
Webfm.js_msg.u_dir = Drupal.t("Directory");
Webfm.js_msg.tree = Drupal.t("");
Webfm.js_msg.work = Drupal.t("Working... please wait");
Webfm.js_msg.refresh = Drupal.t("refresh");
Webfm.js_msg.sort = Drupal.t("sort by this column");
Webfm.js_msg.newdir = Drupal.t("Create New Folder");
Webfm.js_msg.addtodb = Drupal.t("Add files in this folder to database");
Webfm.js_msg.recursivecaddaddtodb = Drupal.t("Recursive add files to database");
Webfm.js_msg.column1 = Drupal.t("Name");
Webfm.js_msg.column2 = Drupal.t("Modified");
Webfm.js_msg.column3 = Drupal.t("Size");
Webfm.js_msg.column4 = Drupal.t("Owner");
Webfm.js_msg.attachtitle = Drupal.t("Attached Files");
Webfm.js_msg.metatitle = Drupal.t("File Meta Data");
Webfm.js_msg.searchtitle = Drupal.t("File Search");
Webfm.js_msg.debugtitle = Drupal.t("WebFM Debug");
Webfm.js_msg.cachetitle = Drupal.t("Directory Cache");
Webfm.js_msg.searchcur = Drupal.t("Search current directory (see listing breadcrumb trail)");
Webfm.js_msg.nomatch = Drupal.t("No match found");
Webfm.js_msg.search = Drupal.t("Search");
Webfm.js_msg.submit = Drupal.t("Submit");
Webfm.js_msg.reset = Drupal.t("Reset");
Webfm.js_msg.clear = Drupal.t("Clear");
Webfm.js_msg.cancel = Drupal.t("Cancel");
Webfm.js_msg.close = Drupal.t("Close");
Webfm.js_msg.resize = Drupal.t("Resize");
Webfm.js_msg.replace = Drupal.t("Replace and Version original copy of ");
Webfm.js_msg.replacedel = Drupal.t("Replace and Delete original copy of ");
Webfm.js_msg.noreplace = Drupal.t("Version new copy of ");
Webfm.js_msg.cache = Drupal.t("Cache Content");
Webfm.js_msg.curdirundef = Drupal.t("current directory undefined");
Webfm.js_msg.ajaxerr = Drupal.t("server is unreachable");
Webfm.js_msg.moveerr = Drupal.t("move operation fail");
Webfm.js_msg.lengtherr = Drupal.t("Too long");
Webfm.js_msg.confirmdel0 = Drupal.t("Do you want to delete the ");
Webfm.js_msg.confirmdel1 = Drupal.t(" and all its contents");
Webfm.js_msg.confirmdet = Drupal.t("Do you want to detach ");
Webfm.js_msg.confirmdbrem0 = Drupal.t("Do you want to remove ");
Webfm.js_msg.confirmdbrem1 = Drupal.t(" from the database?\n     All metadata and attachments for this file will be lost");
Webfm.js_msg.enum_local = Drupal.t("Do you want to add all files of this directory into the database?");
Webfm.js_msg.enum_recur = Drupal.t("Do you want to add all files of this directory and sub-directories into the database?");
Webfm.js_msg.filedwld = Drupal.t("download file");
Webfm.js_msg.nofiledwld = Drupal.t("file not in database");
Webfm.js_msg.inserted = Drupal.t(" files inserted into db");
Webfm.js_msg.notinserted = Drupal.t(" files failed to insert into db");
Webfm.js_msg.metadata = Drupal.t("Metadata");
Webfm.js_msg.fix_input = Drupal.t("correct input");
Webfm.js_msg.perm = Drupal.t("File Permissions");
Webfm.js_msg.noattachments = Drupal.t("No files attached.");

Webfm.meta_msg = [];
Webfm.meta_msg.fid = Drupal.t("fid");
Webfm.meta_msg.uid = Drupal.t("uid");
Webfm.meta_msg.uname = Drupal.t("file owner");
Webfm.meta_msg.name = Drupal.t("name");
Webfm.meta_msg.title = Drupal.t("title");
Webfm.meta_msg.desc = Drupal.t("description");
Webfm.meta_msg.lang = Drupal.t("language");
Webfm.meta_msg.pub = Drupal.t("publisher");
Webfm.meta_msg.format = Drupal.t("format");
Webfm.meta_msg.ver = Drupal.t("version");
Webfm.meta_msg.type = Drupal.t("image type");
Webfm.meta_msg.width = Drupal.t("width");
Webfm.meta_msg.height = Drupal.t("height");
Webfm.meta_msg.dl_cnt = Drupal.t("downloads");
Webfm.meta_msg.link = Drupal.t("link");

Webfm.perm_msg = [];
Webfm.perm_msg.pub_view = Drupal.t("Public download");
Webfm.perm_msg.rol_view = Drupal.t("Role View/Download");
Webfm.perm_msg.rol_att = Drupal.t("Role Attach");
Webfm.perm_msg.rol_full = Drupal.t("Role Full Access");

Webfm.menu_msg = [];
Webfm.menu_msg.mkdir = Drupal.t("Create Subdirectory");
Webfm.menu_msg.rmdir = Drupal.t("Delete Directory");
Webfm.menu_msg.rm = Drupal.t("Delete File");
Webfm.menu_msg.rendir = Drupal.t("Rename Directory");
Webfm.menu_msg.search = Drupal.t("Search Directory");
Webfm.menu_msg.ren = Drupal.t("Rename File");
Webfm.menu_msg.meta = Drupal.t("File meta data");
Webfm.menu_msg.att = Drupal.t("Attach to Node");
Webfm.menu_msg.det = Drupal.t("Detach from Node");
Webfm.menu_msg.dwnld = Drupal.t("Download as file");
Webfm.menu_msg.view = Drupal.t("View file");
Webfm.menu_msg.enum_menu = Drupal.t("Add file to database");
Webfm.menu_msg.denum = Drupal.t("Remove file from database");
Webfm.menu_msg.perm = Drupal.t("File permissions");
Webfm.menu_msg.paste = Drupal.t("Paste link in editor window");
//Do not translate any code below this line

Webfm.current = null;

Webfm.dragCont = null;
Webfm.metaCont = null;
Webfm.contextCont = null;
Webfm.searchCont = null;
Webfm.debugCont = null;
Webfm.viewCont = null;

Webfm.dragStart = null;
Webfm.dragging = false;
Webfm.oldOnContextMenu = null;

Webfm.activeDropCont = null;
Webfm.dropContainers = [];
Webfm.attachContainer = null;

Webfm.browser = null;
Webfm.menuHT = null;
Webfm.metadataHT = null;
Webfm.dirTreeObj = null;
Webfm.dirListObj = null;
Webfm.attachObj = null;
Webfm.alrtObj = null;
Webfm.contextMenuObj = null;
Webfm.progressObj = null;
Webfm.dbgObj = null;
Webfm.metaObj = null;
Webfm.searchObj = null;
Webfm.permObj = null;
Webfm.permKey = null;
Webfm.permHT = null;
Webfm.permInfoHT = null;

Webfm.renameActive = false;
Webfm.admin = false;

Webfm.zindex = 1000;
Webfm.scrollVal = null; // freeze scroll

Webfm.eventListeners = [];

//Name of node edit form hidden field where attached node list is populated by js
Webfm.attachFormInput = "edit-attachlist";

Webfm.icons = {
		epdf: "pdf",
		ephp: "php",
		ephps: "php",
		ephp4: "php",
		ephp5: "php",
		eswf: "swf",
		esfa: "swf",
		exls: "xls",
		eods: "xls",
		edoc: "doc",
		ertf: "doc",
		eodt: "doc",
		eodd: "doc",
		ezip: "zip",
		erar: "zip",
		egz: "zip",
		e7z: "zip",
		etxt: "doc",
		echm: "hlp",
		ehlp: "hlp",
		enfo: "nfo",
		expi: "xpi",
		ec: "c",
		eh: "h",
		emp3: "mp3",
		ewav: "mp3",
		esnd: "mp3",
		einc: "cod",
		esql: "sql",
		edbf: "sql",
		ediz: "nfo",
		eion: "nfo",
		emod: "mp3",
		es3m: "mp3",
		eavi: "avi",
		empg: "avi",
		empeg: "avi",
		ewma: "mp3",
		ewmv: "avi",
		edwg: "dwg",
		ejpg: "i",
		ejpeg: "i",
		egif: "i",
		epng: "i",
		etiff: "i",
		ebmp: "i",
		eico: "i",
		eai: "ai",
		eskp: "skp",
		emov: "qt",
		epps: "pps",
		eppt: "pps"
	};

Webfm.lastarea = null;

/*
 ** Functions
 */

/**
 * Remember last used textarea for inserting file hrefs.
 */
function webfmSetlast() {
	Webfm.lastarea = this;
}

/**
 * Determine browser
 */
Webfm.browserDetect = function () {
	var b = navigator.userAgent.toLowerCase();

	this.isIE = false;
	this.isOP = false;

	if (b.indexOf("msie") >= 0) {
		this.isIE = true;
	}
	if (b.indexOf("opera") >= 0) {
		this.isOP = true;
	}
};

/**
 * Hashtable (used by directory listing cache and metadata)
 */
Webfm.ht = function () {
	this.flush();
};

Webfm.ht.prototype.put = function (key, value) {
	if ((key === null) || (value === null)) {
		throw "NullPointerException {" + key + "},{" + value + "}";
	}

	this.hash[key] = value;
};

Webfm.ht.prototype.get = function (key) {
	return this.hash[key];
};

Webfm.ht.prototype.containsKey = function (key) {
	var i;
	for (i in this.hash) {
		if (i == key && this.hash[i] !== null) {
			return true;
		}
	}
	return false;
};

Webfm.ht.prototype.remove = function (key) {
	if (this.hash[key]) {
		this.hash[key] = null;
		return true;
	}
	return false;
};

Webfm.ht.prototype.dump = function () {
	var i, values;
	values = [];

	for (i in this.hash) {
		if (this.hash[i] !== null) {
			values.push(this.hash[i]);
		}
	}
	return values;
};

Webfm.ht.prototype.flush = function () {
	this.hash = [];
};

/**
 * Hashable Hashtable (used by menus)
 */
Webfm.hht = function () {
	this.flush();
};

Webfm.hht.prototype.put = function (key, obj) {
	if ((key === null) || (obj === null)) {
		throw "NullPointerException {" + key + "},{" + obj + "}";
	}

	//TODO improve null check
	if (this.hash[key] === null || typeof this.hash[key] === 'undefined') {
		this.keys[this.keys.length] = key;
		this.hash[key] = [];
	}

	// append new object to array
	this.hash[key].push(obj);
};

Webfm.hht.prototype.get = function (key) {
	return this.hash[key];
};

Webfm.hht.prototype.remove = function (key) {
	if (this.hash[key]) {
		this.hash[key] = null;
		return true;
	}
	return false;
};

Webfm.hht.prototype.removeSub = function (key, subkey) {
	if (this.hash[key][subkey]) {
		this.hash[key][subkey] = null;
		return true;
	}
	return false;
};

Webfm.hht.prototype.flush = function () {
	this.hash = [];
	this.keys = [];
};

//Webfm.metaElement objects have description/edit/max_size params
Webfm.metaElement = function (desc, edit, size) {
	this.desc = desc;
	this.edit = edit;
	this.size = size;
};

/**
 * Create Web File Manager at 'webfm' id anchor
 * ..or at node-edit 'webfm-inline' id anchor
 */
function webfmLayout() {
	var layoutDiv, textareas, i, textfields;
	layoutDiv = '';

	// Initialize textarea for pasting file hrefs with the most often used
	// in case we try to paste before a textarea or -field was focussed.
	Webfm.lastarea = document.getElementById('edit-body');
	if (!Webfm.lastarea) {
		Webfm.lastarea = document.getElementById('edit-comment');
	}
	// Tell textareas and text fields to remember which one was last focussed.
	textareas = document.getElementsByTagName('textarea');
	i = textareas.length;
	while (i > 0) {
		i = i - 1;
		textareas[i].onfocus = webfmSetlast;
		
	}
	textfields = document.getElementsByTagName('input');
	i = textfields.length;
	while (i > 0) {
		i = i - 1;
		if (textfields[i].type == 'text') {
			textfields[i].onfocus = webfmSetlast;
		}
	}

	layoutDiv = Webfm.$('webfm');

	if (layoutDiv) {
		Webfm.commonInterface(layoutDiv);
		//first param forces trees refresh
		//second param forces list refresh
		Webfm.dirTreeObj.fetch(true, true);
	}
	else {
		layoutDiv = Webfm.$('webfm-inline');
		if (layoutDiv) {
			Webfm.commonInterface(layoutDiv);

			//Add attach-list menu and attach option to listing menu
			try {
				Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.att, Webfm.menuAttach, Webfm.menuFidVal));
				Webfm.menuHT.put('det', new Webfm.menuElement(Webfm.menu_msg.meta, Webfm.menuGetMeta, ''));
				Webfm.menuHT.put('det', new Webfm.menuElement(Webfm.menu_msg.det, Webfm.menuDetach, ''));
				Webfm.menuHT.put('det', new Webfm.menuElement(Webfm.menu_msg.paste, Webfm.menuPasteHref, ''));
			}
			catch (err) {
				alert("Menu Create err\n" + err);
			}
			// attach-list anchored to 'webfm-attach' div in webfm_form_alter()
			Webfm.attachObj = new Webfm.attach('webfm-attach');
			Webfm.attachObj.fetch();
			Webfm.dirTreeObj.fetch(true, true);
		}
	}
}

/*
 ** Initialize webfm on the current page.
 */
if (Drupal.jsEnabled) {
	$(window).load(webfmLayout);
}

//Attaches the upload behaviour to the upload form (borrowed jquery).
function webfmUploadAutoAttach() {
	var uri, button, wrapper, hide, upload;
	button = 'wfmatt-button';
	wrapper = 'wfmatt-wrapper';
	hide = 'wfmatt-hide';
	$('input.webfmupload').each(function () {
		uri = this.value;
		upload = new Webfm.jsUpload(uri, button, wrapper, hide);
	});
}

Webfm.commonInterface = function (parent) {
	var layout_cont, elTreeDiv;

	Webfm.browser = new Webfm.browserDetect();

	//create the popup container for file/dir object draggables
	Webfm.dragCont = new Webfm.popup("webfm-dragCont");

	//create the popup container for image view
	Webfm.viewCont = new Webfm.popup("webfm-viewCont");

	//create context menu popup
	Webfm.oldOnContextMenu = document.body.oncontextmenu;
	Webfm.contextCont = new Webfm.popup("webfm-cxtCont");
	Webfm.contextMenuObj = new Webfm.context(Webfm.contextCont);

	//create metadata popup
	Webfm.metaCont = new Webfm.popup("webfm-paneCont");
	Webfm.metaObj = new Webfm.metadata(Webfm.metaCont, 440, 420);

	//create file permissions popup
	Webfm.permCont = new Webfm.popup("webfm-permCont");
	Webfm.permObj = new Webfm.perm(Webfm.permCont, 300, 200);

	//create search popup
	Webfm.searchCont = new Webfm.popup("webfm-searchCont");
	Webfm.searchObj = new Webfm.search(Webfm.searchCont, 280, 200);

	//create debug popup
	Webfm.debugCont = new Webfm.popup("webfm-debugCont");
	Webfm.dbgObj = new Webfm.debug(Webfm.debugCont, 800, 400);

	//build menu hashtable
	//global to allow external functions to push new menu elements into the menu array
	Webfm.menuHT = new Webfm.hht();

	try {
		Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg.mkdir, Webfm.menuMkdir, Webfm.menuAdmin));
		Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg.search, Webfm.menuSearch, ''));

		Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg.mkdir, Webfm.menuMkdir, Webfm.menuAdmin));
		Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg.rmdir, Webfm.menuRemove, Webfm.menuAdmin));
		Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg.rendir, Webfm.menuRename, Webfm.menuAdmin));
		Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg.search, Webfm.menuSearch, ''));

		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.rm, Webfm.menuRemove, Webfm.menuFileUid));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.ren, Webfm.menuRename, Webfm.menuRenFileUid));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.meta, Webfm.menuGetMeta, Webfm.menuFidVal));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.view, Webfm.menuView, ''));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.dwnld, Webfm.menuDownload, Webfm.menuFidVal));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.enum_menu, Webfm.menuInsert, Webfm.menuAdminNoFidVal));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.denum, Webfm.menuDbRem, Webfm.menuAdminFidVal));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.perm, Webfm.menuGetPerm, Webfm.menuFilePerm));
		Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg.paste, Webfm.menuPasteHref, ''));
	}
	catch (err) {
		alert("Menu Create err\n" + err);
	}

	//build metadata hashtable
	//Webfm.metaElement objects have description/edit/max_size properties
	Webfm.metadataHT = new Webfm.ht();
	Webfm.metadataHT.put('id', new Webfm.metaElement(Webfm.meta_msg.fid, false, 0));
	Webfm.metadataHT.put('u', new Webfm.metaElement(Webfm.meta_msg.uid, true, 10));
	Webfm.metadataHT.put('un', new Webfm.metaElement(Webfm.meta_msg.uname, false, 0));
	Webfm.metadataHT.put('n', new Webfm.metaElement(Webfm.meta_msg.name, false, 0));
	Webfm.metadataHT.put('t', new Webfm.metaElement(Webfm.meta_msg.title, true, 255));
	Webfm.metadataHT.put('d', new Webfm.metaElement(Webfm.meta_msg.desc, true, 256));
	Webfm.metadataHT.put('l', new Webfm.metaElement(Webfm.meta_msg.lang, true, 16));
	Webfm.metadataHT.put('p', new Webfm.metaElement(Webfm.meta_msg.pub, true, 255));
	Webfm.metadataHT.put('f', new Webfm.metaElement(Webfm.meta_msg.format, true, 255));
	Webfm.metadataHT.put('v', new Webfm.metaElement(Webfm.meta_msg.ver, false, 0));
	Webfm.metadataHT.put('i', new Webfm.metaElement(Webfm.meta_msg.type, false, 0));
	Webfm.metadataHT.put('w', new Webfm.metaElement(Webfm.meta_msg.width, false, 0));
	Webfm.metadataHT.put('h', new Webfm.metaElement(Webfm.meta_msg.height, false, 0));
	Webfm.metadataHT.put('c', new Webfm.metaElement(Webfm.meta_msg.dl_cnt, false, 0));
	Webfm.metadataHT.put('lk', new Webfm.metaElement(Webfm.meta_msg.link, false, 0));

	//build permissions hashtable
	//key is permission bit, value is string
	Webfm.permInfoHT = new Webfm.ht();
	Webfm.permInfoHT.put("id", Webfm.meta_msg.fid);
	Webfm.permInfoHT.put("u", Webfm.meta_msg.uid);
	Webfm.permInfoHT.put("n", Webfm.meta_msg.name);
	Webfm.permKey = ['1', '2', '4', '8'];

	Webfm.permHT = new Webfm.ht();
	Webfm.permHT.put('1', Webfm.perm_msg.pub_view);
	Webfm.permHT.put('2', Webfm.perm_msg.rol_view);
	Webfm.permHT.put('4', Webfm.perm_msg.rol_att);
	Webfm.permHT.put('8', Webfm.perm_msg.rol_full);

	layout_cont = Webfm.ce('div');
	Webfm.alrtObj = new Webfm.alert(layout_cont, 'webfm-alert');

	//Container for tree(s)
	elTreeDiv = Webfm.ce('div');
	elTreeDiv.setAttribute('id', 'webfm-tree'); //css id
	layout_cont.appendChild(elTreeDiv);
	Webfm.dirTreeObj = new Webfm.treeBuilder(elTreeDiv, Webfm.menuHT.get('root'), Webfm.menuHT.get('dir'));

	//progress indicator
	Webfm.progressObj = new Webfm.progress(layout_cont, 'webfm-progress');

	//Directory Listing
	Webfm.dirListObj = new Webfm.list(layout_cont, 'webfm-dirlist', 'file', 'narrow', true, Webfm.menuHT.get('dir'), Webfm.menuHT.get('file'));

	//insert trees, listing, search, metadata, progress and alert divs before upload fset built in php
	parent.insertBefore(layout_cont, parent.firstChild);
	//  parent.appendChild(layout_cont); //If upload desired to be placed above browser

	webfmUploadAutoAttach();
};


/**
 * Webfm.jsUpload constructor
 * 1st param is upload iframe url
 * 2nd param is id of redirected button object)
 * 3rd param is id of container of html repainted by ajax
 * 4th param is id of upload input field
 * TODO: fix progress throbber
 */
Webfm.jsUpload = function (uri, button, wrapper, hide) {
	// Note: these elements are replaced after an upload, so we re-select them
	// everytime they are needed.
	this.button = '#' + button;
	this.wrapper = '#' + wrapper;
	this.hide = '#' + hide;
	Drupal.redirectFormButton(uri, $(this.button).get(0), this);
};

//Handler for the form redirection submission.
Webfm.jsUpload.prototype.onsubmit = function () {
	var hide;
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	hide = this.hide;
	$(hide).fadeOut('slow');
};

//Handler for the form redirection completion.
Webfm.jsUpload.prototype.oncomplete = function (data) {
	var div, hide, cp, replaceButton, listener;

	// Remove old form
	Drupal.freezeHeight(); // Avoid unnecessary scrolling
	$(this.wrapper).html('');

	// Place HTML into temporary div
	div = document.createElement('div');
	$(div).html(data.html);
	$(this.wrapper).append(div);

	//div with id='replace-options' present only in case of file overwrite
	this.confirm = Webfm.$('replace-options');
	if (this.confirm) {
		hide = this.hide;
		$(hide).fadeOut('slow');
		cp = this;
		this.confirm.id = 'replace-options';
		replaceButton = Webfm.ce('input');
		replaceButton.setAttribute('type', 'button');
		replaceButton.setAttribute('value', Webfm.js_msg.submit);
		listener = Webfm.eventListenerAdd(Webfm.eventListeners, replaceButton, "click", function (e) {
																							cp.submit();
																							Webfm.stopEvent(e);
																						});
		this.confirm.appendChild(replaceButton);
		this.confirm.file = data.file;
	}
	else {
		$(this.hide, div).fadeIn('slow');
		webfmUploadAutoAttach();
	}

	Drupal.unfreezeHeight();
	Webfm.progressObj.hide();
	Webfm.dirListObj.refresh();

	if (typeof (Webfm.$('webfm-inline')) !== 'undefined' && Webfm.$('webfm-inline') !== null) {
		// attach file to node if inside node-edit
		if (typeof data.fid !== 'undefined' && data.fid !== null) {
			Webfm.menuAttach(null, data.fid);
		}
	}
};

//Handler for the form redirection error.
Webfm.jsUpload.prototype.onerror = function (error) {
	alert('An error occurred:\n\n' + error);
	Webfm.progressObj.hide();

	if (this.confirm) {
		Webfm.clearNodeById('replace-options');
	}

	// Undo hide
	$(this.hide).css({
		position: 'static',
		left: '0px'
	});
};

Webfm.jsUpload.prototype.submit = function () {
	var inputs, selection, i, url, postObj;
	inputs = [];
	selection = '';

	inputs = this.confirm.getElementsByTagName('input');
	for (i = 0; i < inputs.length; i = i + 1) {
		if (inputs[i].checked === true) {
			selection = i;
			break;
		}
	}

	if (typeof selection != 'undefined') {
		url = Webfm.ajaxUrl();
		Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
		postObj = { action: encodeURIComponent("version"), param0: encodeURIComponent(selection), param1: encodeURIComponent(this.confirm.file)};
		Webfm.HTTPPost(url, this.callback, this, postObj);
	}
};

Webfm.jsUpload.prototype.callback = function (string, xmlhttp, cp) {
	var parent, hide, result, elDiv;

	Webfm.progressObj.hide();

	// Remove overwrite radios
	parent = cp.confirm.parentNode;
	parent.removeChild(parent.firstChild);

	hide = cp.hide;
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		if (result.status) {
			Webfm.dirListObj.refresh();
		}
		cp.confirm.style.display = 'none';

		// Insert ajax feedback before upload form
		elDiv = Webfm.ce('div');
		if (result.status) {
			elDiv.className = 'upload-msg';
		}
		else {
			elDiv.className = 'upload-msg-err';
		}
		elDiv.appendChild(Webfm.ctn(result.data.msg));

		$(hide).before($(elDiv));

		// Undo hide
		Webfm.dbgObj.dbg("hide:", Webfm.dump(hide));
		$(hide).css({ display: 'block' });
		webfmUploadAutoAttach();

		if (typeof (Webfm.$('webfm-inline')) != 'undefined' && Webfm.$('webfm-inline') !== null) {
			// attach file to node if inside node-edit
			if (typeof result.data.fid !== 'undefined' && result.data.fid !== null) {
				Webfm.menuAttach(null, result.data.fid);
			}
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

/**
 * Webfm.list constructor
 * 1st param is object to append this list object to
 * 2nd param is base id of listing (multiple listing objects must have unique ids)
 * 3rd param is base id of file rows of table
 * 4th param sets styling for listing
 * 5th param enables drag and drop
 * 6th param is directory menu array
 * 7th param is file menu array
 */
Webfm.list = function (parent, id, type, class_name, dd_enable, dir_menu, file_menu) {
	var wl, node, elTable, elTableBody, elTr, elTd, elA, elImg, listener, elSpan;
	wl = this;

	this.id = id;
	this.type = type;
	this.dd_enable = dd_enable;
	this.url = Webfm.ajaxUrl();
	this.sc_n = 0;
	this.sc_m = 0;
	this.sc_s = 0;
	this.content = '';
	this.iconDir = getWebfmIconDir();
	this.dir_menu = dir_menu;
	this.file_menu = file_menu;
	//directory cache hashtable (key= directory path, val= directory contents)
	this.cache = new Webfm.ht();
	this.eventListeners = [];

	node = Webfm.ce("div");
	node.setAttribute('id', this.id);
	node.className = class_name;
	this.obj = node;

	elTable = Webfm.ce('table');
	elTableBody = Webfm.ce('tbody');
	//elTableBody.setAttribute('id', this.id + 'body');
	this.body = elTableBody;

	// First Row
	elTr = Webfm.ce('tr');
	elTr.setAttribute('id', 'webfm-top-tr');

	// Refresh Icon
	elTd = Webfm.ce('td');
	elTd.className = 'navi';
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	elA.setAttribute('title', Webfm.js_msg.refresh);
	elImg = Webfm.ce('img');
	elImg.setAttribute('src', this.iconDir + '/r.gif');
	elImg.setAttribute('alt', Webfm.js_msg.refresh);
	elA.appendChild(elImg);
	elTd.appendChild(elA);
	elTr.appendChild(elTd);
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) { 
																			wl.refresh(Webfm.current);
																			Webfm.stopEvent(e);
																		});

	// Breadcrumb trail
	elTd = Webfm.ce('td');
	elTd.colSpan = getWebfmOwnerColumn() ? 4 : 3;
	elTd.setAttribute('id', 'webfm-bcrumb-td');
	elTd.setAttribute('class', 'navi');

	// Build breadcrumb trail inside span
	elSpan = Webfm.ce('span');
	elSpan.setAttribute('id', this.id + 'bcrumb');
	elTd.appendChild(elSpan);
	elTr.appendChild(elTd);

	elTableBody.appendChild(elTr);

	// Second Row
	elTr = Webfm.ce('tr');
	this.secondRow = elTr;

	// icon column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTr.appendChild(elTd);

	// Sort dir/files column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	elA.setAttribute('title', Webfm.js_msg.sort);
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																			wl.sc_n ^= 1;
																			wl.loadList("n");
																			wl.sortIcon(e, wl.sc_n);
																			Webfm.stopEvent(e);
																		});

	elImg = Webfm.ce('img');
	elImg.setAttribute('alt', Webfm.js_msg.sort);
	elImg.setAttribute('src', this.iconDir + '/down.gif');
	elA.appendChild(elImg);
	elA.appendChild(Webfm.ctn(Webfm.js_msg.column1));
	elTd.appendChild(elA);
	elTr.appendChild(elTd);

	// date/time column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																			wl.sc_m ^= 1;
																			wl.loadList("m");
																			wl.sortIcon(e, wl.sc_m);
																			Webfm.stopEvent(e);
																		});

	elImg = Webfm.ce('img');
	elImg.setAttribute('alt', Webfm.js_msg.sort);
	elImg.setAttribute('src', this.iconDir + '/down.gif');
	elA.appendChild(elImg);
	elA.appendChild(Webfm.ctn(Webfm.js_msg.column2));
	elTd.appendChild(elA);
	elTr.appendChild(elTd);

	// size column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																			wl.sc_s ^= 1;
																			wl.loadList("s");
																			wl.sortIcon(e, wl.sc_s);
																			Webfm.stopEvent(e);
																		});

	elImg = Webfm.ce('img');
	elImg.setAttribute('alt', Webfm.js_msg.sort);
	elImg.setAttribute('src', this.iconDir + '/down.gif');
	elA.appendChild(elImg);
	elA.appendChild(Webfm.ctn(Webfm.js_msg.column3));
	elTd.appendChild(elA);
	elTr.appendChild(elTd);

	// owner column
	if (getWebfmOwnerColumn()) {
		elTd = Webfm.ce('td');
		elTd.className = 'head';
		elA = Webfm.ce('a');
		elA.setAttribute('href', '#');
		listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																				wl.sc_o ^= 1;
																				wl.loadList("o");
																				wl.sortIcon(e, wl.sc_o);
																				Webfm.stopEvent(e);
																			});

		elImg = Webfm.ce('img');
		elImg.setAttribute('alt', Webfm.js_msg.sort);
		elImg.setAttribute('src', this.iconDir + '/down.gif');
		elA.appendChild(elImg);
		elA.appendChild(Webfm.ctn(Webfm.js_msg.column4));
		elTd.appendChild(elA);
		elTr.appendChild(elTd);
	}

	elTableBody.appendChild(elTr);

	elTable.appendChild(elTableBody);
	node.appendChild(elTable);
	parent.appendChild(node);
};

Webfm.list.prototype.selectBC = function (event) {
	var el = event.target || window.event.srcElement;
	Webfm.selectDir(el.title);
};

Webfm.list.prototype.listenerfn_bcrumb_click = function (cp) {
	var listenerfn = function (e) {
		cp.selectBC(e);
		Webfm.stopEvent(e);
	};
	
	return listenerfn;
};

Webfm.list.prototype.bcrumb = function () {
	var cp, i, pth, elA, listener, elSpan;
	cp = this;
	pth = [];

	Webfm.clearNodeById(this.id + 'bcrumb');
	elSpan = Webfm.$(this.id + 'bcrumb');

	for (i = 0; i < this.content.bcrumb.length - 1; i = i + 1) {
		pth.push(this.content.bcrumb[i]);
		elSpan.appendChild(Webfm.ctn(" / "));
		// No breadcrumb link necessary for current directory
		elA = Webfm.ce('a');
		elA.setAttribute('href', '#');
		// join previous loop iterations to create path for title
		elA.setAttribute('title', "/" + pth.join("/"));
		elA.appendChild(Webfm.ctn(this.content.bcrumb[i]));
		//IE fix
		listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", Webfm.list.prototype.listenerfn_bcrumb_click(cp));
		elSpan.appendChild(elA);
	}

	elSpan.appendChild(Webfm.ctn(" / "));
	elSpan.appendChild(Webfm.ctn(this.content.bcrumb[i]));
};


Webfm.list.prototype.sortIcon = function (event, up_down) {
	var el = event.target || window.event.srcElement;
	if (el.firstChild) {
		el.firstChild.src = this.iconDir + '/' + (up_down ? "up" : "down") + '.gif';
	}
};

Webfm.list.prototype.refresh = function (path, rename_dir) {
	if (path === null || typeof (path) === 'undefined') {
		path = Webfm.current;
	}
	this.cache.remove(path);
	if (typeof rename_dir != 'undefined') {
		this.rename_dir = rename_dir;
	}
	this.fetch(path);
};

Webfm.list.prototype.fetch = function (curr_dir) {
	var uploadpath, postObj;

	Webfm.alrtObj.msg();
	if (curr_dir || (curr_dir = Webfm.current)) {
		Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
		//update current dir if specific dir selected
		Webfm.current = curr_dir;
		Webfm.dbgObj.dbg('fetch: ', curr_dir);

		if (this.cache.containsKey(Webfm.current)) {
			Webfm.dbgObj.dbg('cache hit: ', Webfm.current);
			this.content = this.cache.get(Webfm.current);
			this.bcrumb();
			this.loadList();
			uploadpath = Webfm.$('edit-webfmuploadpath');
			if (uploadpath) {
				uploadpath.value = Webfm.current;
			}
			Webfm.progressObj.hide();
		}
		else {
			Webfm.admin = false;
			postObj = { action: encodeURIComponent("read"), param0: encodeURIComponent(curr_dir) };
			Webfm.HTTPPost(this.url, this.callback, this, postObj);
		}
	}
	else {
		Webfm.dbgObj.dbg(Webfm.js_msg.curdirundef);
	}
};

Webfm.list.prototype.callback = function (string, xmlhttp, cp) {
	var uploadpath, found, i;
	found = false;

	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		cp.content = Drupal.parseJson(string);
		if (cp.content.status) {
			cp.cache.put(cp.content.current, cp.content);
			//Sets client permissions - server will always validate any action
			Webfm.admin = cp.content.admin;

			cp.bcrumb();
			cp.loadList("n");

			// Insert current directory path into upload form
			uploadpath = Webfm.$('edit-webfmuploadpath');
			if (uploadpath) {
				uploadpath.value = cp.content.current;
				Webfm.dbgObj.dbg('uploadpath: ', uploadpath.value);
			}
			Webfm.current = cp.content.current;
			cp.adminCtl(Webfm.admin);

			if (cp.rename_dir) {
				found = false;
				for (i = 0; i < Webfm.dirListObj.dirrows.length; i = i + 1) {
					if (cp.dirrows[i].clickObj.title == "/" + cp.rename_dir) {
						found = true;
						break;
					}
				}
				if (found) {
					Webfm.menuRename(cp.dirrows[i]);
				}
			}
		}
		else {
			Webfm.alrtObj.str_arr(cp.content.data);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

//build admin icons for create dir and file db insertion
//maintain 4 column table
Webfm.list.prototype.adminCtl = function (admin) {
	var col_span, wl, elTd, elSpan, elA, elImg, listener;

	col_span = getWebfmOwnerColumn() ? 4 : 3;
	if (admin) {
		if (Webfm.$('webfm-bcrumb-td').colSpan == col_span) {
			wl = this;
			Webfm.$('webfm-bcrumb-td').colSpan = col_span - 1;
			// Create New Directory and allow db insertions
			elTd = Webfm.ce('td');
			elSpan = Webfm.ce('span');
			elSpan.id = this.id + "-ctls";

			elA = Webfm.ce('a');
			elA.title = Webfm.js_msg.newdir;
			elImg = Webfm.ce('img');
			elImg.setAttribute('alt', Webfm.js_msg.newdir);
			elImg.setAttribute('src', this.iconDir + '/dn.gif');
			elA.appendChild(elImg);
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																					Webfm.stopEvent(e);
																					wl.mkdir();
																				});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseover", function (e) {
																						wl.hover(e, true, 0);
																					});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseout", function (e) {
																						wl.hover(e, false, 0);
																					});
			elSpan.appendChild(elA);

			elA = Webfm.ce('a');
			elA.title = Webfm.js_msg.addtodb;
			elImg = Webfm.ce('img');
			elImg.setAttribute('alt', Webfm.js_msg.addtodb);
			elImg.setAttribute('src', this.iconDir + '/add_loc.gif');
			elA.appendChild(elImg);
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																					Webfm.stopEvent(e);
																					wl.enumLocal();
																				});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseover", function (e) {
																					wl.hover(e, true);
																				});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseout", function (e) {
																					wl.hover(e, false);
																				});
			elSpan.appendChild(elA);

			elA = Webfm.ce('a');
			elA.title = Webfm.js_msg.recursivecaddaddtodb;
			elImg = Webfm.ce('img');
			elImg.setAttribute('alt', Webfm.js_msg.recursivecaddaddtodb);
			elImg.setAttribute('src', this.iconDir + '/add_recur.gif');
			elA.appendChild(elImg);
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																					Webfm.stopEvent(e);
																					wl.enumRecur();
																				});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseover", function (e) {
																						wl.hover(e, true); 
																					});
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "mouseout", function (e) { 
																						wl.hover(e, false); 
																					});
			elSpan.appendChild(elA);
			elTd.appendChild(elSpan);

			Webfm.$('webfm-top-tr').appendChild(elTd);
		}
		else {
			Webfm.$('webfm-bcrumb-td').colSpan = col_span - 1;
		}
	}
	else {
		Webfm.$('webfm-bcrumb-td').colSpan = col_span;
	}
};

//IE does not understand 'this' inside event listener func
Webfm.list.prototype.hover = function (event, state) {
	var el = event.target || window.event.srcElement;
	el.className = state ? 'selected' : '';
};

//Function to create a new directory
Webfm.list.prototype.mkdir = function () {
	Webfm.alrtObj.msg();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	var postObj = { action: encodeURIComponent("mkdir"), param0: encodeURIComponent(this.content.current) };
	Webfm.HTTPPost(this.url, this.mkdir_callback, this, postObj);
};

Webfm.list.prototype.enumLocal = function () {
	if (Webfm.confirm(Webfm.js_msg.enum_local)) {
		Webfm.insert(this.content.current, "dir");
	}
};

Webfm.list.prototype.enumRecur = function () {
	if (Webfm.confirm(Webfm.js_msg.enum_recur)) {
		Webfm.insert(this.content.current, "recur");
	}
};

Webfm.list.prototype.mkdir_callback = function (string, xmlhttp, cp) {
	var result;

	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		Webfm.dbgObj.dbg("mkdir data:", result.data);
		if (result.status) {
			cp.refresh(cp.content.current, result.data);
			if (Webfm.dirTreeObj) {
				//we just updated the listing - 2nd var must be false
				Webfm.dirTreeObj.fetch(true, false);
			}
		}
		else {
			Webfm.alrtObj.str_arr(result.data);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.list.prototype.loadList = function (sortcol) {
	var i, filerow;
	this.c_dir = 0;
	this.c_fil = 0;

	//remove all rows beneath second row of listing table body
	while (this.secondRow.nextSibling) {
		this.body.removeChild(this.secondRow.nextSibling);
	}
	//clear event listeners for old listing
	Webfm.eventUnregister(this.eventListeners);

	// Build directory rows and append to table
	if (this.content.dirs.length) {
		this.dirrows = [];
		//IE hack since dirs have no size and no owner
		if (!(Webfm.browser.isIE && sortcol == "s") && sortcol != "o") {
			this.sortTable(this.content.dirs, sortcol);
		}
		for (i = 0; i < this.content.dirs.length; i = i + 1) {
			this.dirrows[i] = new Webfm.dirrow(this.body, this.content.dirs[i], i, this.dd_enable, this.dir_menu, this.eventListeners);
		}
	}
	// Build file rows and append to table
	// type determines file context menu
	if (this.content.files.length) {
		this.sortTable(this.content.files, sortcol);
		for (i = 0; i < this.content.files.length; i = i + 1) {
			filerow = new Webfm.filerow(this.body, this.content.files[i], this.type, i, this.dd_enable, this.file_menu, this.eventListeners);
		}
	}
};

Webfm.list.prototype.sortTable = function (arr, key) {
	switch (key) {
	case "o":
		arr.sort(Webfm.sortByOwner);
		if (this.sc_o) {
			arr.reverse();
		}
		break;
	case "s":
		arr.sort(Webfm.sortBySize);
		if (this.sc_s) {
			arr.reverse();
		}
		break;
	case "m":
		arr.sort(Webfm.sortByModified);
		if (this.sc_m) {
			arr.reverse();
		}
		break;
	case "n":
		arr.sort(Webfm.sortByName);
		if (this.sc_n) {
			arr.reverse();
		}
		break;
	}
};

/**
 * Webfm.dirrow constructor
 * 1st param is parent obj
 * 2nd param is dir object
 * 3rd param is unique number to append to id
 * 4th param enables drag and drop
 * 5th param is directory menu array
 * 6th param is event listener array for closure cleanup
 */
Webfm.dirrow = function (parent, dir, index, dd_enable, menu, eventListenerArr) {
	var dr, dc_id, elTr, elTd, elImg, owner, listener, size;
	dr = this;
	
	this.draggable = dd_enable;
	this.iconDir = getWebfmIconDir();
	
	//id used for drop container
	dc_id = 'webfm-dirlist' + index;
	elTr = Webfm.ce('tr');
	this.element = elTr;
	this.element.className = 'dirrow';

	elTr.setAttribute('id', dc_id);
	elTr.setAttribute('title', dir.p);
	if (dd_enable && Webfm.admin) {
		//Webfm.draggable must be created after title assigned to set current path
		this.dd = new Webfm.draggable(Webfm.dragCont, elTr, this.element.className);
	}
	else {
		this.draggable = false;
	}

	elTd = Webfm.ce('td');
	elImg = Webfm.ce('img');
	elImg.setAttribute('src', this.iconDir + '/d.gif');
	elImg.setAttribute('id', dc_id + 'dd');
	elImg.setAttribute('alt', Webfm.js_msg.u_dir);
	this.menu = menu;
	elTd.appendChild(elImg);
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	// Title of link = path
	this.clickObj = Webfm.ce('a');
	this.clickObj.setAttribute('href', '#');
	//title is path
	this.clickObj.setAttribute('title', dir.p);
	this.clickObj.appendChild(Webfm.ctn(dir.n));
	elTd.appendChild(this.clickObj);
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	elTd.className = 'txt';
	elTd.appendChild(Webfm.ctn(Webfm.convertunixtime(parseInt(dir.m, 10))));
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	elTd.className = 'txt';
	if (dir.s) {
		size = Webfm.size(dir.s);
		elTd.appendChild(Webfm.ctn(size));
	}
	elTr.appendChild(elTd);

	// Add td if owner column enabled
	if (getWebfmOwnerColumn()) {
		elTd = Webfm.ce('td');
		elTd.className = 'txt';
		if (dir.o) {
			owner = Webfm.ce(dir.o);
			elTd.appendChild(Webfm.ctn(owner));
		}
		elTr.appendChild(elTd);
	}

	//mouse event listeners
	if (dd_enable) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mousedown", function (e) {
																						Webfm.contextMenuObj.hideContextMenu(e);
																						dr.select(e);
																					});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "click", function (e) {
																					Webfm.contextMenuObj.hideContextMenu(e);
																					if (Webfm.renameActive === false) { 
																						Webfm.selectDir(dr.element.title);
																					} 
																				});
	}
	if (this.draggable) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mouseover", function () {
																						dr.hover(dr.element, true);
																					});
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mouseout", function () { 
																						dr.hover(dr.element, false); 
																					});
	}
	if (Webfm.browser.isOP) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mousedown", function (e) { 
																						if (e && e.button === 0 && e.altKey === true) { 
																							if (Webfm.renameActive === false) {
																								Webfm.contextMenuObj.showContextMenu(e, dr);
																							}
																							Webfm.stopEvent(e);
																						}
																					});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "contextmenu", function (e) { 
																							if (Webfm.renameActive === false) {
																								Webfm.contextMenuObj.showContextMenu(e, dr);
																							}
																							Webfm.stopEvent(e); 
																						});
	}

	parent.appendChild(elTr);
	this.c_dir = this.c_dir + 1;
};

Webfm.dirrow.prototype.hover = function (el, state) {
	if (state) {
		el.className += ' selected';
	}
	else {
		el.className = el.className.split(' ', 1);
	}
};
Webfm.dirrow.prototype.select = function (event) {
	var cp, rightclick;
	cp = this;
	
	if (Webfm.renameActive === true) {
		return false;
	}
	event = event || window.event;
	if (event.target || event.srcElement) {
		if (this.clickObj) {
			// Determine mouse button
			rightclick = Webfm.rclick(event);
			if (rightclick) {
				return false;
			}
			setTimeout(function () {
				//if click then no dragging...
				if (Webfm.dragging === false) {
					Webfm.selectDir(cp.element.title);
				}
			}, 200);
		}
		//passthrough
		if (typeof this.dd != "undefined") {
			this.dd.mouseButton(event);
		}
	}
	return false;
};

/**
 * Webfm.filerow constructor
 * 1st param is parent obj
 * 2nd param is fileobject (see desc below)
 * 3rd param is base id name of file row
 * 4th param is unique number to append to id
 * 5th param enables drag and drop
 * 6th param is file menu array
 * 7th param is event listener array for closure cleanup
 *
 * fileobject elements:
 * id -> fid
 * n -> file name
 * p -> path (includes name)
 * s -> file size
 * m -> modified date
 * e -> mimetype extension
 * i -> image file.
 * w -> image width
 * h -> image height
 * un -> file owner user name
 */
Webfm.filerow = function (parent, fileObj, idtype, index, dd_enable, file_menu, eventListenerArr) {
	var fr, elTr, elTd, elImg, size, listener;
	fr = this;
	
	this.draggable = dd_enable;
	this.iconDir = getWebfmIconDir();
	this.ext = fileObj.e;
	this.filepath = fileObj.p + '/' + fileObj.n;
	this.uid = fileObj.u;
	this.ftitle = null;
	this.funame = fileObj.un;
	
	if (typeof fileObj.ftitle != "undefined") {
		this.ftitle = fileObj.ftitle;
	}
	
	elTr = Webfm.ce('tr');
	this.element = elTr;
	elTr.className = idtype + 'row';
	//drag object id used for download type
	if (typeof fileObj.id == "undefined") {
		this.row_id  = 'nodb' + index;
	}
	else {
		if (fileObj.id === 0) {
			this.row_id = idtype + index;
		}
		else {
			//TODO: fix - this causes repeat ids for attach rows!
			this.row_id = 'fid' + fileObj.id;
		}
	}
	elTr.setAttribute('id', this.row_id);

	//title of drag object is path for move
	elTr.setAttribute('title', this.filepath);
	if (dd_enable) {
		//Only admins or owner of file can move it
		if (Webfm.admin || fileObj.u == getWebfmUid()) {
			this.dd = new Webfm.draggable(Webfm.dragCont, elTr, elTr.className);
		}
		else {
			this.draggable = false;
		}
	}

	elTd = Webfm.ce('td');
	elImg = Webfm.ce('img');
	if (fileObj.i) {
		elImg.setAttribute('src', this.iconDir + '/i.gif');
	}
	else {
		elImg.setAttribute('src', fr.getIconByExt());
	}
	
	if ((typeof fileObj.id == "undefined") || fileObj.id === 0 || fileObj.id === false) {
		// Make icon transparent if file not in db - TODO: fix for IE
		elImg.style.opacity = 0.25;
		elImg.style.filter = "alpha(opacity=25)";
	}
	
	elImg.setAttribute('alt', Webfm.js_msg.u_file);
	this.menu = file_menu;
	elTd.appendChild(elImg);
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	this.clickObj = Webfm.ce('a');
	this.clickObj.setAttribute('href', '#');
	
	if ((typeof fileObj.id == "undefined") || fileObj.id === 0 || fileObj.id === false) {
		this.clickObj.setAttribute('title', this.filepath);
	}
	else {
		if (Webfm.browser.isOP) {
			this.clickObj.setAttribute('href', getBaseUrl() + "\/webfm_send\/" + fileObj.id);
		}
		this.clickObj.setAttribute('title', fileObj.id);
	}
	
	if (this.ftitle) {
		this.clickObj.appendChild(Webfm.ctn(fileObj.ftitle));
	}
	else {
		this.clickObj.appendChild(Webfm.ctn(fileObj.n));
	}
	elTd.appendChild(this.clickObj);
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	elTd.className = 'txt';
	elTd.appendChild(Webfm.ctn(Webfm.convertunixtime(parseInt(fileObj.m, 10))));
	elTr.appendChild(elTd);

	elTd = Webfm.ce('td');
	elTd.className = 'txt';
	size = Webfm.size(fileObj.s);
	elTd.appendChild(Webfm.ctn(size));
	elTr.appendChild(elTd);

	//owner field to row
	if (getWebfmOwnerColumn()) {
		elTd = Webfm.ce('td');
		elTd.className = 'txt';
		elTd.appendChild(Webfm.ctn(fileObj.un));
		elTr.appendChild(elTd);
	}

	//mouse event listeners
	if (dd_enable) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mousedown", function (e) {
																						Webfm.contextMenuObj.hideContextMenu(e);
																						fr.select(e);
																					});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "click", function (e) {
																					Webfm.contextMenuObj.hideContextMenu(e);
																					if (Webfm.renameActive === false) { 
																						Webfm.selectFile(fr.clickObj.title, fr.element);
																					} 
																				});
	}
	if (this.draggable) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mouseover", function () {
																										fr.hover(fr.element, true);
																									});
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mouseout", function () {
																										fr.hover(fr.element, false);
																									});
	}
	if (Webfm.browser.isOP) {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "mouseup", function (e) {
																						if (e && e.button === 0 && e.altKey === true) {
																							if (Webfm.renameActive === false) {
																								Webfm.contextMenuObj.showContextMenu(e, fr);
																								Webfm.stopEvent(e);
																							}
																						}
																					});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, this.element, "contextmenu", function (e) {
																							if (Webfm.renameActive === false) {
																								Webfm.contextMenuObj.showContextMenu(e, fr);
																							}
																							Webfm.stopEvent(e);
																						});
	}

	parent.appendChild(elTr);
	this.c_fil = this.c_fil + 1;
};

Webfm.filerow.prototype.hover = function (el, state) {
	if (state) {
		el.className += ' selected';
	}
	else {
		el.className = el.className.split(' ', 1);
	}
};

Webfm.filerow.prototype.select = function (event) {
	var cp, rightclick;
	cp = this;
	
	if (Webfm.renameActive === true) {
		return false;
	}
	event = event || window.event;
	if (event.target || event.srcElement) {
		if (this.clickObj) {
			// Determine mouse button
			rightclick = Webfm.rclick(event);
			if (rightclick) {
				return false;
			}
			setTimeout(function () {
				//if click then no dragging...
				if (Webfm.dragging === false) {
					//element id used for download method (webfm_send||direct http access)
					Webfm.selectFile(cp.clickObj.title, cp.element);
				}
			}, 200);
		}
		//passthrough
		if (typeof this.dd != "undefined") {
			this.dd.mouseButton(event);
		}
	}
	return false;
};

Webfm.filerow.prototype.getIconByExt = function () {
	var ext, last, icon;
	
	// extension stored in file record of db fails - use pathname
	ext = this.ext;
	if (ext) {
		ext = ext.replace(/\//g, "_").toLowerCase();
	}
	else {
		ext = this.filepath.toLowerCase();
		last = ext.lastIndexOf(".");
		if (last != -1) {
			// "." found
			ext = ext.slice(last + 1);
		}
		else {
			ext = "";
		}
	}
	icon = this.iconDir + '/' + (Webfm.icons["e" + ext] ? Webfm.icons["e" + ext] : "f") + '.gif';
	
	return icon;
};

/*
 * Webfm.treeBuilder constructor
 */
Webfm.treeBuilder = function (parent, root_menu, dir_menu) {
	this.parent = parent;
	this.root_menu = root_menu;
	this.dir_menu = dir_menu;
	this.trees = [];
};
Webfm.treeBuilder.prototype.fetch = function (tree_refresh, list_refresh) {
	var url, postObj;
	url = Webfm.ajaxUrl();
	
	this.tree_refresh = tree_refresh;
	this.list_refresh = list_refresh;
	
	Webfm.alrtObj.msg();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	
	postObj = { action: encodeURIComponent("readtrees") };
	if (tree_refresh) {
		postObj.param0 = encodeURIComponent(true);
	}
	Webfm.admin = false;
	Webfm.HTTPPost(url, this.callback, this, postObj);
};

Webfm.treeBuilder.prototype.callback = function (string, xmlhttp, cp) {
	var result, first, i;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		//Webfm.dbgObj.dbg("trees fetch:", Webfm.dump(result));

		if (result.status) {
			if (result.err) {
				Webfm.alrtObj.msg(result.err);
			}
			Webfm.admin = result.admin;
			// build directory trees from php associative array
			// first var used to fetch listing of first tree
			first = true;
			for (i in result.tree) {
//				Webfm.dbgObj.dbg("tree" + i, Webfm.dump(result.tree[i]));
				if (!cp.trees[i]) {
					cp.trees[i] = new Webfm.tree(cp.parent, i, cp.root_menu, cp.dir_menu);
				}
				
				if (first) {
					cp.trees[i].fetch(cp.tree_refresh, cp.list_refresh);
				}
				else {
					cp.trees[i].fetch(cp.tree_refresh, false);
				}
				
				first = false;
			}
		}
		else {
			Webfm.alrtObj.msg(result.err);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.tree_listenerfn_click = function (wt, b) {
	return function (e) {
				wt.exp(b);
				Webfm.stopEvent(e);
			};
	
};

/**
 * Webfm.tree constructor
 * 1st param is parent node to append this Webfm.tree object
 * 2nd param is index to append to base id of Webfm.tree object
 * 3rd param is root directory menu array
 * 4th param is non-root directory menu array
 */
Webfm.tree = function (parent, treeIdx, root_menu, dir_menu) {
	var wt, node, elA, listener, elImg, elSpan, i, elDiv;
	wt = this;
	
	this.id = 'dirtree' + treeIdx;
	this.treeIdx = treeIdx;
	this.icondir = getWebfmIconDir();
	this.content = '';
	this.expAllData = [['collapse', 'minus', 'block'], ['expand', 'plus', 'none']];
	// Set tree exp/collapse behaviour on load (0 = expanded, 1 = collapsed)
	this.expAllIndex = 1;
	this.root_menu = root_menu;
	this.dir_menu = dir_menu;
	this.eventListeners = [];

	node = Webfm.ce("div");
	node.setAttribute('id', this.id);
	node.className = 'dirtree';

	//build tree and display
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	elA.setAttribute('title', Webfm.js_msg.refresh);
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																				wt.fetch(true, '');
																				Webfm.stopEvent(e);
																			});

	elImg = Webfm.ce('img');
	elImg.setAttribute('src', this.icondir + '/r.gif');
	elImg.setAttribute('alt', Webfm.js_msg.refresh);
	elA.appendChild(elImg);
	node.appendChild(elA);
	elSpan = Webfm.ce("span");
	elSpan.appendChild(Webfm.ctn('   '));
	elSpan.setAttribute('id', this.id + 'Name');
	node.appendChild(elSpan);

	// Expand/Collapse all folders buttons
	elSpan = Webfm.ce("span");
	elSpan.setAttribute('id', this.id + 'exp');
	for (i = 0; i < 2; i = i + 1) {
		elA = Webfm.ce('a');
		elA.setAttribute('href', '#');
		if (i) {
			elA.setAttribute('title', 'expand tree');
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", Webfm.tree_listenerfn_click(wt, 0));
		}
		else {
			elA.setAttribute('title', 'collapse tree');
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", Webfm.tree_listenerfn_click(wt, 1));
		}
		elImg = Webfm.ce('img');
		elImg.setAttribute('alt', this.expAllData[i][0]);
		elImg.setAttribute('src', this.icondir + '/' + this.expAllData[i][1] + '.gif');
		elA.appendChild(elImg);
		elSpan.appendChild(elA);
	}
	node.appendChild(elSpan);

	//container div for tree
	elDiv = Webfm.ce("div");
	this.treeCont = elDiv;
	node.appendChild(elDiv);

	parent.appendChild(node);
};

Webfm.tree.prototype.fetch = function (tree_refresh, list_refresh) {
	var url, postObj;
	url = Webfm.ajaxUrl();
	
	// Reset ul count (0=root)
	this.treeUlCounter = 0;
	this.treeNodeCounter = 0;
	this.list_refresh = list_refresh;

	
	Webfm.alrtObj.msg();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	Webfm.dbgObj.dbg("tree fetch path:", this.treeIdx);
	postObj = { action: encodeURIComponent("readtree"), param0: encodeURIComponent(this.treeIdx) };
	if (tree_refresh) {
		postObj.param1 = encodeURIComponent(true);
	}
	Webfm.HTTPPost(url, this.callback, this, postObj);
};

Webfm.tree.prototype.callback = function (string, xmlhttp, cp) {
	var treeName, j;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		cp.content = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg("tree fetch:", Webfm.dump(cp.content));

		//clear tree content
		while (cp.treeCont.hasChildNodes()) {
			cp.treeCont.removeChild(cp.treeCont.firstChild);
		}
		//clear event listeners for old tree content
		Webfm.eventUnregister(cp.eventListeners);
		//clear drop container array;
		Webfm.dropContainers = [];

		//update tree name
		treeName = Webfm.$(cp.id + 'Name');
		treeName.removeChild(treeName.firstChild);
		//TODO: Is this break really correct?
		for (j in cp.content.tree) {
			//textnode = last directory of root path
			treeName.appendChild(Webfm.ctn(' ' + j.substring(j.lastIndexOf("/") + 1) + ' ' + Webfm.js_msg.tree));
			break;
		}

		// Recursively build directory tree from php associative array
		cp.buildTreeRecur(cp.content.tree, cp.treeCont, '');
		cp.init();
		if (cp.list_refresh) {
			Webfm.dirListObj.refresh(cp.content.current);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.tree.prototype.getpath = function () {
	return this.content.current;
};

Webfm.tree.prototype.showHideNode = function (event) {
	var collapseIcon, ul;
	
	event = event || window.event;
	collapseIcon = event.target || event.srcElement;
	if (collapseIcon.style.visibility == 'hidden') {
		return;
	}
	
	ul = collapseIcon.parentNode.parentNode.getElementsByTagName('ul')[0];
	if (collapseIcon.title == this.expAllData[0][0]) {
		collapseIcon.src = this.icondir + '/' + this.expAllData[1][1] + '.gif';
		collapseIcon.alt = this.expAllData[1][0];
		collapseIcon.title = this.expAllData[1][0];
		ul.style.display = this.expAllData[1][2];
	}
	else {
		collapseIcon.src = this.icondir + '/' + this.expAllData[0][1] + '.gif';
		collapseIcon.alt = this.expAllData[0][0];
		collapseIcon.title = this.expAllData[0][0];
		ul.style.display = this.expAllData[0][2];
	}
};

Webfm.tree.prototype.buildTreeRecur_listenerfn_click = function (cp) {
	var listenerfn = function (e) { 
									cp.showHideNode(e);
									Webfm.stopEvent(e);
								};
	return listenerfn;
};

Webfm.listenerfn_mouseoverout = function (cp, indicator) {
	var listenerfn = function (e) {
									cp.hover(e, indicator);
								};
	return listenerfn;
};

Webfm.listenerfn_mouseoverout_dir = function (cp, indicator) {
	var listenerfn = function (e) {
									cp.hoverDir(e, indicator);
								};
	return listenerfn;
};

Webfm.tree.prototype.buildTreeRecur = function (content, parent, input_path) {
	var cp, dc_id, elUl, sortDir, i, j, newpath, elLi, elDiv, elImg, listener, treeNode;
	cp = this;
	
	//php associative array is always a single member array with nested objects
	if (Webfm.isArray(content)) {
		this.buildTreeRecur(content[0], parent, '');
	}
	else if (Webfm.isObject(content)) {
		if (input_path) {
			input_path += "/";
		}
		elUl = Webfm.ce('ul');
				
		if (this.treeUlCounter) {
			this.treeUlCounter = this.treeUlCounter + 1;
		}
		else {
			elUl.className = 'root-list';
			this.treeUlCounter = this.treeUlCounter + 1;
		}
		// sort object here since php does not have a case-sensitive ksort
		sortDir = [];
		for (i in content) {
			if (content.hasOwnProperty(i)) {
				sortDir.push(i);
			}
		}
		if (sortDir.length) {
			sortDir.sort(Webfm.sortByKey);
			for (j = 0; j < sortDir.length; j = j + 1) {
				newpath = input_path + sortDir[j];
				elLi = Webfm.ce('li');
				dc_id = this.id + 'node' + this.treeNodeCounter;
				elLi.setAttribute('id', dc_id);
				elLi.setAttribute('title', newpath);
				elLi.className = "treenode";

				elDiv = Webfm.ce('div');
				elImg = Webfm.ce('img');
				elImg.setAttribute('src', this.icondir + '/' + this.expAllData[this.expAllIndex][1] + '.gif');
				elImg.setAttribute('alt', this.expAllData[this.expAllIndex][0]);
				elImg.setAttribute('title', this.expAllData[this.expAllIndex][0]);
				elDiv.appendChild(elImg);
				elLi.appendChild(elDiv);
				listener = Webfm.eventListenerAdd(this.eventListeners, elImg, "click", Webfm.tree.prototype.buildTreeRecur_listenerfn_click(cp));
				treeNode = new Webfm.treeNode(elDiv, newpath, dc_id, elImg, this.treeNodeCounter ? this.dir_menu : this.root_menu, this.eventListeners);
				listener = Webfm.eventListenerAdd(this.eventListeners, elDiv, "mouseover", Webfm.listenerfn_mouseoverout(cp, true));
				listener = Webfm.eventListenerAdd(this.eventListeners, elDiv, "mouseout", Webfm.listenerfn_mouseoverout(cp, false));

				this.treeNodeCounter = this.treeNodeCounter + 1;
				this.buildTreeRecur(content[sortDir[j]], elLi, newpath);
				elUl.appendChild(elLi);
			}
		}

		// Always show root
		if (elUl.className == 'root-list') {
			elUl.style.display = 'block';
		}
		else {
			elUl.style.display = this.expAllData[this.expAllIndex][2];
		}

		parent.appendChild(elUl);
	}
};

//IE does not understand 'this' inside event listener func
Webfm.tree.prototype.hover = function (event, state) {
	var el;
	el = event.target || window.event.srcElement;
	
	if (state) {
		el.className = 'selected';
		el.parentNode.className = 'selected';
//		el.parentNode.className += ' selected';
	} else {
//		var class_names = [];
//		class_names = el.parentNode.className.split(' ');
//		el.parentNode.className = class_names[0];
		el.className = '';
		el.parentNode.className = '';
	}
};

Webfm.tree.prototype.init = function () {
	// Determine if expand/collapse icon is present
	// Create event objects for each element
	var dirItems, subItems, i;
	dirItems = this.treeCont.getElementsByTagName('li');
	
	if (dirItems.length) {
		for (i = 0; i < dirItems.length; i = i + 1) {
			subItems = dirItems[i].getElementsByTagName('ul');
			if (!subItems.length) {
				// Hide collapse icon if no sub-directories
				dirItems[i].getElementsByTagName('img')[0].style.visibility = 'hidden';
			}
		}
	}
};

Webfm.tree.prototype.exp = function (expcol) {
	//clear tree
	while (this.treeCont.hasChildNodes()) {
		this.treeCont.removeChild(this.treeCont.firstChild);
	}

	// Rebuild dirtree ul/li objects from dirtree object
	this.expAllIndex = expcol;
	this.treeUlCounter = 0;
	this.treeNodeCounter = 0;

	this.buildTreeRecur(this.content.tree, this.treeCont, '');
	this.init();
};

Webfm.treeNode = function (parent, path, id, expImg, menu, eventListenerArr) {
	var tn, icondir, elImg, nodeNumPos, root, listener;
	tn = this;
	icondir = getWebfmIconDir();
	root = [];
	
	this.parent = parent;
	this.element = parent.parentNode;
	
	this.expClickObj = expImg;
	elImg = Webfm.ce('img');
	elImg.setAttribute('id', id + 'dd');
	elImg.setAttribute('src', icondir + '/d.gif');
	parent.appendChild(elImg);
	this.clickObj = Webfm.ce('a');
	this.clickObj.href = '#';
	//anchor title is path
	this.clickObj.setAttribute('title', path);
	nodeNumPos = this.element.id.indexOf("node") + 4;
	
	if ((this.element.id.substring(nodeNumPos) != "0") && Webfm.admin) {
		// root treenode isn't draggable
		this.dd = new Webfm.draggable(Webfm.dragCont, this.element, this.element.className);
	}
	
	root = path.split('/');
	//textNode is last directory name of path string (used for root path)
	this.clickObj.appendChild(Webfm.ctn(root[root.length - 1]));
	parent.appendChild(this.clickObj);

	//mouse event listeners
	this.menu = menu;
	if (typeof this.dd != "undefined") {
		listener = Webfm.eventListenerAdd(eventListenerArr, parent, "mousedown", function (e) {
																					Webfm.contextMenuObj.hideContextMenu(e);
																					tn.select(e);
																				});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, parent, "click", function (e) { 
																				Webfm.selectDir(tn.clickObj.title); 
																				Webfm.stopEvent(e); 
																			});
	}
	if (Webfm.browser.isOP) {
		listener = Webfm.eventListenerAdd(eventListenerArr, parent, "mouseup", function (e) {
																				if (e && e.button === 0 && e.altKey === true) { 
																					if (Webfm.renameActive === false) { 
																						Webfm.contextMenuObj.showContextMenu(e, tn); 
																					} 
																					Webfm.stopEvent(e); 
																				}
																			});
	}
	else {
		listener = Webfm.eventListenerAdd(eventListenerArr, parent, "contextmenu", function (e) {
																					if (Webfm.renameActive === false) {
																						Webfm.contextMenuObj.showContextMenu(e, tn);
																					}
																					Webfm.stopEvent(e);
																				});
	}
};

Webfm.treeNode.prototype.select = function (event) {
	var cp, rightclick;
	cp = this;
	
	// disable during rename
	if (Webfm.renameActive === true) {
		return false;
	}
	event = event || window.event;
	
	switch (event.target || event.srcElement) {
	case this.expClickObj:
		break;
		
	case this.clickObj:
		// Determine mouse button
		rightclick = Webfm.rclick(event);
		if (rightclick) {
			// oncontextmenu will handle this
			break;
		}
		setTimeout(function () {
					//if click then no dragging...
					if (Webfm.dragging === false) {
						Webfm.selectDir(cp.clickObj.title);
					}
				},
					200);
		
		if (typeof this.dd != "undefined") {
			this.dd.mouseButton(event);
		}
		break;
			
	//passthrough
	default:
		if (typeof this.dd != "undefined") {
			this.dd.mouseButton(event);
		}
		break;
	}
	
	return false;
};

Webfm.selectDir = function (path) {
	Webfm.dirListObj.fetch(path);
};

//File download
Webfm.selectFile = function (path, el, as_file) {
	var fid, sent, pths, pluspath, fullpath, url, cache, i, iframeName, pane;
	
	//files not in db use file path in tr title for download
	//files inside webfm have 'fidxxx' as title
	fid = null;
	sent = false;
	pths = [];
	pths = el.title.split('/');
	
	if (el.id.substring(0, 3) == 'fid' || el.id.substring(0, 3) == 'sid') {
		fid = el.id.substring(3);
		fullpath = 'webfm_send/' + path;
		if (as_file) {
			fullpath += '/1';
		}
	}
	else {
		//replace / with ~ since server uses / as param delimiter
		pluspath = pths.join('~');
		fullpath = 'webfm_send/' + encodeURIComponent(pluspath);
	}
	
	if (getWebfmCleanUrl()) {
		url = getBaseUrl() + '/' + fullpath;
	}
	else {
		url = '?q=' + fullpath;
	}
	
	if (as_file) {
		// Send download/open dialog
		window.location = url;
	}
	else {
		cache = Webfm.dirListObj.cache.get(Webfm.current);
		for (i in cache.files) {
			// Display downloaded image in iframe
			// check cache files[] array for path match and image type
			if (((cache.files[i].id == fid) || ((cache.files[i].p + '/' + cache.files[i].n) == path)) && cache.files[i].i) {
				// FF needs unique name per iframe - Webfm.zindex guaranteed unique
				// since incremented with each new pane
				iframeName = "dwnld" + Webfm.zindex;
				pane = new Webfm.pane(Webfm.viewCont, pths[pths.length - 1], "webfm-viewFile", null, 200, 200);
				// I hate using innerHTML but IE iframe requires this method:
				pane.content.innerHTML = '<iframe src="" style="margin:0;padding:0;width:100%;height:100%" name="' + iframeName + '"></iframe>';
				if (fid) {
					pane.headerMsg("fid:" + path);
				}
				pane.show();
				window.frames[iframeName].location.replace(url);
				sent = true;
				break;
			}	
		}
		// Not an image - launch file in new browser instance
		if (!sent) {
//			window.open(url);
			window.location = url;
		}
	}
};

/**
 * Webfm.context constructor
 */
Webfm.context = function (container) {
	Webfm.renameActive = false;
	this.container = container;
	this.eventListeners = this.container.eventListenerArr;
};

Webfm.context.prototype.hideContextMenu = function (event) {
	// Hide context menu and restore document oncontextmenu handler
	this.container.obj.style.visibility = "hidden";
	document.body.oncontextmenu = Webfm.oldOnContextMenu;
};

Webfm.listenerfn_context_mousedown = function (cp, obj) {
	var listenerfn = function (e) { 
									cp.selectMenu(e, obj);
									cp.hideContextMenu(e);
									Webfm.stopEvent(e);
								};
	return listenerfn;
};

//obj is treenode|filerow|dirrow
Webfm.context.prototype.showContextMenu = function (event, obj) {
	var cp, listener, pos, j, elUl, elLi;
	cp = this;
	this.element = obj.element;
	this.clickObj = obj.clickObj;

	//	Webfm.dbgObj.dbg('this.element.title:', this.element.title);
	document.body.oncontextmenu = function () {
												return false;
											};
	event = event || window.event;
	pos = Drupal.mousePosition(event);

	// Remove any existing list in our contextMenu.
	this.container.destroy();

	// Build menu ul and append to this.container
	if (obj.menu !== null || typeof obj.menu !== 'undefined' || obj.menu !== false) {
		elUl = Webfm.ce('ul');
		for (j = 0; j < obj.menu.length; j = j + 1) {
			elLi = Webfm.ce('li');
			//Run prepare function and return true if menuElement to appear in menu
			if (typeof obj.menu[j].prepFunc == 'function' && !obj.menu[j].prepFunc(obj)) {
				continue;
			}
			elLi.appendChild(Webfm.ctn(obj.menu[j].desc));
			//menu selection is title of event
			elLi.id = 'webfm-cxtMenu' + j;
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elLi, "mousedown", Webfm.listenerfn_context_mousedown(cp, obj));
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elLi, "mouseover", Webfm.listenerfn_mouseoverout(cp, true));
			listener = Webfm.eventListenerAdd(Webfm.eventListeners, elLi, "mouseout", Webfm.listenerfn_mouseoverout(cp, false));
			elUl.appendChild(elLi);
		}
		
		this.container.obj.appendChild(elUl);
		this.container.obj.style.visibility = "visible";

		if (pos.x + this.container.obj.offsetWidth > (document.documentElement.offsetWidth - 20)) {
			pos.x = pos.x + (document.documentElement.offsetWidth - (pos.x + this.container.obj.offsetWidth)) - 20;
		}

		this.container.obj.style.left = pos.x + 'px';
		this.container.obj.style.top = pos.y + 'px';

		this.container.obj.style.display = 'block';
		this.container.obj.style.zIndex = Webfm.zindex + 1;
	}
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, document, "mousedown", function (e) {
																					cp.hideContextMenu(e);
																				});
	
	return false;
};

//IE does not understand 'this' inside event listener func
Webfm.context.prototype.hover = function (event, state) {
	var el = event.target || window.event.srcElement;
	el.className = state ? 'selected' : '';
};

//Call Webfm.menuElement execute function
Webfm.context.prototype.selectMenu = function (event, obj) {
	event = event || window.event;
	obj.menu[(event.target || event.srcElement).id.substring(13)].execFunc(obj);
	return false;
};

Webfm.menuElement = function (desc, execFunc, prepFunc) {
	this.desc = desc;
	this.execFunc = execFunc;
	this.prepFunc = prepFunc;
};

//obj is treenode|filerow|dirrow
Webfm.menuElement.prototype.exec = function (obj) {
	this.execFunc(obj);
};

Webfm.menuRemove = function (obj) {
	var url, path, postObj;
	url = Webfm.ajaxUrl();
	if (typeof obj.ext != "undefined") {
		//directories don't have extension properties
		obj.is_file = true;
	}
	
	path = obj.element.title;
	obj.parentPath = path.substring(0, path.lastIndexOf("/"));
	if (Webfm.confirm(Webfm.js_msg.confirmdel0 + (obj.is_file ? Webfm.js_msg.file : Webfm.js_msg.dir) + " " + path + (obj.is_file ? "" : Webfm.js_msg.confirmdel1) + "?")) {
		Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
		postObj = { action: encodeURIComponent("delete"), param0: encodeURIComponent(path) };
		Webfm.HTTPPost(url, Webfm.ctxMenuCallback, obj, postObj);
		return false;
	}
};

Webfm.menuMkdir = function (obj) {
	var url, path, postObj;
	
	url = Webfm.ajaxUrl();
	obj.is_file = false;
	path = obj.element.title;
	obj.parentPath = path;
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("mkdir"), param0: encodeURIComponent(path) };
	Webfm.HTTPPost(url, Webfm.ctxMenuMkdirCallback, obj, postObj);
};

Webfm.menuRename = function (obj) {
	var listener;
	
	if (typeof obj.ext != "undefined") {
		//directories don't have extension properties
		obj.is_file = true;
	}
	obj.renInput = Webfm.ce('input');
	obj.renInput.setAttribute('type', 'textfield');
	obj.renInput.value = obj.clickObj.firstChild.nodeValue;
	//clone input element since Webfm.contextMenuObj isn't destroyed on DOM
	obj.tempInput = obj.renInput.cloneNode(true);
	obj.tempInput.setAttribute('autocomplete', 'off'); //FF focus bug
	obj.clickparent = obj.clickObj.parentNode;
	obj.clickparent.replaceChild(obj.tempInput, obj.clickObj);
	//no change (blur) - restore original textNode
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, obj.tempInput, "blur", function (e) {
																					Webfm.renameActive = false;
																					obj.clickparent.replaceChild(obj.clickObj, obj.tempInput);
																					Webfm.stopEvent(e);  
																				});
	//listen for enter key up - swap names (illegal names are ignored on server and next list
	//refresh will restore the proper filename)
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, obj.tempInput, "keydown", function (e) {
																						if (Webfm.enter(e) && Webfm.renameActive === true) { 
																							Webfm.renameActive = false; 
																							Webfm.menuSwapname(obj); 
																							this.blur(); 
																							Webfm.stopEvent(e);
																						}
																					});
	setTimeout(function () {
								obj.tempInput.focus();
								Webfm.renameActive = true;
							}, 10);
};

Webfm.menuSearch = function (obj) {
	Webfm.searchObj.createForm(obj.element.title);
};

Webfm.menuSwapname = function (obj) {
	var url, oldpath, newpath, postObj;
	url = Webfm.ajaxUrl();
	oldpath = obj.element.title;
	
	obj.parentPath = oldpath.substring(0, oldpath.lastIndexOf("/"));
	newpath = obj.parentPath + '/' + obj.tempInput.value;
	postObj = { action: encodeURIComponent("rename"), param0: encodeURIComponent(oldpath), param1: encodeURIComponent(newpath) };
	Webfm.HTTPPost(url, Webfm.ctxMenuCallback, obj, postObj);
	
	return true;
};

Webfm.menuGetMeta = function (obj) {
	var url, postObj;
	
	url = Webfm.ajaxUrl();
//	Webfm.dbgObj.dbg("obj.clickObj.title:", obj.clickObj.title);
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("getmeta"), param0: encodeURIComponent(obj.clickObj.title) };
	Webfm.HTTPPost(url, Webfm.ctxMenuMetaCallback, '', postObj);
};

Webfm.menuAttach = function (obj, ma_fid) {
	var url, fid, i, postObj, attach_arr;
	url = Webfm.ajaxUrl();
	attach_arr = [];
	
	if (obj === null || typeof obj === 'undefined' || obj === false) {
		fid = ma_fid;
	}
	else {
		fid = obj.clickObj.title;
	}
	//check that this file is not already attached

	attach_arr = Webfm.$(Webfm.attachFormInput).value.split(',');
	for (i = 0; i < attach_arr.length; i = i + 1) {
		if (fid == attach_arr[i]) {
			break;
		}
	}
	if (i == attach_arr.length) {
		Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
		postObj = { action: encodeURIComponent("attachfile"), param0: encodeURIComponent(fid) };
		Webfm.HTTPPost(url, Webfm.ctxMenuAttachCallback, '', postObj);
		Webfm.attach.noAttachments(false);
	}
};

Webfm.menuDetach = function (obj) {
	var path, attach_arr, new_attach_arr, j, i, fid;
	
	Webfm.alrtObj.msg();
	path = obj.element.title;
	if (Webfm.confirm(Webfm.js_msg.confirmdet + path + "?")) {
		// update table
		obj.element.parentNode.removeChild(obj.element);
		// update form input
		attach_arr = [];
		attach_arr = Webfm.$(Webfm.attachFormInput).value.split(',');
		new_attach_arr = [];
		j = 0;
		// tr elements use 'fid#' in 'title' field
		fid = obj.element.id.substring(3);
		for (i = 0; i < attach_arr.length; i = i + 1) {
			if (attach_arr[i] != fid) {
				j = j + 1;
				new_attach_arr[j] = attach_arr[i];
			}
		}
		Webfm.$(Webfm.attachFormInput).value = new_attach_arr.join(',');
		if (!new_attach_arr.length) {
			Webfm.attach.noAttachments(true);
		}
	}
};

Webfm.menuView = function (obj) {
	Webfm.selectFile(obj.clickObj.title, obj.element, false);
};

Webfm.menuDownload = function (obj) {
	Webfm.selectFile(obj.clickObj.title, obj.element, true);
};

Webfm.menuDbRem = function (obj) {
	var path, fid, url, postObj;
	
	path = obj.element.title;
	//strip 'fid' from front of id
	fid = obj.element.id.substring(3);
	if (Webfm.confirm(Webfm.js_msg.confirmdbrem0 + path + Webfm.js_msg.confirmdbrem1)) {
		url = Webfm.ajaxUrl();
		Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
		postObj = { action: encodeURIComponent("dbrem"), param0: encodeURIComponent(fid) };
		// path for callback must be a directory - not a file
		path = path.substring(0, path.lastIndexOf("/"));
		Webfm.HTTPPost(url, Webfm.dbrem_callback, path, postObj);
	}
	return false;
};

Webfm.menuGetPerm = function (obj) {
	var url, postObj;
	
	url = Webfm.ajaxUrl();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	
	postObj = { action: encodeURIComponent("getperm"), param0: encodeURIComponent(obj.clickObj.title) };
	Webfm.HTTPPost(url, Webfm.ctxMenuPermCallback, '', postObj);
};

Webfm.dbrem_callback = function (string, xmlhttp, path) {
	var result;
	
	Webfm.alrtObj.msg();
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		Webfm.dbgObj.dbg("dbrem:", Webfm.dump(result));
		if (result.status) {
			Webfm.dirListObj.refresh(path);
		}
		else {
			if (result.data) {
				Webfm.alrtObj.str_arr(result.data);
			}
			else {
				Webfm.alrtObj.msg("operation fail");
			}
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.menuInsert = function (obj) {
	Webfm.insert(obj.element.title, "file");
};

Webfm.insert = function (path, type) {
	var url, postObj;
	url = Webfm.ajaxUrl();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("insert"), param0: encodeURIComponent(path), param1: encodeURIComponent(type) };
	if (type == "file") {
		// path for callback must be a directory - not a file
		path = path.substring(0, path.lastIndexOf("/"));
	}
	Webfm.HTTPPost(url, Webfm.insert_callback, path, postObj);
	return false;
};
Webfm.insert_callback = function (string, xmlhttp, path) {
	var result;
	
	Webfm.alrtObj.msg();
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		Webfm.dbgObj.dbg("insert:", Webfm.dump(result));
		if (result.status) {
			//if success, update fetch
			if (result.data.cnt) {
				Webfm.dirListObj.refresh(path);
				Webfm.alrtObj.msg(result.data.cnt + Webfm.js_msg.inserted);
			}
			if (result.data.errcnt) {
				Webfm.alrtObj.msg(result.data.errcnt + Webfm.js_msg.notinserted);
				Webfm.alrtObj.str_arr(result.data.err);
			}
		}
		else if (result.data.err) {
			Webfm.alrtObj.str_arr(result.data.err);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.generateFileHref  = function (obj, url) {
	var title, string;
	title = '';

	if (typeof url == 'undefined') {
		url = getBaseUrl();
	}

	if (typeof obj.ftitle != "undefined" && obj.ftitle !== null && obj.ftitle.length) {
		title = obj.ftitle;
	}
	else {
		title = obj.element.title.substring(obj.element.title.lastIndexOf("/") + 1);
	}

	if (getWebfmCleanUrl()) {
		string = "<a href=\"" + url + "\/webfm_send\/" + obj.element.id.substring(3) + "\">" + title + "</a>";
	}
	else {
		string = "<a href=\"" + url + "\/?q=webfm_send\/" + obj.element.id.substring(3) + "\">" + title + "</a>";
	}

	return string;
};

Webfm.menuPasteHref = function (obj) {
	var fileHref, myField, startPos, endPos, sel;
	
	fileHref = Webfm.generateFileHref(obj, Drupal.settings.basePath.replace(/\/$/, ''));
	myField = Webfm.lastarea;

	//IE support
	if (document.selection) {
		myField.focus();
		sel = document.selection.createRange();
		sel.text = fileHref;
	}
	//other browsers
	else if (myField.selectionStart || myField.selectionStart == '0') {
		startPos = myField.selectionStart;
		endPos = myField.selectionEnd;
		myField.value = myField.value.substring(0, startPos) + fileHref + myField.value.substring(endPos, myField.value.length);
	}
	else {
		myField.value += fileHref;
	}
};

Webfm.menuAdmin = function () {
	return Webfm.admin;
};

Webfm.menuFileUid = function (obj) {
	//determine if we are the owner of this file
	return ((Webfm.admin || obj.uid == getWebfmUid()) ? true : false);
};

Webfm.menuRenFileUid = function (obj) {
	if (Webfm.menuFileUid(obj)) {
		// Determine if name is a metadata title
		return (obj.ftitle === null);
	}
	return false;
};

Webfm.menuFilePerm = function (obj) {
	// object must be file in db with user access
	if (Webfm.menuFidVal(obj)) {
		return Webfm.menuFileUid(obj);
	}
	return false;
};

Webfm.menuAdminFidVal = function (obj) {
	if (Webfm.admin) {
		return Webfm.menuFidVal(obj);
	}
	return false;
};

Webfm.menuAdminNoFidVal = function (obj) {
	if (Webfm.admin) {
		return Webfm.menuNoFidVal(obj);
	}
	return false;
};

//menu prepFunc to determine if filerow has 'fid' as first 3 letters of id
Webfm.menuFidVal = function (obj) {
	if (obj.element.id.substring(0, 3) == 'fid') {
		return true;
	}
	return false;
};

Webfm.menuNoFidVal = function (obj) {
	if (obj.element.id.substring(0, 3) != 'fid') {
		return true;
	}
	return false;
};

Webfm.confirm = function (text) {
	var agree = confirm(text);
	return agree ? true : false;
};

/**
 * Context menu callbacks
 */
Webfm.ctxMenuAttachCallback = function (string, xmlhttp, cp) {
	var result, filerow, elInput;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg("attach:", Webfm.dump(result));
		if (result.status) {
			Webfm.admin = result.admin;
			filerow = new Webfm.filerow(Webfm.attachObj.body, result.data, 'attach', '', true, Webfm.menuHT.get('det'), Webfm.attachObj.eventListeners);
			elInput = Webfm.$(Webfm.attachFormInput);
			elInput.setAttribute('value', (elInput.getAttribute('value') ? elInput.getAttribute('value') + ',' : '') + result.data.id);
		}
		else {
			Webfm.alrtObj.msg(result.data);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.ctxMenuCallback = function (string, xmlhttp, obj, mkdir_flag) {
	var result;
	
	Webfm.progressObj.hide();
	Webfm.alrtObj.msg();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg("context:", Webfm.dump(result));
//		Webfm.dbgObj.dbg("obj:", obj.droppath);
		if (result.status) {
			if (!obj.is_file && Webfm.dirTreeObj) {
				Webfm.dirTreeObj.fetch(true, false);
			}
			//if success, flush cache for updated fetch
			if (typeof mkdir_flag != "undefined") {
				Webfm.dirListObj.refresh(obj.parentPath, result.data);
			}
			else {
				Webfm.dirListObj.refresh(obj.parentPath);
			}
		}
		else if (result.data) {
			Webfm.alrtObj.str_arr(result.data);
		}
		else {
			Webfm.alrtObj.msg("operation fail");
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.ctxMenuMkdirCallback = function (string, xmlhttp, obj) {
	var mkdir_flag = true;
	// fourth var to signal the listing to rename the new directory
	Webfm.ctxMenuCallback(string, xmlhttp, obj, mkdir_flag);
};

Webfm.ctxMenuMetaCallback = function (string, xmlhttp, obj) {
	var result;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg("meta result:", Webfm.dump(result));
		if (result.status) {
			Webfm.metaObj.createForm(result.data);
		}
		else {
			Webfm.alrtObj.msg(result.data);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

Webfm.ctxMenuPermCallback = function (string, xmlhttp, obj) {
	var result;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg("perm result:", Webfm.dump(result));
		if (result.status) {
			Webfm.permObj.createForm(result.data);
		}
		else {
			Webfm.alrtObj.msg(result.data);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

/**
 * Webfm.perm constructor
 * 1st param is popup container obj
 * 2nd param is default startup width
 * 2nd param is default startup height
 */
Webfm.perm = function (container, width, height) {
	var elDiv, elForm, elTable, elTBody, elControls;
	
	elDiv = Webfm.ce('div');
	elDiv.setAttribute('id', 'webfm-perm-form');

	elForm = Webfm.ce('form');
	elDiv.appendChild(elForm);

	elTable = Webfm.ce('table');
	elForm.appendChild(elTable);

	elTBody = Webfm.ce('tbody');
	elTable.appendChild(elTBody);

	elControls = Webfm.ce('div');
	elForm.appendChild(elControls);

	this.obj = elDiv;
	this.container = container;
	this.form = elForm;
	this.tbody = elTBody;
	this.controls = elControls;
	this.width = width;
	this.height = height;
	this.eventListeners = this.container.eventListenerArr;
};

Webfm.perm.prototype.createForm = function (data) {
	this.data = data;
	this.permit = (Webfm.admin || this.data.u == getWebfmUid()) ? true : false;
	//clear any permissions info
	this.resetForm();
	//create new pane to house this perm object
	this.pane = new Webfm.pane(this.container, Webfm.js_msg.perm, "webfm-permPane", this.obj, this.width, this.height);
	//create permissions checkboxes
	this.fillFormData();
	//create controls if owner has rights to file
	if (this.permit) {
		this.buildFormControls();
	} else {
		//put read-only indicator in pane header
		this.pane.headerMsg("read-only");
	}
};

Webfm.perm.prototype.resetForm = function () {
	var tbody, controls;
	tbody = this.tbody;
	controls = this.controls;
	
	while (tbody.hasChildNodes()) {
		tbody.removeChild(tbody.firstChild);
	}
	
	while (controls.hasChildNodes()) {
		controls.removeChild(controls.firstChild);
	}
};

Webfm.perm.prototype.fillFormData = function () {
	var elTr, elTd, i, j, check, elInput;
	
	for (j in this.data) {
		if (j != "p") {
			elTr = Webfm.ce('tr');

			// label td
			elTd = Webfm.ce('td');
			elTd.appendChild(Webfm.ctn(Webfm.permInfoHT.get(j) + ': '));
			elTr.appendChild(elTd);

			// info td
			elTd = Webfm.ce('td');
			elTd.appendChild(Webfm.ctn(this.data[j]));
			elTr.appendChild(elTd);

			this.tbody.appendChild(elTr);
		}
	}
	// Webfm.permKey keys correspond to permission bitfields
	for (i in Webfm.permKey) {
		elTr = Webfm.ce('tr');

		// label td
		elTd = Webfm.ce('td');
		elTd.appendChild(Webfm.ctn(Webfm.permHT.get(Webfm.permKey[i]) + ': '));
		elTr.appendChild(elTd);

		// checkbox td
		elTd = Webfm.ce('td');
		// use bitwise operator to determine permission bits
		check = (parseInt(Webfm.permKey[i], 10) & parseInt(this.data.p, 10)) > 0;
		if (this.permit) {
			elInput = Webfm.ce('input');
			elInput.setAttribute('type', 'checkbox');
			// checkbox name is used to build the new permission value
			elInput.setAttribute('name', Webfm.permKey[i]);
			if (check) {
				elInput.setAttribute('checked', check);
			}
			elTd.appendChild(elInput);
		}
		else {
			elTd.appendChild(Webfm.ctn(check ? "True" : "False"));
		}
		elTr.appendChild(elTd);

		this.tbody.appendChild(elTr);
	}
};

//build perm control buttons
Webfm.perm.prototype.buildFormControls = function () {
	var cp, submitButton, listener, resetButton;
	cp = this;

	submitButton = Webfm.ce('input');
	submitButton.setAttribute('type', 'button');
	submitButton.setAttribute('value', Webfm.js_msg.submit);
	submitButton.className = "perm-button";
	listener = Webfm.eventListenerAdd(cp.eventListeners, submitButton, "click", function (e) {
																						if (cp.submitPerm()) {
																							cp.container.destroy();
																						}
																						Webfm.stopEvent(e);
																					});
	this.controls.appendChild(submitButton);

	resetButton = Webfm.ce('input');
	resetButton.setAttribute('type', 'button');
	resetButton.setAttribute('value', Webfm.js_msg.reset);
	resetButton.className = "perm-button";
	listener = Webfm.eventListenerAdd(cp.eventListeners, resetButton, "click", function (e) {
																				cp.createForm(cp.data);
																				Webfm.stopEvent(e);
																			});
	this.controls.appendChild(resetButton);
};

Webfm.perm.prototype.submitPerm = function () {
	var url, inputs, output, i, postObj;
	url = Webfm.ajaxUrl();
	inputs = [];
	output = 0;
	
	inputs = this.tbody.getElementsByTagName('input');

	
	for (i = 0; i < inputs.length; i = i + 1) {
		// Build permission val from checkbox name
		if (inputs[i].checked) {
			output += parseInt(inputs[i].name, 10);
		}
	}

	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("putperm"), param0: encodeURIComponent(this.data.id), param1: encodeURIComponent(output)};
	Webfm.HTTPPost(url, this.submitPermcallback, this, postObj);
};

Webfm.perm.prototype.submitPermcallback = function (string, xmlhttp, obj) {
	var result;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		if (result.status) {
			//put confirmed result back into this.data for proper 'reset'
			obj.data.p = result.data.perm;
//			Webfm.dbgObj.dbg('new perm :', obj.data["p"]);
		}
		obj.pane.headerMsg(result.msg);
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

/**
 * Webfm.metadata constructor
 * 1st param is popup container obj
 * 2nd param is default startup width
 * 2nd param is default startup height
 */
Webfm.metadata = function (container, width, height) {
	var elDiv, elForm, elTable, elTBody, elControls;
	
	elDiv = Webfm.ce('div');
	elDiv.setAttribute('id', 'webfm-meta-form');

	elForm = Webfm.ce('form');
	elDiv.appendChild(elForm);

	elTable = Webfm.ce('table');
	elForm.appendChild(elTable);

	elTBody = Webfm.ce('tbody');
	elTable.appendChild(elTBody);

	elControls = Webfm.ce('div');
	elForm.appendChild(elControls);

	this.obj = elDiv;
	this.container = container;
	this.form = elForm;
	this.tbody = elTBody;
	this.controls = elControls;
	this.fid = null;
	this.width = width;
	this.height = height;
	this.eventListeners = this.container.eventListenerArr;
};

Webfm.metadata.prototype.createForm = function (data) {
	this.data = data;
	//clear any metadata info
	this.resetForm();
	//create new pane to house this metadata object
	this.pane = new Webfm.pane(this.container, Webfm.js_msg.metadata, "webfm-metaPane", this.obj, this.width, this.height);
	//create metadata fields
	this.fillFormData();
	//create controls if owner has rights to file
	if (this.fid) {
		this.buildFormControls();
	} else {
		//put read-only indicator in pane header
		this.pane.headerMsg("read-only");
	}
};

Webfm.metadata.prototype.resetForm = function () {
	var tbody, controls;
	
	tbody = this.tbody;
	while (tbody.hasChildNodes()) {
		tbody.removeChild(tbody.firstChild);
	}

	controls = this.controls;
	while (controls.hasChildNodes()) {
		controls.removeChild(controls.firstChild);
	}
	this.fid = null;
};

Webfm.metadata.prototype.fillFormData = function () {
	var i, meta, elTr, elTd, elInput, elTextArea;
	
	if (typeof this.data.id != "undefined") {
		this.fid = this.data.id;
	}
	for (i in this.data) {
		meta = Webfm.metadataHT.get(i);
		elTr = Webfm.ce('tr');

		// label td
		elTd = Webfm.ce('td');
		elTd.appendChild(Webfm.ctn(meta.desc + ': '));
		elTr.appendChild(elTd);

		// info td
		elTd = Webfm.ce('td');
		if (this.fid && meta.edit) {
			if (meta.size == 256) {
					// textarea
				elTextArea = Webfm.ce('textarea');
				elTextArea.setAttribute('name', i);
				elTextArea.cols = "40";
				elTextArea.rows = "4";
				elTextArea.value = decodeURI(this.data[i]);
				elTd.appendChild(elTextArea);
			}
			else {
				// textfield
				elInput = Webfm.ce('input');
				elInput.setAttribute('type', 'textfield');
				elInput.setAttribute('name', i);
				elInput.setAttribute('size', '40');
				elInput.setAttribute('value', decodeURI(this.data[i]));
				elTd.appendChild(elInput);
			}
		}
		else {
			elTd.appendChild(Webfm.ctn(decodeURI(this.data[i])));
		}
		elTr.appendChild(elTd);

		// validation td
		elTd = Webfm.ce('td');
		elTd.appendChild(Webfm.ctn(String.fromCharCode(160))); //&nbsp;
		elTr.appendChild(elTd);

		this.tbody.appendChild(elTr);
	}
};

//build metadata control buttons
Webfm.metadata.prototype.buildFormControls = function () {
	var cp, submitButton, listener, resetButton;
	cp = this;

	submitButton = Webfm.ce('input');
	submitButton.setAttribute('type', 'button');
	submitButton.setAttribute('value', Webfm.js_msg.submit);
	submitButton.className = "meta-button";
	listener = Webfm.eventListenerAdd(cp.eventListeners, submitButton, "click", function (e) {
																						if (cp.submitMeta()) {
																							cp.container.destroy();
																						}
																						Webfm.stopEvent(e);
																					});
	this.controls.appendChild(submitButton);

	resetButton = Webfm.ce('input');
	resetButton.setAttribute('type', 'button');
	resetButton.setAttribute('value', Webfm.js_msg.reset);
	resetButton.className = "meta-button";
	listener = Webfm.eventListenerAdd(cp.eventListeners, resetButton, "click", function (e) {
																				cp.createForm(cp.data);
																				Webfm.stopEvent(e);
																			});
	this.controls.appendChild(resetButton);
};

//Validate the length of input
Webfm.metadata.prototype.validateMetaInput = function (elInput) {
	var len, data, err_msg;
	
	len = Webfm.metadataHT.get(elInput.name).size;
//	Webfm.dbgObj.dbg("validateMetaInput", elInput.name);
	if (len < 256) {
		data = Webfm.trim(elInput.value);
		if (data.length > len) {
			// Append error msg to validation td
			err_msg = Webfm.ctn(Webfm.js_msg.lengtherr);
			elInput.parentNode.nextSibling.style.color = 'red';
			elInput.parentNode.nextSibling.appendChild(err_msg);
			setTimeout(function () {
								elInput.focus();
							}, 10);
			return false;
		}
		else {
			// Remove any contents of validation td
			while (elInput.parentNode.nextSibling.hasChildNodes()) {
				elInput.parentNode.nextSibling.removeChild(elInput.parentNode.nextSibling.firstChild);
			}
		}
	}
	return true;
};

Webfm.metadata.prototype.submitMeta = function () {
	var url, inputs, arr, textareas, input_num, i, postObj;
	url = Webfm.ajaxUrl();
	inputs = [];
	arr = [];
	textareas = [];
	
	inputs = this.tbody.getElementsByTagName('input');
	textareas = this.tbody.getElementsByTagName('textarea');

	input_num = inputs.length;
	
	for (i = 0; i < input_num; i = i + 1) {
		if (!this.validateMetaInput(inputs[i])) {
			break;
		}
	}

	// All fields validated
	// TODO: Data format needs to be reworked (json?)
	if (i == input_num) {
		this.output = "";
		for (i = 0; i < input_num; i = i + 1) {
			this.output += encodeURIComponent(inputs[i].name) + ":" + encodeURIComponent(Webfm.trim(inputs[i].value)) + ",";
		}
		for (i = 0; i < textareas.length; i = i + 1) {
			this.output += encodeURIComponent(textareas[i].name) + ":" + encodeURIComponent(Webfm.trim(textareas[i].value)) + ",";
		}
		if (this.output.length) {
			Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
			//strip off final comma
			this.output = this.output.substring(0, this.output.length - 1);
			postObj = { action: encodeURIComponent("putmeta"), param0: encodeURIComponent(this.fid), param1: encodeURIComponent(this.output)};
			Webfm.HTTPPost(url, this.submitMetacallback, this, postObj);
		}
	}
	else {
		alert(Webfm.js_msg.fix_input);
	}
	return false;
};

Webfm.metadata.prototype.submitMetacallback = function (string, xmlhttp, obj) {
	var params, result, i, key, val;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
//		Webfm.dbgObj.dbg('submitMetacallback: ', Webfm.dump(result));
		if (result.status) {
			//update data for reset button
			params = [];
			params = obj.output.split(',');
			for (i = 0; i < params.length; i = i + 1) {
				key = params[i].substring(0, params[i].indexOf(":"));
				val = params[i].substring(params[i].indexOf(":") + 1);
				obj.data[key] = val;
			}
		}
		obj.pane.headerMsg(result.data);
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

//Trim leading/trailing whitespace off string
Webfm.trim = function (str) {
	return str.replace(/^\s+|\s+$/g, '');
};

/*
 * Webfm.attach constructor
 * param is parentId from hook_form_alter
 */
Webfm.attach = function (parentId) {
	var wa, elTable, elTableBody, elTr, elTd;
	wa = this;
	this.id = parentId;
	this.attached = '';
	this.eventListeners = [];

	elTable = Webfm.ce('table');
	elTableBody = Webfm.ce('tbody');
	//attached file rows are appended to this div
	this.body = elTableBody;

	//Header
	elTr = Webfm.ce('tr');

	// icon column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTr.appendChild(elTd);

	// Sort dir/files column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTd.appendChild(Webfm.ctn(Webfm.js_msg.attachtitle));
	elTr.appendChild(elTd);

	// date/time column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTd.appendChild(Webfm.ctn(Webfm.js_msg.column2));
	elTr.appendChild(elTd);

	// file size column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTd.appendChild(Webfm.ctn(Webfm.js_msg.column3));
	elTr.appendChild(elTd);

	// owner column
	elTd = Webfm.ce('td');
	elTd.className = 'head';
	elTd.appendChild(Webfm.ctn(Webfm.js_msg.column4));
	elTr.appendChild(elTd);

	elTableBody.appendChild(elTr);
	elTable.appendChild(elTableBody);
	Webfm.$(parentId).appendChild(elTable);
};

//Turn on/off no attachments display as needed.
Webfm.attach.noAttachments = function (show) {
	if (show) {
		$('#webfm-attach').append( '<div id="webfm-no-attachments">' + Webfm.js_msg.noattachments + '</div>');
	}
	else {
		$('#webfm-no-attachments').remove();
	}
};

Webfm.attach.prototype.fetch = function () {
	var url, node_url, postObj, fids;
	
	url = Webfm.ajaxUrl();
	
	// action attribute of node-edit form contains the node number
	if (Webfm.$('node-form')) {
		node_url = Webfm.$('node-form').action;
	}
	if (Webfm.$('comment-form')) {
		node_url = Webfm.$('comment-form').action;
	}
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("attach"), param0: encodeURIComponent(node_url) };
	// If we are in a preview/validate, the fids are still stored in the form.
	// Pass them to webfm_ajax to reload them.
	fids = Webfm.$(Webfm.attachFormInput).value;
	if (fids.length !== 0) {
		postObj.param1 = encodeURIComponent(fids);
	}
	Webfm.HTTPPost(url, this.callback, this, postObj);
};

Webfm.attach.prototype.callback = function (string, xmlhttp, obj) {
	var result, elInput, attach_arr, i, filerow, j;
	
	Webfm.progressObj.hide();
	
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		//   Webfm.dbgObj.dbg("attach.fetch:", Webfm.dump(result));
		if (result.status) {
			if (result.data.length) {
				Webfm.admin = result.admin;
				elInput = Webfm.$(Webfm.attachFormInput);

				attach_arr = [];
				attach_arr = Webfm.$(Webfm.attachFormInput).value.split(',');
				for (i = 0; i < result.data.length; i = i + 1) {
					filerow = new Webfm.filerow(obj.body, result.data[i], 'attach', '', true, Webfm.menuHT.get('det'), obj.eventListeners);
					// Don't add if it already exists.
					// Note that values are kept in the form for preview/failed validate.
					for (j = 0; j < attach_arr.length; j = j + 1) {
						if (result.data[i].id == attach_arr[j]) {
							break;
						}
					}
					if (j == attach_arr.length) {
						elInput.setAttribute('value', (elInput.getAttribute('value') ? elInput.getAttribute('value') + ',' : '') + result.data[i].id);
					}
				}
			}
			else {
				Webfm.attach.noAttachments(true);
			}
		}
		else {
			Webfm.alrtObj.msg(result.data);
		}
	} else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

/**
 * Webfm.search constructor
 * 1st param is popup container obj
 * 2nd param is default startup width
 * 2nd param is default startup height
 */
Webfm.search = function (container, width, height) {
	var elDiv, elForm, elResults;
	
	elDiv = Webfm.ce('div');
	elDiv.setAttribute('id', 'webfm-search');

	elForm = Webfm.ce('form');
	elDiv.appendChild(elForm);

	elResults = Webfm.ce('div');
	elDiv.appendChild(elResults);

	this.obj = elDiv;
	this.container = container;
	this.form = elForm;
	this.results = elResults;
	this.width = width;
	this.height = height;
	this.iconDir = getWebfmIconDir();
	this.eventListeners = this.container.eventListenerArr;
};

Webfm.search.prototype.resetForm = function () {
	var form = this.form;
	while (form.hasChildNodes()) {
		form.removeChild(form.firstChild);
	}
};
Webfm.search.prototype.resetResults = function () {
	var results = this.results;
	while (results.hasChildNodes()) {
		results.removeChild(results.firstChild);
	}
};

Webfm.search.prototype.createForm = function (source) {
	var cp, pane, elInput, listener, searchButton;
	
	cp = this;
	this.source = source;
	//clear any metadata info
	this.resetForm();
	cp.resetResults();
	//create new pane to house this metadata object
	pane = new Webfm.pane(this.container, Webfm.js_msg.searchtitle, "webfm-searchPane", this.obj, this.width, this.height);
	//put search path in header
	pane.headerMsg(source);
	//create search fields
	elInput = Webfm.ce('input');
	elInput.setAttribute('type', 'textfield');
	elInput.setAttribute('size', '30');
	setTimeout(function () {
						elInput.focus();
					}, 10);
	listener = Webfm.eventListenerAdd(this.eventListeners, elInput, "keydown", function (e) {
																				if (Webfm.enter(e)) {
																					cp.submit(elInput.value);
																					elInput.blur();
																					Webfm.stopEvent(e);
																				}
																			});
	this.form.appendChild(elInput);

	searchButton = Webfm.ce('input');
	searchButton.setAttribute('type', 'button');
	searchButton.setAttribute('value', Webfm.js_msg.search);
	listener = Webfm.eventListenerAdd(this.eventListeners, searchButton, "click", function (e) {
																					cp.submit(elInput.value);
																					Webfm.stopEvent(e);
																				});
	this.form.appendChild(searchButton);
	pane.show();
};

Webfm.search.prototype.submit = function (value) {
	var url, postObj;
	
	url = Webfm.ajaxUrl();
	Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
	postObj = { action: encodeURIComponent("search"), param0: encodeURIComponent(this.source), param1: encodeURIComponent(value) };
	Webfm.HTTPPost(url, this.callback, this, postObj);
};

Webfm.listenerfn_search_click_dir = function (cp) {
	var listenerfn = function (e) {
									cp.selectDir(e);
									Webfm.stopEvent(e);
								};
	return listenerfn;
};

Webfm.listenerfn_search_click = function (cp) {
	var listenerfn = function (e) {
									cp.selectFile(e);
									Webfm.stopEvent(e);
								};
	return listenerfn;
};



Webfm.search.prototype.callback = function (string, xmlhttp, cp) {
	var result, searchList, i, elLi, elImg, listener, elA, no_match;
	
	Webfm.progressObj.hide();
	if (xmlhttp.status == 200) {
		cp.resetResults();

		//build ul of search results
		result = Drupal.parseJson(string);
		if (result.files.length) {
			searchList = Webfm.ce('ul');
			for (i = (result.files.length - 1); i >= 0; i = i - 1) {
				elLi = Webfm.ce('li');

				elImg = Webfm.ce('img');
				elImg.setAttribute('src', cp.iconDir + '/d.gif');
				elImg.setAttribute('alt', Webfm.js_msg.u_dir);
				//title is path to directory
				elImg.setAttribute('title', result.files[i].p);
				listener = Webfm.eventListenerAdd(cp.eventListeners, elImg, "click",  Webfm.listenerfn_search_click_dir(cp));
				listener = Webfm.eventListenerAdd(cp.eventListeners, elImg, "mouseover", Webfm.listenerfn_mouseoverout_dir(cp, true));
				listener = Webfm.eventListenerAdd(cp.eventListeners, elImg, "mouseout", Webfm.listenerfn_mouseoverout_dir(cp, false));
				elLi.appendChild(elImg);

				elLi.appendChild(Webfm.ctn('  '));

				elA = Webfm.ce('a');
				elA.setAttribute('href', '#');
				if (result.files[i].id) {
					//first three letters of id must be 'sid' for selectFile
					//tooltip indicates if file is downloadable
					elA.setAttribute('id', 'sid' + result.files[i].id);
					elA.setAttribute('title', Webfm.js_msg.filedwld);
					listener = Webfm.eventListenerAdd(cp.eventListeners, elA, "click", Webfm.listenerfn_search_click(cp));
				}
				else {
					elA.setAttribute('title', Webfm.js_msg.nofiledwld);
				}
				elA.appendChild(Webfm.ctn(result.files[i].n));
				elLi.appendChild(elA);

				searchList.appendChild(elLi);
			}
			cp.results.appendChild(searchList);
		}
		else {
			no_match = Webfm.ctn(Webfm.js_msg.nomatch);
			cp.results.style.color = 'red';
			cp.results.appendChild(no_match);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};
//IE does not understand 'this' inside event listener func
Webfm.search.prototype.hoverDir = function (event, state) {
	var el = event.target || window.event.srcElement;
	el.src = this.iconDir + (state ? '/open.gif' : '/d.gif');
};

Webfm.search.prototype.selectDir = function (event) {
	var el = event.target || window.event.srcElement;
	Webfm.selectDir(el.title);
};
Webfm.search.prototype.selectFile = function (event) {
	var el = event.target || window.event.srcElement;
	Webfm.selectFile(el.id.substring(3), el, true);
};

/*
 * debug constructor
 * 1st param is popup container obj
 * 2nd param is default startup width
 * 2nd param is default startup height
 */
Webfm.debug = function (container, width, height) {
	var dbgButton, cp, cacheCont, report, listener;
	dbgButton = '';
	
	//id from webfm_main()
	dbgButton = Webfm.$("webfm-debug-link");
	this.enable = dbgButton;
	if (dbgButton) {
		this.container = container;
		this.eventListeners = this.container.eventListenerArr;
		this.width = width;
		this.height = height;
		cp = this;

		//create cache popup
		cacheCont = new Webfm.popup("webfm-cacheCont");
		this.cacheCont = cacheCont;

		report = Webfm.ce('div');
		this.report = report;

		//attach popup creation to [debug] link
		listener = Webfm.eventListenerAdd(Webfm.eventListeners, dbgButton, "click", function () {
																									cp.show();
																								});
	}
};

Webfm.debug.prototype.dbg = function (title, msg) {
	var elSpan, elBr;
	
	if (this.enable) {
		//put latest msg at top (less scrolling)
		elBr = Webfm.ce('br');
		this.report.insertBefore(elBr, this.report.firstChild);
		if (msg) {
			this.report.insertBefore(Webfm.ctn(msg), this.report.firstChild);
		}
		if (title) {
			elSpan = Webfm.ce('span');
			elSpan.className = 'g';
			elSpan.appendChild(Webfm.ctn(title));
			this.report.insertBefore(elSpan, this.report.firstChild);
		}
	}
};

Webfm.debug.prototype.clear = function () {
	if (this.enable) {
		Webfm.alrtObj.msg();
		while (this.report.hasChildNodes()) {
			this.report.removeChild(this.report.firstChild);
		}
		this.cacheCont.destroy();
	}
};

Webfm.debug.prototype.show = function () {
	var elDiv, pane, cp, elControls, elClear, listener, elCache, report;
	
	if (this.enable) {
		elDiv = Webfm.ce('div');
		elDiv.setAttribute('id', 'webfm-debug');
		this.obj = elDiv;

		//create new pane to house this metadata object
		pane = new Webfm.pane(this.container, Webfm.js_msg.debugtitle, "webfm-debugPane", this.obj, this.width, this.height);

		cp = this;
		elControls = Webfm.ce('div');
		this.controls = elControls;
		
		elClear = Webfm.ce('input');
		elClear.setAttribute('type', 'button');
		elClear.setAttribute('value', Webfm.js_msg.clear);
		elClear.className = "dbg-button";
		listener = Webfm.eventListenerAdd(this.eventListeners, elClear, "click", function () {
																								cp.clear();
																							});
		this.controls.appendChild(elClear);
		
		elCache = Webfm.ce('input');
		elCache.setAttribute('type', 'button');
		elCache.setAttribute('value', Webfm.js_msg.cache);
		elCache.className = "dbg-button";
		listener = Webfm.eventListenerAdd(this.eventListeners, elCache, "click", function (e) { 
																								cp.showCache(); 
																								Webfm.stopEvent(e);
																							});
		this.controls.appendChild(elCache);
		this.obj.appendChild(elControls);

		report = this.report;
		this.obj.appendChild(report);
	}
};

Webfm.debug.prototype.showCache = function () {
	var dump, pane;
	
	if (this.enable) {
		//create new pane to house this metadata object
		dump = Webfm.ctn(Webfm.dump(Webfm.dirListObj.cache.dump()));
		pane = new Webfm.pane(this.cacheCont, Webfm.js_msg.cachetitle, "webfm-cachePane", dump, this.width, this.height);
		pane.show();
	}
};

/**
 * Progress indicator
 */
Webfm.progress = function (parent, id) {
	var elSpan = Webfm.ce('span');
	
	this.id = id;
	this.flag = false;
	elSpan.setAttribute('id', this.id);
	parent.appendChild(elSpan);
};

Webfm.progress.prototype.hide = function () {
	if (this.flag) {
		this.flag = false;
		Webfm.clearNodeById(this.id);
	}
};

Webfm.progress.prototype.show = function (x, y) {
	var prog, elSpan;
	
	if (!this.flag) {
		this.flag = true;
		prog = Webfm.$(this.id);
		elSpan = Webfm.ce('span');
		elSpan.style.backgroundColor = y;
		elSpan.appendChild(Webfm.ctn(x));
		prog.appendChild(elSpan);
		prog.style.visibility = 'visible';
	}
};

/**
 * Alert indicator
 */
Webfm.alert = function (parent, id) {
	var elDiv;
	this.id = id;
	
	elDiv = Webfm.ce('div');
	this.node = elDiv;
	elDiv.setAttribute('id', id);
	parent.appendChild(elDiv);
};

Webfm.alert.prototype.msg = function (msg) {
	var elSpan;
	
	if (!msg) {
		Webfm.clearNodeById(this.id);
	}
	else {
		elSpan = Webfm.ce('span');
		elSpan.className = 'alertspan';
		elSpan.appendChild(Webfm.ctn(msg));
		this.node.appendChild(elSpan);
		this.lf();
	}
};

Webfm.alert.prototype.lf = function () {
	var elBr = Webfm.ce('br');
	this.node.appendChild(elBr);
};

/*
 * Display an array of strings alert
 */
Webfm.alert.prototype.str_arr = function (arr) {
	var i;
	
	if (typeof arr == 'function') {
		return;
	}
	else if (Webfm.isArray(arr)) {
		for (i = 0; i < arr.length; i = i + 1) {
			this.str_arr(arr[i]);
		}
	}
	
	if (typeof arr == 'string') {
		this.msg(arr);
	}
};

/**
 * Webfm.popup constructor
 */
Webfm.popup = function (Id) {
	var elDiv;
	this.id = Id;
	
	elDiv = Webfm.ce('div');
	elDiv.id = Id; //id for css
	elDiv.style.cssText = 'position:absolute;display:none;';
	document.body.appendChild(elDiv);
	this.obj = elDiv;

	//vars used for storing position info for contained object (pane)
	this.width = null;
	this.content_height = null;
	this.top = null;
	this.left = null;
	this.eventListenerArr = [];
};

Webfm.popup.prototype.getScrollY = function () {
	var scrY = 0;
	
	if (typeof window.pageYOffset == 'number') {
		scrY = window.pageYOffset;
	}
	else if (document.body && document.body.scrollTop) {
		scrY = document.body.scrollTop;
	}
	else if (document.documentElement && document.documentElement.scrollTop) {
		scrY = document.documentElement.scrollTop;
	}
	
	return scrY;
};
Webfm.popup.prototype.getObjectOffset = function (event, element, comp) {
	var objPos, mousePos;
	
	objPos = this.objPosition(element, comp);
	mousePos = Drupal.mousePosition(event);
	
	return {x: mousePos.x - objPos.x, y: mousePos.y - objPos.y};
};

Webfm.popup.prototype.objPosition = function (element, comp) {
	var curleft, curtop;
	curleft = 0;
	curtop = 0;
	
	if (element.offsetParent) {
		do {
			curleft += element.offsetLeft;
			curtop += element.offsetTop;
		} while (element = element.offsetParent);
	}
	
	// IE must compensate for relative positioning in css
	if (Webfm.browser.isIE && comp) {
		curleft += parseInt(getWebfmIEListOffset(), 10);
		if (comp == 'tree') {
			curleft += parseInt(getWebfmIETreeOffset(), 10);
		}
	}
	
	return { x: curleft, y: curtop };
};

Webfm.popup.prototype.destroy = function () {
	//cleanup event closures for this popup
	Webfm.eventUnregister(this.eventListenerArr);

	while (this.obj.hasChildNodes()) {
		this.obj.removeChild(this.obj.firstChild);
	}
	
	document.onmousemove = null;
	document.onmouseup = null;
	this.obj.style.display = 'none';
};
Webfm.popup.prototype.isEmpty = function () {
	return (!this.obj.hasChildNodes());
};

/**
 * Webfm.pane constructor
 * 1st param is popup container obj
 * 2nd param is title displayed in pane header
 * 3rd param is this obj id
 * 4th param is content appended to this obj content div
 * 5th param is default startup width
 * 6th param is default startup height
 */
Webfm.pane = function (popupCont, title, paneId, content, paneWidth, contentHeight) {
	var cp, body_scroll_top, body_height, body_scroll_left, body_width, iconDir, pane, header, elTitle, elMsg, elA, listener, elImg, contentContainer, footer, elResize;
	cp = this;
	this.popupCont = popupCont;
	
	body_scroll_top = this.popupCont.getScrollY();
	body_height = Webfm.browser.isIE ? document.documentElement.clientHeight : window.innerHeight;
	// locate pane in centre of screen only if drag container not already in use
	if ((!popupCont.width) ||
			(!popupCont.content_height) ||
			(!popupCont.top) ||
			(!popupCont.left)) {
		body_scroll_left = Webfm.browser.isIE ? document.documentElement.scrollLeft : window.pageXOffset;
		body_width = Webfm.browser.isIE ? document.documentElement.clientWidth : window.innerWidth;

		this.popupCont.width = paneWidth;
		this.popupCont.content_height = contentHeight;
		this.popupCont.top = body_scroll_top + (body_height - contentHeight) / 2;
		this.popupCont.left = body_scroll_left + (body_width - paneWidth) / 2;
	}

	//Compensate pane position for vertical scroll that places it out of view
	if ((body_scroll_top + body_height) < this.popupCont.top) {
		this.popupCont.top = body_scroll_top + (body_height - contentHeight) / 2;
	}
	if ((body_scroll_top) > this.popupCont.top + this.popupCont.content_height) {
		this.popupCont.top = body_scroll_top + (body_height - contentHeight) / 2;
	}

	// Set top of drag popup and empty any contents
	this.popupCont.obj.style.top  = this.popupCont.top + "px";
	this.popupCont.obj.style.left = this.popupCont.left + "px";
	this.popupCont.destroy();

	// Pane container div
	iconDir = getWebfmIconDir();
	pane = Webfm.ce('div');
	this.pane = pane;
	pane.id = paneId;
	pane.className = "webfm-pane";
	pane.style.width = this.popupCont.width + "px";
	this.popupCont.obj.appendChild(this.pane);
	
	// pane header
	header = Webfm.ce('div');
	this.header = header;
	header.id = pane.id + '-header';
	header.className = "webfm-pane-header";
	
	elTitle = Webfm.ce('div');
	elTitle.appendChild(Webfm.ctn(title));
	elTitle.className = "webfm-pane-title";
	header.appendChild(elTitle);
	
	elMsg = Webfm.ce('div');
	this.msg = elMsg;
	elMsg.className = "webfm-pane-msg";
	header.appendChild(elMsg);
	cp = this;
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, header, "mousedown", function (e) {
																					cp.dragStart(e, 1);
																				});
	
	elA = Webfm.ce('a');
	elA.setAttribute('href', '#');
	elA.setAttribute('title', Webfm.js_msg.close);
	elA.className = "webfm-pane-close";
	
	elImg = Webfm.ce('img');
	elImg.setAttribute('src', iconDir + '/x.gif');
	elImg.setAttribute('alt', Webfm.js_msg.close);
	elA.appendChild(elImg);
	header.appendChild(elA);
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elA, "click", function (e) {
																			cp.popupCont.destroy();
																			Webfm.stopEvent(e);
																		});
	pane.appendChild(header);
	
	// pane content
	contentContainer = Webfm.ce('div');
	this.content = contentContainer;
	contentContainer.id = pane.id + '-content';
	contentContainer.className = "webfm-pane-content";
	contentContainer.style.height = this.popupCont.content_height + "px";
	pane.appendChild(contentContainer);

	// pane footer
	footer = Webfm.ce('div');
	this.footer = footer;
	footer.className = "webfm-pane-footer";
	
	elResize = Webfm.ce('a');
	elResize.setAttribute('href', '#');
	elResize.setAttribute('title', Webfm.js_msg.resize);
	elResize.className = "webfm-pane-resize";
	elImg = Webfm.ce('img');
	elImg.setAttribute('src', iconDir + '/resize.gif');
	elImg.setAttribute('alt', Webfm.js_msg.resize);
	elResize.appendChild(elImg);
	footer.appendChild(elResize);
	listener = Webfm.eventListenerAdd(Webfm.eventListeners, elResize, "mousedown", function (e) {
																						cp.dragStart(e, 0);
																					});
	pane.appendChild(footer);

	if (content) {
		this.fill(content);
	}
};
Webfm.pane.prototype.dragStart = function (event, move) {
	var cp;
	cp = this;
	
	Webfm.dragging = true;
	event = event || window.event;

	// Hide content - mousing over visible iframe can cause problems
	this.content.style.visibility = "hidden";
	this.popupCont.obj.style.zIndex = Webfm.zindex + 1;
	if (move == 1) {
		//Offset of cursor position form top/left corner of drag object on mousedown
		this.offset = this.popupCont.getObjectOffset(event, this.header);
		document.onmousemove = function (e) {
								cp.drag(e);
							};
		document.onmouseup = function (e) {
								cp.dragEnd(e);
							};
		this.drag(event);
	}
	else {
		//Start position of cursor on mousedown
		this.initPos = Drupal.mousePosition(event);
		//Start width and height of pane content on mousedown
		this.width = parseInt(this.pane.offsetWidth, 10);
		this.height = parseInt(this.content.offsetHeight, 10);
		document.onmousemove = function (e) {
								cp.resize(e);
							};
		document.onmouseup = function (e) {
								cp.resizeEnd(e);
							};
		Webfm.scrollVal = this.popupCont.getScrollY();
		this.resize(event);
	}
};
Webfm.pane.prototype.drag = function (event) {
	var pos, x, y, scroll;
	
	event = event || window.event;
	pos = Drupal.mousePosition(event);
	x = pos.x - this.offset.x;
	y = pos.y - this.offset.y;
	scroll = this.popupCont.getScrollY();//Scroll page if near top or bottom edge.  Hardcoded values
	
	if (typeof window.innerHeight == 'number') {
		if (pos.y > (window.innerHeight + scroll - 35)) {
			window.scrollBy(0, 20);
		}
		else if (pos.y  - scroll < (25)) {
			window.scrollBy(0, -20);
		}
	}
	else if (document.documentElement && document.documentElement.clientHeight) {
		if (pos.y > (document.documentElement.clientHeight + scroll - 35)) {
			window.scrollBy(0, 20);
		}
		else if (pos.y  - scroll < (25)) {
			window.scrollBy(0, -20);
		}
	}

	// Move our drag container to wherever the mouse is (adjusted by mouseOffset)
	this.popupCont.obj.style.top  = y + 'px';
	this.popupCont.obj.style.left = x + 'px';

	// Prevent selection of textnodes
	Webfm.stopEvent(event);
};

Webfm.pane.prototype.resize = function (event) {
	var pos, x, y;
	
	event = event || window.event;

	// Calculate distance from start position
	pos = Drupal.mousePosition(event);
	x = this.width + (pos.x - this.initPos.x);
	y = this.height + (pos.y - this.initPos.y);

	// Adjust pane width and height css with min size constraint
	this.pane.style.width = Math.max(x, 100) + "px";
	this.content.style.height = Math.max(y, 100) + "px";

	// Prevent selection of textnodes
	Webfm.stopEvent(event);
};

Webfm.pane.prototype.dragEnd = function (event) {
	// Store popup container top/left position global vars
	this.popupCont.top  = parseInt(this.popupCont.obj.style.top, 10);
	this.popupCont.left = parseInt(this.popupCont.obj.style.left, 10);
	this.end(event);
};

Webfm.pane.prototype.resizeEnd = function (event) {
	// Store popup container width/height size global vars
	this.popupCont.width = parseInt(this.pane.style.width, 10);
	this.popupCont.content_height = parseInt(this.content.style.height, 10);
	this.end(event);
	// counter browser scrolling
	setTimeout(function () {
							window.scrollTo(0, Webfm.scrollVal);
						}, 1);
};

Webfm.pane.prototype.end = function (event) {
	this.content.style.visibility = "visible";
	Webfm.dragging = false;
	document.onmousemove = null;
	document.onmouseup = null;
	Webfm.stopEvent(event);
};

Webfm.pane.prototype.headerMsg = function (string) {
	while (this.msg.hasChildNodes()) {
		this.msg.removeChild(this.msg.firstChild);
	}
	this.msg.appendChild(Webfm.ctn(string));
};

Webfm.pane.prototype.fill = function (content) {
	if (content) {
		this.content.appendChild(content);
	}
	this.show();
};

Webfm.pane.prototype.clear = function () {
	while (this.content.hasChildNodes()) {
		this.content.removeChild(this.content.firstChild);
	}
	while (this.msg.hasChildNodes()) {
		this.msg.removeChild(this.content.firstChild);
	}
};

Webfm.pane.prototype.hide = function () {
	this.popupCont.obj.style.display = 'none';
};

Webfm.pane.prototype.show = function () {
	this.popupCont.obj.style.zIndex = Webfm.zindex + 1;
	this.popupCont.obj.style.display = 'block';
};

/**
 * Webfm.draggable constructor
 */
Webfm.draggable = function (popupCont, element, d_class) {
	this.dragCont = popupCont;
	this.element = element;
	this.curpath = element.title;
	this.isTree = d_class.substring(0, 4) == 'tree';
	this.isDir = (d_class == 'dirrow') || this.isTree;
	this.isAttach = d_class == 'attachrow';
	this.icondir = getWebfmIconDir();
	this.comp = this.isTree ? 'tree' : 'list';
};

Webfm.draggable.prototype.mouseButton = function (event) {
	var rightclick;
	
	event = event || window.event;

	// Determine mouse button
	rightclick = Webfm.rclick(event);
	if (!rightclick && event.altKey === false) {
		this.dragStart(event);
	}
};

Webfm.draggable.prototype.dragStart = function (event) {
	var cp;
	
	Webfm.dragging = true;
	// Destroy all contents in popup container.
	this.dragCont.destroy();

	cp = this;
	document.onmousemove = function (e) {
							cp.drag(e); 
						};
	document.onmouseup = function (e) { 
							cp.dragEnd(e); 
						};

	// Make transparent
	this.element.style.opacity = 0.5;

	// Process
	this.drag(event);
};

Webfm.draggable.prototype.drag = function (event) {
	var cp, elDiv, dirListRows, dirListCont, k, class_names, droptarget, cont_pos, skip, droppath, curtemp, droptemp, children, dirTreeCont, j, pos, x, y, scroll, container, att_table_body, attachRows, prevNode, nextNode, curr, i, att_pos;
	
	cp = this;
	event = event || window.event;

	//Build Webfm.dropContainers array
	//The dragStart flag ensures that the following executes once only at the
	//beginning of a drag-drop
	if (!Webfm.dragStart) {
		Webfm.dragStart = true;

		//copy dragged element into drag container and make visible
		this.offset = this.dragCont.getObjectOffset(event, this.element, this.comp);
		if (Webfm.browser.isIE && !this.isTree) {
			/* IE cannot clone table rows */
			elDiv = Webfm.ce('div');
			elDiv.setAttribute('id', 'webfm-ieDD');
			elDiv.appendChild(this.element.getElementsByTagName('img')[0].cloneNode(false));
			elDiv.appendChild(this.element.getElementsByTagName('a')[0].firstChild.cloneNode(false));
			this.dragCont.obj.appendChild(elDiv);
		}
		else {
			this.dragCont.obj.appendChild(this.element.cloneNode(true));
		}
		this.dragCont.obj.style.display = 'block';
		this.dragCont.obj.style.zIndex = Webfm.zindex + 1;

		Webfm.dropContainers = [];

		// Build list drop container array
		if (!this.isAttach) {
			dirListRows = Webfm.dirListObj.obj.getElementsByTagName('tr');
			dirListCont = [];
			for (k = 0; k < dirListRows.length; k = k + 1) {
				if (dirListRows[k].className == 'dirrow') {
					dirListCont.push(dirListRows[k]);
				}
			}
			if (dirListCont.length) {
				for (i = 0; i < dirListCont.length; i = i + 1) {
					// DragDrop element is folder icon
					
					droptarget = dirListCont[i];
					cont_pos = this.dragCont.objPosition(droptarget, this.comp);
					skip = false;
					droppath = dirListCont[i].title;
					if (this.curpath == droppath) {
						continue;
					}
					if (this.isTree) {
						curtemp = [];
						curtemp = this.curpath.split('/');
						droptemp = [];
						droptemp = droppath.split('/');
						// Test if drop path is beneath the drag path
						for (j = 0; j < curtemp.length; j = j + 1) {
							if (curtemp[j] != droptemp[j]) {
								break;
							}
						}
						if (j == curtemp.length) {
							skip = true;
						}
						else {
							// Test if drop path is directly above drag path (already a subdir)
							for (j = 0; j < droptemp.length; j = j + 1) {
								if (curtemp[j] != droptemp[j]) {
									break;
								}
							}
							if ((j == droptemp.length) && (curtemp.length == j + 1)) {
								skip = true;
							}
						}
					}
					if (skip === false) {
						// Valid drop container
						container = { id: dirListCont[i].id, x: cont_pos.x, y: cont_pos.y, w: droptarget.offsetWidth, h: droptarget.offsetHeight };
						Webfm.dropContainers.push(container);
					}
				}
			}
			 
			dirTreeCont = '';
			dirTreeCont = Webfm.$("webfm-tree");
			if (dirTreeCont) {
				//reuse var for container list
				dirTreeCont = Webfm.$("webfm-tree").getElementsByTagName('li');
				if (dirTreeCont.length) {
					// Build tree drop container array
					for (i = 0; i < dirTreeCont.length; i = i + 1) {
						// DragDrop element is folder icon
						droptarget = dirTreeCont[i].getElementsByTagName('div')[0];
						cont_pos = this.dragCont.objPosition(droptarget, this.comp);
						skip = false;

						if (this.isTree) {
							// Prevent a directory drop onto itself or its direct parent li (already a sub-directory)
							if ((dirTreeCont[i] != this.element) && (dirTreeCont[i] != this.element.parentNode.parentNode)) {
								children = this.element.getElementsByTagName('li');
								// Prevent a directory drop into a sub-directory
								for (j = 0; j < children.length; j = j + 1) {
									if (children[j] == dirTreeCont[i]) {
										skip = true;
									}
								}
							}
							else {
								skip = true;
							}
						}
						else {
							droppath = dirTreeCont[i].title;
							// A regex would be preferable here
							if (this.curpath != droppath) {
								curtemp = [];
								curtemp = this.curpath.split('/');
								droptemp = [];
								droptemp = droppath.split('/');
								// Test if drop path is beneath the drag path
								for (j = 0; j < curtemp.length; j = j + 1) {
									if (curtemp[j] != droptemp[j]) {
										break;
									}
								}
								
								if (j == curtemp.length) {
									skip = true;
								}
								else {
									// Test if drop path is directly above drag path (already a subdir)
									for (j = 0; j < droptemp.length; j = j + 1) {
										if (curtemp[j] != droptemp[j]) {
											break;
										}
									}
									if ((j == droptemp.length) && (curtemp.length == j + 1)) {
										skip = true;
									}
								}
							}
							else {
								skip = true;
							}
						}

						if (skip === false) {
							// Valid drop container - add to array of drop containers
							container = { id: dirTreeCont[i].id, x: cont_pos.x, y: cont_pos.y, w: droptarget.offsetWidth, h: droptarget.offsetHeight };
							Webfm.dropContainers.push(container);
						}
					}
				}
			}
		}
		else {
			// attachment container is attach table body
			droptarget = Webfm.attachObj.body;
			cont_pos = this.dragCont.objPosition(droptarget, this.comp);
			Webfm.attachContainer = { x: cont_pos.x, y: cont_pos.y, w: droptarget.offsetWidth, h: droptarget.offsetHeight };
		}
	}

	pos = Drupal.mousePosition(event);
	x = pos.x; //- this.offset.x;
	y = pos.y; // - this.offset.y;
	scroll = this.dragCont.getScrollY();//Scroll page if near top or bottom edge.  Hardcoded values
	
	if (typeof window.innerHeight == 'number') {
		if (pos.y > (window.innerHeight + scroll - 35)) {
			window.scrollBy(0, 20);
		}
		else if (pos.y  - scroll < (25)) {
			window.scrollBy(0, -20);
		}
	}
	else if (document.documentElement && document.documentElement.clientHeight) {
		if (pos.y > (document.documentElement.clientHeight + scroll - 35)) {
			window.scrollBy(0, 20);
		}
		else if (pos.y  - scroll < (25)) {
			window.scrollBy(0, -20);
		}
	}

	// move our drag container to wherever the mouse is (adjusted by mouseOffset)
	this.dragCont.obj.style.top  = y + 'px';
	this.dragCont.obj.style.left = x + 'px';

	if (!this.isAttach) {
		Webfm.activeDropCont = null;
		if (Webfm.dropContainers.length) {
			// Compare mouse position to every drop container
			for (i = 0; i < Webfm.dropContainers.length; i = i + 1) {
				if ((Webfm.dropContainers[i].x < pos.x) &&
						(Webfm.dropContainers[i].y < pos.y) &&
						((Webfm.dropContainers[i].w + Webfm.dropContainers[i].x) > pos.x) &&
						((Webfm.dropContainers[i].h + Webfm.dropContainers[i].y) > pos.y)) {
					// Found a valid drop container - Highlight selection
					Webfm.activeDropCont = Webfm.$(Webfm.dropContainers[i].id);
					Webfm.$(Webfm.dropContainers[i].id + 'dd').src = this.icondir + '/open.gif';
					Webfm.$(Webfm.dropContainers[i].id).className += ' selected';
				}
				else {
					// De-highlight container
					Webfm.$(Webfm.dropContainers[i].id + 'dd').src = this.icondir + '/d.gif';					
					class_names  = [];
					class_names = Webfm.$(Webfm.dropContainers[i].id).className.split(' ');
					Webfm.$(Webfm.dropContainers[i].id).className = class_names[0];
				}
			}
		}
	} else {
		// Ignore all movement outside of the attach table
		if ((Webfm.attachContainer.x < pos.x) &&
			(Webfm.attachContainer.y < pos.y) &&
			((Webfm.attachContainer.w + Webfm.attachContainer.x) > pos.x) &&
			((Webfm.attachContainer.h + Webfm.attachContainer.y) > pos.y)
			) {

			// In attach container
			att_table_body = Webfm.attachObj.body;
			attachRows = att_table_body.getElementsByTagName('TR');
			prevNode = '';
			nextNode = '';
			curr = false;
			// Start at 1 since first row is header
			for (i = 1; i < attachRows.length; i = i + 1) {
				if (this.element.id != attachRows[i].id) {
					att_pos = this.dragCont.objPosition(attachRows[i], this.comp);
					
					if ((att_pos.y + (attachRows[i].offsetHeight / 2)) < pos.y) {
						if (curr === true) {
							prevNode = attachRows[i];
						}
					}
					else if ((att_pos.y + (attachRows[i].offsetHeight / 2)) > pos.y) {
						if (curr === false) {
							nextNode = attachRows[i];
						}
					}
				}
				else {
					curr = true;
				}
			}
			if (prevNode) {
				att_table_body.insertBefore(prevNode, this.element);
			}
			else if (nextNode) {
				att_table_body.insertBefore(this.element, nextNode);
			}
		}
	}

	//prevent selection of textnodes
	Webfm.stopEvent(event);
};

Webfm.draggable.prototype.dragEnd = function (event) {
	var class_names, url, postObj, elInput, attachRows, i, fid;
	
	event = event || window.event;

	// Restore opacity
	this.element.style.opacity = 1.0;
	Webfm.dragging = false;
	Webfm.dragStart = false;
	this.dragCont.obj.style.display = 'none';
	Webfm.stopEvent(event);

	if (!this.isAttach) {
		// Move dragged object if a valid drop container
		if (Webfm.activeDropCont) {
			this.droppath = Webfm.activeDropCont.title;
			// De-highlight container
			Webfm.$(Webfm.activeDropCont.id + 'dd').src = this.icondir + '/d.gif';
			class_names = [];
			class_names = Webfm.activeDropCont.className.split(' ');
			Webfm.activeDropCont.className = class_names[0];
			url = Webfm.ajaxUrl();
			Webfm.progressObj.show(Webfm.js_msg.work,  "blue");
			postObj = { action: encodeURIComponent("move"), param0: encodeURIComponent(this.curpath), param1: encodeURIComponent(this.droppath) };
			Webfm.HTTPPost(url, this.callback, this, postObj);
			Webfm.activeDropCont = null;
		}
	}
	else {
		// Put the current order of attachments into the form in preparation for submit
		elInput = Webfm.$('edit-attachlist');
		attachRows = Webfm.attachObj.body.getElementsByTagName('tr');
		elInput.setAttribute('value', '');
		// Ignore 1st row (header)
		for (i = 1; i < attachRows.length; i = i + 1) {
			fid = attachRows[i].id.substring(3);
			elInput.setAttribute('value', (elInput.getAttribute('value') ? elInput.getAttribute('value') + ',':'') + fid);
		}
	}

	// Empty drag container + uncapture mouse.
	document.onmousemove = null;
	document.onmouseup = null;
	this.dragCont.destroy();
};

Webfm.draggable.prototype.callback = function (string, xmlhttp, cp) {
	var result;
	
	Webfm.progressObj.hide();
	Webfm.alrtObj.msg();
	if (xmlhttp.status == 200) {
		result = Drupal.parseJson(string);
		//Webfm.dbgObj.dbg("move result:", Webfm.dump(result));
		if (result.status) {
			//flush cache for target dir and source dir of object
			Webfm.dirListObj.cache.remove(cp.droppath);
			Webfm.dirListObj.cache.remove(cp.curpath.substring(0, cp.curpath.lastIndexOf("/")));
			//update tree if directory is moved
			if (cp.isDir) {
				Webfm.dirTreeObj.fetch(true);
			}
			if (!cp.isTree) {
				//listing draggable - update current listing by removing dropped item
				cp.element.parentNode.removeChild(cp.element);
			}
			else {
				//tree draggable - update target directory
				Webfm.dirListObj.fetch(cp.droppath);
			}
		}
		if (result.data) {
			Webfm.alrtObj.str_arr(result.data);
		}
		else {
			Webfm.alrtObj.msg(Webfm.js_msg.moveerr);
		}
	}
	else {
		Webfm.alrtObj.msg(Webfm.js_msg.ajaxerr);
	}
};

/**
 * Event Handler adapted from http://ajaxcookbook.org (Creative Commons Attribution 2.5)
 */
Webfm.eventListenerAdd = function (listener_arr, instance, eventName, listener) {
	var listenerFn, event;
	listenerFn = listener;
	
	if (instance.addEventListener) {
		instance.addEventListener(eventName, listenerFn, false);
	}
	else if (instance.attachEvent) {
		listenerFn = function () {
						listener(window.event);
					};
		instance.attachEvent("on" + eventName, listenerFn);
	}
	else {
		throw new Error("Event registration not supported");
	}
	event = {
			instance: instance,
			name: eventName,
			listener: listenerFn
		};
	listener_arr.push(event);
	
	return event;
};

Webfm.eventListenerRemove = function (event, listener_arr) {
	var i, instance;
	i = 0;
	instance = event.instance;
	
	if (instance.removeEventListener) {
		instance.removeEventListener(event.name, event.listener, false);
	}
	else if (instance.detachEvent) {
		instance.detachEvent("on" + event.name, event.listener);
	}
	
	for (i = 0; i < listener_arr.length; i = i + 1) {
		if (listener_arr[i] == event) {
			listener_arr.splice(i, 1);
			break;
		}
	}
};

//remove local event listeners
Webfm.eventUnregister = function (listener_arr) {
	while (listener_arr.length > 0) {
		Webfm.eventListenerRemove(listener_arr[0], listener_arr);
	}
	listener_arr = [];
};

//remove global events at window.onunload
Webfm.eventUnregisterWebfm = function () {
	while (Webfm.eventListeners.length > 0) {
		Webfm.eventListenerRemove(Webfm.eventListeners[0], Webfm.eventListeners);
	}
	Webfm.eventListeners = [];
};

/**
 * Helper Functions
 */
Webfm.$ = function (id) {
			return document.getElementById(id);
		};
		
Webfm.ctn = function (textNodeContents) {
				var textNode = document.createTextNode(textNodeContents);
				return textNode;
			};
			
Webfm.ce = function (elementName) {
				var element;
				elementName = elementName.toLowerCase();
				element = document.createElement(elementName);
				
				return element;
			};

/**
 * Creates an HTTP POST request and sends the response to the callback function
 *
 * Note: passing null or undefined for 'object' makes the request fail in Opera 8.
 *       Pass an empty string instead.
 */
Webfm.HTTPPost = function (uri, callbackFunction, callbackParameter, object) {
	var xmlHttp, bAsync, toSend, i;
	bAsync = true;
	toSend = '';

	if (window.XMLHttpRequest) {
		// If IE7, Mozilla, Safari, etc: Use native object
		xmlHttp = new XMLHttpRequest();
	}
	else {
		if (window.ActiveXObject) {
			// ...otherwise, use the ActiveX control for IE5.x and IE6
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
	}

	if (!callbackFunction) {
		bAsync = false;
	}

	xmlHttp.open('POST', uri, bAsync);

	if (typeof object == 'object') {
		xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		xmlHttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
		for (i in object) {
			toSend += (toSend ? '&' : '') + i + '=' + encodeURIComponent(object[i]);
		}
	}
	else {
		toSend = object;
	}

	xmlHttp.send(toSend);

	if (bAsync) {
		xmlHttp.onreadystatechange = function () {
			if (xmlHttp.readyState == 4) {
				callbackFunction(Webfm.trim(xmlHttp.responseText), xmlHttp, callbackParameter);
			}
		};
		return xmlHttp;
	}
	else {
		return Webfm.trim(xmlHttp.responseText);
	}
};

/**
 * Prevents an event from propagating.
 */
Webfm.stopEvent = function (event) {
	event = event || window.event;
	if (event.preventDefault) {
		event.preventDefault();
		event.stopPropagation();
	}
	else {
		event.returnValue = false;
		event.cancelBubble = true;
	}
};

//the getModUrl() is used by contrib modules for custom ajax
Webfm.ajaxUrl = function () {
	var path = getBaseUrl() + "/?q=";
	path += (typeof getModUrl == "undefined") ? "webfm_js" : getModUrl();
	return path;
};

//Empty all child nodes
Webfm.clearNodeById = function (elementId) {
	var node = Webfm.$(elementId);
	while (node.hasChildNodes()) {
		node.removeChild(node.firstChild);
	}
};

//Sort methods
Webfm.sortByName = function (a, b) {
	var x, y;
	
	if (typeof a.ftitle != "undefined") {
		x = a.ftitle.toLowerCase();
	}
	else {
		x = a.n.toLowerCase();
	}
	
	if (typeof b.ftitle != "undefined") {
		y = b.ftitle.toLowerCase();
	}
	else {
		y = b.n.toLowerCase();
	}
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
};
Webfm.sortBySize = function (a, b) {
	var x, y;
	
	x = parseInt(parseFloat(a.s), 10);
	y = parseInt(parseFloat(b.s), 10);
	return x - y;
};
Webfm.sortByModified = function (a, b) {
	var x, y;
	
	x = a.m;
	y = b.m;
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
};
Webfm.sortByOwner = function (a, b) {
	var x, y;
	
	if (typeof a.funame != "undefined") {
		x = a.funame.toLowerCase();
	}
	else {
		x = a.un.toLowerCase();
	}
	
	if (typeof b.funame != "undefined") {
		y = b.funame.toLowerCase();
	}
	else {
		y = b.un.toLowerCase();
	}
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
};
Webfm.sortByKey = function (a, b) {
	var x, y;
	
	x = a.toLowerCase();
	y = b.toLowerCase();
	return ((x < y) ? -1 : ((x > y) ? 1 : 0));
};

//Build date stamp
Webfm.convertunixtime = function (unixtime) {
	var c_date, c_min, c_hours, c_day, c_mon, c_year, format;
	// unix date format doesn't have millisec component
	c_date = new Date(unixtime * 1000);

	c_min = c_date.getMinutes();
	c_hours = c_date.getHours();
	c_day = c_date.getDate();
	c_mon = c_date.getMonth() + 1;
	c_year = c_date.getFullYear();

	c_min = Webfm.doubleDigit(c_min);
	c_hours = Webfm.doubleDigit(c_hours);
	c_day = Webfm.doubleDigit(c_day);
	c_mon = Webfm.doubleDigit(c_mon);
	
	if (c_year > 1999) {
		c_year -= 2000;
	}
	else {
		c_year -= 1900;
	}
	c_year = Webfm.doubleDigit(c_year);

	// Get day/month order from db variable
	format = getWebfmDateFormat();
	if (format == 1) {
		return c_day + "/" + c_mon + "/" + c_year + " " + c_hours + ":" + c_min;
	}
	else {
		return c_mon + "/" + c_day + "/" + c_year + " " + c_hours + ":" + c_min;
	}
};
Webfm.doubleDigit = function (num) {
	return (num < 10) ? "0" + num : num;
};

Webfm.size = function (sz) {
	var size, units;
	size = sz;

	if (size < 1024) {
		units = " B";
	}
	else {
		size = parseInt(size >> 10, 10);
		if (size < 1024) {
			units = " KB";
		}
		else {
			size = parseInt(size >> 10, 10);
			if (size < 1024) {
				units = " MB";
			}
			else {
				size = parseInt(size >> 10, 10);
				if (size < 1024) {
					units = " GB";
				}
				else {
					size = parseInt(size >> 10, 10);
					units = " TB";
				}
			}
		}
	}
	return size + units;
};

Webfm.enter = function (event) {
	var code;
	
	event = event || window.event;
	if (event.keyCode) {
		code = event.keyCode;
	}
	else if (event.which) {
		code = event.which;
	}
	return (code == 13) ? true : false;
};

Webfm.rclick = function (event) {
	var rightclick;
	if (event.which) {
		rightclick = (event.which == 3);
	} else {
		if (event.button) {
			rightclick = (event.button == 2);
		}
	}
	return rightclick;
};

//Dump debug function
//return a string version of a thing, without name.
//calls recursive function to actually traverse content.
Webfm.dump = function (content, txt_arr) {
	if (txt_arr == 'undefined') {
		txt_arr = false;
	}
	return Webfm.dumpRecur(content, 0, true, txt_arr) + "\n";
};

//recursive function traverses content, returns string version of thing
//content: what to dump.
//indent: how far to indent.
//neednl: true if need newline, false if not
Webfm.dumpRecur = function (content, indent, neednl, txt_arr) {
	var out, inside, i;
	out = "";

	if (typeof content == 'function') {
		return 'function';
	}
	else if (Webfm.isArray(content)) {  // handle real arrays in brackets
		if (neednl) {
			out += "\n" + Webfm.dumpSpaces(indent);
		}
		if (!txt_arr) {
			out += "[ ";
		}
		
		inside = false;
		for (i = 0; i < content.length; i = i + 1) {
			if (inside) {
				out += " \n" + Webfm.dumpSpaces(indent + 1);
			}
			else {
				inside = true;
			}
			out += Webfm.dumpRecur(content[i], indent + 1, false, txt_arr);
		}
		out += "\n" + Webfm.dumpSpaces(indent);
		if (!txt_arr) {
			out += "]";
		}
	}
	else if (Webfm.isObject(content)) {   // handle objects by association
		if (neednl) {
			out += "\n" + Webfm.dumpSpaces(indent);
		}
		if (!txt_arr) {
			out += "{ ";
		}
		inside = false;
		for (i in content) {
			if (content.hasOwnProperty(i)) {
				if (inside) {
					out += " \n" + Webfm.dumpSpaces(indent + 1);
				}
				else {
					inside = true;
				}
				out += "'" + i + "':" + Webfm.dumpRecur(content[i], indent + 1, true, txt_arr);
			}
		}
		out += "\n" + Webfm.dumpSpaces(indent);
		if (!txt_arr) {
			out += "}";
		}
	}
	else if (typeof content == 'string') {
		out += "'" + content + "'";
	}
	else {
		out += content;
	}
	return out;
};

//print n groups of two spaces for indent
Webfm.dumpSpaces = function (n) {
	var out, i;
	out = '';
	for (i = 0; i < n; i = i + 1) {
		out += '  ';
	}
	return out;
};

Webfm.isArray = function (array) {
	return (array && array.constructor == Array);
};
Webfm.isObject = function (object) {
	return (object && object.constructor == Object);
};

//Unregister all global listener events
window.onunload = Webfm.eventUnregisterWebfm;

///////////////////////////////////////////////////////ADD IN LEGACY DRUPAL JS FUNCTIONS USED BY WEBFM////////////////////////////////////////////////
/**
 * Redirects a button's form submission to a hidden iframe and displays the result
 * in a given wrapper. The iframe should contain a call to
 * window.parent.iframeHandler() after submission.
 */
Drupal.redirectFormButton = function (uri, button, handler) {
	var action, target, response, iframe;
	
	// Trap the button
	button.onmouseover = button.onfocus = function () {
		button.onclick = function () {
			// Create target iframe
			Drupal.createIframe();

			// Prepare variables for use in anonymous function.
			button = this;
			action = button.form.action;
			target = button.form.target;

			// Redirect form submission to iframe
			this.form.action = uri;
			this.form.target = 'redirect-target';

			handler.onsubmit();

			// Set iframe handler for later
			window.iframeHandler = function () {
				iframe = $('#redirect-target').get(0);
				// Restore form submission
				button.form.action = action;
				button.form.target = target;

				// Get response from iframe body
				try {				
					response = (iframe.contentWindow || iframe.contentDocument || iframe).document.body.innerHTML;
					// Firefox 1.0.x hack: Remove (corrupted) control characters
					response = response.replace(/[\f\n\r\t]/g, ' ');
					if (window.opera) {
						// Opera-hack: it returns innerHTML sanitized.
						response = response.replace(/&quot;/g, '"');
					}
				}
				catch (e) {
					response = null;
				}
				response = Drupal.parseJson(response);
				// Check response code
				if (response.status === 0) {
					handler.onerror(response.data);
					return;
				}
				handler.oncomplete(response.data);

				return true;
					
			};

			return true;
		};
	};
	button.onmouseout = button.onblur = function () {
		button.onclick = null;
	};
};

/**
 * Create an invisible iframe for form submissions.
 */
Drupal.createIframe = function () {
	var div, iframe;
	
	if ($('#redirect-holder').size()) {
		return;
	}
	
	// Note: some browsers require the literal name/id attributes on the tag,
	// some want them set through JS. We do both.
	window.iframeHandler = function () {};
	
	div = document.createElement('div');
	div.id = 'redirect-holder';
	$(div).html('<iframe name="redirect-target" id="redirect-target" class="redirect" onload="window.iframeHandler();"></iframe>');
	iframe = div.firstChild;
	$(iframe).attr({
			name: 'redirect-target',
			id: 'redirect-target'
		});
	$(iframe).css({
			position: 'absolute',
			height: '1px',
			width: '1px',
			visibility: 'hidden'
		});
	$('body').append(div);
};

/**
 * Delete the invisible iframe
 */
Drupal.deleteIframe = function () {
	$('#redirect-holder').remove();
};

/**
 *  Returns the position of the mouse cursor based on the event object passed
 */
Drupal.mousePosition = function (e) {
	if (Webfm.browser.isIE)
	{
		return { x: e.clientX + document.documentElement.scrollLeft, y: e.clientY + document.documentElement.scrollTop };
	}
	else
	{
		return { x: e.pageX, y: e.pageY };
	}
};