/* Copyright (c) 2008 Extentech Inc. All Rights Reserved

##### Sheetster&trade; Web Application #####

This file is a part of the Sheetster&trade; Web Application

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

If you would like to redistribute this software in a closed-source application,
dual-licensed commercial versions are available from Extentech.

For a fully supported and redistributable commercial license, 
please visit www.extentech.com or contact us at:

 sales@extentech.com
 Extentech Inc.
 1032 Irving Street #910
 San Francisco, CA 94122
 415-759-5292
 
*/
/*
Site-wide Javascript Utils

	Event Attaching		_/
	Windowing / Dialog
	Browser Detection 
	Element Addition  _/
	Element Position _/
	Debug/Logging
	Constants	

*/

/******* VARIABLES *********/

var gstrActiveLayer = ""
var is;


/*
 * check to see if a font exists on this system
 * 
 * Checkfont class attribution:
 * http://www.lalit.org/lab/javascript-css-font-detect
 * 
 * 
 */
var Checkfont = Class.create({
    checkedFonts: new Hash(),
    testString: 'mmmmmmmmmmlilg',
    testFontSize: '72px',
    standardFont: 'serif',
 
    initialize: function() {
        this.span = new Element('span').hide();
 
        try{
        	document.body.insert(this.span);
        }catch(x){
        	// showError("toolkit.initialize failed: " + x.toString()); // fails in IE sometimes... what is the implication?
        }
        this.defaultDimensions = this._getFontDimensions(this.standardFont);
    },
 
    test: function(font) {
        font = font.toLowerCase();
      //  alert('testing: ' + font);
        var alreadyChecked = this.checkedFonts.get(font);
        if (!Object.isUndefined(alreadyChecked)) {
            return alreadyChecked;
        }
 
        this.testDimensions = this._getFontDimensions(font);
 
        var test = true;
        if (font != this.standardFont) {
            test = (this.testDimensions.width != this.defaultDimensions.width
                || this.testDimensions.height != this.defaultDimensions.height);
        }
        this.checkedFonts.set(font, test);
        return test;
    },
 
    _getFontDimensions: function(font) {
        this.span.setStyle({
            fontFamily: font,
            fontSize: this.testFontSize
        }).update(this.testString);
        var fontDimensions = this.span.getDimensions();
        this.span.update();
        return fontDimensions;
    }
});

/**
 * populate the ribbon menu font list
 */
function populateFontList(){
	var ftx = getFontList();
	var fsel = ftx; //  Object.toJSON(ftx);
	// alert(fsel);
	var fontDrop = $('fontFamily');
	
	setDropDown(
			fontDrop,
			fsel,
			function(e) {  
			     var opt =  new Option( e[1], e[1] );
			     opt.setStyle({'fontFamily':e[1],'fontSize':'12pt'});
			     return opt;
			   }
		   );
	
}

/**
 * get the font list dynamically
 */
function getFontList(){
	var check = new Checkfont();
	var fonts = new Array();
	var fontlist = ["Abadi",
	                "Abracadabra",
	                "Ad Lib",
	                "Adobe Jenson",
	                "Agency FB",
	                "Aharoni",
	                "Akzidenz Grotesk",
	                "Al Bayan",
	                "Albertus",
	                "Aldus",
	                "Alexandria",
	                "Algerian",
	                "Allegro",
	                "American Text",
	                "American Typewriter",
	                "Amienne",
	                "AMS Euler",
	                "Amsterdam Old Style",
	                "Andale Mono",
	                "Andale Mono",
	                "Andalus",	                
	                "Andreas",
	                "Andy",
	                "Antiqua",
	                "Apex",
	                "Apple Casual",
	                "Apple Chancery",
	                "Apple Chancery",
	                "Apple Garamond",
	                "Apple Gothic",
	                "Apple LiGothic",
	                "Apple LiSung",
	                "Apple Myungjo",
	                "Apple Symbols",
	                "Aptifer",
	                "AquaKana",
	                "Aquiline",
	                "Arial",
	                "Arial Hebrew",
	                "Arial Unicode MS",
	                "Arial Monospaced",
	                "Aristocrat",
	                "Arno",
	                "Arnold Bricklin",
	                "Ashley Script",
	                "Aster",
	                "Astur",
	                "Aurora",
	                "Avant Garde Gothic",
	                "Avenir",
	                "Ayuthaya",
	                "Baghdad",
	                "Balloon",
	                "Balloon Pop Outlaw Black",
	                "Banco",
	                "Bank Gothic",
	                "Barmeno",
	                "Baskerville",
	                "Baskerville",
	                "Batak Script",
	                "Batang",	                
	                "Bauer Bodoni",
	                "Bauhaus",
	                "Bauhaus",
	                "Beijing",
	                "Bell Centennial",
	                "Bell Gothic",
	                "Bell",
	                "Bembo",
	                "Bembo Schoolbook",
	                "Benguiat",
	                "Benguiat Gothic",
	                "Berkeley Old Style",
	                "Berlin Sans",
	                "Bernhard Modern",
	                "Berthold City",
	                "Beteckna",
	                "BiauKai",
	                "Bickley Script",
	                "Big Caslon",
	                "Bitstream Vera",
	                "Blue Highway",
	                "Bodoni",
	                "Book Antiqua",
	                "Bookman",
	                "Bordeaux Roman",
	                "Braggadocio",
	                "Broadway",
	                "Brush Script",
	                "Brush Script",
	                "Cafeteria",
	                "Caflisch Script",
	                "Calibri",
	                "Californian FB",
	                "Calisto",
	                "Calvert",
	                "Cambria",
	                "Capitals",
	                "Cartier",
	                "Caslon",
	                "Caslon Antique",
	                "Caslon Antique / Fifteenth Century",
	                "Catull",
	                "Centaur",
	                "Century Gothic",
	                "Century Old Style",
	                "Century Schoolbook",
	                "Century Schoolbook Infant",
	                "Cezanne",
	                "Chalkboard",
	                "Chandas",
	                "Chaparral",
	                "Charcoal",
	                "Charcoal CY",
	                "Charcoal",
	                "Charis SIL",
	                "Cheltenham",
	                "Chicago",
	                "Chiller",
	                "Choc",
	                "Cholla Slab",
	                "Cinderella",
	                "Civitype",
	                "Clarendon",
	                "Clearface",
	                "Clearface Gothic",
	                "Clearview",
	                "Cloister Black",
	                "Co Headline",
	                "Co Text",
	                "Cochin",
	                "Cochin",
	                "Codex",
	                "Colonna",
	                "Comic Sans",
	                "Computer Modern",
	                "Concrete Roman",
	                "Consolas",
	                "Constantia",
	                "Cooper",
	                "Cooper Black",
	                "Cooper Black",
	                "Copperplate",
	                "Corbel",
	                "Corona",
	                "Corsiva Hebrew",
	                "Courier",
	                "Courier New",
	                "CourierHP",
	                "CourierPS",
	                "Cupola",
	                "Curlz",
	                "Dax",
	                "DecoType Naskh",
	                "DejaVu Sans",
	                "DejaVu Sans Mono",
	                "DejaVu Serif",
	                "Denmark",
	                "Devanagari",
	                "Didot",
	                "Divona",
	                "Dom Casual",
	                "Dotum",
	                "Dragonwick",
	                "Droid",
	                "Droid Sans Mono",
	                "Ecotype",
	                "Edwardian Script",
	                "Egyptienne",
	                "Elephant",
	                "Ellington",
	                "Eras",
	                "Espy Sans",
	                "Espy Serif",
	                "Eupheima UCAS",
	                "Eurocrat",
	                "Eurostile",
	                "Eurostyle",	                
	                "Everson Mono",
	                "Excelsior",
	                "Exocet",
	                "Fairfield",
	                "Fang Song",
	                "Fedra Mono",
	                "FF Meta",
	                "FF Scala",
	                "FF Scala Sans",
	                "FIG Script",
	                "Fixed",
	                "Fixedsys",
	                "Fixedsys Excelsior",
	                "Flama",
	                "Folkard",
	                "Fontcraft Courier",
	                "Fontoon",
	                "Footlight",
	                "Formata BQ",
	                "Forte",
	                "Fraktur",
	                "Franklin Gothic",
	                "FreeSans",
	                "FreeSerif",
	                "French Script",
	                "Friz Quadrata",
	                "Frutiger",
	                "Frutiger NEXT",
	                "Futura",
	                "Gadget",
	                "Garamond",
	                "Geeza Pro",
	                "Geezah",
	                "Geneva",
	                "Geneva CY",
	                "Gentium",
	                "Georgia",
	                "Gigi",
	                "Gill Sans",
	                "Gill Sans Schoolbook",
	                "Gloucester",
	                "Gotham",
	                "Goudy Old Style/Goudy",
	                "Goudy Pro Font",
	                "Goudy Schoolbook",
	                "Goudy Text",
	                "Granjon",
	                "Guardian Egyptian",
	                "Gujarati",
	                "Gung Seoche",
	                "Gurmukhi",
	                "Haettenschweiler",
	                "Hanacaraka",
	                "Handel Gothic",
	                "Hangangche",
	                "Harlow Solid",
	                "Harrington",
	                "HeadlineA",
	                "Heather",
	                "Hei",
	                "Helvetica",
	                "Helvetica CY",
	                "Helvetica Neue",
	                "Helvetica Neue",
	                "Herculanum",
	                "Hercules",
	                "High Tower Text",
	                "Highway Gothic",
	                "Hiragino Kaku Gothic Pro",
	                "Hiragino Kaku Gothic ProN",
	                "Hiragino Kaku Gothic Std",
	                "Hiragino Kaku Gothic StdN",
	                "Hiragino Maru Gothic Pro",
	                "Hiragino Maru Gothic ProN",
	                "Hiragino Mincho Pro",
	                "Hiragino Mincho ProN",
	                "Hiroshige",
	                "Hiroshige Sans",
	                "Hobo",
	                "Hoefler Text",
	                "Hoefler Text",
	                "Horizon",
	                "Humana Serif",
	                "Hyborean",
	                "HyperFont",
	                "Impact",
	                "Imprint",
	                "Inai Mathi",
	                "Inconsolata",
	                "Industria",
	                "Interstate",
	                "Ionic No. 5",
	                "Janson",
	                "Japanese Gothic",
	                "Jefferson",
	                "Jenson",
	                "Joanna",
	                "Johnston",
	                "Jokerman",
	                "Jomolhari Font",
	                "Juice",
	                "Jung Gothic",
	                "Kabel",
	                "Kai",
	                "Keyboard",
	                "Kochi",
	                "Korinna",
	                "Kristen",
	                "Krungthep",
	                "Kuenstler Script",
	                "KufiStandard GK",
	                "LastResort",
	                "Legacy Sans",
	                "Legacy Serif",
	                "Letter Gothic",
	                "Lexicon",
	                "Liberation Mono",
	                "Liberation Sans",
	                "Liberation Serif",
	                "LiHei Pro",
	                "Linux Libertine",
	                "LiSong Pro",
	                "Literaturnaya",
	                "Lontara Script",
	                "Lo-Type",
	                "Lucida Blackletter",
	                "Lucida Bright",
	                "Lucida Console",
	                "Lucida Grande",
	                "Lucida Handwriting",
	                "Lucida Sans Typewriter",
	                "lucida sans unicode",
	                "Lucida Sans",
	                "Little Lord Fontleroy",
	                "Lucida Typewriter",
	                "Magneto",
	                "Magnificat",
	                "Marker Felt",
	                "Megadeth",
	                "Meiryo",
	                "Melior",
	                "Memphis",
	                "Menlo",
	                "MICR",
	                "Microgramma",
	                "Miller",
	                "Minch??",
	                "Ming",
	                "Minion",
	                "Miriam Fixed",
	                "Mistral",
	                "Modern",
	                "Mona",
	                "Mona Lisa",
	                "Monaco",
	                "Monaco CY",
	                "Monaco",
	                "Monofur",
	                "Monospace",
	                "Monotype Corsiva",
	                "Motorway",
	                "Mrs Eaves",
	                "MS Gothic",
	                "MS Mincho",
	                "MS Sans Serif",
	                "MS Serif",
	                "Mshtakan",
	                "Museo Sans",
	                "Museo Slab",
	                "Myriad",
	                "Nadeem",
	                "Nastaliq Navees",
	                "Neuland",
	                "New Century Schoolbook",
	                "New Peninim",
	                "New York",
	                "New York",
	                "News 701",
	                "News 702",
	                "News 705",
	                "News 706",
	                "News Gothic",
	                "Nimbus Mono L",
	                "Nimbus Roman",
	                "Nimbus Sans L",
	                "Nina",
	                "NISC GB18030",
	                "NPS Rawlinson Roadway",
	                "Nu Sans",
	                "OCR-A",
	                "OCR-B",
	                "Old English Text",
	                "Old English Text MT",
	                "Optima",
	                "Orator",
	                "Ormaxx",
	                "Osaka",
	                "Palace Script",
	                "Palatino",
	                "Palatino",
	                "Papyrus",
	                "Parisine",
	                "Park Avenue",
	                "PC Myungjo",
	                "Peignot",
	                "Perpetua",
	                "Perpetua Greek",
	                "Pilgiche",
	                "Plantagenet Cherokee",
	                "Plantin",
	                "Plantin Schoolbook",
	                "Playbill",
	                "Poor Richard",
	                "Portobello",
	                "Prestige Elite",
	                "Pricedown",
	                "Prima Sans",
	                "ProFont",
	                "Proggy Programming Fonts",
	                "PT Sans",
	                "Raanana",
	                "Rage Italic",
	                "Rail Alphabet",
	                "Ravie",
	                "Rawlinson Roadway",
	                "Renault",
	                "Requiem",
	                "Revue",
	                "Rockwell",
	                "Roman",
	                "Rotis Sans",
	                "Rotis Semi Serif",
	                "Rotis Serif",
	                "Rufscript",
	                "Sabon",
	                "San Francisco",
	                "Sand",
	                "Sathu",
	                "Scala",
	                "Scala Sans",
	                "Scribble",
	                "Script",
	                "Scriptina",
	                "Seagull",
	                "Segoe Script",
	                "Segoe UI",
	                "Seoul",
	                "Shelley Volante",
	                "Shin Myungjo Neue",
	                "Showcard Gothic",
	                "Silom",
	                "SimSun",
	                "Sistina",
	                "Skia",
	                "Small Fonts",
	                "Snap",
	                "Song",
	                "Soupbone",
	                "Souvenir",
	                "Souvenir Gothic",
	                "Square 721",
	                "ST FangSong",
	                "ST Heiti",
	                "ST Kaiti",
	                "ST Song",
	                "Stencil",
	                "Stone Informal",
	                "Stone Sans",
	                "Stone Serif",
	                "Sundanese Unicode",
	                "Swiss 721",
	                "Sydnie",
	                "Sylfaen",
	                "Symbol",
	                "Syntax",
	                "Tae Graphic",
	                "Tahoma",
	                "Tai Le Valentinium",
	                "Taipei",
	                "Techno",
	                "Tekton",
	                "Tema Cantante",
	                "Tempus Sans",
	                "Tengwar Noldor",
	                "Tengwar Quenya",
	                "Tengwar Sindarin",
	                "Terminal",
	                "Terminus",
	                "Tex Gyre Cursor",
	                "Textile",
	                "Thonburi",
	                "Tibetan Machine Uni",
	                "Times",
	                "Times CY",
	                "Times New Roman",
	                "Times New Roman",
	                "Tiresias",
	                "Trade Gothic",
	                "Trajan",
	                "Transport",
	                "Trebuchet",
	                "Trinity",
	                "Trump Gothic",
	                "Trump Mediaeval",
	                "Tunga",
	                "Twentieth Century",
	                "Ubuntu",
	                "UM Typewriter",
	                "Umbra",
	                "Univers",
	                "Utopia",
	                "Vale Type",
	                "Vera Sans",
	                "Vera Sans Mono",
	                "Vera Serif",
	                "Verdana",
	                "Versailles",
	                "Virtue",
	                "Vivaldi",
	                "Vladimir Script",
	                "Wadalab",
	                "Wanted",
	                "Wedding Text",
	                "Weiss",
	                "Westminster",
	                "Wide Latin",
	                "William Monospace",
	                "Willow",
	                "Windsor",
	                "Wyld",
	                "Year Supply of Fairy Cakes",
	                "Zapf Chancery",
	                "Zapf Dingbats",
	                "Zapfino",
	                "Zurich"];
	
	for (x=0;x< fontlist.length; x++) {
		var value = fontlist[x];
		if(check.test(value))
			fonts.push(['font',value]);
	};
	
	return fonts;

}

// from:
// http://joezack.com/index.php/2009/11/16/populating-select-boxes-with-prototype/
function setDropDown(field, data, method, index) {  
	if(typeof(field.options) == 'undefined'){
		field = $(field);
	}
   field.options.length = index == null ? 1 : index;  
   data.each(  
    function(e) {  
      field.options.add(method(e));  
   }  
 );  
}  

// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function encode64(input) {
	var output;
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	while (i < input.length) {
		chr1 = input.charCodeAt(i++);
		chr2 = input.charCodeAt(i++);
		chr3 = input.charCodeAt(i++);

		enc1 = chr1 >> 2;
		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		enc4 = chr3 & 63;

		if (isNaN(chr2)) {
			enc3 = enc4 = 64;
		} else if (isNaN(chr3)) {
			enc4 = 64;
		}

		output += (keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4));
   }
   
   return output;
}

function decode64(input) {
	var output = new StringMaker();
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
	input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

	while (i < input.length) {
		enc1 = keyStr.indexOf(input.charAt(i++));
		enc2 = keyStr.indexOf(input.charAt(i++));
		enc3 = keyStr.indexOf(input.charAt(i++));
		enc4 = keyStr.indexOf(input.charAt(i++));

		chr1 = (enc1 << 2) | (enc2 >> 4);
		chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
		chr3 = ((enc3 & 3) << 6) | enc4;

		output += (String.fromCharCode(chr1));

		if (enc3 != 64) {
			output += (String.fromCharCode(chr2));
		}
		if (enc4 != 64) {
			output += (String.fromCharCode(chr3));
		}
	}

	return output;
}



/**
 * http://www.shawnolson.net
 *
 * @param theClass
 * @param element
 * @param value
 * @return
 */
function changecss(theClass,element,value) {
	//Last Updated on June 23, 2009
	//documentation for this script at
	//http://www.shawnolson.net/a/503/altering-css-class-attributes-with-javascript.html
	 var cssRules;

	 var added = false;
	 for (var S = 0; S < document.styleSheets.length; S++){
		 if (document.styleSheets[S]['rules']) {
			 cssRules = 'rules';
		 }else if (document.styleSheets[S]['cssRules']) {
			 cssRules = 'cssRules';
		 } else {
			 //no rules found... browser unknown
		 }
		 for (var R = 0; R < document.styleSheets[S][cssRules].length; R++) {
			 if (document.styleSheets[S][cssRules][R].selectorText == theClass) {
				 if(document.styleSheets[S][cssRules][R].style[element]){
					 document.styleSheets[S][cssRules][R].style[element] = value;
					 added=true;
					 break;
				 }
			 }
		 }
		 if(!added){
		  	if(document.styleSheets[S].insertRule){
		  		document.styleSheets[S].insertRule(theClass+' { '+element+': '+value+'; }',document.styleSheets[S][cssRules].length);
		  	}else if (document.styleSheets[S].addRule) {
		  		document.styleSheets[S].addRule(theClass,element+': '+value+';');
		  	}
		 }
	 }
	}


/******* Browser detection *********/

/**
 * NOTE: if you add browser agents to the list do not forget to lowercase
 * the string!
 * 
 */
function Is (){
    
	var agt= window.navigator.userAgent.toLowerCase();
	
    this.major		= parseInt(navigator.appVersion);
    this.minor		= parseFloat(navigator.appVersion);
    
    this.nav		= ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1) && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1) && (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
    this.nav4		= (this.nav && (this.major == 4));
    this.nav6up		= (this.nav && (this.major >= 5));
    this.ie			= ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    
    if (this.ie) {
    	var res = navigator.userAgent.match( /MSIE (\d+).(\d+);/ );
    	this.major = parseInt( res[ 1 ] );
    	this.minor = parseInt( res[ 2 ] );
    }
    
    this.ie4		= (this.ie && (this.major == 4) && (agt.indexOf("msie 5")==-1) );
    this.ie4up		= (this.ie && (this.major >= 4));
    this.ie6down	= (this.ie && (this.major <= 6));
    this.ie7down	= (this.ie && (this.major <= 7));
    this.ie8down    = (this.ie && (this.major <= 8));
    this.ie9up		= (this.ie && this.major >= 9);
    
    this.chrome		= (agt.indexOf("chrome")!=-1);
    this.mac		= (agt.indexOf("mac")!=-1);
    this.iphone		= (agt.indexOf("iphone")!=-1);
	this.ipad		= (agt.indexOf("ipad")>-1);    
    this.ff			= (agt.indexOf("firefox")>-1);
    this.ff2		= (agt.indexOf("firefox/2.")>-1);
    this.ff3		= (agt.indexOf("firefox/3.")>-1);
    this.ff4		= (agt.indexOf("firefox/4.")>-1);     
    this.webkit		= (agt.indexOf("applewebkit")>-1);
    
    // iphone and ipad are also webkit, ensure setting
    if(this.iphone || this.ipad){
    	this.webkit = true;
    }
    
  //  alert('toolkit.js is webkit:' + this.webkit + ' ' + agt);
}
is = new Is(); 

/******* End Browser detection ******/	

/**
	add a help item to the UI
*/
function addTooltip(meme_id, thema){
/* 
*/
  
  TooltipManager.options = {cssClassName: thema, 
		delayOver: 200, 
		delayOut: 5000, 
		shiftX: 5, 
		shiftY: 5,
        className: thema, 
        width: 300, 
        height: 400, 
        draggable: true, 
        minimizable: false, 
        maximizable: false, 
        showEffect: Element.show, 
        overflow:'auto',
        hideEffect: Element.hide};

  // TooltipManager.init();
  try{
	  TooltipManager.addURL("tooltip_"+meme_id, "http://www.extentech.com/uimodules/docs/docs_minimal_detail.jsp?meme_id="+meme_id,300,400);
  }catch(e){;}
}

/**
	Taking an array remove all matching values and return an array 
	with unique values
**/
function dedupeArray(dupeArray){
	var hash = new Object();
	for (i = 0; i <dupeArray.length; i++) {hash[dupeArray[i]] = true}
	var uniqueArray = new Array();
	for (value in hash) {uniqueArray.push(value)};
	return uniqueArray;
}

/**
	 get the width of the browser window
*/ 
function getBrowserWidth(){
// detect browsers and determine window size	
var myWidth = 0, myHeight = 0;
  if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    return window.innerWidth;
  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    return document.documentElement.clientWidth;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    return document.body.clientWidth;
  }
}


/**
 get the height of the browser window
*/ 
function getBrowserHeight(){
	
	if( typeof( window.innerWidth ) == 'number' ) {
	    //Non-IE
	    return window.innerHeight;
	  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
	    //IE 6+ in 'standards compliant mode'
	    return document.documentElement.clientHeight;
	  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
	    //IE 4 compatible
	    return document.body.clientHeight;
	  }
}
	


/**
 add a timeout to the Prototype.js AJAX responses to handle
 long-running REST calls without a timeout

// from:  http://codejanitor.com/wp/2006/03/23/ajax-timeouts-with-prototype/
*/
function callInProgress (xmlhttp) {
	switch (xmlhttp.readyState) {
		case 1:
		case 2:
		case 3:
			return true;
			break;

	// Case 4 and 0
		default:
		return false;
		break;
	}
}
function showFailureMessage() {
	showError('Server Timeout. Server is unable to respond in time.');
}

// Register global responders that will occur on all AJAX requests
/**/
if(Ajax != undefined){
	Ajax.Responders.register({
		onCreate: function(request) {
			request['timeoutId'] = window.setTimeout(
				function() {
					// If we have hit the timeout and the AJAX request is active, abort it and let the user know
					if (callInProgress(request.transport)) {
						// request.transport.abort();
						// showFailureMessage();
						// Run the onFailure method if we set one up when creating the AJAX object
						if (request.options['onFailure']) {
							request.options['onFailure'](request.transport, request.json);
						}
					}
				},
				120000 // set high for long queries -- seems to have random effect
			);
		},
		onComplete: function(request) {
			// Clear the timeout, the request completed ok
			window.clearTimeout(request['timeoutId']);
		}
	});
}


/**
	deletes a doc
*/
function deleteDoc(midx){
	Dialog.confirm('Permanently Delete '+midx+'?', {
	    okLabel: 'delete',
		top: 100,
		width:200,
		height:150,
		className:  'extentech',
	    id: 'delete_prompt',
	    cancel:function(win) {
	    		try{
					parent.showStatus('Delete canceled.')
	    		}catch(e){;}
			},
	    ok:function(win) {
	    	parent.showStatus('deleting '+midx+'...');
	    	new Ajax.Request('/workbook/id/'+midx+'/xml/workbook/delete/0' , {
					method: 'get',
					onSuccess: function(transport){
						parent.showStatus('Deleted: ' + transport.responseText);
					},
					onFailure: function(){ 
						var response = transport.responseText || "failure";     
						parent.showStatus("Delete failed: " + response); 
					}
				});
				return true;
	    	}
	   });
}


/**
	get a local scoped element
*/
function getElement(name){
	return $(name);
}

/**
 	make a div appear/disappear
*/
function appear(d){
	d = $(d);
	var vx = false;
	try{
		vx = d.visible();
		// alert(vx);
	}catch(e){
	//	alert(e);
	}
	
	if(vx){
		try{
			new Effect.Fade(d);
		}catch(e){;}
	}else{
		try{
			new Effect.Appear(d);
		}catch(e){;}
		// new Effect.Highlight(d);
	}
}
/**
	create a new spreadsheet...
*/
function createNewSpreadsheet(theme){
	var url = '/workbook/id/-1/json/workbook/getid';
	new Ajax.Request(url , {
		method: 'GET',
		onSuccess: function(transport){
			var memeId = transport.responseJSON.midi + '';
			uiWindowing.openDoc( '[ ' + memeId + ' ] New Spreadsheet',
					memeId, theme, false, 'workbook' );
		},
		onFailure: function(transport){ 
			var response = transport.responseText || "failure";     
			showError("Unable to create new spreadsheet: " + response); 
		}
	});	
}

/**
	create a new doc...
*/
function createNewDoc(theme){
	var midz = -1;
	var urlx = '/doc/id/-1/json/document/getid/';
	new Ajax.Request(urlx , {
			method: 'get',
			onSuccess: function(transport){
				midz = transport.responseText.evalJSON().midi;
				showStatus("Created new doc with id: " + midz);
				createWindow('New Doc', '/doc_edit/doc.jsp?maximize=true&meme_id='+midz, theme);
				uiWindowing.reloadFileMenu();
			},
			onFailure: function(transport){ 
				var response = transport.responseText || "failure";     
				showError("Unable to create new doc: " + response); 
			}
		});	
}

// TODO: make this less annoying
function saveDoc(){

try{
		if(uiWindowing.getActiveSheet()==null){
			// showInfoDialog("TIP: To save your changes, hit the 'save' button or 'ctrl+s' key while editing your spreadsheet.", "extentech");
		}else{
			if(uiWindowing.getActiveDocument()!=null){
				if(uiWindowing.getActiveDocument().dirty){
					
					var answer = confirm("Save Changes to Active Doc?");
					if (answer){
						try{
							showStatus('Saving... ');
							uiWindowing.getActiveSheet().save();
						}catch(e){
							alert(e);
						}
						return "Active Doc Saved.";
					}
					return "Changes to Active Doc Made During Editing Session are Still Available but Have NOT Been Permanently Saved.";
				}
			}
		}
	}catch(e){
		alert(e);
		return false;
	}
	
}

// wrap Prototype AJAX binding to a div container to standardize handling
function ajaxBind(div, url){
	
	var _divx = $(div);
	if(_divx == null) // hmmm not found...
		return;
		
	


	if(is.ie){
		_divx.innerHTML = "";
		new Ajax.Updater(div,
		url ,
		{
			method: 'get',  
			evalScripts: true,  
			onSuccess:function(transport){
				_divx.style.overflow = 'auto';
			},
			onFailure: function(transport){ 
				var response = transport.responseText || "failure";     
				//parent.showError("Communication problem with " +url+ ":<br>" + response); 
			},
			insertion: Insertion.Top
		});
		_divx.style.overflow = 'auto';
	}else{
		new Ajax.Updater(div,
			url ,
			{
				method: 'get',  
				evalScripts: true,  
				onSuccess:function(transport){
					_divx.setStyle({overflow:'auto'});
				},
				onFailure: function(transport){ 
					var response = transport.responseText || "failure";     
					//parent.showError("Communication problem with " +url+ ":<br>" + response); 
				}
			});
		_divx.setStyle({overflow:'auto'});
	}
}

// call an ajax method and output results to status
function ajaxCall(url){
	//alert(url);
	new Ajax.Request(url , {
		method: 'get',
		onSuccess: function(transport){
			showStatus('success: ' + transport.responseText);
		},
		onFailure: function(){ 
			var response = transport.responseText || "failure";     
			showError("AJAX request failed: " + response); 
		}
	});
}

function getActiveWindow(){
	var activeWin = Windows.getFocusedWindow();
	var theId = activeWin.getId();
	theId = theId+'_content';
	return $(theId);
}

/**
	bummer we need this here as a way to get global handles... 
*/
/**
	Get the active sheet that is being worked on.  Note that this class could
	be instantiated either in the grid iframe, where we have a 'sheet' variable
	available, or outside of the iframe where we could have multiple sheets.
**/
function getActiveSheet(){
	try {
		if (typeof sheet !== 'undefined')
			return sheet;
	} catch (e) {}
	
	try {
		if (typeof parent.sheet !== 'undefined')
			return parent.sheet;
	} catch (e) {}
	
	try {
		if (typeof parent.getActiveSheet !== 'undefined'
				&& parent.getActiveSheet !== getActiveSheet)
			return parent.getActiveSheet();
	} catch (e) {}
	
	try{
		if(uiWindowing)
			return uiWindowing.getActiveSheet();
	}catch(e){;}
	
	try{	
		if(parent.uiWindowing)
			return parent.uiWindowing.getActiveSheet();	
	}catch(e){;}
	
	try{
		if(parent.parent.uiWindowing)
			return parent.parent.uiWindowing.getActiveSheet();	
	}catch(e){;}
	
	try{
		if(top.uiWindowing)
			return top.uiWindowing.getActiveSheet();	
	}catch(e){;}
	
	
	// oops
	showError('Could not find active sheet.');

}

/**
 * given element, return the parent frame's window/contentWindow
 * Browser-specific
 */
function srcWindow(el) {
	if (el.ownerDocument.parentWindow) { // IE and Safari require, but works for Firefox too
		var frame= el.ownerDocument.parentWindow.frameElement;
		return frame.contentWindow;
    } else if (el.ownerDocument.defaultView) { // Works for Firefox, DOM level 2 standard
        return el.ownerDocument.defaultView;
	}
	return null;
}


// Deal with user login links

// set the user login div with a valid login
function setUserDiv(id,username){
	try{
		var userdivtxt = '&nbsp;<a href="javascript:createWindow(\'edit user data\',\'/uimodules/user/user_update.jsp\',\'extentech\',true);" onMouseOver="ddrivetip(\'Click to edit your user information\')"; onMouseOut="hideddrivetip()">';
		userdivtxt += username;
		userdivtxt += '</a>&nbsp;'
		// <a href="javascript:createDialogWindow(\'sign on\',\'/uimodules/user/login.jsp?intro=true\',\'extentech\');" onMouseOver="ddrivetip(\'<b>click to sign out</b>\')"; onMouseOut="hideddrivetip()">';
		//userdivtxt += '<img src="/themes/icons/dd_arrow.gif" title="sign out" width="16" height="16" border="0" align="absmiddle" class=\'rteImage\'></a>&nbsp;';
				
		$('user_login_links').innerHTML = userdivtxt;
	}catch(e){
		// showError(e);
	}
}



	/*********************************** LOGGING *******************************************/	

	/* Debug Logging for FF or IE (sorry, that's all for now!)*/
	/* TODO: replace with Debug window ...*/
	var dLog= function(s) { if (typeof console!="undefined") return console.log(s); else if (typeof Debug!="undefined") return Debug.write(s); }
	
	/** Add an event to the event handling
	**/
	function addEvent( obj, type, fn ) {
	   if ( obj.attachEvent ) {
		 obj['e'+type+fn] = fn;
		 obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
		 obj.attachEvent( 'on'+type, obj[type+fn] );
	   }else{
		 obj.addEventListener( type, fn, false );
	   }
	 }
	function removeEvent( obj, type, fn ) {
	   if ( obj.detachEvent ) {
		obj.detachEvent( 'on'+type, obj[type+fn] );
		obj[type+fn] = null;
	   } else
		obj.removeEventListener( type, fn, false );
	 }


 	/*********************************** EVENT HANDLING *******************************************/	
    /* Deprecated, needed for grid resize as the event passed in is an object
	*/
	function evtAttach(obj, eString, method) {
		 if (obj.attachEvent) {	// IE
			for (var i= 1; i+1 < arguments.length; i+=2) {				
				obj.attachEvent('on' +arguments[i], arguments[i+1]);	
			}
		} else if (obj.addEventListener) {
			for (var i= 1; i+1 < arguments.length; i+=2) {							
				obj.addEventListener(arguments[i], arguments[i+1], false);		// assume no capture for now
			}
		} else {
			for (var i= 1; i+1 < arguments.length; i+=2) {				
	            obj['on'+arguments[i]]= arguments[i+1];
	        }
		}	
	}
	/* detach one or more events to Object obj
		cross-browser tested - ALMOST!!
		usage:  evtDetach(OBJECT, eventName, method[, eventName, method ...])
	*/
	function evtDetach(obj, eString, method) {
		if (obj.detachEvent) {
			for (var i= 1; i+1 < arguments.length; i+=2) {				
				obj.detachEvent( 'on' + arguments[i], arguments[i+1]);	
				obj['on' + arguments[i]] = null;
			}
		
		} else if (obj.removeEventListener) {
			for (var i= 1; i+1 < arguments.length; i+=2) {							
				obj.removeEventListener( arguments[i], arguments[i+1], false);		// assume no capture for now
			}
		} else {
			for (var i= 1; i+1 < arguments.length; i+=2) {				
	            obj['on' + arguments[i]]= null;
	        }
        }
	}
	function evtSwallow(evt) {
		if (window.event /*&& window.event.cancelBubble*/) {	// IE
			 window.event.cancelBubble = true;
		}
		if (evt.cancelBubble)
			evt.cancelBubble= true;
		if (evt && evt.stopPropagation) {
			evt.stopPropagation();
		}	
	}

	

	/*********************************** ELEMENT POSITIONING *******************************************/	
/* given object obj, return the position as [left, top] 
	cross-browser tested
*/
    function elementPos(obj) { 
		return Position.cumulativeOffset(obj);
    }
    
	/* returns the parent.tagName of node*/    
    function getParent(node, tagName) {
        while (node != null && 
               node.tagName != tagName)
            node = node.parentNode;
        return node;
	}
    
    /* returns the current mouse position from the evt event object
    	this is relative to the VIEWPORT (ie including scroll offsets of documents);
    
     */
     function curPos(evt) { 
     	var point = [Event.pointerX(evt), Event.pointerY(evt)];
     	return point;
    }
    /*********************************** Element Addition *******************************************/
     /* parameters: 
      * 	id			string unique id required
      * 	tag 		string html tagname
      * 	_parent		Object parent node
      * 
	  *     e.g.: newEl= addElementToDOM("newElementID", "div"); 
				removed the style/css attributes as they only workedin static pages - NR	  
	  
      */
    function addElementToDOM(id, tag, _parent) {
		if ($(id)) return $(id);
    	var newEl;	
				newEl= document.createElement(tag);
				newEl.setAttribute("id", id);
				if (newEl)	{ 
					_parent.appendChild(newEl);
				}		
		return newEl
    }
    

	
	/*********************************** CONSTANT GETTERS *******************************************/	
	var SCROLLBARLEFT= (is.ie)?12:12;	
	var SCROLLBARBOTTOM= (is.ie)?6:12;
	


	/*********************************** WINDOWING/DIALOG *******************************************

	use these functions to display windows and dialogs
	
	DO NOT use 'alert', 'prompt' or other browser-based dialogs
	
	
	
*/

// for use by dialogs that need to submit a form, then close and call a function in the MDI
function submitAndClose(somediv, txt){



}

// open win fs
function fullScreen(thisURL) {
	if(is.ie){
		window.open(thisURL, '', 'fullscreen=yes, scrollbars=auto');
	}else{
	   window.locationbar.visible=false;
	   window.toolbar.visible=false;
	   window.personalbar.visible=false;
	   window.statusbar.visible=false;
	   // Force resolution
	   document.documentElement.setAttribute("width", "1024");
	   document.documentElement.setAttribute("height", "768");
      // Main pulldown menu bar
      window.menubar.visible=false;
     //  BrowserFullScreen();
	}
}





/**
	pops up a 3 second info window... 
*/
var stimeout =3;
function showInfoDialog(content, theme) {
	var imgsrc= "<img src='/themes/icons/tango/32x32/status/dialog-information.png' style='float:left; margin-right:-32px;'>";
	
	Dialog.info(imgsrc + content,
	 {windowParameters: {className: theme,
	 	width:250,
		height:150},
	showProgress: true});
	setTimeout("infoTimeout()", 1000)
}
function infoTimeout() {
  stimeout--;
  if (stimeout >0) {
   // Dialog.setInfoMessage(stimeout + "s ...")
	setTimeout("infoTimeout()", 1000)
}
  else
    Dialog.closeInfo()
}

// show:hide with SwitchOff:down
function toggleUpDown(divname){
	var bar = $(divname);
	if(bar==null)
		return;
	if(!bar.visible()){
//		bar.show();
		new Effect.Appear(bar);
	//	new Effect.Appear(bar);
	}else{
		try{
			new Effect.SwitchOff(bar);
		}catch(e){;}
		// bar.toggle();
	}
}
/**
	done with status message, hide it...
*/


/**
	done with status message, hide it...
*/

// cascading and arranging
var idx = 0;
var menuOffset = 70;
var wincount = 0;
var xidx = 0;
var cascade = 0;
var cOffset = 23; // what is it?
var activeWindowTitles = new Array();

// edit win 
function createWindow(title,  module, theme, scrolls, wide, height){
	var ht = height;
	if(ht==null)
		ht=500;
		
	if(activeWindowTitles.indexOf(title)>-1){
		////debug('already have a: ' + title);
		Windows.close(title);
// 		title += wincount++;
	}
	if(module.indexOf('?')>-1)
		module += '&frame='+title;
	else
		module += '?frame='+title;
	
	var lx = 370, hx = 250;
	var wx = wide;
	
	if(wx==null)
		wx = getBrowserWidth() - (lx+10);
	
	if(height!=null)
		ht = getBrowserHeight() - hx;
	
	//openAlert('creating window: ' + title);
	////debug('creating window: ' + title);
	// Windows with an URL as content
	var winx = new Window(title, {title: title, className: theme, 
	  top:80, left:lx, width:wx, height:ht, gridX:10, gridY:10,
	  destroyOnClose: true,
	  resizable: true, 
	  url:module,
	 hideEffectOptions: {duration:0},
	  showEffectOptions: {duration:0}
	  })
	if(scrolls)
		winx.content.setStyle({ overflowY:'scroll' });
	else
		winx.content.setStyle({ overflow:'hidden' });
	
	winx.setDestroyOnClose(true);
	
 	winx.showCenter();
	activeWindowTitles.push(title);	
	// WindowStore.show(winx);
	// WindowStore.init();
	 winx.show();
	// return winx; // causes [Object] errors!
}

// edit win 
function createBigWindow(title,  module, theme, scrolls){
	if(activeWindowTitles.indexOf(title)>-1){
		////debug('already have a: ' + title);
		Windows.close(title);
// 		title += wincount++;
	}
	if(module.indexOf('?')>-1)
		module += '&frame='+title;
	else
		module += '?frame='+title;
	
	//openAlert('creating window: ' + title);
	////debug('creating window: ' + title);
	// Windows with an URL as content
	var winx = new Window(title, {title: title, className: theme, 
		  top:25, left:330, width:850, height:530, gridX:10, gridY:10,
		  destroyOnClose: true,
		  resizable: true, 
		  url:module,
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}
		  })
	if(scrolls)
		winx.content.setStyle({ overflowY:'scroll' });
	else
		winx.content.setStyle({ overflow:'hidden' });
	
	winx.setDestroyOnClose(true);
 	
 	// winx.showCenter(); // hateful over centering
	activeWindowTitles.push(title);	
	// WindowStore.show(winx);
	// WindowStore.init();
	winx.show();
}

// create user interface dialogs
function createSheetDialog(title,  module, theme, w, h, opa){

	if(opa==null)
		opa=1;
	
	var wdx = 350;
	var htx = 375;
	if(w != null)
		wdx = w;
		
	if(h != null)
		htx = h;
		
	theme='alert';
	
	if(theme == null)
		theme = 'extentech';

	// Windows with an URL as content
	var dlg = new Window(title, {title: title, className: 'extentech', 
	  width:wdx, height:htx,
	  destroyOnClose: true,
	  minimizable:      false,
	  maximizable:      false,
	  closable:			true,
	  resizable: 		true,
	  draggable:			false,
	  url:module,
	  cancel:function(win) {
	  	////debug("validate cancel panel"); 
	  	return true;
	  },
	 hideEffectOptions: {duration:0},
	  showEffectOptions: {duration:0}
	  })
	dlg.content.setStyle({overflow: "hidden"});
	dlg.setDestroyOnClose(true);
	// dlg.show(true);
	dlg.showCenter(true);
	
}

// configurable window
function openWindow(tt, url, theme, x, y, w, h){
	// theme = "'" + theme + "'";
	//alert('opening: ' + tt + url + th + x + y + w + h);
	
	// Windows with an URL as content
	var winx = new Window(tt, {title: tt,
			 className: theme,
			  top:y, left:x, width:w, height:h,
			  //  gridX:10, gridY:10,
			  resizable: 		true,
			  minimizable:      false,
			  maximizable:      false,
	       	  destroyOnClose:   true, 		  

			  /* 20080414 KSC: use passed in module!  url:'/themes/',  */
			  url:url,
			 hideEffectOptions: {duration:0},
			 showEffectOptions: {duration:0}
			})
	//alert(winx);
	winx.content.setStyle({overflow: "hidden"});
	winx.setDestroyOnClose(true);
//20080414 KSC: passing in coordnates so don't automatically center 	winx.showCenter();
	activeWindowTitles.push(tt/*20080414 title*/);	
	winx.show();
	// return winx; // causes [Object] errors
}

// draggable dialog
function createDialogWindow(title,  module, theme){
	createSizedDialogWindow(title, 380, 220, module, theme);
}

/**
	Creates a custom sized draggable dialog window
**/
function createSizedDialogWindow(title, wide, high, module, theme){
	var winx = new Window(title, {title: title, className: theme, 
		  top:70, bottom:0, 
		  left:0, 
		  width:wide, 
		  height:high, 
		  gridX:10, 
		  gridY:10,
		  opacity:5,
      	  zIndex:100,
		  destroyOnClose: true,
		  minimizable:      false,
		  maximizable:      false,
		  resizable: 		true,
       	  destroyOnClose:    true, 
		  url:module,
 		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}
		  })
	winx.content.setStyle({overflow: "auto"});
	winx.showCenter();
	activeWindowTitles.push(title);	
	winx.show();
}

// a map of dialog contents so we dont have dupes
var activeDialogContents = {};

function showError(msg, error){
	if (uiWindowing !=null &&
			typeof(uiWindowing)!='undefined'){
		uiWindowing.showError(msg, error);
	}else{
		createStatusPopup(msg);
	}
}

function showStatus(msg){
	if (uiWindowing !=null &&
			typeof(uiWindowing)!='undefined'){
		uiWindowing.showStatus(msg);
	}else{
		createStatusPopup(msg);
	}
}

/**
 * create modal dialog status
 */
function modalDialog(msg){
	Dialog.alert('<b>'+msg+'</b>', {
	    okLabel: 'ok',
		top: 100,
		width:200,
		height:120,
		zIndex:100,
		className: 'extentech'
   });
}


	/**
	 * create popup status
	 * 
	 * @param {Object} content of the message
	 */
	function createStatusPopup(sab, autohide){
	
		var wdx = 350;
		var htx = 100;
		var theme='alert';
		var cx = '';
		if(typeof(sab.innerHTML)!='undefined')
			cx = sab.innerHTML;
		else
			cx = sab;

		// debug(cx);
		
		var tt = 'alert_'+ new Date().getMilliseconds();
		
		// reuse windows
		// output.abc  
		// output['abc']  
		
		var winx = null;
		if(activeDialogContents[cx]!=null){
			winx = activeDialogContents[cx];
			
		}else{
			winx = new Window(tt,{
				  className: theme, 
				  bottom:50, 
				  right:50, 
				  width:wdx, 
				  height:null,  
				
		      	  draggable: true,
				  destroyOnClose: 	true,
				  minimizable:      false,
				  maximizable:      false,
				  resizable: 		false,
		
		 		  hideEffectOptions: {duration:0},
				  showEffectOptions: {duration:3}
			});
		}
		
		// put in the content map so we can reuse windows
		// activeDialogContents[cx] = {winx};
		
		winx.setHTMLContent(cx);
		
		debug(cx);
		// winx.content.setStyle({overflow-y: "auto"});
		// winx.content.setStyle({padding-right: "5px"});
		
		winx.show();
		/**/
		try{// NEVER CLOSE?
			if(autohide){
				this.setTimeout("Windows.close('"+winx.getId()+"');", 8000);	
			}else {// it's an error, show debug help tools
				var tt = Element.extend(document.createElement('div'));
				tt.innerHTML = "<br/><a href='javascript:showDebug();'>show debug</a> &nbsp;|&nbsp; <a href=\"javascript:createDialogWindow('Report a Bug or Feature Request','http://extentech.com/workbook/id/1966/html/namedrange/getform/insert_issue','extentech');\">send a support request</a>";
				winx.setStatusBar(tt);
			}
		}catch(e){;}
		
	}


// edit win 
function createDialog(title,  module, theme){

	var winx = new Window(title, {title: title, className: theme, 
		  width:350, height:300, zIndex:150, opacity:5,
		  resizable: false,
		  //closable: false,
		  minimizable:       false,
		  maximizable:       false,
		  draggable:         false,								  
		  url:module,
       	  destroyOnClose:    true, 
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}
		  })
	winx.content.setStyle({overflow: "hidden"});
	winx.showCenter();
	winx.show(true);	
}

function createModalDialog(title,  module, theme){
	var winx = new Window(title, { 
			title: title, 
			className: theme,
			url:module, 
			width:350, 
			height:300, 
			zIndex:100, 
			opacity:1,
			resizable: false,
	  		closable: false,
		  	minimizable:       false,
		  	maximizable:       false,
		  	draggable:         false,			
       	  	destroyOnClose:    true,
  			 hideEffectOptions: {duration:0},
  		  	showEffectOptions: {duration:0}
		  	})
	winx.content.setStyle({overflow: "hidden"});
	winx.setContent("select")
	winx.setDestroyOnClose(true);
	winx.showCenter();
	winx.show(true);	
}

function openSave(confirmtxt) {
  Dialog.confirm(confirmtxt, 
                 {top: 150, width:250, className: "extentech_minimus", okLabel: "Yes", cancelLabel:"No",
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}})
}

function openAlert(alerttxt) {
 Dialog.alert(alerttxt, {windowParameters: {className: "extentech", top:300,
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}}})
}

function openConfirm(confirmtxt) {
  return Dialog.confirm(confirmtxt, 
                 {top: 150, width:250, className: "extentech", okLabel: "Yes", cancelLabel:"No",
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}})
}

function confirmDialog(urlx, theme) {
    Dialog.confirm({url: urlx, options: {method: 'get'}}, 
                   {top: 300, width:250, className: theme, okLabel: "Yes", cancelLabel:"No",
		 hideEffectOptions: {duration:0},
		  showEffectOptions: {duration:0}})    
}


function errorDialog(message, theme) {
    Dialog.confirm({url: urlx, options: {method: 'get'}}, 
                   {top: 300, width:250, className: theme,
		 hideEffectOptions: {duration:0},
    	showEffectOptions: {duration:0}})    
//    Dialog.alert(message, {windowParameters: {className: theme}})   
}


/** set the style dynamically **/
function setTheme(themename, doc){
	// get handle to the theme include css
	var preloadtheme = $('themepreload');
	preloadtheme.href='/themes/'+themename+'.css'
	
	
	var theme = $('theme');
	theme.href='/themes/'+themename+'.css'
	
	if(theme.length){ // handle dupes
		for(i = 0;i<theme.length;i++)
			theme[i].href='/themes/'+themename+'.css'	
	}
	
	// iterate the DOM, find the styles starting with 'extentech'

	var kids = document.childNodes;    // Get the list of children
	
	if(doc)
		kids = doc.childNodes;
		
 	var numkids = kids.length;  // Figure out how many children there are
    for(var i = numkids-1; i >= 0; i--) {  // Loop backward through the children
        
        try{
        	var kk = Element.extend(kids[i]);
        	if(kk.setTheme)
        		kk.setTheme(themename);
    	}catch(e){
    		;
    	}
    }
}

/** dynamically load a css include **/
function loadCSS(url){
   var e = document.createElement("include");
   e.src = url;
   e.type="html/css";
   document.getElementsByTagName("head")[0].appendChild(e);
}

/** dynamically load a js include **/
function loadScript(url){
   var e = document.createElement("script");
   e.src = url;
   e.type="text/javascript";
   document.getElementsByTagName("head")[0].appendChild(e);
}

/* reverse the nodes in the document... Funny?*/
function reverse(n) {           // Reverse the order of the children of Node n 
    var kids = n.childNodes;    // Get the list of children
    var numkids = kids.length;  // Figure out how many children there are
    for(var i = numkids-1; i >= 0; i--) {  // Loop backward through the children
        reverse(kids[i]);
		var c = n.removeChild(kids[i]);    // Remove a child
        n.appendChild(c);                  // Put it back at its new position
    }
}

// moved from header.js

function MM_validateForm() { //v3.0
  var i,p,q,nm,test,num,min,max,errors='',args=MM_validateForm.arguments;
  for (i=0; i<(args.length-2); i+=3) { test=args[i+2]; val=MM_findObj(args[i]);
    if (val) { nm=val.name; if ((val=val.value)!="") {
      if (test.indexOf('isEmail')!=-1) { p=val.indexOf('@');
        if (p<1 || p==(val.length-1)) errors+='- '+nm+' must contain an e-mail address.<br/>';
      } else if (test!='R') { num = parseFloat(val);
        if (val!=''+num) errors+='- '+nm+' must contain a number.<br/>';
        if (test.indexOf('inRange') != -1) { p=test.indexOf(':');
          min=test.substring(8,p); max=test.substring(p+1);
          if (num<min || max<num) errors+='- '+nm+' must contain a number between '+min+' and '+max+'.<br/>';
    } } } else if (test.charAt(0) == 'R') errors += '- '+nm+' is required.<br/>'; }
  } if (errors) modalDialog('<span class=\'title2\'>Please Correct:</span><br/><br/>'+errors);
  document.MM_returnValue = (errors == '');
}
function MM_openBrWindow(theURL,winName,features) { //v2.0
  window.open(theURL,winName,features);
}
function MM_popupMsg(msg) { //v1.0
  showStatus(msg);
}
function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}
function MM_findObj(n, d) { //v4.0
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && document.getElementById) x=document.getElementById(n); return x;
}
function setfocus(){
	if(document.form1 != null){
		if(document.form1.username != null){
			document.form1.username.focus(); 
		}
	}
}


function ddrivetip(thetext, thewidth, thecolor){
	showStatus(thetext);
}

function hideddrivetip(){
	// showStatus('hidedrivetip');
}


// -------------------------------------------------------------------
// autoComplete (text_input, select_input, ["text"|"value"], [true|false])
//   Use this function when you have a SELECT box of values and a text
//   input box with a fill-in value. Often, onChange of the SELECT box
//   will fill in the selected value into the text input (working like
//   a Windows combo box). Using this function, typing into the text
//   box will auto-select the best match in the SELECT box and do
//   auto-complete in supported browsers.
//   Arguments:
//      field = text input field object
//      select = select list object containing valid values
//      property = either "text" or "value". This chooses which of the
//                 SELECT properties gets filled into the text box -
//                 the 'value' or 'text' of the selected option
//      forcematch = true or false. Set to 'true' to not allow any text
//                 in the text box that does not match an option. Only
//                 supported in IE (possible future Netscape).
// -------------------------------------------------------------------
function autoComplete (field, select, property, forcematch) {
	var found = false;
	for (var i = 0; i < select.options.length; i++) {
	if (select.options[i][property].toUpperCase().indexOf(field.value.toUpperCase()) == 0) {
		found=true; break;
		}
	}
	if (found) { select.selectedIndex = i; }
	else { select.selectedIndex = -1; }
	if (field.createTextRange) {
		if (forcematch && !found) {
			field.value=field.value.substring(0,field.value.length-1); 
			return;
			}
		var cursorKeys ="8;46;37;38;39;40;33;34;35;36;45;";
		if (cursorKeys.indexOf(event.keyCode+";") == -1) {
			var r1 = field.createTextRange();
			var oldValue = r1.text;
			var newValue = found ? select.options[i][property] : oldValue;
			if (newValue != field.value) {
				field.value = newValue;
				var rNew = field.createTextRange();
				rNew.moveStart('character', oldValue.length) ;
				rNew.select();
			}
		}
	}
}

var lastshown = null, lasthiifrm = null;
// this is for the menu divs
function toggleShow(divname){
	var mydiv = $(divname);
	if(lastshown != null)lastshown.style.visibility = "hidden";
	lastshown = mydiv;
	//showStatus(lastshown.id);
	// showStatus(divname +  ' is ' + mydiv.style.visibility); 	
	if(mydiv.style.visibility == "hidden")mydiv.style.visibility = "visible";
	else mydiv.style.visibility = "hidden";
}

//anonymous closure scope for postpone
(function(){
	var callbacks = {};
	var oldIE = false;
	
	function runCallback (ident) {
		var callback = callbacks[ ident ];
		if (typeof callback !== 'function') return false;
		
		callbacks[ ident ] = null;
		delete callbacks[ ident ];
		
		try { 
			callback();
		} catch (caught) {
			Logger.warn( "error running postponed function", caught );
		}
		
		return true;
	}
	
	function messageHandler (event) {
		// reject cross-window messages; they're the other use for postMessage
		if (event.source !== window) return;
		
		if (typeof event.data !== 'string') return;
		
		if (runCallback( event.data ))
			event.stopPropagation();
	}
	
	if (!window.addEventListener) {
		oldIE = true;
	} else{
		window.addEventListener( "message", messageHandler, false );
	}
	
	/** Adds a function to the event queue.
	 * @param {function} callback the function to be called
	 * @param {boolean} [duplicate=false] whether to allow duplicates
	 */
	window.postpone = function (callback, duplicate) {
		if (typeof callback !== 'function')
			throw new TypeError( 'callback must be a function' );
		
		// if duplicates aren't allowed make sure it's not scheduled
		if (!duplicate) for (var value in callbacks) {
			if (callbacks[ value ] === callback) return;
		}
		
		var ident = (new Date()).getTime() + Math.random() + '';
		callbacks[ ident ] = callback;
		
		if (oldIE) {
			window.setTimeout( function() { runCallback( ident ); }, 0 );
		} else {
			window.postMessage( ident, '*' );
		}
	};
})();
