text: /*!
   * Fancytree Taxonomy Browser
   *
   * Copyright (c) 2015, Martin Wendt (https://wwWendt.de)
   *
   * Released under the MIT license
   * https://github.com/mar10/fancytree/wiki/LicenseInfo
   *
   * @version @VERSION
   * @date @DATE
   */
  
  /* global Handlebars */
  /* eslint-disable no-console */
  
  (function($, window, document) {
  	"use strict";
  
  	/*******************************************************************************
  	 * Private functions and variables
  	 */
  
  	var taxonTree,
  		searchResultTree,
  		timerMap = {},
  		tmplDetails, // =
  		USER_AGENT = "Fancytree Taxonomy Browser/1.0",
  		ITIS_URL = "//www.itis.gov/ITISWebService/jsonservice/",
  		glyphOpts = {
  			preset: "bootstrap3",
  			map: {
  				expanderClosed: "glyphicon glyphicon-menu-right", // glyphicon-plus-sign
  				expanderLazy: "glyphicon glyphicon-menu-right", // glyphicon-plus-sign
  				expanderOpen: "glyphicon glyphicon-menu-down", // glyphicon-collapse-down
  			},
  		};
  
  	// Load and compile handlebar templates
  
  	$.get("details.tmpl", function(data) {
  		tmplDetails = Handlebars.compile(data);
  	});
  
  	/** Update UI elements according to current status
  	 */
  	function updateControls() {
  		var query = $.trim($("input[name=query]").val());
  
  		$("#btnPin").attr("disabled", !taxonTree.getActiveNode());
  		$("#btnUnpin")
  			.attr("disabled", !taxonTree.isFilterActive())
  			.toggleClass("btn-success", taxonTree.isFilterActive());
  		$("#btnResetSearch").attr("disabled", query.length === 0);
  		$("#btnSearch").attr("disabled", query.length < 2);
  	}
  
  	/**
  	 * Invoke callback after `ms` milliseconds.
  	 * Any pending action of this type is cancelled before.
  	 */
  	function _delay(tag, ms, callback) {
  		/*jshint -W040:true */
  		var self = this;
  
  		tag = "" + (tag || "default");
  		if (timerMap[tag] != null) {
  			clearTimeout(timerMap[tag]);
  			delete timerMap[tag];
  			// console.log("Cancel timer '" + tag + "'");
  		}
  		if (ms == null || callback == null) {
  			return;
  		}
  		// console.log("Start timer '" + tag + "'");
  		timerMap[tag] = setTimeout(function() {
  			// console.log("Execute timer '" + tag + "'");
  			callback.call(self);
  		}, +ms);
  	}
  
  	/**
  	 */
  	function _callItis(cmd, data) {
  		return $.ajax({
  			url: ITIS_URL + cmd,
  			data: $.extend(
  				{
  					jsonp: "itis_data",
  				},
  				data
  			),
  			cache: true,
  			headers: { "Api-User-Agent": USER_AGENT },
  			jsonpCallback: "itis_data",
  			dataType: "jsonp",
  		});
  	}
  
  	/**
  	 */
  	// function countMatches(query) {
  	// 	$("#tsnDetails").text("Loading TSN " + tsn + "...");
  	// 	_callItis("getAnyMatchCount", {
  	// 		srchKey: query
  	// 	}).done(function(result){
  	// 		console.log("updateTsnDetails", result);
  	// 		$("#tsnDetails").html(tmplDetails(result));
  	// 		updateControls();
  	// 	});
  	// }
  
  	/**
  	 */
  	function updateTsnDetails(tsn) {
  		$("#tsnDetails").addClass("busy");
  		// $("#tsnDetails").text("Loading TSN " + tsn + "...");
  		$.bbq.pushState({ tsn: tsn });
  
  		_callItis("getFullRecordFromTSN", {
  			tsn: tsn,
  		}).done(function(result) {
  			console.log("updateTsnDetails", result);
  			$("#tsnDetails")
  				.html(tmplDetails(result))
  				.removeClass("busy");
  
  			updateControls();
  		});
  	}
  
  	/**
  	 */
  	function updateBreadcrumb(tsn, loadTreeNodes) {
  		// var $ol = $("ol.breadcrumb").text("...");
  		var $ol = $("ol.breadcrumb").addClass("busy");
  		_callItis("getFullHierarchyFromTSN", {
  			tsn: tsn,
  		}).done(function(result) {
  			console.log("updateBreadcrumb", result);
  			// Convert to simpler format
  			var list = [];
  			// Display as <OL> list (for Bootstrap breadcrumbs)
  			$ol.empty().removeClass("busy");
  			$.each(result.hierarchyList, function(i, o) {
  				if (o.parentTsn === tsn) {
  					return;
  				} // skip direct children
  				list.push(o.tsn);
  				if (o.tsn === tsn) {
  					$ol.append(
  						$("<li class='active'>").append(
  							$("<span>", {
  								text: o.taxonName,
  								title: o.rankName,
  							})
  						)
  					);
  				} else {
  					$ol.append(
  						$("<li>").append(
  							$("<a>", {
  								href: "#tsn=" + o.tsn,
  								text: o.taxonName,
  								title: o.rankName,
  							})
  						)
  					);
  				}
  			});
  			if (loadTreeNodes) {
  				console.log("updateBreadcrumb - loadKeyPath", list);
  				taxonTree.loadKeyPath("/" + list.join("/"), function(
  					node,
  					status
  				) {
  					// console.log("... updateBreadcrumb - loadKeyPath", status, node);
  					switch (status) {
  						case "loaded":
  							node.makeVisible();
  							break;
  						case "ok":
  							node.setActive();
  							break;
  					}
  				});
  			}
  		});
  	}
  
  	/**
  	 */
  	function search(query) {
  		query = $.trim(query);
  		console.log("searching for '" + query + "'...");
  		// NOTE:
  		// It seems that ITIS searches don't work with jsonp (always return valid
  		// but empty result sets).
  		// When debugging, make sure cross domain requests are allowed.
  		searchResultTree
  			.reload({
  				url: ITIS_URL + "searchForAnyMatchPaged",
  				data: {
  					// jsonp: "itis_data",
  					srchKey: query,
  					pageSize: 10,
  					pageNum: 1,
  					ascend: false,
  				},
  				cache: true,
  				// jsonpCallback: "itis_data",
  				// dataType: "jsonp"
  			})
  			.done(function(result) {
  				// console.log("search returned", result);
  				// result.anyMatchList
  				updateControls();
  			});
  	}
  
  	/*******************************************************************************
  	 * Pageload Handler
  	 */
  
  	$(function() {
  		$("#taxonTree").fancytree({
  			extensions: ["filter", "glyph", "wide"],
  			filter: {
  				mode: "hide",
  			},
  			glyph: glyphOpts,
  			activeVisible: true,
  			source: {
  				// We could use getKingdomNames, but that returns an individual JSON format.
  				// getHierarchyDownFromTSN?tsn=0 seems to work as well and allows
  				// unified parsing in postProcess.
  				// url: ITIS_URL + "getKingdomNames",
  				url: ITIS_URL + "getHierarchyDownFromTSN",
  				data: {
  					jsonp: "itis_data",
  					tsn: "0",
  				},
  				cache: true,
  				jsonpCallback: "itis_data",
  				dataType: "jsonp",
  			},
  			init: function(event, data) {
  				updateControls();
  				$(window).trigger("hashchange"); // trigger on initial page load
  			},
  			lazyLoad: function(event, data) {
  				data.result = {
  					url: ITIS_URL + "getHierarchyDownFromTSN",
  					data: {
  						jsonp: "itis_data",
  						tsn: data.node.key,
  					},
  					cache: true,
  					jsonpCallback: "itis_data",
  					dataType: "jsonp",
  				};
  			},
  			postProcess: function(event, data) {
  				var response = data.response;
  
  				data.node.info(response);
  				data.result = $.map(response.hierarchyList, function(o) {
  					return (
  						o && {
  							title: o.taxonName,
  							key: o.tsn,
  							folder: true,
  							lazy: true,
  						}
  					);
  				});
  			},
  			activate: function(event, data) {
  				$("#tsnDetails").addClass("busy"); //text("...");
  				updateControls();
  				_delay("showDetails", 1000, function() {
  					updateTsnDetails(data.node.key);
  					updateBreadcrumb(data.node.key);
  				});
  			},
  		});
  
  		$("#searchResultTree").fancytree({
  			extensions: ["table", "wide"],
  			source: [{ title: "No Results." }],
  			minExpandLevel: 2,
  			icon: false,
  			table: {
  				nodeColumnIdx: 1,
  			},
  			postProcess: function(event, data) {
  				var response = data.response;
  
  				data.node.info("pp", response);
  				data.result = $.map(response.anyMatchList, function(o) {
  					if (!o) {
  						return;
  					}
  					var res = {
  						title: o.sciName,
  						key: o.tsn,
  						author: o.author,
  						matchType: o.matchType,
  					};
  					res.commonNames = $.map(
  						o.commonNameList.commonNames,
  						function(x) {
  							return x && x.commonName
  								? { name: x.commonName, language: x.language }
  								: undefined;
  						}
  					);
  					return res;
  				});
  				// console.log("pp2", data.result)
  			},
  			renderColumns: function(event, data) {
  				var node = data.node,
  					$tdList = $(node.tr).find(">td"),
  					cnList = node.data.commonNames
  						? $.map(node.data.commonNames, function(o) {
  								return o.name;
  						  })
  						: [];
  
  				$tdList.eq(0).text(node.key);
  				$tdList.eq(2).text(cnList.join(", "));
  				$tdList.eq(3).text(node.data.matchType);
  				$tdList.eq(4).text(node.data.author);
  			},
  			activate: function(event, data) {
  				_delay("activateNode", 1000, function() {
  					updateTsnDetails(data.node.key);
  					updateBreadcrumb(data.node.key);
  				});
  			},
  		});
  
  		taxonTree = $.ui.fancytree.getTree("#taxonTree");
  		searchResultTree = $.ui.fancytree.getTree("#searchResultTree");
  
  		// Bind a callback that executes when document.location.hash changes.
  		// (This code uses bbq: https://github.com/cowboy/jquery-bbq)
  		$(window).on("hashchange", function(e) {
  			var tsn = $.bbq.getState("tsn");
  			console.log("bbq tsn", tsn);
  			if (tsn) {
  				updateBreadcrumb(tsn, true);
  			}
  		}); // don't trigger now, since we need the the taxonTree root nodes to be loaded first
  
  		$("input[name=query]")
  			.on("keyup", function(e) {
  				var query = $.trim($(this).val());
  
  				if ((e && e.which === $.ui.keyCode.ESCAPE) || query === "") {
  					$("#btnResetSearch").trigger("click");
  					return;
  				}
  				if (e && e.which === $.ui.keyCode.ENTER && query.length >= 2) {
  					$("#btnSearch").trigger("click");
  					return;
  				}
  				$("#btnResetSearch").attr("disabled", query.length === 0);
  				$("#btnSearch").attr("disabled", query.length < 2);
  			})
  			.focus();
  
  		$("#btnResetSearch").click(function(e) {
  			$("#searchResultPane").collapse("hide");
  			$("input[name=query]").val("");
  			searchResultTree.clear();
  			// $("#btnSearch").attr("disabled", true);
  			// $(this).attr("disabled", true);
  			updateControls();
  		});
  
  		$("#btnSearch")
  			.click(function(event) {
  				$("#searchResultPane").collapse("show");
  				search($("input[name=query]").val());
  			})
  			.attr("disabled", true);
  
  		$("#btnPin").click(function(event) {
  			taxonTree.filterBranches(function(n) {
  				return n.isActive();
  			});
  			updateControls();
  		});
  		$("#btnUnpin").click(function(event) {
  			taxonTree.clearFilter();
  			updateControls();
  		});
  
  		// -----------------------------------------------------------------------------
  	}); // end of pageload handler
  })(jQuery, window, document);
  