// $E Development H.D$
// $D http://wiki.newsonline.tc.nca.bbc.co.uk/wiki/moin.cgi/HieuLuuDanh/MilCasualties

/**
 * @fileOverview Military Casualties
 * @author Hieu Luu Danh &lt;hieu.danh (at) bbc.co.uk&gt;
 * @version 1.1.0-rls
 */

(function () {
	
	function createObject(strName) {
		var nameParts = strName.split("."),
		i = 0,
		len = nameParts.length,
		obj = window;
		
		for (; i < len; i++){
			if (obj[nameParts[i]] === undefined) {
				obj[nameParts[i]] = {};
			}
			
			obj = obj[nameParts[i]];
		}
	}
	
	createObject("bbc.fmtj.apps");
	
	bbc.fmtj.apps.createObject = createObject;
	
})();

bbc.fmtj.apps.createObject("bbc.fmtj.csd.MilCasualties");

bbc.fmtj.csd.MilCasualties = {
	// ================ MASTER ================ //
	Master: function Master(glow) {
		var that = {};
		
		bbc.fmtj.csd.MilCasualties.glow = glow || false;
		
		if (!bbc.fmtj.csd.MilCasualties.glow) {
			throw("Glow not given");
		}
		
		// MVC instantiation
		var model = new bbc.fmtj.csd.MilCasualties.Model(),
			controller = new bbc.fmtj.csd.MilCasualties.Controller(model),
			view = new bbc.fmtj.csd.MilCasualties.View(controller);
		
		// Meant to be called on page load to trigger everything else
		function init(opts) { // [Public] init()
			// input handling
			var opts = opts || {controller: {}, view: {}, model: {}};
			
			var rcode = true; // return code
			// blanken map
			if(rcode) { rcode = view.init(opts); }
			// trigger 1
			if(rcode) { rcode = controller.initialRequestHandler(opts); }
		}
		that.init = init;
		
		
		return that;
	},
	// ================ MODEL ================ //
	Model: function Model() {
		var that = {}; // durable object
		
		var glow = bbc.fmtj.csd.MilCasualties.glow; // glow quickhand
		
		// Attributes
		var dataModel = {
			data: {},
			additions: {},
			separations: {
				byYear: {
					iraq: {},
					afghanistan: {},
					combined: {}
				}
			}
		};
		
		var _minYear_store = null,
			_maxYear_store = null; // stores the queries for {min,max}Year
		
		function get_dataModel() { // [Package] data()
			return dataModel.data;
		}
		function get_errorMessage() { // [Package] error()
			return dataModel.additions.error;
		}
		function get_aggregatedYear(year, selector) { // [Package] getYear()
			var selector = selector || "combined"; // defaults to combined
			if (typeof(dataModel.separations.byYear[selector][year]) == 'undefined') {
				return null;
			} else {
				return dataModel.separations.byYear[selector][year];
			}
		}
		function get_maxYear() { // [Package] getMaxYear()
			var maxYear = 1980;
			if (_maxYear_store !== null) {
				maxYear = _maxYear_store;
			} else {
				for (var year in dataModel.separations.byYear.combined) {
					if (year > maxYear) {
						maxYear = year;
					} else {
						// continue;
					}
				}
				_maxYear_store = maxYear;
			}
			return maxYear;
		}
		function get_minYear() { // [Package] getMinYear()
			var minYear = 2099;
			// skip the loop if already done
			if (_minYear_store !== null) {
				minYear = _minYear_store;
			} else {
				for (var year in dataModel.separations.byYear.combined) {
					if (year < minYear) {
						minYear = year;
					} else {
						// continue;
					}
				}
				_minYear_store = minYear;
			}
			return minYear;
		}
		function get_totalNumberOfDeathsByYear(year, selector) { // [Package] getTotalByYear()
			var selector = selector || "combined";
			if (typeof(dataModel.separations.byYear[selector][year]) == 'undefined') {
				return 0;
			} else {
				return dataModel.separations.byYear[selector][year].length;
			}
		}
		function get_individualItemById(id) { // [Package] getId()
			// uses unique_id
			for (var country in dataModel.data) {
				for (var i = 0, il = dataModel.data[country].length; i < il; i++) {
					if (dataModel.data[country][i].unique_id == id) {
						return dataModel.data[country][i];
					} else {
						continue;
					}
				}
			}
			return null; // if we arrive here, it's not found
		}
		function get_allItemsById() { // [Package] getAllId()
			var allItems = {};
			for (var country in dataModel.data) {
				for (var i = 0, il = dataModel.data[country].length; i < il; i++) {
					allItems[dataModel.data[country][i].unique_id.toString()] = ObjectHandler.getCloneOfObject(dataModel.data[country][i]);
				}
			}
			return allItems;
		}
		that.data = get_dataModel;
		that.error = get_errorMessage;
		that.getYear = get_aggregatedYear;
		that.getMaxYear = get_maxYear;
		that.getMinYear = get_minYear;
		that.getTotalByYear = get_totalNumberOfDeathsByYear;
		that.getId = get_individualItemById;
		that.getAllId = get_allItemsById;
		
		// Asserts
		function _isValid() { // [Package] _isValid()
			return true;
		}
		that._isValid = _isValid;
		function _myName() { // [Package] _getName()
			return "Model";
		}
		that._getName = _myName;
		
		// plug M1
		/***************
		- Interact with data -
		Trigger: Controller-driven
		Output: Success boolean
		Stores: dataModel [accessed via data()]
		
		Internal:
		- Works with json string data
		- glow.net and error handling
		- Basic string processing to object
		****************/
		function retrieveDataHandler(opts) { // [Package] retrieveDataHandler()
			opts.model.dataLocation = opts.model.dataLocation || "/nol/shared/spl/hi/in_depth/uk_military_casualties/combined/js/uk_casualties_combined_json.js";
			var dataLocation = opts.model.dataLocation, // short hand
				netGetParams = { // parameters for retrieving data
				 useCache: false,
				 async: false,
				 onError: function(response) {
				 	 success = false;
				 	 dataModel.additions.error = response.status + ": " + response.statusText();
				 },
				 onLoad: function(response) {
				 	 success = true;
				 	 dataModel.data = response.json(); // full data exported
				 }
				}, // end netGetParams
				success = false; // error or not
			
			// requires glow.net, defined in controller
			try {
				glow.net.get(dataLocation, netGetParams);
			} catch(e) {
				dataModel.additions.error = e.message;
			}
			
			return success;
		}
		that.retrieveDataHandler = retrieveDataHandler;
		// end plug M1 ************************
		
		// plug M2
		/***************
		- Sort the data -
		Trigger: Controller-driven
		Output: None
		
		Internal:
		- Separates and process the data inside dataModel
		****************/
		function sortData() { // [Package] sortData()
			// modify id
			// sortData_id(); DEPRECATED
			// name modification
			sortData_name();
			// sort the year
			sortData_year();
		}
		that.sortData = sortData;
		// end plug M2 ************************
		
		// [HELPER] plug M2 001
		// Sorting the year
		function sortData_year() { // [Internal] [Private]
			// work directly with the dataModel
			var origData = dataModel.data;
			var byYearMod = dataModel.separations.byYear; // byYearMod is a reference
			var slcArray = [];
			/* Speed optimization :
				Problem: this sortData risk to have the following for loop repeated
				causing delays
				Solution: assume iraq or afghanistan as first, collect it into an array
				and use that array as reference
			*/
			for (var slc in origData) {
				if (glow.lang.hasOwnProperty(origData, slc)) { // safeguard
				slcArray.push(slc);
				}
			}
			// Asserts
			if (slcArray.length != 2) {
				throw("Unexpected data structure");
			}
			
			//var iraqFirst = (slcArray[0] == "iraq");
			var afghanistanFirst = (slcArray[0] == "afghanistan");
			
			// end Asserts
			
			// looping over to sort data now
			var loopElm; // what is the element being looped in
			var contextSlc; // what's the context (iraq/afghanistan)
			var tag; // differing tags
			
			loopElm = origData[slcArray[0]].slice(); // array cloning
			contextSlc = slcArray[0]; // afghanistan_if_afghan_first should be here
			for (var i = 0, il = loopElm.length; i < il; i++) {
				// where to put that
				var elmYear = loopElm[i].date.year;
				// if it doesn't exist yet, create it
				if (typeof(byYearMod[contextSlc][elmYear]) == 'undefined') {
					byYearMod[contextSlc][elmYear] = [];
				}
				byYearMod[contextSlc][elmYear].push(loopElm[i]);
			}
			
			loopElm = origData[slcArray[1]].slice(); // array cloning
			contextSlc = slcArray[1]; // iraq_if_afghan_first should be here
			for (var i = 0, il = loopElm.length; i < il; i++) {
				// where to put that
				var elmYear = loopElm[i].date.year;
				// if it doesn't exist yet, create it
				if (typeof(byYearMod[contextSlc][elmYear]) == 'undefined') {
					byYearMod[contextSlc][elmYear] = [];
				}
				byYearMod[contextSlc][elmYear].push(loopElm[i]);
			}
			
			// now combine them
			// clone iraq_if_afghan_first onto combined and loop over afghan_if_afghan_first
			// to push it into combined
			// metadata inside combined for iraq/afghan separation
			// glow.lang.apply(byYearMod.combined, byYearMod[slcArray[0]]);
			byYearMod.combined = ObjectHandler.getCloneOfObject(byYearMod[slcArray[1]]);
			// byYearMod.combined[2010] = {}; // << combined != iraq
			//byYearMod.combined = byYearMod[slcArray[0]];
			// tag iraq_if_afghan_first
			tag = (afghanistanFirst) ? "iraq" : "afghanistan";
			for (var year in byYearMod.combined) {
				for (var i = 0, il = byYearMod.combined[year].length; i < il; i++) {
					byYearMod.combined[year][i]._metadata = { slc: tag };
				}
			} // end tag iraq_if_afghan_first
			// push afghan_if_afghan_first
			for (var year in byYearMod[slcArray[0]]) {
				if (typeof(byYearMod.combined[year]) == 'undefined') { // create it if 404
					byYearMod.combined[year] = {};
				}
				for (var i = 0, il = byYearMod[slcArray[0]][year].length; i < il; i++) {
					// push..push!
					if(typeof(byYearMod.combined[year].push) != 'undefined') {
					byYearMod.combined[year].push(
					ObjectHandler.getCloneOfObject(byYearMod[slcArray[0]][year][i])
					);
					} else {
						byYearMod.combined[year] = [];
						byYearMod.combined[year].push(
						ObjectHandler.getCloneOfObject(byYearMod[slcArray[0]][year][i]));
					}
					// not shallow copying
				}
			} // end push afghan_if_afghan_first
			// tag afghan_if_afghan_first
			tag = (afghanistanFirst) ? "afghanistan" : "iraq";
			for (var year in byYearMod.combined) {
				for (var i = 0, il = byYearMod.combined[year].length; i < il; i++) {
					if (typeof(byYearMod.combined[year][i]._metadata) == 'undefined') {
						byYearMod.combined[year][i]._metadata = {};
						byYearMod.combined[year][i]._metadata.slc = tag;
					}
					if (typeof(byYearMod.combined[year][i]._metadata) != 'undefined') {
						if (typeof(byYearMod.combined[year][i]._metadata.slc) == 'undefined') {
							byYearMod.combined[year][i]._metadata.slc = tag;
						}
					}
				}
			} // end tag afghan_if_afghan_first
			
			
			// now copy that over to the real dataModel (shallow copy)
			// dataModel.separations.byYear = byYearMod;
			// !! byYearMod is a reference, no need to copy back
			
			// and we're done
			return true;
		}
		// end [HELPER] plug M2 001 ***********
		
		// [HELPER] plug M2 002
		// Deep-copy
		var ObjectHandler = {
			getCloneOfObject: function getCloneOfObject(oldObject) { // [Internal] [Private]
				var tempClone;
				if (oldObject === null) {
					tempClone = null;
				} else if (typeof(oldObject) == "object" && typeof(oldObject.length) != 'undefined') {
					//array
					tempClone = [];
					for (var i = 0, il = oldObject.length; i < il; ++i ) {
						tempClone[i] = ObjectHandler.getCloneOfObject(oldObject[i]);
					}
				} else if (typeof(oldObject) == "object") {
					//object
					tempClone = {};
					for (var i in oldObject) {
						tempClone[i] = ObjectHandler.getCloneOfObject(oldObject[i]);
					}
				} else {
					//plain
					tempClone = oldObject;
				}
				return tempClone;
			}
		};
		// end [HELPER] plug M2 002 ***********
		
		// [HELPER] plug M2 003
		// Append the country initials to id
		function sortData_id() { // [Internal] [Private] [DEPRECATED]
			// work directly with the dataModel
			for (var country in dataModel.data) {
				for (var i = 0, il = dataModel.data[country].length; i < il; i++) {
					dataModel.data[country][i].unique_id = country.substring(0,1) + dataModel.data[country][i].id;
				}
			}
		}
		// end [HELPER] plug M2 003 ***********
		
		// [HELPER] plug M2 003
		// Append the country initials to id
		function sortData_name() { // [Internal] [Private]
			// replaces names
			for (var country in dataModel.data) {
				for (var i = 0, il = dataModel.data[country].length; i < il; i++) {
					if(dataModel.data[country][i].name === null) {
						dataModel.data[country][i].name = "Not released";
						continue;
					}
					dataModel.data[country][i].name = dataModel.data[country][i].name.split(", ")[1] + " " + dataModel.data[country][i].name.split(", ")[0];
				}
			}
		}
		// end [HELPER] plug M2 003 ***********
		
		return that;
	},
	// ================ CONTROLLER ================ //
	Controller: function Controller(model) {
		var that = {}; // durable object
		
		var glow = bbc.fmtj.csd.MilCasualties.glow; // glow quickhand
		
		// Asserts
		function _isValid() { // [Package] _isValid()
			return true;
		}
		that._isValid = _isValid;
		function _myName() { // [Package] _getName()
			return "Controller";
		}
		that._getName = _myName;
		
		// Object declarations
		var view; // View instance
		var model = model; // Model instance
		/*assert*/ if (model._isValid() !== true) {
			throw("Model object error");
		}
		var enclosureElm;
		var panelInstance,
			elm_panel; // panel
		var _evnt_opt; // external opts for event
		var navbarYdef,
			navbarXdef,
			navbarHdef,
			maxScrollElm,
			navbarAnimation; // for navbar scrolling
		// Accumulates
		function accumulator(o) { // [Package] announce()
			if (o._getName() == "View") {
				view = o;
			} else {
				throw("Object given not recognized");
			}
		}
		that.announce = accumulator;
		
		// Handles
		
		// plug C1
		/***************
		- Initial Request -
		Trigger: User-driven, on initial page request
		Output: None, modifies View directly
		
		Internal:
		- Works with data retrieved
		- Assess init situation - e.g. do we have a hash or not?
		- Assess error handling - show error message if error on retrieval
		- Stores the retrieved data inside the controller object for future referencing
		- Modifies View to show init state of View
		- Attach all events to enable View interaction with Controller
		****************/
		function initialRequestHandler(opts) { // [Public] initialRequestHandler()
			
			enclosureElm = glow.dom.get(opts.controller.enclosureId.glow); // the element
			
			// export opts for event fx
			_evnt_opt = opts.controller;
			// blank State
			view.blankStateView(enclosureElm);
			
			// requirements : glow.dom, glow.net, glow.events, glow.anim, glow.widgets.Panel, glow.tweens
			var _req = [ glow.dom, glow.net, glow.events, glow.anim, glow.widgets.Panel, glow.tweens ];
			for (var i = 0, il = _req.length; i < il; i++) {
				if (typeof(_req[i]) == 'undefined') {
					throw("Controller:initialRequestHandler() - missing requirement");
				}
			}
			// input handling
			// we should always already have .controller, .model and .view
			
			// first of all, retrieve data
			var success = model.retrieveDataHandler(opts); // passes opts
			if (!success) {
				// handle the error
				if (enclosureElm.length === 0) {
					throw("Enclosure element specified not found");
				}
				enclosureElm.empty();
				enclosureElm.text("We're sorry but an error occured with our data. (" + model.error() + ")");
				enclosureElm.css("display", "block");
				enclosureElm.css("color", "#ff0000");
				// and return false
				return false;
			}
			
			// hurray, we've got the data!
			// did the user specify a hash?
			var hashDirv = window.location.hash; // directive for future development
			if (hashDirv.match(/iraq/)) {
				hashDirv = "iraq";
			} else if (hashDirv.match(/afghan/)) {
				hashDirv = "afghanistan";
			} else {
				hashDirv = "combined";
			}
			// sort the data please
			model.sortData();
			// test:
			// console.log(model.getYear(2008));
			
			// modifies view now
			view.setSkeleton(enclosureElm, generateSkeleton(opts, hashDirv)); // opts passed
			// re-get enclosureElm (updated one)
			enclosureElm = glow.dom.get(opts.controller.enclosureId.glow);
			
			// populate view
			var maxYear = model.getMaxYear();
			var minYear = model.getMinYear();
			// --
			var contentsCollection = {};
			for (var year = maxYear, i = 0; year >= minYear; year--) {
				contentsCollection[year] = glow.dom.get(
				opts.controller.contentsId.glow + " div" +
				opts.controller.collectionNavClass.glow).slice(i,i+1).children().filter(
				function(i){return (this.tagName == 'ul' || this.tagName == 'UL');});
				i++;
			}
			var navCollection = {};
			for (var year = maxYear, i = 0; year >= minYear; year--) {
				navCollection[year] = glow.dom.get(
				opts.controller.navbarId.glow + " div" +
				opts.controller.collectionNavClass.glow).slice(i,i+1).children().filter(
				function(i){return (this.tagName == 'ul' || this.tagName == 'UL');});
				i++;
			}
			// --
			view.populate(contentsCollection, generateContents(opts, hashDirv));
			view.populate(navCollection, generateNav(opts, hashDirv));
			// hashDirv dependency moved here
			view.hideData(contentsCollection, navCollection, hashDirv);
			
			// attach events
			var eventAttachFx = {
				individualEntry: evnt_indivEntryClick,
				navbarYear: evnt_yearNavClick,
				switcherInput: evnt_switcherClick,
				topLink: evnt_goToTopClick
			};
			var individualEntries = glow.dom.get(
				opts.controller.contentsId.glow + " div" + opts.controller.collectionNavClass.glow + " ul li");
			var navbarYears = glow.dom.get(
				opts.controller.navbarId.glow + " div" + opts.controller.collectionNavClass.glow + " h4");
			var switcherInputs = glow.dom.get(
				opts.controller.switcherId.glow + " input");
			/*var topLinks = glow.dom.get(
				opts.controller.contentsId.glow + " div" + opts.controller.collectionNavClass.glow + " p a");*/
			var topLinks = glow.dom.get(
				opts.controller.navbarId.glow + " h5");
			
			_attachEvents(individualEntries, eventAttachFx.individualEntry);
			_attachEvents(navbarYears, eventAttachFx.navbarYear);
			_attachEvents(switcherInputs, eventAttachFx.switcherInput);
			_attachEvents(topLinks, eventAttachFx.topLink);
			
			// counter and general contextual information generator
			var counterViewDirective = _generateCounters(opts, hashDirv);
			view.initCounters(counterViewDirective.nodes, counterViewDirective.compl);
			
			// finally special event for the navbar
			var navbarEnclosureForScroll = glow.dom.get(opts.controller.navbarId.glow);
			glow.events.addListener(
				window,
				"scroll",
				evnt_navbarScroll,
				navbarEnclosureForScroll);
			
			// contents <li> bgimg replacing
			view.populateContentImages(generateContentImages(opts));
			
			// display
			var navbarInitDirv = {};
			navbarInitDirv.elm = glow.dom.get(opts.controller.navbarId.glow + " div" + opts.controller.collectionNavClass.glow + " ul li");
			navbarInitDirv.className = opts.controller.prefix.selectorLiColorClass + "combined";
			navbarInitDirv.hash = hashDirv;
			view.initDisplay(enclosureElm, navbarInitDirv);
			
			// set navbar{X,Y}def
			navbarYdef = parseInt(navbarEnclosureForScroll.offset().top, 10);
			navbarXdef = parseInt(navbarEnclosureForScroll.offset().left, 10);
			// to be reset when window resized
			window.onresize = function() { evnt_windowresize(navbarEnclosureForScroll); };
			// height remains the same though
			navbarHdef = navbarEnclosureForScroll.height();
			// get maxScrollElm
			maxScrollElm = glow.dom.get(opts.controller.enclosureId.glow).next();
			// navbarAnim
			var animDuration = 2;
			var tweenUsed = glow.tweens.easeOut();
			navbarAnimation = new glow.anim.Animation(animDuration, {tween: tweenUsed});
			
			// success so far
			return true;
		}
		that.initialRequestHandler = initialRequestHandler;
		// end plug C1 ************************
		// [HELPER] plug C1 001
		function generateSkeleton(opts, hashDirv) { // [Internal] [Private]
			var node;
			node = enclosureElm.clone(); /* Skeleton */
			// see skeleton!.html
			node.empty();
			// shorthand
			var opt = opts.controller;
			
			// year limits
			var maxYear = model.getMaxYear(),
				minYear = model.getMinYear();
			
			// switcher
			var switcher = glow.dom.create( /* Overall Switcher */
			'<div id="' + opt.switcherId.normal + '">' +
			/*'<a class="' + opt.contentAnchorClass.normal + '" name="' + opt.prefix.topAnchor + '"></a>' +*/
			'<div id="' + opt.prefix.smallSquareId + 'iraq">' + 
			'<img src="/shared/img/o.gif" width="15" height="15" /></div>' +
			'<input id="ukcas-switcher-iraq" type="radio" value="Iraq" name="Switcher" /><label for="ukcas-switcher-iraq">Iraq</label>&nbsp;' +
			'<div id="' + opt.prefix.smallSquareId + 'afghanistan">' + 
			'<img src="/shared/img/o.gif" width="15" height="15" /></div>' +
			'<input id="ukcas-switcher-afghanistan" type="radio" value="Afghanistan" name="Switcher" /><label for="ukcas-switcher-afghanistan">Afghanistan</label>&nbsp;' +
			'<div id="' + opt.prefix.smallSquareId + 'combined">' + 
			'<img src="/shared/img/o.gif" width="15" height="15" /></div>' +
			'<input id="ukcas-switcher-combined" type="radio" value="Combined" name="Switcher" /><label for="ukcas-switcher-combined">Iraq/Afghanistan combined</label>' +
			'</div>');
			switcher.children().each(function(i) {
				if (switcher.children().slice(i, i+1).is("label")) {
					return;
				} else {
					switch (hashDirv) {
						case 'iraq':
						if (switcher.children().slice(i, i+1).attr("value") == null) {
							// skip, do not glow.lang.trim
						} else if (glow.lang.trim(switcher.children().slice(i, i+1).attr("value")) == "Iraq") {
							switcher.children().slice(i, i+1).attr("checked", "checked");
							//switcher.children().slice(i, i+1).val("Iraq");
						}
						break;
						case 'afghanistan':
						if (switcher.children().slice(i, i+1).attr("value") == null) {
							// skip, do not glow.lang.trim
						} else if (glow.lang.trim(switcher.children().slice(i, i+1).attr("value")) == "Afghanistan") {
							switcher.children().slice(i, i+1).attr("checked", "checked");
							//switcher.children().slice(i, i+1).val("Afghanistan");
						}
						break;
						default: // case 'combined'
						if (switcher.children().slice(i, i+1).attr("value") == null) {
							// skip, do not glow.lang.trim
						} else if (glow.lang.trim(switcher.children().slice(i, i+1).attr("value")) == "Combined") {
							switcher.children().slice(i, i+1).attr("checked", "checked");
							//switcher.children().slice(i, i+1).val("Combined");
						}
						break;
					}
				}
			});
			
			// header
			var header = glow.dom.create( /* Overall Header */
			"<h1>Total deaths: {total}, killed in action {kia}, other causes {other}</h1>");
			
			// navbar
			var navbar = {};
			navbar.closure = glow.dom.create( /* Navbar Closure */
			"<div class='" + opt.closureClass.normal + "' id='" + opt.navbarId.normal + "'></div>");
			navbar.header = glow.dom.create( /* Navbar Header */
			"<h3>Fatalities by year</h3>");
			navbar.collection = {}; /* Navbar Collection */
			for (var i = minYear; i <= maxYear; i++) { // Yearly Loop
				navbar.collection[i] = {};
				navbar.collection[i].closure = glow.dom.create( /* Navbar Collection Yearly Closure */
				"<div class='" + opt.collectionNavClass.normal + "'></div>");
				navbar.collection[i].header = glow.dom.create( /* Navbar Collection Yearly Header */
				"<h4>" + i + "</h4>");
				navbar.collection[i].listing = glow.dom.create( /* Navbar Collection Yearly Listing */
				"<ul></ul>");
			}
			navbar.footer = glow.dom.create( /* Navbar Footer */
			"<h5>Go to top</h5>");
			
			// contents
			var contents = {};
			contents.closure = glow.dom.create( /* Contents Closure */
			"<div class='" + opt.closureClass.normal + "' id='" + opt.contentsId.normal + "'></div>");
			contents.collection = {}; /* Contents Collection */
			for (var i = minYear; i <= maxYear; i++) {
				contents.collection[i] = {};
				
				if (i == maxYear) { // first child
				contents.collection[i].closure = glow.dom.create( /* Contents Collection Yearly Closure FC */
				"<div class='" + opt.collectionNavClass.normal + " " + opt.collectionNavFCClass.normal + "'></div>");
				} else {
				contents.collection[i].closure = glow.dom.create( /* Contents Collection Yearly Closure */
				"<div class='" + opt.collectionNavClass.normal + "'></div>");
				}
				
				contents.collection[i].header = glow.dom.create( /* Contents Collection Yearly Header */
				"<h2><a class='" + opt.contentAnchorClass.normal +
				"' name='" + opt.prefix.contentAnchor + i + "'>" + i + "</a> - total deaths <span>{" +
				opt.prefix.totalDeathsByYear + i + "}</span></h2>");
				contents.collection[i].listing = glow.dom.create( /* Contents Collection Yearly Listing */
				"<ul></ul>");
			}
			
			// hook it all up, bottom-up
			navbar.closure.append(navbar.header);
			for (var i = maxYear; i >= minYear; i--) { // pushing
				navbar.collection[i].closure.append(navbar.collection[i].header);
				navbar.collection[i].closure.append(navbar.collection[i].listing);
				navbar.closure.append(navbar.collection[i].closure);
				
				contents.collection[i].closure.append(contents.collection[i].header);
				contents.collection[i].closure.append(contents.collection[i].listing);
				//contents.collection[i].closure.append(contents.collection[i].topLink);
				contents.closure.append(contents.collection[i].closure);
			}
			navbar.closure.append(navbar.footer);
			node.append(switcher);
			node.append(header);
			node.append(contents.closure);
			node.append(navbar.closure);
			
			// panel
			elm_panel = glow.dom.create('<div class="' +
			opts.controller.panelClass.all.normal + " " +
			opts.controller.panelClass.def.normal +
			'" id="' + opts.controller.panelId.normal +
			'">' +
			'<h2 class="hd">{name}</h2>' +
			'<div>' +
				'' + // lhs content
			'<img class="'+ opts.controller.panelClass.img.normal +'" src="{image_link}" alt="{name}" />' +
				'' + // rhs content
			'<div class="'+ opts.controller.panelClass.info.enclosure.normal +'">' +
				'<div class="'+ opts.controller.panelClass.info.indiv.normal +'">' +
					'<p class="'+ opts.controller.panelClass.info.indivtype.normal +'">Rank:</p>' +
					'<p class="'+ opts.controller.panelClass.info.indivdesc.normal +'">{rank}</p>' +
				'</div>' +
				'<div class="'+ opts.controller.panelClass.info.indiv.normal +'">' +
					'<p class="'+ opts.controller.panelClass.info.indivtype.normal +'">Age:</p>' +
					'<p class="'+ opts.controller.panelClass.info.indivdesc.normal +'">{age}</p>' +
				'</div>' +
				'<div class="'+ opts.controller.panelClass.info.indiv.normal +'">' +
					'<p class="'+ opts.controller.panelClass.info.indivtype.normal +'">Unit:</p>' +
					'<p class="'+ opts.controller.panelClass.info.indivdesc.normal +'">{unit}</p>' +
				'</div>' +
				'<div class="'+ opts.controller.panelClass.info.indiv.normal +'">' +
					'<p class="morelink"><a href="{morelink}">Full story (you will leave this page)</a></p>' +
				'</div>' +
			'</div>' +
				'' +
			'</div>' +
				'' +
			'</div>');
			
			return node;
		}
		// end [HELPER] plug C1 001 ***********
		// [HELPER] plug C1 002
		function generateContents(opts, hashDirv) { // [Internal] [Private]
			var nodes;
			// this should output a NodeList of <li>
			// inside a Hash having the year as the key and the Nodelist as the value
			// a bit tricker is that li should have id="ukcas_entry_${ID}"
			nodes = {};
			
			// year limits
			var maxYear = model.getMaxYear(),
				minYear = model.getMinYear();
			
			var /*convHash,*/
				oneList = "<li id='" + 
				opts.controller.prefix.indivEntry + 
				"{unique_id}' class='" +
				opts.controller.prefix.contentDifferentiation +
				"{slc}'></li>",
				fullList;
			if (glow.env.ie) {
				oneList = "<li id='" + 
				opts.controller.prefix.indivEntry + 
				"{unique_id}' class='" +
				opts.controller.prefix.contentDifferentiation +
				"{slc}'><img src='/shared/img/o.gif' height='59' width='59' style='cursor:pointer;' /></li>";
			}
			
			// the looping bits
			for (var year = maxYear; year >= minYear; year--) {
				fullList = ""; // empty it first
				//workdata = model.getYear(year, convHash);
				workdata = model.getYear(year, "combined");
				specificList = oneList; // init
				for (var i = workdata.length - 1; i >= 0; i--) {
					//fullList = fullList + oneList;
					//workdata[i].id
					specificList = glow.lang.interpolate(specificList, workdata[i]);
					specificList = glow.lang.interpolate(specificList, workdata[i]._metadata);
					fullList = fullList + specificList;
					specificList = oneList; // back again
				}
				nodes[year] = glow.dom.create(fullList);
				/*
				delete workdata
				delete specificList
				*/
				workdata = null;
				specificList = null;
			}
			
			return nodes;
		}
		// end [HELPER] plug C1 002 ***********
		// [HELPER] plug C1 003
		function generateNav(opts, hashDirv) { // [Internal] [Private]
			var nodes;
			// this should output a NodeList of <li>
			// inside a Hash having the year as the key and the Nodelist as the value
			nodes = {};
			
			// year limits
			var maxYear = model.getMaxYear(),
				minYear = model.getMinYear();
			
			var oneList = "<li class='" +
				opts.controller.prefix.navbarDifferentiation +
				"{slc}'></li>",
				fullList;
			
			if (glow.env.ie) {
				oneList = "<li class='" +
				opts.controller.prefix.navbarDifferentiation +
				"{slc}'><img src='/shared/img/o.gif' width='8' height='8' /></li>";
			}
			
			// hashDirv dependant .. not anymore
			
			// the looping bits
			for (var year = maxYear; year >= minYear; year--) {
				fullList = ""; // empty it first
				workdata = model.getYear(year, "combined");
				specificList = oneList; // init
				for (var i = workdata.length - 1; i >= 0; i--) {
					//fullList = fullList + oneList;
					//workdata[i].id
					specificList = glow.lang.interpolate(specificList, workdata[i]._metadata);
					fullList = fullList + specificList;
					specificList = oneList; // back again
				}
				nodes[year] = glow.dom.create(fullList);
			}
			
			return nodes;
		}
		// end [HELPER] plug C1 003 ***********
		// [HELPER] plug C1 004
		function _attachEvents(elms, fx) { // [Internal] [Private]
			// duck typing is good
			elms.each(function(i) {
				glow.events.addListener(
					elms[i],
					'click',
					fx,
					elms[i]
				);
			});
		}
		// end [HELPER] plug C1 004 ***********
		// Note for event handlers : Lexical scoping! + (this) == {DOMElement clicked}
		// [HELPER] plug C1 004.1 EVENT HANDLER
		function evnt_indivEntryClick() { // [Internal] [Private]
			// actual implementation
			// var entry = glow.dom.get(this);
			var idClicked = this.id.split("_")[2];
			var dataEntry = model.getId(idClicked);
			if (dataEntry === null) {
				return false; // data not found
			}
			var person = {
				name: (dataEntry.name) ? dataEntry.name : "Not released",
				age: (dataEntry.age) ? dataEntry.age : "Not released",
				rank: (dataEntry.rank) ? dataEntry.rank : "Not released",
				morelink: (dataEntry.link_more) ? dataEntry.link_more : "#",
				link_img: {
					"170": (dataEntry.link_img["170"]) ? dataEntry.link_img["170"] : "/nol/shared/spl/hi/in_depth/uk_military_casualties/facewall/img/170ph.gif"
				}
			};
			// sometimes data's name returns "undefined " instead of null
			if (person.name.match(/undefined/)) {
				person.name = "Not released";
			}
			// special corrections
			for (var faultyname in _evnt_opt.name_corrections) {
				if (person.name == faultyname) {
					person.name = _evnt_opt.name_corrections[faultyname];
				}
			}
			// /nol/shared/spl/hi/in_depth/uk_military_casualties/combined/img/170ph.gif
			var pcsdUnit = dataEntry.unit.split(", ");
			for (var i = 0, il = pcsdUnit.length; i < il; i++) {
				if (pcsdUnit[i] === "") {
					pcsdUnit.splice(i,1);
				}
			}
			person.unit = pcsdUnit.join(", ");
			if (person.unit === "") {
				person.unit = "Not released";
			}
			
			return i_evnt_panelProcess(person); // pass onwards to panel processing
		}
		// end [HELPER] plug C1 004.1 ***********
		// [HELPER] plug C1 004.1.P.1
		function i_evnt_panelProcess(person) {
			// if panel exists, modify panel
			if (typeof(panelInstance) != 'undefined') {
				p_modifyPanel(person);
			} else { // create panel then modify panel
				p_createPanel();
				p_modifyPanel(person);
			}
			// then open the panel
			panelInstance.show();
			return true;
		}
		// end [HELPER] plug C1 004.1.P.1 ***********
		// [HELPER] plug C1 004.1.P.2
		function _panelOpenCheck() { // [Internal] [Private]
			if (typeof(panelInstance) == 'undefined') {
				return false; // obviously if panel is not created, it's not open
			} else {
				return panelInstance.isShown;
			}
		}
		// end [HELPER] plug C1 004.1.P.2 ***********
		// [HELPER] plug C1 004.1.P.3
		function _closePanel() { // [Internal] [Private]
			panelInstance.hide();
			// set back to default with interpolators
			// no need
		}
		// end [HELPER] plug C1 004.1.P.3 ***********
		// ----- PANEL -----
		function p_createPanel() {
			var panelOpts = {
				modal: false,
				theme: "light",
				width: 466,
				anim: "fade"
			};
			panelInstance = new glow.widgets.Panel(
				elm_panel,
				panelOpts);
			panelInstance.container.addClass(_evnt_opt.panelClass.inst.normal);
			panelInstance.container.addClass(_evnt_opt.panelClass.all.normal);
		}
		function p_modifyPanel(person) {
			//var panelBox = glow.dom.get("div" + _evnt_opt.panelClass.def.glow);
			
			var panelHeader = glow.dom.get("div" + _evnt_opt.panelClass.inst.glow + " h2.hd");
			if (panelHeader.text() != "{name}") {
				panelHeader.text("{name}");
			}
			panelHeader.text(glow.lang.interpolate(panelHeader.text(), person));
			
			var panelImg = glow.dom.get(_evnt_opt.panelId.glow + " img" + _evnt_opt.panelClass.img.glow);
			if (panelImg.attr("alt").indexOf("name") == -1) {
				panelImg.attr("alt", "{name}");
			}
			panelImg.attr("alt", glow.lang.interpolate(panelImg.attr("alt"), person));
			panelImg.attr("src", "/nol/shared/spl/hi/in_depth/uk_military_casualties/facewall/img/buffer_anim.gif");
			panelImg.attr("src", glow.lang.interpolate("{170}", person.link_img));
			
			var panelInfo = {};
			panelInfo.rank = glow.dom.get( /* Rank */
			_evnt_opt.panelId.glow + " div" + _evnt_opt.panelClass.info.enclosure.glow +
			" div" + _evnt_opt.panelClass.info.indiv.glow).slice(0).children().filter(
			function(i) { return (this.className == _evnt_opt.panelClass.info.indivdesc.normal); });
			panelInfo.age = glow.dom.get( /* Age */
			_evnt_opt.panelId.glow + " div" + _evnt_opt.panelClass.info.enclosure.glow +
			" div" + _evnt_opt.panelClass.info.indiv.glow).slice(1).children().filter(
			function(i) { return (this.className == _evnt_opt.panelClass.info.indivdesc.normal); });
			panelInfo.unit = glow.dom.get( /* Unit */
			_evnt_opt.panelId.glow + " div" + _evnt_opt.panelClass.info.enclosure.glow +
			" div" + _evnt_opt.panelClass.info.indiv.glow).slice(2).children().filter(
			function(i) { return (this.className == _evnt_opt.panelClass.info.indivdesc.normal); });
			
			for (var info in panelInfo) {
				panelInfo[info].text("{" + info + "}");
				panelInfo[info].text(glow.lang.interpolate(panelInfo[info].text(), person));
			}
			
			var panelMorelink = glow.dom.get(
			"div" + _evnt_opt.panelClass.inst.glow + " div" + _evnt_opt.panelClass.info.indiv.glow + " p.morelink a");
			panelMorelink.attr("href", "{morelink}");
			panelMorelink.attr("href", glow.lang.interpolate(panelMorelink.attr("href"), person));
				
		}
		// ===== ENDPN =====
		// [HELPER] plug C1 004.2 EVENT HANDLER
		function evnt_yearNavClick() { // [Internal] [Private]
			// glow panel checker
			if (_panelOpenCheck()) {
				_closePanel();
			}
			// actual implementation
			var entry = glow.dom.get(this);
			var anchor = _evnt_opt.prefix.contentAnchor + entry.text();
			
			var anchorElm = glow.dom.get(
			"a" + _evnt_opt.contentAnchorClass.glow).filter(
			function(i) { return this.name == anchor; });
			
			var anchorElmPos = { x: anchorElm.offset().left, y: parseInt(anchorElm.offset().top, 10) };
			
			function compatibleGetCurrentYPos() { 
			 if (document.body && document.body.scrollTop) {
			   return document.body.scrollTop;
			 }
			 if (document.documentElement && document.documentElement.scrollTop) {
			   return document.documentElement.scrollTop;
			 }
			 if (window.pageYOffset) {
			   return window.pageYOffset;
			 }
			 return 0; 
			}
			
			function df_scrollTo(elm, coord, step, intervalHandler) {
				var past_y = compatibleGetCurrentYPos();
				var isAbove = (past_y < coord);
				window.scrollTo(0, past_y + step);
				var current_y = compatibleGetCurrentYPos();
				var isAboveAfterScroll = (current_y < coord);
				if ((isAbove != isAboveAfterScroll) || (past_y == current_y)) {
					elm[0].scrollIntoView();
					clearInterval(intervalHandler);
				}
			}
			
			if (glow.env.ie) {
				window.scrollTo(0, anchorElmPos.y);
			} else {
				var rate = 1;
				var step = parseInt((anchorElmPos.y - compatibleGetCurrentYPos())/rate, 10);
				
				var intervalHandler = window.setInterval(
				function() { df_scrollTo(anchorElm, anchorElmPos.y, step, intervalHandler); },10);
			}
			
			
			
			return false;
		}
		// end [HELPER] plug C1 004.2 ***********
		// [HELPER] plug C1 004.3 EVENT HANDLER
		function evnt_switcherClick() { // [Internal] [Private]
			// glow panel checker
			if (_panelOpenCheck()) {
				_closePanel();
			}
			// actual implementation
			// Be careful for this as if another country appears it becomes tricky
			var entry = glow.dom.get(this);
			var convHash = entry.val().toLowerCase();
			var contraDict = {"afghanistan": "iraq", "iraq": "afghanistan"};
			var contraHash = contraDict[convHash];
			if (convHash == "combined") {
				glow.dom.get(
				"div" + _evnt_opt.collectionNavClass.glow + " ul li").css("display", "block");
				glow.dom.get(
				"div" + _evnt_opt.collectionNavClass.glow + " ul li").addClass(
				_evnt_opt.prefix.selectorLiColorClass + convHash);
				// clear the anchor link
				window.location.hash = "combined";
				// update counters
				i_evnt_updateCounters(_evnt_opt, convHash);
			} else {
				// show current
				glow.dom.get(
				"li." + _evnt_opt.prefix.contentDifferentiation + convHash).css("display", "block");
				glow.dom.get(
				"li." + _evnt_opt.prefix.navbarDifferentiation + convHash).css("display", "block");
				// hide contra
				glow.dom.get(
				"li." + _evnt_opt.prefix.contentDifferentiation + contraHash).css("display", "none");
				glow.dom.get(
				"li." + _evnt_opt.prefix.navbarDifferentiation + contraHash).css("display", "none");
				// add anchor link to help journalists
				window.location.hash = convHash;
				// remove combined colouring
				glow.dom.get(
				"div" + _evnt_opt.collectionNavClass.glow + " ul li").removeClass(
				_evnt_opt.prefix.selectorLiColorClass + "combined");
				// update counters
				i_evnt_updateCounters(_evnt_opt, convHash);
			}
			// update maxheight
			navbarHdef = glow.dom.get(_evnt_opt.navbarId.glow).height();
			return true;
		}
		// end [HELPER] plug C1 004.3 ***********
		// [HELPER] plug C1 004.3.1 EVENT HANDLER
		function i_evnt_updateCounters(evnt_opt, hash) { // [Internal] [Private]
			// var compl = {};
			var maxYear = model.getMaxYear(),
				minYear = model.getMinYear();
			var nodes, node;
			var opt = evnt_opt;
			// contents navcollection h2 a >> name.text match year?
			nodes = glow.dom.get(
			opt.contentsId.glow +
			" div" + opt.collectionNavClass.glow +
			" h2 a");
			
			for (var year = maxYear; year >= minYear; year--) {
				var regexp = new RegExp(year);
				node = nodes.filter(
				function(i) {
					return this.name.match(regexp);
				});
				node = node.parent().children().filter(
				function(i) {
					return (glow.dom.get(this).is("span"));
				});
				node.text(model.getTotalByYear(year, hash));
			}
		}
		// end [HELPER] plug C1 004.3.1 ***********
		// [HELPER] plug C1 004.4 EVENT HANDLER
		function evnt_goToTopClick() { // [Internal] [Private]
			// glow panel checker
			if (_panelOpenCheck()) {
				_closePanel();
			}
			
			function df_scrollTo(coord, step, intervalHandler) {
				var past_y = compatibleGetCurrentYPos();
				var isAbove = (past_y < coord);
				window.scrollTo(0, past_y + step);
				var current_y = compatibleGetCurrentYPos();
				var isAboveAfterScroll = (current_y < coord);
				if ((isAbove != isAboveAfterScroll) || (past_y == current_y)) {
					window.scrollTo(0, 0);
					clearInterval(intervalHandler);
				}
			}
			
			function compatibleGetCurrentYPos() { 
			 if (document.body && document.body.scrollTop) {
			   return document.body.scrollTop;
			 }
			 if (document.documentElement && document.documentElement.scrollTop) {
			   return document.documentElement.scrollTop;
			 }
			 if (window.pageYOffset) {
			   return window.pageYOffset;
			 }
			 return 0; 
			}
			
			// actual implementation
			// window.scrollTo(0, 0);
			if (glow.env.ie) {
				window.scrollTo(0, 0);
			} else {
				var rate = 4;
				var step = parseInt((0 - compatibleGetCurrentYPos())/rate, 10);
				var intervalHandler = window.setInterval(
				function() { df_scrollTo(0, step, intervalHandler); },10);
			}
			
		}
		// end [HELPER] plug C1 004.4 ***********
		// [HELPER] plug C1 007
		function _generateCounters(opts, hashDirv) { // [Internal] [Private]
			var compl = {};
			var nodes;
			compl.totalDeaths = {};
			
			var tdprefix = opts.controller.prefix.totalDeathsByYear;
			
			// year limits
			var maxYear = model.getMaxYear(),
				minYear = model.getMinYear();
			
			for (var year = maxYear; year >= minYear; year--) {
				compl.totalDeaths[tdprefix+year] = model.getTotalByYear(year, hashDirv);
			}
			
			nodes = glow.dom.get(
			opts.controller.contentsId.glow +
			" div" + opts.controller.collectionNavClass.glow +
			" h2");
			
			
			return {"nodes": nodes, "compl": compl};
		}
		// end [HELPER] plug C1 007 ***********
		// [HELPER] plug C1 008
		function evnt_navbarScroll() { // [Internal] [Private]
			function compatibleGetCurrentYPos() { 
			 if (document.body && document.body.scrollTop) {
			   return document.body.scrollTop;
			 }
			 if (document.documentElement && document.documentElement.scrollTop) {
			   return document.documentElement.scrollTop;
			 }
			 if (window.pageYOffset) {
			   return window.pageYOffset;
			 }
			 return 0; 
			}
			var windowPosAfterScroll = compatibleGetCurrentYPos();
			var alternative_test = (glow.env.ie < 7 || glow.env.gecko);
			/* unused
			if (alternative_test) {
				var elmPosBeforeScroll = parseInt(this.offset().top, 10);
			}
			*/
			var maxYPos = maxScrollElm.offset().top - navbarHdef;
			
			var scrollStepDuration = 0.2;
			if (alternative_test) {
				var tweenUsed = glow.tweens.easeOut();
			}
			/*var navbarAnimation = navbarAnimation; // ref copy
			
			if (!(navbarAnimation.isPlaying())) {
				navbarAnimation.start();
			}*/
			
			/**
			* Delay for a number of milliseconds
			*/
			function sleep(delay)
			{
			    var start = new Date().getTime();
			    while (new Date().getTime() < start + delay) {
			    	// sleep
			    }
			}
			
			if (alternative_test) {
				if (typeof(panelInstance) == 'undefined') {
					sleep(2);
				} else if (panelInstance.isShown) {
					// don't sleep
				} else {
					sleep(2);
				}
			}
			
			
			if (windowPosAfterScroll < navbarYdef) {
				// set it back to navbarYdef
				/*glow.anim.css(
					this,
					scrollStepDuration, {
						"margin-top": {to: "0px"}
					},
					tweenUsed
				).start();*/
				this.css("position", "static");
				this.css("margin-top", "0px");
			} else if (windowPosAfterScroll >= maxYPos - ((glow.env.ie) ? 16 : 4)) {
				if (typeof(panelInstance) != 'undefined') {
					panelInstance.hide();
				}
				if (alternative_test) {
					glow.anim.css(
						this,
						scrollStepDuration, {
							"margin-top": {to: (maxYPos - navbarYdef - ((glow.env.ie) ? 16 : 4)) + "px"}
						},
						tweenUsed).start();
				} else {
					this.css("position", "static");
					this.css("margin-top", (maxYPos - navbarYdef - ((glow.env.ie) ? 16 : 4)) + "px");
				}
				/*glow.anim.css(
					this,
					scrollStepDuration, {
						"margin-top": {to: (maxYPos - navbarYdef - ((glow.env.ie) ? 16 : 4)) + "px"}
					},
					tweenUsed
				).start();*/
			} else {
				if (alternative_test) {
					glow.anim.css(
					this,
					scrollStepDuration, {
						"margin-top": {to: (windowPosAfterScroll - navbarYdef) + "px"}
					},
					tweenUsed).start();
				} else {
					if (this.css("position") == "fixed") {
					} else {
						this.css("position", "fixed");
					}
					if (this.css("left") == (navbarXdef + "px")) {
					} else {
						this.css("left", navbarXdef + "px");
					}
					if (this.css("top") == "5px") {
					} else {
						this.css("top", "5px");
					}
					this.css("margin-top", "0px");
				}
				/*
				glow.anim.css(
					this,
					scrollStepDuration, {
						"margin-top": {to: (windowPosAfterScroll - navbarYdef) + "px"}
					},
					tweenUsed
				).start();
				*/
				//elm.css("margin-top", (windowPosAfterScroll - navbarYdef) + "px");
			}
		}
		// end [HELPER] plug C1 008 ***********
		// [HELPER] plug C1 009
		function generateContentImages(opts) {
			var idHash = model.getAllId();
			// unique_id passed here
			var imgHash = {};
			var i_opts = opts.controller;
			for (var id in idHash) {
				imgHash[i_opts.prefix.indivEntry + id] = (idHash[id].link_img["59"]) ? idHash[id].link_img["59"] : "/nol/shared/spl/hi/in_depth/uk_military_casualties/facewall/img/59x59ph.gif";
			}
			return imgHash;
		}
		// end [HELPER] plug C1 009 ***********
		// [HELPER] plug C1 010
		function evnt_windowresize(elm) {
			var previousPos = elm.css("position");
			elm.css("position", "static");
			navbarYdef = parseInt(elm.offset().top, 10);
			navbarXdef = parseInt(elm.offset().left, 10);
			elm.css("position", previousPos);
		}
		// end [HELPER] plug C1 010 ***********
		// end plug C1 ************************
		
		return that;
	},
	// ================ VIEW ================ //
	View: function View(controller) {
		var that = {}; // durable object
		
		var glow = bbc.fmtj.csd.MilCasualties.glow; // glow quick hand
		
		// Asserts
		function _isValid() { // [Package] _isValid()
			return true;
		}
		that._isValid = _isValid;
		function _myName() { // [Package] _getName()
			return "View";
		}
		that._getName = _myName;
		
		// Variable declaration
		var controller = controller;
		/*assert*/ if (controller._isValid() !== true) {
			throw("Controller object error");
		}
		
		// Called at start (Trigger: MilCasualties.Master)
		function init(opts) { // [Public] init()
			// no need to handle input (yet?)
			controller.announce(self); // delay self to end
			return true;
		}
		that.init = init;
		
		// state V1
		/***************
		- Modifiable blank state -
		Public trigger: Blank state waiting for initialization via C1
		End state: display-none
		
		Internal:
		- TBF
		****************/
		function blankStateView(enclosureElm) { // [Package] blankStateView()
			enclosureElm.css("display", "none");
		}
		that.blankStateView = blankStateView;
		// end plug V1 ************************
		
		// state V2
		/***************
		- Skeleton -
		Public trigger: Initialization after dataModel processing via Controller
		End state: Skeleton on page
		
		Internal:
		- TBF
		****************/
		function setSkeleton(enclosureElm, node) { // [Package] setSkeleton()
			node.insertBefore(enclosureElm);
			enclosureElm.remove();
			//console.log(node.html());
		
		}
		that.setSkeleton = setSkeleton;
		// end plug V2 ************************
		
		// state V3
		/***************
		- Skeleton -
		Public trigger: Finish initializing
		End state: Fully functional init view
		
		Internal:
		- TBF
		****************/
		function initDisplay(enclosureElm, navbarInitDirv) { // [Package] initDisplay()
			// simple
			enclosureElm.css("display", "block");
			if (navbarInitDirv.hash == "combined") {
				navbarInitDirv.elm.addClass(navbarInitDirv.className);
			}
		}
		that.initDisplay = initDisplay;
		// end plug V3 ************************
		
		// state V4
		/***************
		- Skeleton -
		Public trigger: Populate the individual data for navbar and contents
		End state: View with data
		Modifier: Hash?
		
		Internal:
		- TBF
		****************/
		function populate(elm, data) { // [Package] populate()
			if (typeof(data) == 'undefined' || data === null) {
				return;
			}
			for (var year in elm) {
				elm[year].append(data[year]);
			}
		}
		that.populate = populate;
		// end plug V4 ************************
		// state V5
		/***************
		- Skeleton -
		Public trigger: hash defined at init
		
		Internal:
		- TBF
		****************/
		function hideData(content, nav, hash) { // [Package] hideData()
			var convHash;
			switch (hash) {
				case 'iraq':
				convHash = /iraq/;
				break;
				case 'afghanistan':
				convHash = /afghanistan/;
				break;
				default: // case 'combined'
				convHash = hash;
				if (convHash != "combined") {
					throw("something wrong inside view hideData()");
				}
				break;
			}
			if (convHash == "combined") {
				return;
			} else {
				for (var year in content) {
					content[year].children().filter(
					function(i) { return !(this.className.match(convHash)); }).css("display", "none");
				}
				for (var year in nav) {
					nav[year].children().filter(
					function(i) { return !(this.className.match(convHash)); }).css("display", "none");
				}
			}
		}
		that.hideData = hideData;
		// end plug V5 ************************
		// state V5
		/***************
		- Skeleton -
		Public trigger: hash defined at init
		
		Internal:
		- TBF
		****************/
		function initCounters(nodes, data) { // [Package] initCounters()
			var ldata = data.totalDeaths;
			nodes.each(function(i) {
				glow.dom.get(nodes[i]).html(glow.lang.interpolate(glow.dom.get(nodes[i]).html(), ldata));
			});
		}
		that.initCounters = initCounters;
		// end plug V5 ************************
		// state V6
		/***************
		- Skeleton -
		Public trigger: generate content images
		
		Internal:
		- TBF
		****************/
		function populateContentImages(hash, allelms) {
			// then fill it
			for (var id in hash) {
				glow.dom.get("li#" + id).css("background-image", "url(" + hash[id] + ")");
			}
		}
		that.populateContentImages = populateContentImages;
		// end plug V6 ************************
		
		
		// !! these two lines must be at the end and right next to each other !!
		var self = that; // to give to controller
		return that;
	}
};

bbc.fmtj.csd.MilCasualties.glow = {}; // instantiated glow storage (static)