GRO Index Search Helper

Adds additional functionality to the UK General Register Office (GRO) BMD index search

当前为 2017-11-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GRO Index Search Helper
  3. // @description Adds additional functionality to the UK General Register Office (GRO) BMD index search
  4. // @namespace cuffie81.scripts
  5. // @include https://www.gro.gov.uk/gro/content/certificates/indexes_search.asp
  6. // @version 1.10
  7. // @grant none
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.10/handlebars.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js
  11. // ==/UserScript==
  12.  
  13.  
  14. this.$ = this.jQuery = jQuery.noConflict(true);
  15.  
  16. $(function() {
  17. var resources, recordType, results;
  18. var main = function() {
  19. buildResources();
  20. recordType = getRecordType();
  21. //console.log("resources:\r\n%s", JSON.stringify(resources));
  22. // Load the general css
  23. $("body").append($(resources.baseStyle));
  24.  
  25. initialiseSearchForm();
  26. initialiseResultViews();
  27. // Scroll down to the form. Do this last as we may add/remove/chnage elements in the previous calls.
  28. $("h1:contains('Search the GRO Online Index')")[0].scrollIntoView();
  29. // Wire up accesskeys to clicks, to avoid having to use the full accesskey combo (eg ALT+SHFT+#)
  30. $(document).on("keypress", function(e) {
  31. if (!document.activeElement || document.activeElement.tagName.toLowerCase() !== "input")
  32. {
  33. var char = String.fromCharCode(e.which);
  34. //console.log("keypress: %s", char);
  35. if ($("*[id^='groish'][accesskey='" + char + "']").length)
  36. $("*[id^='groish'][accesskey='" + char + "']").click();
  37. else if (char == "{")
  38. adjustSearchYear(-10);
  39. else if (char == "}")
  40. adjustSearchYear(10);
  41. else if (char == "?")
  42. $("form[name='SearchIndexes'] input[type='submit']").click();
  43. else if (char == '@')
  44. switchRecordType();
  45. }
  46. });
  47. }
  48. var initialiseSearchForm = function() {
  49. // Hide superfluous spacing, text and buttons
  50. $("body > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(2)").hide();
  51. $("h1:contains('Search the GRO Online Index')").closest("tr").next().hide();
  52. $("strong:contains('Which index would you like to search?')").closest("tr").hide();
  53. $("table[summary*='contains the search form fields'] > tbody > tr:nth-of-type(2)").hide();
  54. $("table[summary*='contains the search form fields'] > tbody > tr:nth-of-type(3) td.main_text[colspan='5']").parent().hide();
  55. $("form[name='SearchIndexes'] input[type='submit'][value='Reset']").hide();
  56. $("form[name='SearchIndexes'] a.tooltip").hide();
  57. // Change text
  58. $("form[name='SearchIndexes'] td span.main_text:contains('year(s)')").text("yrs");
  59. $("form[name='SearchIndexes'] td.main_text:contains('Surname at Death:')").html("Surname:<span class='redStar'>*</span>");
  60. $("form[name='SearchIndexes'] td.main_text:contains('First Forename at Death:')").text("Forename 1:");
  61. $("form[name='SearchIndexes'] td.main_text:contains('Second Forename at Death:')").text("Forename 2:");
  62. $("form[name='SearchIndexes'] td.main_text:contains('District of Death:')").text("District:");
  63. $("form[name='SearchIndexes'] td.main_text:contains('Age at'):contains('Death'):contains('in years')").text("Age:");
  64. $("form[name='SearchIndexes'] td.main_text:contains('Surname at Birth:')").html("Surname:<span class='redStar'>*</span>");
  65. $("form[name='SearchIndexes'] td.main_text:contains('First Forename:')").text("Forename 1:");
  66. $("form[name='SearchIndexes'] td.main_text:contains('Second Forename:')").text("Forename 2:");
  67. $("form[name='SearchIndexes'] td.main_text:contains('Maiden Surname:')").text("Mother:");
  68. $("form[name='SearchIndexes'] td.main_text:contains('District of Birth:')").text("District:");
  69.  
  70. // Add gender and year navigation buttons, and style them
  71. var searchButton = $("form[name='SearchIndexes'] input[type='submit'][value='Search']");
  72. $(searchButton).attr("accesskey", "?");
  73. $(searchButton).parent().find("br").remove();
  74.  
  75. $("<input type='button' class='formButton' accesskey='#' id='groish_BtnToggleGender' value='Gender' />").insertBefore($(searchButton));
  76. $("<input type='button' class='formButton' accesskey='[' id='groish_BtnYearsPrev' value='&lt; Years' />").insertBefore($(searchButton));
  77. $("<input type='button' class='formButton' accesskey=']' id='groish_BtnYearsNext' value='Years &gt;' />").insertBefore($(searchButton));
  78. var buttonContainer = $("form[name='SearchIndexes'] input[type='submit'][value='Search']").closest("td").addClass("groish_ButtonContainer");
  79. // Add button event handlers
  80. $("input#groish_BtnYearsPrev").click(function() { navigateYears(false); });
  81. $("input#groish_BtnYearsNext").click(function() { navigateYears(true); });
  82. $("input#groish_BtnToggleGender").click(function() { toggleGender(); });
  83. }
  84. var initialiseResultViews = function() {
  85. // Move default results table into a view container
  86. var defaultTable = $("form[name='SearchIndexes'] h3:contains('Results:')").closest("table").css("width", "100%").addClass("groish_ResultsTable");
  87. $(defaultTable).before($("<div results-view='default' />"));
  88. var defaultView = $("div[results-view='default']");
  89. $(defaultView).append($("table.groish_ResultsTable"));
  90.  
  91. // Move header row to before default view
  92. $(defaultView).before($("<div class='groish_ResultsHeader' style='margin: 10px 0px; position: relative' />"));
  93. $(".groish_ResultsHeader").append($("table.groish_ResultsTable h3:contains('Results:')"));
  94.  
  95. // Move pager row contents to after default view
  96. $(defaultView).after($("table.groish_ResultsTable > tbody > tr:last table:first"));
  97. $("div[results-view='default'] + table").css("width", "100%").addClass("groish_ResultsInfo");
  98.  
  99. // Get results, sort them and populate views
  100. results = getResults(recordType);
  101. sortResults();
  102. populateAlternateViews();
  103. }
  104. var sortResults = function(reverse, sortFieldsCsv) {
  105. //console.log("sorting results, sort fields: %s", sortFieldsCsv);
  106. if (!results || !results.items)
  107. return;
  108. var defaultSortFields = "year,quarter";
  109. // Get the last sort fields and order for the record type
  110. var sortFieldsKey = recordType + "-sort-fields";
  111. var sortOrderKey = recordType + "-sort-order";
  112. var lastSortFields = sessionStorage.getItem(sortFieldsKey);
  113. var lastSortOrder = sessionStorage.getItem(sortOrderKey);
  114. // Cleanup values
  115. sortFieldsCsv = (sortFieldsCsv || "").replace(/\s\s+/g, ' ');
  116. lastSortFields = (lastSortFields || "").replace(/\s\s+/g, ' ');
  117. //console.log("last sort fields: %s; last sort order: %s", lastSortFields, lastSortOrder);
  118. var sortOrder = "asc";
  119. if (!sortFieldsCsv) {
  120. sortFieldsCsv = lastSortFields || defaultSortFields;
  121. sortOrder = lastSortOrder || "asc";
  122. }
  123. else if (sortFieldsCsv.localeCompare(lastSortFields) == 0 && sortOrder.localeCompare(lastSortOrder) == 0 && reverse) {
  124. sortOrder = "desc";
  125. }
  126. // Build sort fields and order arrays
  127. var sortFields = sortFieldsCsv.split(",");
  128. var sortOrders = Array.apply(null, Array(sortFields.length)).map(String.prototype.valueOf, sortOrder);
  129. // Append defaults if needed
  130. if (sortFieldsCsv.localeCompare(defaultSortFields) != 0) {
  131. sortFields.push("year");
  132. sortFields.push("quarter");
  133. sortOrders.push("asc");
  134. sortOrders.push("asc");
  135. }
  136. //console.log("sorting results by: %s (%s)", sortFields, sortOrders);
  137. results.items = _.orderBy(results.items, sortFields, sortOrders);
  138. sessionStorage.setItem(sortFieldsKey, sortFieldsCsv);
  139. sessionStorage.setItem(sortOrderKey, sortOrder);
  140. }
  141.  
  142. var populateAlternateViews = function() {
  143. // Add alternate view(s)
  144. if (recordType && resources && results && results.items && results.items.length > 0) {
  145. // Remove any existing views
  146. $("div[results-view][results-view!='default']").remove();
  147. // Add alternate views
  148. //console.log("Adding alternate views...");
  149. var viewPrefix = "view_" + recordType; // record type = EW_Birth, EW_Death
  150. for (var resourceName in resources) {
  151. var resourceNamePrefix = resourceName.substring(0, viewPrefix.length);
  152. if (resources.hasOwnProperty(resourceName) && viewPrefix.localeCompare(resourceNamePrefix) == 0) {
  153. var template = resources[resourceName].toString();
  154. var compiledTemplate = Handlebars.compile(template);
  155. var html = compiledTemplate(results);
  156. if (html)
  157. $("div[results-view]").filter(":last").after($(html));
  158. }
  159. }
  160. // Add view helpers and event handlers, if not already added
  161. if ($("div[results-view]").length > 1) {
  162. // Add event handler to hide/show actions row
  163. // TODO: Make adding view event handlers more dynamic, so they can be specific to the view
  164. $("div[results-view][results-view!='default'] tbody tr.rec")
  165. .off("click.groish")
  166. .on("click.groish", function(event) {
  167.  
  168. event.preventDefault();
  169. $(this).next("tr.rec-actions:not(:empty)").toggle();
  170. }
  171. );
  172.  
  173. // Add event handler for column sorting
  174. $("div[results-view][results-view!='default'] thead td[sort-fields]")
  175. .off("click.groish")
  176. .on("click.groish", function(event) {
  177.  
  178. event.preventDefault();
  179. //var defaultSortFields = ($(this).closet("div[results-view]").attr("default-sort-fields");
  180. var sortFields = ($(this).attr("sort-fields") ? $(this).attr("sort-fields") : $(this).text());
  181. sortResults(true, sortFields);
  182. populateAlternateViews();
  183. }
  184. );
  185.  
  186. // Add view switcher, if it doesn't already exist
  187. if ($("#groish_ViewSwitcher").length == 0) {
  188. $(".groish_ResultsHeader").append($("<a href='#' id='groish_ViewSwitcher' class='main_text' accesskey='~'>Switch view</a>"));
  189. $("#groish_ViewSwitcher").off("click.groish").on("click.groish", function() { switchResultsView(); return false; });
  190.  
  191.  
  192. // Add results selector (if supported)
  193. if (window.getSelection && document.createRange) {
  194. $(".groish_ResultsHeader").append($("<a href='#' id='groish_ResultsSelector' class='main_text' accesskey='|'>Select results</a>"));
  195. $("#groish_ResultsSelector")
  196. .off("click.groish")
  197. .on("click.groish", function(event) {
  198.  
  199. event.preventDefault();
  200. var resultsBody = $("div[results-view]:visible tbody")[0];
  201. if (resultsBody) {
  202. var selection = window.getSelection();
  203. var range = document.createRange();
  204. range.selectNodeContents(resultsBody);
  205. selection.removeAllRanges();
  206. selection.addRange(range);
  207. }
  208.  
  209. return false;
  210. });
  211. }
  212. }
  213. }
  214.  
  215. // Show the last used view
  216. var viewName = sessionStorage.getItem("groish_view." + recordType);
  217. //console.log("initialising view: %s", viewName);
  218. if (viewName && $("div[results-view='" + viewName + "']:hidden").length == 1) {
  219. //console.log("setting active view: %s", viewName);
  220. $("div[results-view][results-view!='" + viewName + "']").hide();
  221. $("div[results-view][results-view='" + viewName + "']").show();
  222. }
  223. }
  224. }
  225.  
  226. var switchResultsView = function() {
  227. var views = $("div[results-view]");
  228. if (views.length > 1) {
  229. var curIndex = -1;
  230. $(views).each(function(index) {
  231. if ($(this).css("display") != "none")
  232. curIndex = index;
  233. });
  234.  
  235. //console.log("current view index: %s", curIndex);
  236. if (curIndex !== -1) {
  237. var newIndex = ((curIndex == (views.length-1)) ? 0 : curIndex+1);
  238. $(views).hide();
  239. $("div[results-view]:eq(" + newIndex + ")").show();
  240.  
  241. // Get the name and save it
  242. var viewName = $("div[results-view]:eq(" + newIndex + ")").attr("results-view")
  243. sessionStorage.setItem("groish_view." + recordType, viewName); //save it
  244. //console.log("new view: %s", viewName);
  245. }
  246. }
  247. }
  248. var getResults = function(recordType) {
  249. var results = { "ageWarningThreshold": 24, "items": [], "failures": [] };
  250. // Lookup record type - birth or death
  251. if (recordType !== null && (recordType === "EW_Birth" || recordType === "EW_Death")) {
  252. var gender = $("form[name='SearchIndexes'] select#Gender").val();
  253. $("div[results-view='default'] > table > tbody > tr")
  254. .has("img[src='./graphics/order_certificate_button.gif'],img[src='./graphics/order_button.gif']")
  255. .each(function(index) {
  256. try
  257. {
  258. //console.log("Parsing record (%d)...", index);
  259. // Get names and reference
  260. var names = $(this).find("td:eq(0)").text().replace(/\u00a0/g, " ").replace(/\s\s+/g, ' ').trim();
  261. var ref = $(this).next().find("td:eq(0)").text();
  262.  
  263. // Clean up reference
  264. ref = ref.replace(/\u00a0/g, " ");
  265. ref = ref.replace(/\s\s+/g, ' ');
  266. ref = ref.replace(/GRO Reference: /g, "");
  267. ref = ref.replace(/M Quarter in/g, "Q1");
  268. ref = ref.replace(/J Quarter in/g, "Q2");
  269. ref = ref.replace(/S Quarter in/g, "Q3");
  270. ref = ref.replace(/D Quarter in/g, "Q4");
  271.  
  272. var age = 0;
  273. if (recordType === "EW_Death") {
  274. var ageArr = /^([0-9]{1,3})$/.exec($(this).find("td:eq(1)").text().replace(/\u00a0/g, " ").replace(/\s\s+/g, ' ').trim());
  275. if (ageArr)
  276. age = parseInt(ageArr[1], 10);
  277. }
  278.  
  279. var mother = null;
  280. if (recordType === "EW_Birth")
  281. mother = toTitleCase($(this).find("td:eq(1)").text().replace(/\u00a0/g, " ").replace(/\s\s+/g, ' ')).trim();
  282.  
  283. var actions = [];
  284. var orderCertUrl = $(this).find("a[href^='indexes_order.asp']:eq(0)").prop("href");
  285. var orderPdfUrl = $(this).next().find("a[href^='indexes_order.asp']:eq(0)").prop("href");
  286.  
  287. if (orderCertUrl) actions.push( {"text": "Order Certificate", "url": orderCertUrl });
  288. if (orderPdfUrl) actions.push( {"text": "Order Research Copy", "title": "PDF", "url": orderPdfUrl });
  289.  
  290. // Parse forenames, surname, year, quarter, district, vol, page
  291. var namesArr = /([a-z' -]+),([a-z' -]*)/gi.exec(names);
  292. var refArr = /([0-9]{4}) Q([1-4]) ([a-z\.\-,\(\)0-9\&'/ ]*)Volume ([a-z0-9]+)( Page ([0-9a-z]+)|)/gi.exec(ref); // NB: the district may not be set in some cases
  293.  
  294. //console.log("index: %d, namesArr: %s, refArr: %s", index, namesArr, refArr);
  295.  
  296. var record =
  297. {
  298. "gender": gender,
  299. "forenames": toTitleCase(namesArr[2]).trim(),
  300. "surname": toTitleCase(namesArr[1]).trim(),
  301. "age": age,
  302. "mother": mother,
  303. "year": parseInt(refArr[1], 10),
  304. "quarter": parseInt(refArr[2], 10),
  305. "district": toTitleCase(refArr[3]).trim(),
  306. "volume": refArr[4].toLowerCase(),
  307. "page": refArr[6],
  308. "actions": actions
  309. };
  310. record.noForenames = (!record.forenames || record.forenames == "-");
  311. record.ageWarning = (age != null && age > 0 && age <= results.ageWarningThreshold);
  312. record.birth = (age != null ? record.year - age : null);
  313. //console.log(record);
  314. results.items.push(record);
  315. }
  316. catch (e)
  317. {
  318. //console.log("Failed to parse record (%d): %s", index, e.message);
  319. results.failures.push({ "index": index, "ex": e });
  320. }
  321. });
  322. }
  323. return results;
  324. }
  325.  
  326.  
  327. var toTitleCase = function(str) {
  328. return str.replace(/([^\W_]+[^\s-]*) */g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
  329. }
  330. var switchRecordType = function() {
  331. var recordTypes = $("form[name='SearchIndexes'] input[type='Radio'][name='index']");
  332.  
  333. var curIndex = -1;
  334. for (var i = 0; i < recordTypes.length; i++) {
  335. if ($(recordTypes).eq(i).prop("checked")) {
  336. curIndex = i;
  337. break;
  338. }
  339. }
  340. //console.log("current record type: %d", curIndex);
  341.  
  342. if (curIndex >= 0) {
  343. var nextIndex = (curIndex == (recordTypes.length-1)) ? 0 : curIndex + 1;
  344.  
  345. if (nextIndex != curIndex)
  346. $(recordTypes).eq(nextIndex).prop("checked", true).click();
  347. //console.log("next record type: %d", nextIndex);
  348. }
  349. }
  350.  
  351. var toggleGender = function() {
  352. var curGender = $("form[name='SearchIndexes'] select#Gender").val();
  353. $("form[name='SearchIndexes'] select#Gender").val((curGender === "F" ? "M" : "F"));
  354. $("form[name='SearchIndexes'] input[type='submit'][value='Search']").click();
  355. }
  356. var adjustSearchYear = function(step) {
  357. var adjusted = false;
  358. // Get min and max years
  359. var minYear = parseInt($("form[name='SearchIndexes'] select#Year option:eq(2)").val(), 10);
  360. var maxYear = parseInt($("form[name='SearchIndexes'] select#Year option:last").val(), 10);
  361.  
  362. //console.log("Year range: %s - %s", minYear, maxYear);
  363.  
  364. if (!isNaN(step) && !isNaN(minYear) && !isNaN(maxYear)) {
  365. // Read current year and range
  366. var curYear = parseInt($("form[name='SearchIndexes'] select#Year").val(), 10);
  367. var curRange = parseInt($("form[name='SearchIndexes'] select#Range").val(), 10);
  368.  
  369. if (!isNaN(curRange)) {
  370. // Calculate the new year
  371. var newYear = (isNaN(curYear) ? minYear : curYear+step);
  372. newYear = Math.min(Math.max(newYear, minYear), maxYear);
  373. if (newYear != curYear) {
  374. $("form[name='SearchIndexes'] select#Year").val(newYear);
  375. adjusted = true;
  376. }
  377. }
  378.  
  379. //console.log("Current year: %d +-%d (%d-%d), New year: %d (%d-%d)", curYear, curRange, curYear-curRange, curYear+curRange, newYear, newYear-curRange, newYear+curRange);
  380. }
  381.  
  382. return adjusted;
  383. }
  384.  
  385. var navigateYears = function(forward) {
  386. var curRange = parseInt($("form[name='SearchIndexes'] select#Range").val(), 10);
  387. if (!isNaN(curRange)) {
  388. // Calculate the new year
  389. var step = (curRange * 2) + 1;
  390. if (!forward) step = -step;
  391. if (adjustSearchYear(step)) {
  392. $("form[name='SearchIndexes'] input[type='submit'][value='Search']").click();
  393. }
  394. }
  395. }
  396. var getRecordType = function() {
  397. return $("form[name='SearchIndexes'] input[type='radio'][name='index']:checked").val();
  398. }
  399.  
  400.  
  401. var buildResources = function() {
  402. resources = {
  403.  
  404. baseStyle: `
  405. <style type="text/css">
  406. body
  407. {
  408. min-height: 1200px;
  409. }
  410. .groish_ButtonContainer
  411. {
  412. padding-bottom: 10px;
  413. }
  414. .groish_ButtonContainer input[type='submit'],
  415. .groish_ButtonContainer input[type='button']
  416. {
  417. margin-right: 20px;
  418. min-width: 100px;
  419. font-size: 13px;
  420. padding: 4px 10px;
  421. }
  422. .groish_ButtonContainer input[type='submit']
  423. {
  424. margin-right: 0px;
  425. }
  426. #groish_ResultsSelector,
  427. #groish_ViewSwitcher
  428. {
  429. display:inline-block;
  430. position: absolute;
  431. bottom: 0px;
  432. color: #993333;
  433. font-weight: bold;
  434. cursor: pointer;
  435. }
  436. #groish_ResultsSelector
  437. {
  438. right: 120px;
  439. }
  440. #groish_ViewSwitcher
  441. {
  442. right: 10px;
  443. }
  444. div[results-view] td[sort-fields]:hover
  445. {
  446. cursor: pointer;
  447. }
  448.  
  449. </style>
  450. `,
  451.  
  452. view_EW_Birth_Simple: `
  453. <style type="text/css">
  454. div[results-view='EW_Birth-Simple'] td
  455. {
  456. padding: 5px 3px;
  457. font-size: 75%;
  458. color: #663333;
  459. vertical-align: top;
  460. }
  461. div[results-view='EW_Birth-Simple'] thead td
  462. {
  463. font-weight: bold;
  464. }
  465. div[results-view='EW_Birth-Simple'] tbody tr:nth-child(4n+1),
  466. div[results-view='EW_Birth-Simple'] tbody tr:nth-child(4n+2)
  467. {
  468. background-color: #F9E8A5;
  469. }
  470. div[results-view='EW_Birth-Simple'] tr.rec-actions a
  471. {
  472. padding: 0px 5px;
  473. font-size: 90%;
  474. color: #663333;
  475. text-decoration: none;
  476. }
  477. </style>
  478. <div results-view='EW_Birth-Simple' style='display: none; margin-bottom: 25px' default-sort-fields='year,quarter'>
  479. <table style='width: 100%; border-collapse: collapse'>
  480. <thead>
  481. <tr>
  482. <td style='width: 12%' sort-fields='year,quarter'>Date</td>
  483. <td style='width: 30%' sort-fields='forenames,surname'>Name</td>
  484. <td style='width: 15%' sort-fields='mother'>Mother</td>
  485. <td style='width: 27%' sort-fields='district'>District</td>
  486. <td style='width: 8%' sort-fields='volume,district'>Vol</td>
  487. <td style='width: 8%' sort-fields='page,volume'>Page</td>
  488. </tr>
  489. </thead>
  490. <tbody>
  491. {{#each items}}
  492. <tr class='rec'>
  493. <td>{{year}} Q{{quarter}}</td>
  494. <td><span class='forenames'>{{forenames}}</span> <span class='surname'>{{surname}}</span>{{#if noForenames}} ({{gender}}){{/if}}</td>
  495. <td>{{mother}}</td>
  496. <td>{{district}}</td>
  497. <td>{{volume}}</td>
  498. <td>{{page}}</td>
  499. </tr>
  500. <tr class='rec-actions' style='display: none'>
  501. <td colspan='6' style='text-align: right'>
  502. {{#actions}}
  503. <a href='{{url}}' {{#if title}}title='{{title}}'{{/if}}>{{text}}</a>
  504. {{/actions}}
  505. </td>
  506. </tr>
  507. {{/each}}
  508. </tbody>
  509. </table>
  510. {{#if failures}}
  511. <p class='main_text' style='color: Red'>WARNING: Failed to parse {{failures.length}} records. See default view for full list.</p>
  512. <!--
  513. {{#each failures}}record parse exception ({{index}}): exception: {{ex.message}}{{/each}}
  514. -->
  515. {{/if}}
  516. </div>`,
  517. view_EW_Birth_SimpleList: `
  518. <style type="text/css">
  519. div[results-view='EW_Birth-SimpleList'] td
  520. {
  521. padding: 5px 3px;
  522. font-size: 75%;
  523. color: #663333;
  524. vertical-align: top;
  525. }
  526. div[results-view='EW_Birth-SimpleList'] thead td
  527. {
  528. font-weight: bold;
  529. }
  530. div[results-view='EW_Birth-SimpleList'] tbody tr:nth-child(odd)
  531. {
  532. background-color: #F9E8A5;
  533. }
  534.  
  535. </style>
  536. <div results-view='EW_Birth-SimpleList' style='display: none; margin-bottom: 25px' default-sort-fields='year,quarter'>
  537. <table style='width: 100%; border-collapse: collapse'>
  538. <thead>
  539. <tr>
  540. <td style='width: 100%' sort-fields='year,quarter'>Births</td>
  541. </tr>
  542. </thead>
  543. <tbody>
  544. {{#each items}}
  545. <tr class='rec'>
  546. <td>
  547. {{year}} Q{{quarter}} Birth -
  548. {{forenames}} {{surname}}{{#if noForenames}} ({{gender}}){{/if}}
  549. (mmn: {{mother}});
  550. {{district}}; {{volume}}; {{page}}
  551. </tr>
  552. {{/each}}
  553. </tbody>
  554. </table>
  555. {{#if failures}}
  556. <p class='main_text' style='color: Red'>WARNING: Failed to parse {{failures.length}} records. See default view for full list.</p>
  557. <!--
  558. {{#each failures}}record parse exception ({{index}}): exception: {{ex.message}}{{/each}}
  559. -->
  560. {{/if}}
  561. </div>`,
  562.  
  563. view_EW_Death_Simple: `
  564. <style type="text/css">
  565. div[results-view='EW_Death-Simple'] td
  566. {
  567. padding: 5px 3px;
  568. font-size: 75%;
  569. color: #663333;
  570. vertical-align: top;
  571. }
  572. div[results-view='EW_Death-Simple'] thead td
  573. {
  574. font-weight: bold;
  575. }
  576. div[results-view='EW_Death-Simple'] tbody tr:nth-child(4n+1),
  577. div[results-view='EW_Death-Simple'] tbody tr:nth-child(4n+2)
  578. {
  579. background-color: #F9E8A5;
  580. }
  581. div[results-view='EW_Death-Simple'] tr.rec-actions a
  582. {
  583. padding: 0px 5px;
  584. font-size: 90%;
  585. color: #663333;
  586. text-decoration: none;
  587. }
  588. </style>
  589. <div results-view='EW_Death-Simple' style='display: none; margin-bottom: 25px' default-sort-fields='year,quarter'>
  590. <table style='width: 100%; border-collapse: collapse'>
  591. <thead>
  592. <tr>
  593. <td style='width: 12%' sort-fields='year,quarter'>Date</td>
  594. <td style='width: 26%' sort-fields='forenames,surname'>Name</td>
  595. <td style='width: 8%' sort-fields='age'>Age{{#if ageCautionThreshold}}*{{/if}}</td>
  596. <td style='width: 8%' sort-fields='birth'>Birth</td>
  597. <td style='width: 30%' sort-fields='district'>District</td>
  598. <td style='width: 8%' sort-fields='volume,district'>Vol</td>
  599. <td style='width: 8%' sort-fields='page,volume'>Page</td>
  600. </tr>
  601. </thead>
  602. <tbody>
  603. {{#each items}}
  604. <tr class='rec'>
  605. <td>{{year}} Q{{quarter}}</td>
  606. <td><span class='forenames'>{{forenames}}</span> <span class='surname'>{{surname}}</span>{{#if noForenames}} ({{gender}}){{/if}}</td>
  607. <td>{{age}}{{#if ageWarning}}*{{/if}}</td>
  608. <td>{{birth}}
  609. <td>{{district}}</td>
  610. <td>{{volume}}</td>
  611. <td>{{page}}</td>
  612. </tr>
  613. <tr class='rec-actions' style='display: none'>
  614. <td colspan='7' style='text-align: right'>
  615. {{#actions}}
  616. <a href='{{url}}' {{#if title}}title='{{title}}'{{/if}}>{{text}}</a>
  617. {{/actions}}
  618. </td>
  619. </tr>
  620. {{/each}}
  621. </tbody>
  622. </table>
  623. {{#if failures}}
  624. <p class='main_text' style='color: Red'>WARNING: Failed to parse {{failures.length}} records. See default view for full list.</p>
  625. <!--
  626. {{#each failures}}record parse exception ({{index}}): exception: {{ex.message}}{{/each}}
  627. -->
  628. {{/if}}
  629. <p class='main_text'>
  630. * Age is presumed to be years but <i>may</i> be months.
  631. {{#if ageWarningThreshold}}An age below {{ageWarningThreshold}} <i>may</i> be a child, treat with caution.{{/if}}
  632. An age of zero <i>may</i> have be used when a child was aged less than 12 months.
  633. </p>
  634. </div>`,
  635.  
  636. view_EW_Death_SimpleList: `
  637. <style type="text/css">
  638. div[results-view='EW_Death-SimpleList'] td
  639. {
  640. padding: 5px 3px;
  641. font-size: 75%;
  642. color: #663333;
  643. vertical-align: top;
  644. }
  645. div[results-view='EW_Death-SimpleList'] thead td
  646. {
  647. font-weight: bold;
  648. }
  649. div[results-view='EW_Death-SimpleList'] tbody tr:nth-child(odd)
  650. {
  651. background-color: #F9E8A5;
  652. }
  653. </style>
  654. <div results-view='EW_Death-SimpleList' style='display: none; margin-bottom: 25px' default-sort-fields='year,quarter'>
  655. <table style='width: 100%; border-collapse: collapse'>
  656. <thead>
  657. <tr>
  658. <td style='width: 100%' sort-fields='year,quarter'>Deaths</td>
  659. </tr>
  660. </thead>
  661. <tbody>
  662. {{#each items}}
  663. <tr class='rec'>
  664. <td>
  665. {{year}} Q{{quarter}} Death -
  666. {{forenames}} {{surname}}{{#if noForenames}} ({{gender}}){{/if}};
  667. age {{age}}{{#if ageWarning}}*{{/if}} (b{{birth}});
  668. {{district}}; {{volume}}; {{page}}
  669. </td>
  670. </tr>
  671. {{/each}}
  672. </tbody>
  673. </table>
  674. {{#if failures}}
  675. <p class='main_text' style='color: Red'>WARNING: Failed to parse {{failures.length}} records. See default view for full list.</p>
  676. <!--
  677. {{#each failures}}record parse exception ({{index}}): exception: {{ex.message}}{{/each}}
  678. -->
  679. {{/if}}
  680. <p class='main_text'>
  681. * Age is presumed to be years but <i>may</i> be months.
  682. {{#if ageWarningThreshold}}An age below {{ageWarningThreshold}} <i>may</i> be a child, treat with caution.{{/if}}
  683. An age of zero <i>may</i> have be used when a child was aged less than 12 months.
  684. </p>
  685. </div>`
  686.  
  687. };
  688. }
  689.  
  690. //Get the ball rolling...
  691. main();
  692. });