// These regexes are handy for Notes-y aspects of the DB, namely matching attachments and finding the DB's web namevar urlPattern    = /^[^\/]+\/\/([^\/]+\/)?[^\/]+\/[^\/]+\.nsf\/[\da-f]{32}(\/[\da-f]{32})?\/\$FILE\/.*$/ivar imgPattern    = /^[^\/]+\/\/([^\/]+\/)?[^\/]+\/[^\/]+\.nsf\/[\da-f]{32}(\/[\da-f]{32})?\/\$?Body\/[\d\.\w]+[!\?]OpenElement&(amp;)?FieldElemFormat=.*$/ivar dbNamePattern = /^[^\/]+\/\/([^\/]+\/)?([^\/]+\/[^\/]+\.nsf)\/.*/var first_real_element = function(v) { for(var i = 0; i < v.length; i++) { if(v[i].nodeType == 1) { return v[i] } } return null }var second_real_element = function(v) { var foundone = false; for(var i = 0; i < v.length; i++) { if(v[i].nodeType == 1 && !foundone) { foundone = true } else if(v[i].nodeType == 1) { return v[i] } } return null }var _doClickvar TemplateRules = {	'body:loaded': function() {		// I use placeholder <div>s inside rich text to allow the userland to control the placement		//   of form elements on a page, so when the page loads, I place the various bits into their		//   specified places, defaulting to the top or bottom of the content box if a placeholder		//   doesn't exist			var placeSpecificBlock = function(blockName, defaultPlacement) {			var original = $(blockName + "Box")			var placeholder = $(blockName + "Placeholder")			var content = $("content")			if(original && placeholder) {				var parent = placeholder.parentNode				parent.replaceChild(original, placeholder)				original.id = blockName + "Placeholder"			} else if(original && defaultPlacement != "none") {				if(defaultPlacement == "top") { content.insertBefore(original, content.firstChild) }				else { content.appendChild(original) }				original.id = blockName + "Placeholder"			}		}		placeSpecificBlock("eventInfo", "top")		placeSpecificBlock("scheduledDateInfo", "none")		placeSpecificBlock("onDemandInfo", "none")		placeSpecificBlock("fields", "bottom")		placeSpecificBlock("podcastInfo", "bottom")		placeSpecificBlock("submitButton", "bottom")		placeSpecificBlock("authentication", "none")				// I also want to set the height of the content box to be at least that of the linksbar, to		//   ease in the creation of colorful linksbars		if($('linksbar') && $('content') && $("linksbar").offsetHeight > $("content").offsetHeight) {			$("content").style[(navigator.appName=="Microsoft Internet Explorer")?"height":"minHeight"] = $("linksbar").offsetHeight.toString() + "px"		}				// how about stuff to maintain XHTML-ness?		if(location.href.indexOf("?") > -1) {			var query = location.href.substring(location.href.indexOf("?")+1)			var params = query.split("&")			for(var i = 0; i < params.length; i++) {				if(params[i].toLowerCase() == "outputformat=xhtml") {					var dbname = location.href.match(dbNamePattern)[2]									var links = document.getElementsByTagName("a")					for(var j = 0; j < links.length; j++) {						if(links[j].href.indexOf(dbname) > -1 && !urlPattern.test(links[j].href)) {							links[j].href += links[j].href.indexOf("?") > -1 ? "&OutputFormat=xhtml" : "?Open&OutputFormat=xhtml"						}					}					break				}			}		}	},	'form:loaded': function() {		// On register forms, I want to trap "refresh on keyword change" events and place		//   an image in place of the field for a visual indicator of the imminent page refresh		var originalDoClick = _doClick		_doClick = function(v, o, t, h) {			if(v == "$Refresh") {							var parent = $(o.name + "_Row")				if(parent) {					// i.e. if we're dealing with a register form field					var inQuestion = second_real_element(parent.childNodes)							var newBlock = document.createElement("div")					newBlock.className = "field"					newBlock.innerHTML = "<img src='progress-animation_circles.gif' alt='Reloading page...' />"					newBlock.style.height = inQuestion.offsetHeight < 25 ? 25 : inQuestion.offsetHeight							inQuestion.style.display = "none"					parent.appendChild(newBlock)				}			} else {				document.__is_submitting = true			}						if($('submitButton')) {				$('submitButton').disabled = true				$('submitButton').value = "Please wait..."			}						return originalDoClick(v, o, t, h)		}	},	'body.RegisterForm:loaded': function() {		// After a Notes page refresh, only the Notes-created EventSet fields get their right values back		//   Since I hide those, I want to go through and move their values to the actual field		var eventFields = document._Register.EventSet		var shouldBeSet = []		for(var i = 0; i < eventFields.length; i++) {			var currentElement = eventFields[i]			if(/\bnotesEventField\b/.test(currentElement.className)) {				if(currentElement.checked) {					shouldBeSet.push(currentElement.value)					currentElement.checked = false				}				currentElement.disabled = true			} else {				for(var innerIndex = 0; innerIndex < shouldBeSet.length; innerIndex++) {					var thisElement = shouldBeSet[innerIndex]					if(currentElement.tagName.toUpperCase() == "SELECT") {						for(var j = 0; j < currentElement.length; j++) {							if(currentElement[j].value == thisElement) { currentElement.selectedIndex = j; break }						}					} else if(currentElement.value == thisElement) {						currentElement.checked = true						break					}				}			}		}	},	'input#submitButton': function(button) {		// "submit" buttons in JavaScript-enabled Notes forms aren't really submit buttons and thus don't		//   respond to the enter key. Thus, I want to change it in-place, but I have to create a whole new element,		//   as the 'type' attribute is read-only in existing ones.		if(button.type == "button") {			var newButton = document.createElement("input")					newButton.type      = "submit"			newButton.value     = button.value			newButton.id        = button.id			newButton.onclick   = button.onclick			newButton.className = button.className				// I hide the old button rather than replacing it because IE's tab behavior			//   was messed up when removing the old one			button.parentNode.appendChild(newButton)			button.style.display = "none"		}	},	'body.userland img': function(img) {		// IE's PNG support sucks, so I'll apply the proprietary workaround as soon as the image exists		if(navigator.appName=="Microsoft Internet Explorer") {			var imgsrc = img.src.split("?", 1)[0]			if(/.*\.png$/i.test(imgsrc)) {				img.style.height = img.height				img.style.width  = img.width				img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imgsrc + "', sizingMethod='scale')"				img.src = "design/blank.gif"			}		}				// Notes attachments open in the current window, but I'd rather make them open in a new one.		// Fortunately, they have particular signatures for the link href and image src that I can match reliably		var parent = img.parentNode		if(parent.tagName.toUpperCase() == "A" && urlPattern.test(parent.href) && imgPattern.test(img.src)) {			parent.target = "_blank"			parent.className += " file-attachment"		}	},	'div#linksbar li': function(link) { if(link.id == document.body.id + "_LI") { link.className += " currentPage" } },	'body.webAdmin div#linksbar a:click': function(link) {		if(!/\bjavaScriptLink\b/.test(link.parentNode.className)) { link.style.backgroundImage = "url(/javascript/yui-ext/resources/images/grid/loading.gif)" }	},	'body.formPlaceholder:loaded': function() {		// In spite of my efforts, there is still a consistent extra <br> at the beginning of		//   the content block on Attend, AttendArch, Exit Evals, and Register forms. I wish		//   to destroy it.		var tag = first_real_element(first_real_element($('content').childNodes).childNodes)		if(tag && tag.tagName.toUpperCase() == "BR") {			$('content').firstChild.removeChild(tag)		}	},	'body.speaker:loaded, body.event:loaded': function() {		// A similar thing as above happens with speaker and event pages thanks to the subform,		//   but they don't have the extra .blockImport		var tag = first_real_element($('content').childNodes)		if(tag && tag.tagName.toUpperCase() == "BR") {			$('content').removeChild(tag)		}	},	'body#EventSummary table img': function(img) {		img.alt = img.title =			/design%5Ccircle-on.gif!OpenImageResource$/.test(img.src)      ? "Normal" :			/design%5Ccircle-waiting.gif!OpenImageResource$/.test(img.src) ? "Not shown on web" :			/design%5Ccircle-dim.gif!OpenImageResource$/.test(img.src)     ? "Disabled" :			/design%5Ccanceled.gif!OpenImageResource$/.test(img.src)       ? "Canceled" :			/design%5Crescheduled.gif!OpenImageResource$/.test(img.src)    ? "Rescheduled" :			/design%5Cspeaker.gif!OpenImageResource$/.test(img.src)        ? "Speaker" :			/design%5Cobserver.gif!OpenImageResource$/.test(img.src)       ? "Observer" :			/design%5Cweb.gif!OpenImageResource$/.test(img.src)            ? "Attended via web" :			/design%5Cphone.gif!OpenImageResource$/.test(img.src)          ? "Attended via phone" :			/design%5Cboth.gif!OpenImageResource$/.test(img.src)           ? "Attended via both web and phone" :			""	},	'body#EventCalendar:loaded': function(body) {		var tables = [], elements = $('fieldsPlaceholder').childNodes, i		for(i = 0; i < elements.length; i++) {			if(elements[i].nodeType == 1 && elements[i].tagName.toUpperCase() == "TABLE") {				tables.push(elements[i])			}		}		if(tables.length >= 4) {			// If we're here, we know the structure: four top-level tables.			//  [0] = top navigation			//  [1] = calendar			//  [2] = next/previous (already tagged as 'next-prev')			//  [3] = mode switcher (already tagged as 'modes')			tables[0].className = 'top-nav'			tables[1].className = 'calendar'			var params = window.location.search.substring(1), vars = {}			params.split("&").each(function(aVar) { var pair = aVar.split("="); if(pair.length > 1) { vars[pair[0]] = pair[1] } })			switch(vars.Grid) {				case '1': tables[1].className += ' two-days'; break				case '2': tables[1].className += ' one-week'; break				case '4': tables[1].className += ' monthly'; break				case '5': tables[1].className += ' yearly'; break				case '6': tables[1].className += ' one-day'; break				default: tables[1].className += ' two-weeks'; break			}		}	},		/*************************************************************	 * Time for our crazy WebAdmin behaviors!	 *************************************************************/	'a.excelExport, a.xmlExport': function(a) { a.target = "_blank" },	'body.webAdmin:loaded': function(body) { body.style.visibility = 'visible'},	'body.webAdmin a.eventDialogLink:click': function(input) {		input.style.backgroundImage = "url(/javascript/yui-ext/resources/images/grid/loading.gif)"		var unid = input.id.slice(5)				var placeholder = ($('webadmin-dlg') ? $('webadmin-dlg') : function() {			var newPlaceholder = document.createElement("div")			newPlaceholder.style.visibility = "hidden"			newPlaceholder.id = 'webadmin-dlg'			document.body.appendChild(newPlaceholder)			return $('webadmin-dlg')		}())		placeholder.innerHTML = ""				var tabs = new Ext.TabPanel({			activeTab: 0,			defaults: {autoScroll: true},			items:[				{ title: "Overview", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Overview"} }			]		})		if(/\bhasRegs\b/.test(input.className)) {			tabs.add({ title: "Registrations", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Registrations"} })		}		if(/\bhasEvals\b/.test(input.className)) {			tabs.add({ title: "Evaluations", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Evaluations"} })		}		if(			(/\bdate\b/.test(input.className) && (Date.parse(input.innerHTML.replace(" at ", " ")) - (new Date())) < 0) ||			/\bopenRoom\b/.test(input.className)) {			tabs.add({ title: "Attendance Data", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=AttendanceData"} })			tabs.add({ title: "Chat Text", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=ChatText"} })		}				var win = new Ext.Window({			closable: true,			modal: true,			plain: true,			title: (/\bonDemand\b/.test(input.className) ? "OnDemand" : "Scheduled Date") + " Information",			shadow: true,						items: tabs,			buttons: [{text: "Close", handler: function() { win.close() }}]		})				win.width = "80%"		win.height = document.body.offsetHeight*.8		win.items.first().height = document.body.offsetHeight*.8 - 41		win.show()		win.center()		input.style.backgroundImage = ""	/*				var dialog = new Ext.BasicDialog(placeholder, {			width: '80%',			height: '80%',			minWidth: 300,			minHeight: 30,			shadow: true,			modal: true,			autoTabs: true,			title: (/\bonDemand\b/.test(input.className) ? "OnDemand" : "Scheduled Date") + " Information"		})		dialog.addKeyListener(27, dialog.hide, dialog)		dialog.addButton('Close', dialog.hide, dialog)		dialog.addListener('hide', function(d) { d.destroy() })				dialog.show(input)		tabs.syncHeight()		overview.activate()				// This seems to fix an error in which IE6 wouldn't load up the first panel's content sometimes.		// Furthermore, IE7 doesn't show the dialog at all - collapsing and expanding seems to fix that.		if(navigator.appName=="Microsoft Internet Explorer") {			dialog.addListener('show', function(d) { overview.refresh(); d.collapse(); d.expand() })		}*/			},	'body.webAdmin table a': function(link) {		// This converts twisties in Notes-generated HTML views to expand and collapse via JavaScript, rather than		//   server-side. This will only work with data existing on the page - it won't look up new data.			if(link.firstChild.nodeType == 1 && link.firstChild.tagName.toUpperCase() == "IMG") {			link.onclick = function() {				var expand = /.*\/icons\/expand.gif$/.test(link.firstChild.src)							var parent = link.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode				var spanLevel = link.parentNode.parentNode.parentNode.parentNode.parentNode.colSpan				var sibling = parent.nextSibling				while(sibling) {					if(sibling.nodeType == 1) {						var isSpanRow = false, children = sibling.getElementsByTagName("td")						for(var i = 0; i < children.length; i++) {							if(children[i].nodeType == 1 && children[i].colSpan >= spanLevel) {								isSpanRow = true								break							}						}						if(!isSpanRow) {							// there may be collapsed categories beneath this one. If so, don't touch them at all							if(!(sibling.__collapsed_at_level && sibling.__collapsed_at_level < spanLevel)) {								//Effect[expand ? "Appear" : "Fade"](sibling, {duration: 0.2})								//Effect[expand ? "Grow" : "Shrink"](sibling, {duration: 0.2})								//Effect[expand?"BlindDown":"BlindUp"](sibling, {duration: 0.2})								sibling.style.display = expand ? (navigator.appName=="Microsoft Internet Explorer" ? "block" : "table-row") : "none"								sibling.__collapsed_at_level = expand ? null : spanLevel							}						} else {							break						}					}					sibling = sibling.nextSibling				}				link.firstChild.src = expand ? "/icons/collapse.gif" : "/icons/expand.gif"				// let's swap the wording for Notes's "Hide details for..." alt text as appropriate				link.firstChild.alt = link.firstChild.alt.replace(/^\w+/, expand ? "Hide" : "Show")								return false			}		}	},	"body#WebAdmin div.anEventBlock a:click, body#Evaluations div.anEventBlock a:click": function(a) {		var div = a.parentNode.parentNode.lastChild		if(div.style.display == "none") {			//Effect.BlindDown(div, {duration: 0.1})			div.style.display = "block"			a.className = "open"		} else {			//Effect.BlindUp(div, {duration: 0.1})			div.style.display = "none"			a.className = "closed"		}	}}// now join in selectors from this specific DB's rule setvar templateRuleCache = {}for(selector in DBRules) {	if(typeof TemplateRules[selector] != "undefined") {		templateRuleCache[selector] = TemplateRules[selector]		TemplateRules[selector] = function(element, event) { templateRuleCache[selector](element, event); DBRules[selector](element, event) }	} else {		TemplateRules[selector] = DBRules[selector]	}}