/*================================================== *  Default Event Source *================================================== */Timeline.DefaultEventSource = function(eventIndex) {	this._events = (eventIndex instanceof Object) ? eventIndex : new SimileAjax.EventIndex();	this._listeners = [];};Timeline.DefaultEventSource.prototype.addListener = function(listener) {	this._listeners.push(listener);};Timeline.DefaultEventSource.prototype.removeListener = function(listener) {	for (var i = 0; i < this._listeners.length; i++) {		if (this._listeners[i] == listener) {			this._listeners.splice(i, 1);			break;		}	}};Timeline.DefaultEventSource.prototype.loadXML = function(xml, url) {	var base = this._getBaseURL(url);	var wikiURL = xml.documentElement.getAttribute("wiki-url");	var wikiSection = xml.documentElement.getAttribute("wiki-section");	var dateTimeFormat = xml.documentElement.getAttribute("date-time-format");	var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);	var node = xml.documentElement.firstChild;	var added = false;	while (node != null) {		if (node.nodeType == 1) {			var description = "";			if (node.firstChild != null && node.firstChild.nodeType == 3) {				description = node.firstChild.nodeValue;			}			// instant event: default is true. Or use values from isDuration or durationEvent			var instant = (node.getAttribute("isDuration")    === null &&				node.getAttribute("durationEvent") === null) ||				node.getAttribute("isDuration") == "false" ||				node.getAttribute("durationEvent") == "false";			var evt = new Timeline.DefaultEventSource.Event( {				id: node.getAttribute("id"),				start: parseDateTimeFunction(node.getAttribute("start")),				end: parseDateTimeFunction(node.getAttribute("end")),				latestStart: parseDateTimeFunction(node.getAttribute("latestStart")),				earliestEnd: parseDateTimeFunction(node.getAttribute("earliestEnd")),				// mwra addition - string based screen 'date' for event bubble				dateLabel: node.getAttribute("dateLabel"),				instant: instant,				text: node.getAttribute("title"),				description: description,				image: this._resolveRelativeURL(node.getAttribute("image"), base),				imageHeight: node.getAttribute("imageHeight"),				imageWidth: node.getAttribute("imageWidth"),				link: this._resolveRelativeURL(node.getAttribute("link") , base),				icon: this._resolveRelativeURL(node.getAttribute("icon") , base),				color: node.getAttribute("color"),				textColor: node.getAttribute("textColor"),				hoverText: node.getAttribute("hoverText"),				classname: node.getAttribute("classname"),				tapeImage: node.getAttribute("tapeImage"),				tapeRepeat: node.getAttribute("tapeRepeat"),				caption: node.getAttribute("caption"),				eventID: node.getAttribute("eventID"),				trackNum: node.getAttribute("trackNum")			});			evt._node = node;			evt.getProperty = function(name) {				return this._node.getAttribute(name);			};			evt.setWikiInfo(wikiURL, wikiSection);			this._events.add(evt);			added = true;		}		node = node.nextSibling;	}	if (added) {			this._fire("onAddMany", []);	}};Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {	var base = this._getBaseURL(url);	var added = false;  	if (data && data.events){		var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;		var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;		var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;		var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);		for (var i=0; i < data.events.length; i++){			var event = data.events[i];			// Fixing issue 33:			// instant event: default (for JSON only) is false. Or use values from isDuration or durationEvent			// isDuration was negated (see issue 33, so keep that interpretation			var instant = event.isDuration || (event.durationEvent != null && !event.durationEvent);			var evt = new Timeline.DefaultEventSource.Event({				id: ("id" in event) ? event.id : undefined,				start: parseDateTimeFunction(event.start),				end: parseDateTimeFunction(event.end),				latestStart: parseDateTimeFunction(event.latestStart),				earliestEnd: parseDateTimeFunction(event.earliestEnd),				instant: instant,				// mwra addition - string based screen 'date' for event bubble				dateLabel: event.dateLabel,				text: event.title,				description: event.description,				image: this._resolveRelativeURL(event.image, base),				imageHeight: event.imageHeight,				imageWidth: event.imageWidth,				link: this._resolveRelativeURL(event.link , base),				icon: this._resolveRelativeURL(event.icon , base),				color: event.color,				textColor: event.textColor,				hoverText: event.hoverText,				classname: event.classname,				tapeImage: event.tapeImage,				tapeRepeat: event.tapeRepeat,				caption: event.caption,				eventID: event.eventID,				trackNum: event.trackNum			});			evt._obj = event;			evt.getProperty = function(name) {				return this._obj[name];			};			evt.setWikiInfo(wikiURL, wikiSection);			this._events.add(evt);			added = true;		}	}	if (added) {		this._fire("onAddMany", []);	}};/* *  Contributed by Morten Frederiksen, http://www.wasab.dk/morten/ */Timeline.DefaultEventSource.prototype.loadSPARQL = function(xml, url) {	var base = this._getBaseURL(url);	var dateTimeFormat = 'iso8601';	var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);	if (xml == null) {		return;	}	/*	 *  Find <results> tag	 */	var node = xml.documentElement.firstChild;	while (node != null && (node.nodeType != 1 || node.nodeName != 'results')) {		node = node.nextSibling;	}	var wikiURL = null;	var wikiSection = null;	if (node != null) {		wikiURL = node.getAttribute("wiki-url");		wikiSection = node.getAttribute("wiki-section");		node = node.firstChild;	}	var added = false;	while (node != null) {		if (node.nodeType == 1) {			var bindings = { };			var binding = node.firstChild;			while (binding != null) {				if (binding.nodeType == 1 && 					binding.firstChild != null && 					binding.firstChild.nodeType == 1 && 					binding.firstChild.firstChild != null && 					binding.firstChild.firstChild.nodeType == 3) {					bindings[binding.getAttribute('name')] = binding.firstChild.firstChild.nodeValue;				}				binding = binding.nextSibling;			}			if (bindings["start"] == null && bindings["date"] != null) {				bindings["start"] = bindings["date"];			}			// instant event: default is true. Or use values from isDuration or durationEvent			var instant = (bindings["isDuration"]    === null &&				bindings["durationEvent"] === null) ||				bindings["isDuration"] == "false" ||				bindings["durationEvent"] == "false";			var evt = new Timeline.DefaultEventSource.Event({				id: bindings["id"],				start: parseDateTimeFunction(bindings["start"]),				end: parseDateTimeFunction(bindings["end"]),				latestStart: parseDateTimeFunction(bindings["latestStart"]),				earliestEnd: parseDateTimeFunction(bindings["earliestEnd"]),				instant: instant, // instant				// mwra addition - string based screen 'date' for event bubble				dateLabel: bindings["dateLabel"],				text: bindings["title"], // text				description: bindings["description"],				image: this._resolveRelativeURL(bindings["image"], base),				imageHeight: bindings["imageHeight"],				imageWidth: bindings["imageWidth"],				link: this._resolveRelativeURL(bindings["link"] , base),				icon: this._resolveRelativeURL(bindings["icon"] , base),				color: bindings["color"],				textColor: bindings["textColor"],				hoverText: bindings["hoverText"],				caption: bindings["caption"],				classname: bindings["classname"],				tapeImage: bindings["tapeImage"],				tapeRepeat: bindings["tapeRepeat"],				eventID: bindings["eventID"],				trackNum: bindings["trackNum"]			});			evt._bindings = bindings;			evt.getProperty = function(name) {				return this._bindings[name];			};			evt.setWikiInfo(wikiURL, wikiSection);			this._events.add(evt);			added = true;		}		node = node.nextSibling;	}	if (added) {			this._fire("onAddMany", []);	}};Timeline.DefaultEventSource.prototype.add = function(evt) {	this._events.add(evt);	this._fire("onAddOne", [evt]);};Timeline.DefaultEventSource.prototype.addMany = function(events) {	for (var i = 0; i < events.length; i++) {		this._events.add(events[i]);	}	this._fire("onAddMany", []);};Timeline.DefaultEventSource.prototype.clear = function() {	this._events.removeAll();	this._fire("onClear", []);};Timeline.DefaultEventSource.prototype.getEvent = function(id) {	return this._events.getEvent(id);};Timeline.DefaultEventSource.prototype.getEventIterator = function(startDate, endDate) {	return this._events.getIterator(startDate, endDate);};Timeline.DefaultEventSource.prototype.getEventReverseIterator = function(startDate, endDate) {	return this._events.getReverseIterator(startDate, endDate);};Timeline.DefaultEventSource.prototype.getAllEventIterator = function() {	return this._events.getAllIterator();};Timeline.DefaultEventSource.prototype.getCount = function() {    return this._events.getCount();};Timeline.DefaultEventSource.prototype.getEarliestDate = function() {	return this._events.getEarliestDate();};Timeline.DefaultEventSource.prototype.getLatestDate = function() {	return this._events.getLatestDate();};Timeline.DefaultEventSource.prototype._fire = function(handlerName, args) {	for (var i = 0; i < this._listeners.length; i++) {		var listener = this._listeners[i];		if (handlerName in listener) {			try {				listener[handlerName].apply(listener, args);			} catch (e) {				SimileAjax.Debug.exception(e);			}		}	}};Timeline.DefaultEventSource.prototype._getBaseURL = function(url) {	if (url.indexOf("://") < 0) {		var url2 = this._getBaseURL(document.location.href);		if (url.substr(0,1) == "/") {			url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;		} else {			url = url2 + url;		}	}	var i = url.lastIndexOf("/");	if (i < 0) {		return "";	} else {		return url.substr(0, i+1);	}};Timeline.DefaultEventSource.prototype._resolveRelativeURL = function(url, base) {	if (url == null || url == "") {		return url;	} else if (url.indexOf("://") > 0) {		return url;	} else if (url.substr(0,1) == "/") {		return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;	} else {		return base + url;	}};Timeline.DefaultEventSource.Event = function(args) {	//	// Attention developers!	// If you add a new event attribute, please be sure to add it to	// all three load functions: loadXML, loadSPARCL, loadJSON. 	// Thanks!	//	// args is a hash/object. It supports the following keys. Most are optional	//   id            -- an internal id. Really shouldn't be used by events.	//                    Timeline library clients should use eventID	//   eventID       -- For use by library client when writing custom painters or	//                    custom fillInfoBubble    	//   start	//   end	//   latestStart	//   earliestEnd	//   instant      -- boolean. Controls precise/non-precise logic & duration/instant issues	//   text         -- event source attribute 'title' -- used as the label on Timelines and in bubbles.	//   description  -- used in bubbles   	//   image        -- used in bubbles	//   imageHeight  -- used in bubbles	//   imageWidth   -- used in bubbles	//   link         -- used in bubbles	//   icon         -- on the Timeline	//   color        -- Timeline label and tape color	//   textColor    -- Timeline label color, overrides color attribute	//   hoverText    -- deprecated, here for backwards compatibility.	//                   Superceeded by caption	//   caption      -- tooltip-like caption on the Timeline. Uses HTML title attribute 	//   classname    -- used to set classname in Timeline. Enables better CSS selector rules	//   tapeImage    -- background image of the duration event's tape div on the Timeline	//   tapeRepeat   -- repeat attribute for tapeImage. {repeat | repeat-x | repeat-y }	//   dateLabel    -- custom replacement for date/time string displayed in event bubble (string literal)	function cleanArg(arg) {		// clean up an arg		return (args[arg] != null && args[arg] != "") ? args[arg] : null;	}	var id = args.id ? args.id.trim() : "";	this._id = id.length > 0 ? id : Timeline.EventUtils.getNewEventID();	this._instant = args.instant || (args.end == null);	this._start = args.start;	this._end = (args.end != null) ? args.end : args.start;	this._latestStart = (args.latestStart != null) ?		args.latestStart : (args.instant ? this._end : this._start);	this._earliestEnd = (args.earliestEnd != null) ? args.earliestEnd : this._end;	// check sanity of dates since incorrect dates will later cause calculation errors	// when painting	var err=[];	if (this._start > this._latestStart) {		this._latestStart = this._start;		err.push("start is > latestStart");	}	if (this._start > this._earliestEnd) {		this._earliestEnd = this._latestStart;		err.push("start is > earliestEnd");	}	if (this._start > this._end) {		this._end = this._earliestEnd;		err.push("start is > end");	}	if (this._latestStart > this._earliestEnd) {		this._earliestEnd = this._latestStart;		err.push("latestStart is > earliestEnd");	}	if (this._latestStart > this._end) {		this._end = this._earliestEnd;		err.push("latestStart is > end");	}	if (this._earliestEnd > this._end) {		this._end = this._earliestEnd;		err.push("earliestEnd is > end");	}	this._eventID = cleanArg('eventID');	this._text = (args.text != null) ? SimileAjax.HTML.deEntify(args.text) : ""; // Change blank titles to ""	if (err.length > 0) {		this._text += " PROBLEM: " + err.join(", ");	}	this._description = SimileAjax.HTML.deEntify(args.description);	this._image = cleanArg('image');	this._imageHeight = cleanArg('imageHeight');	this._imageWidth = cleanArg('imageWidth');	this._link =  cleanArg('link');	this._title = cleanArg('hoverText');	this._title = cleanArg('caption');	this._dateLabel = cleanArg('dateLabel');	this._icon = cleanArg('icon');	this._color = cleanArg('color');	this._textColor = cleanArg('textColor');	this._classname = cleanArg('classname');	this._tapeImage = cleanArg('tapeImage');	this._tapeRepeat = cleanArg('tapeRepeat');	this._trackNum = cleanArg('trackNum');	if (this._trackNum != null) {			this._trackNum = parseInt(this._trackNum);	}	this._wikiURL = null;	this._wikiSection = null;};Timeline.DefaultEventSource.Event.prototype = {	getID:          function() { return this._id; },	isInstant:      function() { return this._instant; },	isImprecise:    function() { return this._start != this._latestStart || this._end != this._earliestEnd; },	getStart:       function() { return this._start; },	getEnd:         function() { return this._end; },	getLatestStart: function() { return this._latestStart; },	getEarliestEnd: function() { return this._earliestEnd; },	getEventID:     function() { return this._eventID; },	getText:        function() { return this._text; }, // title	getDescription: function() { return this._description; },	getImage:       function() { return this._image; },	getImageHeight: function() { return this._imageHeight; },	getImageWidth:  function() { return this._imageWidth; },	getLink:        function() { return this._link; },	getIcon:        function() { return this._icon; },	getColor:       function() { return this._color; },	getTextColor:   function() { return this._textColor; },	getClassName:   function() { return this._classname; },	getTapeImage:   function() { return this._tapeImage; },	getTapeRepeat:  function() { return this._tapeRepeat; },	getTrackNum:    function() { return this._trackNum; },	getDateLabel:   function() { return this._dateLabel; },	getProperty:    function(name) { return null; },	getWikiURL:     function() { return this._wikiURL; },	getWikiSection: function() { return this._wikiSection; },	setWikiInfo: function(wikiURL, wikiSection) {		this._wikiURL = wikiURL;		this._wikiSection = wikiSection;	},	fillDescription: function(elmt) {		elmt.innerHTML = this._description;	},	fillWikiInfo: function(elmt) {		// Many bubbles will not support a wiki link. 		// 		// Strategy: assume no wiki link. If we do have		// enough parameters for one, then create it.		elmt.style.display = "none"; // default		if (this._wikiURL == null || this._wikiSection == null) {			return; // EARLY RETURN		}		// create the wikiID from the property or from the event text (the title)      		var wikiID = this.getProperty("wikiID");		if (wikiID == null || wikiID.length == 0) {			wikiID = this.getText(); // use the title as the backup wiki id		}		if (wikiID == null || wikiID.length == 0) {			return; // No wikiID. Thus EARLY RETURN		}		// ready to go...		elmt.style.display = "inline";		wikiID = wikiID.replace(/\s/g, "_");		var url = this._wikiURL + this._wikiSection.replace(/\s/g, "_") + "/" + wikiID;		var a = document.createElement("a");		a.href = url;		a.target = "new";		a.innerHTML = Timeline.strings[Timeline.clientLocale].wikiLinkLabel;		elmt.appendChild(document.createTextNode("["));		elmt.appendChild(a);		elmt.appendChild(document.createTextNode("]"));	},	fillTime: function(elmt, labeller) {		if (this._dateLabel) {			elmt.innerHTML = this._dateLabel;		}		/*if (this._instant) {			if (this.isImprecise()) {				elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));				elmt.appendChild(elmt.ownerDocument.createElement("br"));				elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));			} else {				elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));			}		} else {			if (this.isImprecise()) {				elmt.appendChild(elmt.ownerDocument.createTextNode(				labeller.labelPrecise(this._start) + " ~ " + labeller.labelPrecise(this._latestStart)));				elmt.appendChild(elmt.ownerDocument.createElement("br"));				elmt.appendChild(elmt.ownerDocument.createTextNode(				labeller.labelPrecise(this._earliestEnd) + " ~ " + labeller.labelPrecise(this._end)));			} else {				elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));				elmt.appendChild(elmt.ownerDocument.createElement("br"));				elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));			}		}*/	},	fillInfoBubble: function(elmt, theme, labeller) {		var doc = elmt.ownerDocument;		var title = this.getText();		var link = this.getLink();		var image = this.getImage();		if (image != null) {			var divImage = doc.createElement("div");			var img = doc.createElement("img");			img.src = image;			if (this._imageWidth) {				img.width = this.getImageWidth();				img.height = this.getImageHeight();			}			//img.style.float = "none"; // ensures image stays above text			theme.event.bubble.imageStyler(img);			//elmt.appendChild(img);			divImage.className="timeline-event-bubble-image-block";			divImage.appendChild(img);			elmt.appendChild(divImage);		}		var divTitle = doc.createElement("div");		var textTitle = doc.createTextNode(title);		if (link != null) {			var a = doc.createElement("a");			a.href = link;			a.target="new";			a.title=title;			a.appendChild(textTitle);			divTitle.appendChild(a);		} else {			divTitle.appendChild(textTitle);		}		theme.event.bubble.titleStyler(divTitle);		elmt.appendChild(divTitle);				var divBody = doc.createElement("div");		this.fillDescription(divBody);		theme.event.bubble.bodyStyler(divBody);		elmt.appendChild(divBody);		var divTime = doc.createElement("div");		this.fillTime(divTime, labeller);		theme.event.bubble.timeStyler(divTime);		elmt.appendChild(divTime);		var divWiki = doc.createElement("div");		this.fillWikiInfo(divWiki);		theme.event.bubble.wikiStyler(divWiki);		elmt.appendChild(divWiki);	}};
