/*
$Id$

***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 SQI, Inc.
All rights reserved.
***** END LICENSE BLOCK *****

*/


/**
	SQI AppBar Javascript Client
	============================
	
	Notes
	-----
	* Cache
		* Reads Javascript Cookie to load cached info
			* apps
			* urls
			* login info
		
		* if cache is > 15 min, do not display any info until after refresh
		* if cache is > 5 min, display cache, then do an immediate refresh
		* if cache is < 5 min, display cache, but do not do immediate refresh
		
		* cache should be updated every 1m through AJAX and cookie should be updated
		
		* soft expire: 5m
		* hard expire: 15m
		* refresh: 1m
	
	* Component Based
		* Displayed Components
		  1 CIE Logo/Menu
		  2 Company Logo/Name
		  3 Primary Apps
		  4 Spring Spacer
		  5 User and Preferences
		  6 Help
		  7 AJAX Status

	  | 1    | 2            | 3                               | 4       | 5                        |6 |7 |
	  | CIE3 \ Some Company >Incident Manager >Knowledge Base           username Preferences Logout ? (o)|
	  | Incident Manager          |                                                | Submit Bug Report   |
	  | Knowledge Base            |                                                | Open Debug Console  |
	  | ------------------------- |                                                | About Appbar        |
	  | Doc Server                |                                                 ---------------------
	  | Another App               |
	  | ------------------------- |----------------
	  | Utilities               > | Calculator     |
	  | ------------------------- | Mini Search    |
	  | Logout                    | Mini Ticket    |
	  | Preferences               | ...            |
	  | ------------------------- |----------------
	  | About CIE3                |
	   ---------------------------

	* Application Parts
		* Component manager
		* Component Widgets
			* Pre render - rendered right when page is rendered, cache is not available
			* post render - right after page is rendered, cache is available
			* on cache update - right after updating cache
		* Cache manager - provides a dictionary with information that is partially cached
		
*/

/*
Name Spaces:
SQI
SQI.AppBar
SQI.AppBar.ComponentManager
SQI.AppBar.Config
SQI.AppBar.

TODO:
Appbar load all component widgets at startup
Appbar load data from server
save cachable data in cookies
load cookie cache on startup
*/

// SQI
if (typeof(SQI) == 'undefined') SQI = {};


// SQI.AppBar - Core AppBar functionality
if (typeof(SQI.AppBar) == 'undefined') SQI.AppBar = {};

SQI.AppBar.VERSION = "0.1";
SQI.AppBar.NAME = "SQI.AppBar";

// SQI.AppBar.Config
if (typeof(SQI.AppBar.Config) == 'undefined') SQI.AppBar.Config = {};
MochiKit.Base.update(SQI.AppBar.Config, {
	sitename: "SQI",
	pubPrefix: "http://sqi-inc.com",
	pubMatch: "sqi-inc.com",
	privPrefix: "https://sqi.ecosq.com",
	privMatch: "sqi.ecosq.com",
	enabledComponents: [
		'SideBar',
		'Logo',
		'SiteLogo',
		'AppList',
		'Chat',
        'Spam',
		'spring',
		'User',
		'Search'
	],
	logo: {
		image: '/cie/appbar/static/img/ciebar-title.png'
	},
	data: {
		infoUrl: "/cie/appbar/getInfo",
		maxAge: 600,
		refresh: 15,
		checkInterval: 5
	},
	search: {
		'reqLimit': 10,
		'limit': 10
	}
});

MochiKit.Base.update(SQI.AppBar, {
	__repr__: function() {
		return "[" + this.NAME + " " + this.VERSION + "]";
	},
	
	toString: function() {
		return this.__repr__();
	},
	
	defError: function(err) {
		// Deferred Error
		if (err instanceof CancelledError) {
			return;
		}
		var myError = "\n"
		for (var o in keys(err)) {
			var key = keys(err)[o]
			var val = err[key]
			if (key == 'repr') { continue }
			if (key == 'toString') { continue }
			myError = myError + "    " + key + ": " + val + "\n"
		}
		logError("Deferred Error:", myError);		
	},
	
	fmtError: function(err) {
		var myError = "\n"
		for (var o in keys(err)) {
			var key = keys(err)[o]
			var val = err[key]
			if (key == 'repr') { continue }
			if (key == 'toString') { continue }
			myError = myError + "    " + key + ": " + val + "\n"
		}
		return myError
	},
	
	createAppBar: function() {
		logDebug(this.NAME + ": createAppBar called");
		// Get the base div
		if (typeof(this._bar) != "undefined") {
			logDebug(this.NAME + ": bar already created.");
			return;
		}
		
		var cookies = document.cookie;
		var pos = cookies.indexOf("amsession=");
		if (pos == -1 || cookies.indexOf("amsession=validate") != -1) {
			log(this.NAME, "No Cookie!")
			// Check if we are running on localhost, if so, then let it pass
			if (location.href.indexOf("http://192.168") != 0) {
			//	return null
				SQI.AppBar.Config.data.infoUrl = SQI.AppBar.Config.data.infoUrl + "NoAuth"
			}
		}

		this._barDom = $('SQIAppBar');
		if ( !this._barDom ) {
			logCrit("Can not create AppBar because '_SQIAppBar' does not exist.");
			return;
		}
		
		SQI.AppBar.Data.start()
		
		logDebug(repr(keys(SQI.AppBar.UI.prototype)));
		this._bar = new SQI.AppBar.UI(this._barDom);
		this._bar.start();
		
	}
	
});

SQI.AppBar.Data = {}
MochiKit.Base.update(SQI.AppBar.Data, {
	/***
		SQI.AppBar.Data - Data management
		
		Signals:
			cacheUpdated: When cachable information is updated
			dataUpdated: When extended data is updated
	***/
	NAME: "SQI.AppBar.Data",
	VERSION: "0.1",
	
	data: {
		cachable: {}
	},
	timestamp: 0,
	
	__repr__: function() {
		// Get representation of data
		var out = "[" + this.NAME + " " + this.VERSION + ":" +
			"\n timestamp: " + this.timestamp + 
			"\n age(): " + this.age() +
			"\n isExpired(): " + this.isExpired() + 
			"\n cacheUsed(): " + this.cacheUsed() + 
			"\n cacheMax(): " + this.cacheMax() +
			"\n]"
		return out
	},
	
	toString: function() {
		return this.__repr__();
	},
	
	start: function() {
		// Start the data manager and set up timers for updating
		this._updateFromCache()
		this._cacheCheck(true)
		//this._updateFromServer()
	},
	
	_cacheCheck: function(force) {
		// check if cache is stale every 'cacheInterval'
		var self = SQI.AppBar.Data
		
		//var now = new Date
		//logDebug(self.NAME, "_cacheCheck: heartbeat: " + (now.getTime()/1000))
		
		// Check if past 'refresh'
		if (self.age() > SQI.AppBar.Config.data.refresh*1000 || force) {
			logDebug(self.NAME, "_cacheCheck: Refreshing data.")
			try {
				self.update();
			} catch (e) {
				log(this.NAME, "Problem with update():", repr(e));
			}
		}
		
		//logDebug(self.NAME, "_cacheCheck: calling self in the future."+SQI.AppBar.Config.data.checkInterval)
		callLater(SQI.AppBar.Config.data.checkInterval, self._cacheCheck)
	},
	
	_cookieString: function () {
		// Generate the final cookie string for what is in cachable
		var data = Object()
		data.cachable = clone(this.data.cachable)
		data.timestamp = this.timestamp
		
		var s = escape(serializeJSON(data))
		var cookie = "SAC=" + s + "; path=/"
		return cookie
	},
	
	_writeCookie: function () {
		// Writes cachable cookie
		document.cookie = this._cookieString();
	},
	
	_readCookie: function () {
		// Reads cachable cookie and returns object
		// Returns null if no cookie
		logDebug(this.NAME, "Reading cookie")
		var cookies = document.cookie;
		var pos = cookies.indexOf("SAC=");
		if (pos == -1) {
			log(this.NAME, "No Cookie!")
			return null
		}
		
		var start = pos+4;
		var end = cookies.indexOf(';', start);
		if (end == -1) {
			end = cookies.length;
		}
		var value = cookies.substring(start,end);
		value = unescape(value);
		value = evalJSON(value);
		
		return value
	},
		
	_updateFromCache: function() {
		// Get cache
		var cache = this._readCookie()
		if (!cache) {
			logDebug(this.NAME, "Can not update from cache because cookie returns no data.")
			return null
		}
		
		// Check timestamp
		if (cache.timestamp < this.timestamp) {
			log(this.NAME, "Cache is older than what we already have? o.O Something is Wrong!")
			return null
		}
		
		var now = new Date()
		var age = now.getTime() - cache.timestamp
		if (age > SQI.AppBar.Config.data.maxAge*1000) {
			// cache is very stale, dont update
			logDebug(this.NAME, "Cache is very old ("+cache.timestamp+"). Will not update.")
			return null
		}
		
		// updatetree
		updatetree(this.data.cachable, cache.cachable)
		this.timestamp = cache.timestamp
		
		// signal cacheUpdated
		try {
		signal(window, 'AppBar.cacheUpdated') // Cachable Data
		} catch (e) { alert(repr(e)) }
	},
	
	_updateFromServer: function() {
		// get url
		var url = SQI.AppBar.Config.data.infoUrl;
		
		// loadJSONDoc
		//logDebug(this.NAME, ": _updateFromServer: Loading URL:", url)
		var d = loadJSONDoc(url);
		
		// callbacks
		d.addCallbacks(this._updateFromServerCallback, SQI.AppBar.defError);
	},
	
	_updateFromServerCallback: function(results) {
		//logDebug(this.NAME, ": _updateFromServerCallback: got results: ", serializeJSON(results))
		
		// clear out apps list
		try {
			this.data.cachable.apps = []
			this.data.cachable.user.username = ''
			this.data.cachable.user.actions = []
		} catch (e) { }
		
		updatetree(this.data, results)

		try {
		signal(window, 'AppBar.cacheUpdated') // Cachable data
		signal(window, 'AppBar.dataUpdated')  // Everything else
		
		var now = new Date()
		this.timestamp = now.getTime()
		
		// Save cachable
		this._writeCookie();
		} catch (e) { alert(repr(e)) }
		
	},
	
	age: function() {
		// Return age in milliseconds
		var now = new Date()
		var age = now.getTime() - this.timestamp
		return age
	},
	
	isExpired: function () {
		// Return true if cache is stale
		return (this.age() > SQI.AppBar.Config.data.maxAge*1000)
	},
	
	isFresh: function () { return !this.isExpired() },

	cacheUsed: function () {
		// Return number of bytes used in cache 
		// (including cookie headers and encoding)
		return this._cookieString().length
	},
	
	cacheMax: function () {
		// Return maximum size for cache
		return 4*1024
	},
	
	cacheGraph: function () {
		// graph for cache used
		var percent = parseFloat(this.cacheUsed())/parseFloat(this.cacheMax())
		var totalWidth = 200
		var bars = parseInt(percent * totalWidth)
		return TABLE({'style':'border: 1px solid black;'},
			TBODY(null,
				TR(null,
					TD({'style':'width:'+bars+'px; background-color:blue; height:20px;'}),
					TD({'style':'width:'+(totalWidth-bars)+'px;'}))))
	},
	
	update: function() {
		// Update data from server
		this._updateFromServer()
	},
	
	save: function() {
		// Save cachable data
		this._writeCookie()
	}
})

SQI.AppBar.Data.__new__ = function () {
	nameFunctions(this)
	bindMethods(this)
}

SQI.AppBar.Data.__new__()

SQI.AppBar.UI = function (dom) {
	logDebug("UI Called");
	this._dom = dom;
	
	bindMethods(this);
}

SQI.AppBar.UI.prototype = {
	start: function() {
		logDebug("UI.start called.")
		// Init data
		
		// Initialize the dom and load components
		this._leftSide = TR()
		this._rightSide = TR()
		this._curBox = this._leftSide
		this._table = TABLE({'class':'appBarMain'},
			TBODY(null,
				TR(null,
					TD({'class':'appBarLeftSide'},
						TABLE({'class':'appBarLeftSideTable'},
							TBODY(null,
								this._leftSide
							)
						)
					),
					TD({'class':'appBarRightSide','align':'right'},
						TABLE({'class':'appBarRightSideTable'},
							TBODY(null,
								this._rightSide
							)
						)
					)
				)
			)
		)
		appendChildNodes(this._dom, this._table)
		
		// Go through each component and call start
		for (comp in SQI.AppBar.Config.enabledComponents) {
			compName = SQI.AppBar.Config.enabledComponents[comp]
			
			// Spacer
			if (compName == "spring") {
				this._curBox = this._rightSide;
				continue;
			}
			
			logDebug("Starting component " + compName)
			var c = SQI.AppBar.Components[compName]

			// Common properties
			c.ui = this
			c.data = SQI.AppBar.Data
			bindMethods(c)
			
			var dom = c.start()
			if (dom) {
				// We may have components that dont have elements in the bar
				appendChildNodes(this._curBox, TD({'class':'appBarComponent'},dom))
			}
		}
		
		// Check if cache already populated. If so, signal update
		if (SQI.AppBar.Data.isFresh()) {
			signal(window, "AppBar.cacheUpdated");
		}
		
	},
	
	getIcon: function(icon) {
		// Return an IMG object with the icon requested
		return IMG({'src':'/cie/appbar/static/icons/'+icon+'.gif', 'height':'16', 'width':'16'})
	}
}

// AppBar Components
if (typeof(SQI.AppBar.Components) == 'undefined') SQI.AppBar.Components = {};

SQI.AppBar.Components.Logo = {
	title: "[CIE Logo]",
	dom: DIV({'class':'barLogo'}, null),
	
	start: function() {
		if (SQI.AppBar.Config.logo.image) {
			replaceChildNodes(this.dom, IMG({'class':'barLogoImage', src:SQI.AppBar.Config.logo.image}));
		} else {
			replaceChildNodes(this.dom, this.title);
		}
		
		// Get mouseclick and signal something
		
		connect(this.dom, 'onclick', partial(signal, this.ui, 'logoClick'))
		return this.dom;
	}
}

SQI.AppBar.Components.SiteLogo = {
	title: SQI.AppBar.Config.sitename,
	dom: DIV({'class':'barSiteLogo'}),
	
	start: function() {
		this.title = SQI.AppBar.Config.sitename;
		replaceChildNodes(this.dom, this.title);
		
		// Get mouseclick and signal something
		
		return this.dom;
	}
}

SQI.AppBar.Components.User = {
	NAME: "SQI.AppBar.Components.User",
	
	title: "User Info",
	ul: UL(),
	dom: DIV(),
	
	start: function(ui) {
		appendChildNodes(this.dom, this.ul)
		connect(window, 'AppBar.cacheUpdated', this._update)
		return this.dom;
	},
	
	_update: function() {
		// Called when cache is updated
		
		// Clear list
		replaceChildNodes(this.ul, null);
		
		logDebug(this.NAME, ": _update called.")
		appendChildNodes(this.ul, LI({'class':'barUserName'}, this.data.data.cachable.user.fullName))
		forEach(this.data.data.cachable.user.actions, 
			partial(function(self,action) {
				href = action.href
				if (action.title=='Login') {
					lhref = location.href
					if (lhref.indexOf(SQI.AppBar.Config.pubMatch) > 0) {
						href = SQI.AppBar.Config.privPrefix + href
						lhref = lhref.replace(/^http\:\/\/(.*?)\/(.*)/, SQI.AppBar.Config.privPrefix + "/$2")
					}
					href = href + "?" + queryString({'url':lhref})
				}
				appendChildNodes(self.ul, 
					LI(null, A({href:href},action.title)))
			}, this)
		)
	}
}

SQI.AppBar.Components.Chat = {
	NAME: "SQI.AppBar.Components.Chat",
	
	title: "Chat",
	ul: UL(),
	dom: DIV(),
	
	start: function(ui) {
		appendChildNodes(this.dom, this.ul)
		connect(window, 'AppBar.cacheUpdated', this._update)
		return this.dom;
	},
	
	_update: function() {
		var enabled = 0
		forEach(this.data.data.cachable.apps, 
			partial(function(self,action) {
				if (action.name == 'Chat') {
					enabled = 1
				}
			}, this)
		)
		if (!enabled){
			replaceChildNodes(this.ul, null);
		return}
		
		// Called when cache is updated
		var server = SQI.AppBar.Config.privMatch
		var jid = this.data.data.cachable.user.username
		jid = jid.replace(/@/g,"__at__")
		jid = jid + '@' + server + '/appbar'
		var wName = jid
		
		// BEGIN IE window name (from JWChat/shared.js)
		wName = wName.replace(/@/,"at");
		wName = wName.replace(/\./g,"dot");
		wName = wName.replace(/\//g,"slash");
		wName = wName.replace(/&/g,"amp");
		wName = wName.replace(/\'/g,"tick");
		wName = wName.replace(/=/g,"equals");
		wName = wName.replace(/#/g,"pound");
		wName = wName.replace(/:/g,"colon");	
		wName = wName.replace(/%/g,"percent");
		wName = wName.replace(/-/g,"dash");
		wName = wName.replace(/ /g,"blank");
		wName = wName.replace(/\*/g,"asterix");
		// END IE Window Name
		
		// BEGIN Cookie
		var cookies = document.cookie;
		var pos = cookies.indexOf("amsession=");
		if (pos == -1) {
			log(this.NAME, "No Cookie for chat!")
			// must not be logged in
			return null
		}
		
		var start = pos+10;
		var end = cookies.indexOf(';', start);
		if (end == -1) {
			end = cookies.length;
		}
		var cascookie = cookies.substring(start,end);
		// END Cookie
		
		var opts={
			'server':server,
			'btype':'polling',
			'base':'/http-poll/',
			'pass':cascookie,
			'jid':jid,
			'register':0
		}
		
		var query = queryString(opts)
		var href = SQI.AppBar.Config.privPrefix + '/jwchat/jwchat.html?'+query
		log("chat href is", href)
		// Clear list
		replaceChildNodes(this.ul, null);
		
		logDebug(this.NAME, ": _update called.")
		appendChildNodes(this.ul, LI(null, 
			A({'href':'#','onclick':partial(
				this._openChat,
				href,
				wName
			)
			},'Chat')
		))
	},
	
	_openChat: function(href, wName) {
		log("_openChat called", href, wName)
		window.open(href,wName,'width=180,height=390,resizable=yes');
		return false;
	}
}

SQI.AppBar.Components.AppList = {
	NAME: "SQI.AppBar.Components.AppList",
	
	title: "App List",
	ul: UL(),
	dom: DIV(),
	
	start: function(ui) {
		appendChildNodes(this.dom, this.ul)
		connect(window, 'AppBar.cacheUpdated', this._update)
		return this.dom;
	},
	
	_update: function() {
		// Called when cache is updated
		logDebug(this.NAME, ": _update called.")
		
		// Clear list
		replaceChildNodes(this.ul, null);
		
		forEach(this.data.data.cachable.apps, 
			partial(function(self,action) {
				if (action.href.indexOf('*') == -1) {
				var liprop = {}
				//logDebug('Checking if we should highlight "' + location.href + '", "' + action.href + '"')
				if (location.href.indexOf(action.href) == 0) {
					liprop['class'] = "siteSelected"
					//logDebug('YES!')
				}
				// Check if we are on the public side
				if (location.href.indexOf(SQI.AppBar.Config.pubMatch ) > 1) {
					var tmpaction = action.href.replace(/^https?\:\/\/(.*?)\/(.*)/, "$2")
					if( location.href.indexOf(SQI.AppBar.Config.pubPrefix + '/' + tmpaction) == 0) {
						liprop['class'] = "siteSelected"
					}
				}
				appendChildNodes(self.ul, 
					LI(liprop, A({href:action.href},action.name)))
				}
			}, this)
		)
	}
}
SQI.AppBar.Components.Test = {
	NAME: "SQI.AppBar.Components.Test", 
	
	title: "[L]",
	dom: DIV(),
	
	start: function(ui) {
		replaceChildNodes(this.dom, "[",
			A({href:"javascript:void(createLoggingPane(true));"},"I")," ",
			A({href:"javascript:logger.debuggingBookmarklet();"},"W"),"]");
		connect(this.ui, 'logoClick', function() {
			alert(this.NAME+': yay');
		})
		return this.dom;
	}
}

SQI.AppBar.Components.Search = {
	NAME: "SQI.AppBar.Components.Search",
	
	title: "[Search]",
	dom: DIV(),
	searchbox: INPUT({'type':'text', 'size':'10', 'value':'Search'}),
	
	searchDeferred: null,
	
	searchUrl: "/cie/kdsearch/doSearch",
	fullSearchUrl: "/webapps/kdsearch/search.html",
	
	resultsTab: null,
	
	firstTime: true,	// Used to keep track of search box 'Search' text
	initialized: false,
	interval: 1, 		// Time between stop typing and start searching
	
	iconMap: {
		'generic':'page',
		'Wiki Topic':'cycle',
		'Ticket':'ticket',
		'Forum Topic':'web'
	},
	
	start: function(ui) {
		connect(window, 'AppBar.cacheUpdated', this._update)
		
		return this.dom	
	},

	_update: function() {
		if (this.initialized) { return }
		
		var enabled
		forEach(this.data.data.cachable.apps, 
			partial(function(self,action) {
				if (action.name == 'Search') {
					enabled = 1
				}
			}, this)
		)
		
		if (!enabled) { return }
		
		replaceChildNodes(this.dom, 
			DIV({'class':'searchInput'},
				this.ui.getIcon('page_find'),
				this.searchbox
			)
		)
		
		connect(this.searchbox, 'onkeyup', this.searchKeyPress)
		connect(this.searchbox, 'onfocus', bind(function() {
			if (this.firstTime) {
				this.searchbox.value=''
			}
			this.firstTime = false
		}, this))
		
		this.resultsTab = SQI.AppBar.Components.SideBar.addTab('Search')
		
		this.initialized = true
	},
	
	searchKeyPress: function(e) {
		if (e.modifier().any) {
			return
		}
		
		if (e.key().code == 13) {
			location.href = this.fullSearchUrl + '?' + queryString({'query':this.searchbox.value})
		}
		
		// if we already had deferred, cancel it
		if (this.searchDeferred) {
			// we can call cancel even if it was successful
			this.searchDeferred.cancel()
		}
		this.searchDeferred = callLater(this.interval, this.maybeSearch)
	}, 
	
	maybeSearch: function() {
		var query = this.searchbox.value
		
		// TODO: Stop current search if in waiting for results
		
		if (!query) {
			return
		}
		this.performSearch(query)
	},
	
	performSearch: function(query) {
		// Fire off an AJAX request for search results, then display results
		var limit = SQI.AppBar.Config.search.reqLimit
		var url = this.searchUrl + '?' + queryString({'term':query, 'limit':limit})
		//alert('i might search now for ' + url)
		
		SQI.AppBar.Components.SideBar.show()
		this.showLoading()
		
		var d = loadJSONDoc(url);
		
		// callbacks
		d.addCallbacks(this.displaySearchResults, SQI.AppBar.defError);
	}, 
	
	displaySearchResults: function(results) {
		//alert(serializeJSON(results))
		logDebug('Search Results:', serializeJSON(results))
		var hitsThisPage = results.end
		var title = 'Top Results:'
		var dispResults = results.results.slice(0,SQI.AppBar.Config.search.limit)
		replaceChildNodes(this.resultsTab, /*DIV({}, title),*/
			/*serializeJSON(results), */
			TABLE({},TBODY({},
				map(this.resultRow, dispResults),
				TR({}, 
					TD(),
					TD({}, this.ui.getIcon('icon_wand')), 
					TD({},
						A({'href':this.fullSearchUrl + '?' + queryString({'query':results.query})},
							'See more results...'
						)
					)
				),
				TR({}, 
					TD(),
					TD({}, this.ui.getIcon('icon_info')), 
					TD({}, 
						A({'target':'_blank', 'href':'http://sqi-inc.com/ic/Glossary/KnowledgeDex'},
							'Learn more about KnowledgeDex'
						)
					)
				)			
			))
		)
		SQI.AppBar.Components.SideBar.redraw()
	},
	
	resultRow: function(row) {
		var img = this.ui.getIcon(this.iconMap[row.doc.typename])
		var swidth = roundToFixed(row.score*16,0);
		var score = TD({'style':'color: #008800; font-size: 0.6em;'},
			TABLE({'cellpadding':'0','cellspacing':'0','style':'padding:0px;'},
				TBODY({},
					TR({},
						TD({'height':'5', 'width':swidth},
							IMG({'src':'/cie/appbar/static/img/green.png', 'height':'5', 'border':'0', 'width':swidth})
						),
						TD({'height':'5', 'width':16-swidth},
							IMG({'src':'/cie/appbar/static/img/grey.png', 'height':'5', 'border':'0', 'width':16-swidth})
						)
					)
				)
			),
			roundToFixed(row.score*100, 0)
		)
		var url = row.doc.url
		if (location.href.indexOf(SQI.AppBar.Config.pubMatch) > 0) {
			url = url.replace(/^https\:\/\/(.*?)\/(.*)/, SQI.AppBar.Config.pubPrefix + "/$2")
		}
		var div = TR({}, 
			score, 
			TD({}, img),
			TD({},A({'href':url}, row.doc.summary))
		)
		return div
	},
	
	showLoading: function() {
		// Show a loading message
		replaceChildNodes(this.resultsTab, "Loading...")
		SQI.AppBar.Components.SideBar.redraw()
	}
}

SQI.AppBar.Components.SideBar = {
	NAME: "SQI.AppBar.SideBar",
	
	dom: DIV({'id':'SQIAppBarSideBar'}),
	
	body: TD({'id':'SQIAppBarPageBody'}),
	sidebar: TD({'id':'SQIAppBarPageSideBar'}),
	page: TABLE(),
	
	ieSelectZHack: createDOM('iframe',{'class':'selectFree','src':'/cie/appbar/static/blank.html'}),
	
	tabs: {},
	
	start: function() {
		this.detect()
		this.hide()
		
		//appendChildNodes(this.page, TBODY(null, TR(null, this.body, this.sidebar)))
		
		//var curbody = currentDocument().getElementsByTagName('body')[0]
		
		// move page elements to this.page
		//appendChildNodes(this.body, curbody.childNodes)
		//swapDOM(currentDocument(), this.page)
		
		appendChildNodes(currentDocument().getElementsByTagName('body')[0], this.dom)
		//appendChildNodes(this.sidebar, this.dom)
		
		connect(currentDocument(), 'onclick', bind(function() {
			this.hide()
		}, this))
		
		connect(this.dom, 'onclick', bind(function(evt) {
			evt.stopPropagation()
		}, this))
		
		return null
	},
	
	detect : function() {
		this.browser = { ie:false, ff:false };
		if(navigator.appName.indexOf("Microsoft") !=- 1) this.browser.ie = true;
		else if (navigator.appName == "Netscape") this.browser.ff = true;
	},
	
	hide: function() {addElementClass(this.dom, "invisible")},
	show: function() {removeElementClass(this.dom, "invisible")},
	redraw: function(full) {
		// TODO: Check to see if IE is < 7
		var hack = null;
		if (this.browser.ie) {
			hack = this.ieSelectZHack
		}
		
		if (full) {
		replaceChildNodes(this.dom);
		replaceChildNodes(this.dom, hack,
			DIV({'class':'tabTitle'},
				"Top Search Results", 
				A({'onclick':this.hide, 'style':'cursor:hand'},
				this.ui.getIcon('action_stop'))
			),
			DIV({'class':'tabs'}, map(
				function(x){return x[1]}, 
				items(this.tabs)
			))
		)
		}
		
		if (hack) {
			setElementDimensions(hack, {w:this.dom.clientWidth, h:this.dom.clientHeight})
		}
	},
	
	Tab: {
		// Prototype for a tab
		// Include a tab header and subcontent with padding
	},
	
	addTab: function(tab, priority) {
		// Add a tab to the list if not already added
		if (!priority) { priority = 1000 }
		if (!this.tabs[tab]) {
			this.tabs[tab] = DIV()
		}
		this.redraw(1)
		return this.getTab(tab)
	},
	
	getTab: function(tab) {
		return this.tabs[tab]
	}

}

SQI.AppBar.Components.Spam = {
	NAME: "SQI.AppBar.Components.Spam",
	
	title: "Spam",
	ul: UL(),
	dom: DIV(),
    titleSpan: A({'href':'/samgr/queue/list'},"Spam"),
	
	start: function(ui) {
		appendChildNodes(this.dom, this.ul)
		connect(window, 'AppBar.cacheUpdated', this._update)
        this._check_num()
		return this.dom;
	},
	
    _check_num: function() {
        var d = loadJSONDoc('/samgr/queue/numInQueue')
        d.addCallback(bind(this._cb_check_num, this))  
    },
    
    _cb_check_num: function(doc) {
        replaceChildNodes(this.titleSpan, "Spam (" + doc.count + ")")
        callLater(60, bind(this._check_num, this))
    },
    
	_update: function() {
		var enabled = 0
		forEach(this.data.data.cachable.apps, 
			partial(function(self,action) {
				if (action.name == 'Spam') {
					enabled = 1
				}
			}, this)
		)
		if (!enabled){
			replaceChildNodes(this.ul, null);
		return}
		
		// Called when cache is updated
		
		logDebug(this.NAME, ": _update called.")
        var liprop={}
		if (location.href.indexOf("/samgr") >= 0) {
			liprop['class'] = "siteSelected"
			//logDebug('YES!')
		}
		replaceChildNodes(this.ul, LI(liprop, 
			this.titleSpan
		))
	},
	
	_openChat: function(href, wName) {
		log("_openChat called", href, wName)
		window.open(href,wName,'width=180,height=390,resizable=yes');
		return false;
	}
}

SQI.AppBar.__new__ = function (win) {
	this._window = win

	nameFunctions(this)
	
}

SQI.AppBar.__new__(this);

createAppBar = function() {
	SQI.AppBar.createAppBar()
}

logDebug("SQI.AppBar Loaded");

// Stupid IE
connect(window, 'onload', createAppBar)

MochiKit.Base.update(SQI.AppBar.Config, {
	sitename: "OSNV",
	pubPrefix: "http://osnv.org",
	pubMatch: "osnv.org",
	privPrefix: "https://osnv.org",
	privMatch: "osnv.org",
	enabledComponents: [
		'SideBar',
		'Logo',
		'SiteLogo',
		'AppList',
		'Chat',
		'Spam',
		'spring',
		'User',
		'Search',
	],
	logo: {
		image: '/cie/appbar/static/img/ciebar-title.png'
	},
	data: {
		infoUrl: "/cie/appbar/getInfo",
		maxAge: 600,
		refresh: 15,
		checkInterval: 5
	},
	search: {
		'reqLimit': 10,
		'limit': 10
	}

});


