// 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function toggleAdminSection(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"	}}function toggleNotesViewCategory() {	// 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.		var parent = this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode	var spanLevel = this.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[this.collapsed ? "Appear" : "Fade"](sibling, {duration: 0.2})					//Effect[this.collapsed ? "Grow" : "Shrink"](sibling, {duration: 0.2})					//Effect[this.collapsed ? "BlindDown" : "BlindUp"](sibling, {duration: 0.2})					sibling.style.display = this.collapsed ? (navigator.appName=="Microsoft Internet Explorer" ? "block" : "table-row") : "none"					sibling.__collapsed_at_level = this.collapsed ? null : spanLevel				}			} else {				break			}		}		sibling = sibling.nextSibling	}	this.firstChild.src = this.collapsed ? MCL.images.openTwistie : MCL.images.closedTwistie	// let's swap the wording for Notes's "Hide details for..." alt text as appropriate	this.firstChild.alt = this.firstChild.alt.replace(/^\w+/, this.collapsed ? "Hide" : "Show")	this.collapsed = !this.collapsed		return false}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(/^outputformat=/i.test(params[i]) || /^linksbarsite=/i.test(params[i])) {					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)) {							if(/^outputformat=/i.test(params[i])) {								links[j].href += links[j].href.indexOf("?") > -1 ? "&OutputFormat=" + params[i].split("=")[1] : "?Open&OutputFormat=" + params[i].split("=")[1]							}							if(/^linksbarsite=/i.test(params[i])) {								links[j].href += links[j].href.indexOf("?") > -1 ? "&LinksbarSite=" + params[i].split("=")[1] : "?Open&LinksbarSite=" + params[i].split("=")[1]							}						}					}				}			}		}	},	'form': 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 a.eventDialogLink:click': function(input) {		input.style.backgroundImage = "url(/javascript/yui-ext/resources/images/grid/loading.gif)"		var unid = input.id.slice(5)				var tabs = new Ext.TabPanel({			activeTab: 0,			defaults: {autoScroll: true},			id: 'webadmin-tabs',			items:[				{ title: "Overview", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Overview"} }			]		})		if(/\bhasRegs\b/.test(input.className)) {			tabs.add({				title: "Registrations",				tbar: [{disabled: true}],				autoLoad: {					url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Registrations",					callback: function(el, success, response) {						var tab = Ext.ComponentMgr.get("webadmin-tabs").getActiveTab()						$(el.dom).getElementsBySelector("ul.actions li").each(function(link) {							tab.getTopToolbar().add(liToAction(link))							link.style.display = 'none'						})					}				}			})			tabs.add({ title: "Registrations by Type", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=RegistrationsByType"} })			//tabs.add({ title: "Registrations by Type", autoLoad: {url: "ChartEventRegs?Open&UNID=" + unid, scripts: true} })		}		if(/\bhasEvals\b/.test(input.className)) {			tabs.add({ title: "Evaluations", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Evaluations"} })		}		if(			(true || (/\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",				tbar: [{disabled: true}],				autoLoad: {					url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=ChatText",					callback: function(el, success, response) {						var tab = Ext.ComponentMgr.get("webadmin-tabs").getActiveTab()												$(el.dom).getElementsBySelector("ul.actions li").each(function(link) {							tab.getTopToolbar().add(liToAction(link))							link.style.display = 'none'						})					}				}			})			tabs.add({ title: "Time Line", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=TimeLine"} })			tabs.add({ title: "Polling", autoLoad: {url: "DateSummary?Open&ParentUNID=" + unid + "&Tab=Polling"} })		}				var win = new Ext.Window({			closable: true,			modal: true,			plain: true,			title: (				/\bonDemand\b/.test(input.className) ? "OnDemand" :				/\bopenRoom\b/.test(input.className) ? "Open Room" :				"Scheduled Date"			) + " Information",			shadow: true,						items: tabs,			buttons: [{text: "Close", handler: function() { win.close() }}],			autoHeight: true		})				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 = ""			},	'body.webAdmin div.notesView table a img': function(img) {		img.parentNode.className += " twistie"		img.parentNode.collapsed = false		img.parentNode.onclick = toggleNotesViewCategory	},	"body.webAdmin div.anEventBlock a:click": function(a) { toggleAdminSection(a) },	/*"body.webAdmin table.webAdmin": function(table) {		//table.style.width = 500		var grid = new Ext.grid.TableGrid(table.id, {			stripeRows: true,			disableSelection: true,			sm: null,			trackMouseOver: false,			style: "margin: 10px"		})		grid.render()	},*/	"body.webAdmin table.grid": function(table) {		var grid = new Ext.grid.TableGrid(table, {			stripeRows: true,			disableSelection: true,			selModel: null,			trackMouseOver: false,			enableColumnHide: true		})		//grid.render()	},	/*"div.notesView > table > tbody > tr > td:last-child": function(td) { td.style.display = 'none' },	"div.notesView th": function(th) { th.width = th.offsetWidth },	"div.notesView td span.remove": function(span) {		var td = span.parentNode.tagName == 'FONT' ? span.parentNode.parentNode : span.parentNode		td.style.display = 'none'		td.previousSibling.colSpan++	},	"div.notesView th:first-child": function(th) { th.className += ' firstChild' },	"div.notesView th:last-child": function(th) {		var realLast = th		while(realLast.innerHTML == '') { realLast.style.display = 'none'; realLast = realLast.previousSibling }		realLast.className += ' lastChild'		realLast.width = "auto"	},	"div.notesView td table": function(table) {		var tr = table.parentNode		while(tr != null && tr.tagName != "TR") { tr = tr.parentNode }		if(tr) { tr.className += " category" }		//tr.onclick = function() { $(this).getElementsBySelector("a.twistie")[0].onclick() }	},*/	"a#contactPMs:click": function(a) {		var fs = new Ext.FormPanel({			labelAlign: 'left',			labelWidth: 85,			autoWidth: true,			autoHeight: true,			border: false,			frame: true,						items: [				new Ext.form.FieldSet({					border: false,					autoHeight: true,					defaultType: 'textfield',					items: [{						fieldLabel: 'Your Email Address',						name: 'SenderEmail',						width: '50%',						allowBlank: false,						vtype: 'email'					}, {						fieldLabel: 'Category',						name: 'Category',						xtype: 'combo',						editable: false,						store: ["Question", "Feature Request"],						allowBlank: false					}, {					/*	fieldLabel: 'Subject',						name: 'MessageSubject',						width: '95%',						allowBlank: false					}, {*/						fieldLabel: 'Body',						name: 'PlainBody',						xtype: 'textarea',						anchor: '95%',						height: 200,						allowBlank: false					}]				})			]		})				var win = new Ext.Window({			width: 600,			autoHeight: true,			closable: true,			draggable: true,			resizable: true,			modal: true,			plain: true,			title: "Contact Program Managers",			shadow: true,						items: fs,			buttons: [{text: "Send Email", handler: function() {				if(fs.getForm().isValid()) {					var form = fs.getForm().getEl().dom					form.method = 'post'					form.action = 'Admin+Site+Email?CreateDocument'					$(form).request({						onSuccess: function(response) {							win.close()						}					})				}			}}]		})				win.show()		win.center()	}}/* Now join in selectors from this specific DB's rule set * I originally had this combine the two such that, when the rules had the * same patterns, they both executed. I think it will be more useful to * have the DB-specific rules override the template ones entirely. */var templateRuleCache = {}for(selector in DBRules) {	TemplateRules[selector] = DBRules[selector]}