1function popupLink(record) 2{ 3 try{ 4 return `dialPopup('${record.ln}','${record.ti}')`; 5 }catch{ 6 return "" 7 } 8 9} 10 11var qd = {}; 12location.search.substr(1).split("&").forEach(function(item) 13{var k = item.split("=")[0], v = decodeURIComponent(item.split("=")[1]); (k in qd) ? qd[k].push(v) : qd[k] = [v]}) 14 15// default to only showing those with pictures and locations 16var gPicsOnly = true; 17var gLocationOnly = false; 18var gViewType = 'A'; 19var g_viewableDials = []; 20 21var gSearchTerms = {words:"",field:"",type: "part",andWords:"",andField:"",andType:"part"} 22const possibleFields = { 23 "to":"Town", 24 "ti":"Name", 25 "cy":"County", 26 "tp":"Type", 27 "id": "SRN/MDN", 28 "de":"Description", 29 "mkr":"Maker", 30 "oth":"Other", 31 "all":"All Fields" 32} 33 34 35function getField(fName, record) 36{ 37 var result = record[fName]; 38} 39 40function listingHTML(listOfRecords) 41{ 42 var html = headerHTML(); 43 var SRNList = "" 44 45 46 for(var i=0; i< listOfRecords.length;i++) 47 { 48 var record = listOfRecords[i]; 49 SRNList += listOfRecords[i].id + "," 50 html += ` 51 <div class="wp-bootstrap-blocks-row row"> 52 <div class="col-4 col-lg-2 col-md-4 col-sm-4"> 53 <A HREF="#!" onCLick="${popupLink(record)}"> 54 <IMG SRC="${record.tn}" class="img-thumbnail object-fit-scale border rounded"></A> 55 </DIV> 56 <div class="col-8 col-lg-10 col-md-8 col-sm-8"> 57 <P><B><A HREF="#!" onCLick="${popupLink(record)}">${record.id}: ${record.ti}</A></B></P> 58 <P>${record.de}...</P> 59 60 61 </DIV> 62 </div> 63 <hr> 64 ` 65 } 66 return html; 67} 68 69 70function galleryHTML(listOfRecords, rows, cols) 71{ 72 var html= ""; 73 var item = 0; 74 for(var row = 0; row < rows; row++) 75 { 76 html += `<div class="wp-bootstrap-blocks-row row gx-10 gy-10">`; 77 for(var col=0; col<cols; col++) 78 { 79 try{ 80 var record = listOfRecords[item]; 81 html += `<div class="col-6 col-sm-6 col-md-4 col-lg-2 col-sm-6 text-center"> 82 <A HREF="#!" 83 onCLick="${popupLink(record)}"> 84 <IMG SRC="${record.tn}" class="img-thumbnail object-fit-scale border rounded" width="150px" height="150px"><BR> 85 <P class="fs-6">${record.id}: ${record.ti}</P></A> 86 </div>`; 87 }catch{ 88 // do nothing 89 } 90 item++; 91 } 92 html += "</div>"; 93 94 } 95 return html; 96 97} 98 99function dialPopup(link,title) 100{ 101 var xhttp = new XMLHttpRequest(); 102 xhttp.onreadystatechange = function() { 103 if (this.readyState == 4 && this.status == 200) { 104 document.getElementById("bridolModalLabel").innerHTML = title; 105 // get just the content from the page, not any other HTML etc 106 var parser = new DOMParser(); 107 var doc = parser.parseFromString(this.responseText, "text/html"); 108 var dial_info = doc.getElementById("dial-info").innerHTML; 109 document.getElementById("bridolModalBody").innerHTML = dial_info; 110 const popup = new bootstrap.Modal(document.getElementById('bridolModal'), {}); 111 popup.show(); 112 113 } 114 }; 115 xhttp.open("GET", link, true); 116 xhttp.send(); 117 118} 119 120function picturePopup(src,date) 121{ 122 const popup = new bootstrap.Modal(document.getElementById('bridolPictureModal'), {}); 123 document.getElementById("bridolPictureModalImage").src=src; 124 if (date != " "){ 125 date = "Picture taken: " + date; 126 } 127 128 document.getElementById("bridolPictureModalLabel").innerHTML=date; 129 popup.show(); 130} 131 132function showSearchOptions() 133{ 134 var s = document.getElementById('search-modal-body'); 135 s.innerHTML = ` 136 Search for<br> 137 <div class="row"> 138 <div class="col-md-6"> 139 <input type="text" id="searchWords" value="${gSearchTerms.words}"class="form-control" aria-label="Search Bridol"> 140 </div> 141 <div class="col-md-3"> 142 <select class="form-select" id="searchField" aria-label="Where to search"> 143 <option value="all" ${gSearchTerms.field == "all" ? "selected": ""}>Anywhere</option> 144 <option value="ti" ${gSearchTerms.field == "ti" ? "selected": ""}>in Name</option> 145 <option value="to" ${gSearchTerms.field == "to" ? "selected": ""}>in Town</option> 146 <option value="cy" ${gSearchTerms.field == "cy" ? "selected": ""}>in County</option> 147 <option value="tp" ${gSearchTerms.field == "tp" ? "selected": ""}>in Type</option> 148 <option value="mkr" ${gSearchTerms.field == "mkr" ? "selected": ""}>in Maker</option> 149 <option value="de" ${gSearchTerms.field == "de" ? "selected": ""}>in Description</option> 150 <option value="id" ${gSearchTerms.field == "id" ? "selected": ""}>in SRN</option> 151 </select> 152 </div> 153 <div class="col-md-3"> 154 <select class="form-select" id="searchWordMatch" aria-label="Type of searche"> 155 <option value="whole" ${gSearchTerms.type == "whole" ? "selected": ""}>Whole word</option> 156 <option value="start" ${gSearchTerms.type == "start" ? "selected": ""}>Start of word</option> 157 <option value="part" ${gSearchTerms.type == "part" ? "selected": ""}>Part of word</option> 158 159 </select> 160 </div> 161 </div> 162 <P>and (optionally)</P> 163 <div class="row"> 164 <div class="col-md-6"> 165 <input type="text" id="andSearchWords" value="${gSearchTerms.andWords}"class="form-control" aria-label="Search Bridol"> 166 </div> 167 <div class="col-md-3"> 168 <select class="form-select" id="andSearchField" aria-label="Default select example"> 169 <option value="all" ${gSearchTerms.andField == "all" ? "selected": ""}>Anywhere</option> 170 <option value="ti" ${gSearchTerms.andField == "ti" ? "selected": ""}>in Name</option> 171 <option value="to" ${gSearchTerms.andField == "to" ? "selected": ""}>in Town</option> 172 <option value="cy" ${gSearchTerms.andField == "cy" ? "selected": ""}>in County</option> 173 <option value="tp" ${gSearchTerms.andField == "tp" ? "selected": ""}>in Type</option> 174 <option value="mkr" ${gSearchTerms.andField == "mkr" ? "selected": ""}>in Maker</option> 175 176 <option value="de" ${gSearchTerms.andField == "de" ? "selected": ""}>in Description</option> 177 <option value="id" ${gSearchTerms.andField == "id" ? "selected": ""}>in SRN</option> 178 </select> 179 </div> 180 <div class="col-md-3"> 181 <select class="form-select" id="searchAndWordMatch" aria-label="Default select example"> 182 <option value="whole" ${gSearchTerms.andType == "whole" ? "selected": ""}>Whole word</option> 183 <option value="start" ${gSearchTerms.andType == "start" ? "selected": ""}>Start of word</option> 184 <option value="part" ${gSearchTerms.andType == "part" ? "selected": ""}>Part of word</option> 185 186 </select> 187 </div> 188 </div> 189` 190} 191 192function headerHTML() 193{ 194 return ""; 195 // no longer needed 196 return ` 197 <div class="wp-bootstrap-blocks-row row gx-10 gy-10"> 198 <div class="col-lg-12 col-md-12"">
199 <Button type="button" style="width:200px" class="btn btn-outline-secondary" onClick = "showGallery();">Random Selection</button> 200 <Button type="button" style="width:200px" class="btn btn-outline-secondary" onClick = "showMap();">Map</button> 201 <button type="button" style="width:200px" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#searchModal" onClick="showSearchOptions()">Search</button> 202 </div> 203 </DIV> 204 <hr> 205 ` 206} 207 208// this page looks at all dials, regardless of settings for pics and location 209function showSinglePage(id) 210{ 211 // define some error text 212 var errorText = ` 213 <h2>Sorry, there is no dial with SRN ${id} in the online system</h2>` 214 // see if the id is valid 215 var done = false; 216 for(var i=0; i< g_bridol_list.length; i++) 217 { 218 if (g_bridol_list[i].id == id) 219 { 220 const b_page = new XMLHttpRequest(); 221 b_page.onload = function() { 222 223 // get just the content from the page, not any other HTML etc 224 var parser = new DOMParser(); 225 var doc = parser.parseFromString(this.responseText, "text/html"); 226 var dial_info = doc.getElementById("full_dial_info").innerHTML; 227 document.getElementById("bridol").innerHTML = dial_info; 228 229 } 230 b_page.enerror = function() { 231 document.getElementById("bridol").innerHTML = errorText; 232 233 } 234 b_page.open("GET", `/wp_bridol/pages/S${id.slice(0,1)}/S${id}.html`, true); 235 b_page.send(); 236 done = true 237 } 238 } 239 if (!done) 240 { 241 // send an error page 242 document.getElementById("bridol").innerHTML = errorText; 243 } 244 245 246} 247 248 249function getViewableList() 250{ 251 g_viewableDials = []; 252 g_bridol_list.forEach(dial => { 253 var ok = true; 254 if (gPicsOnly && (dial.hasPics == 0) ) ok = false; 255 if (gLocationOnly && (dial.loc == "" )) ok = false; 256 if (gViewType != 'A') 257 { 258 if (gViewType != dial.dialtype) ok = false; 259 } 260 if (ok) 261 { 262 g_viewableDials.push(dial) 263 } 264 }); 265} 266 267// run this on load 268getViewableList(); 269 270function setFilterToggles() 271{ 272 document.getElementById("locationonly").checked = gLocationOnly 273 document.getElementById("locationonlys").checked = gLocationOnly 274 document.getElementById("picsonly").checked = gPicsOnly; 275 document.getElementById("picsonlys").checked = gPicsOnly; 276} 277 278function toggleLocation(id) 279{ 280 gLocationOnly = document.getElementById(id).checked; 281 setFilterToggles(); 282 getViewableList(); 283} 284 285function togglePics(id) 286{ 287 gPicsOnly = document.getElementById(id).checked; 288 setFilterToggles(); 289 getViewableList(); 290} 291 292function toggleViews(id) 293{ 294 gViewType = document.getElementById(id).value; 295 var selInd = 0; 296 if (gViewType == 'S') 297 { 298 selInd = 1; 299 }else if (gViewType == 'M') 300 { 301 selInd = 2; 302 } 303 document.getElementById("viewdials").selectedIndex = selInd; 304 document.getElementById("viewdialss").selectedIndex = selInd; 305 getViewableList(); 306} 307 308 309function doSearch() 310{ 311 312 313 // get the search terms with trailing and double spaces removed 314 gSearchTerms.words = document.getElementById("searchWords").value.trim().toUpperCase().replace(/\s+/g, ' '); 315 gSearchTerms.field = document.getElementById("searchField").value; 316 gSearchTerms.type = document.getElementById("searchWordMatch").value; 317 gSearchTerms.andWords = document.getElementById("andSearchWords").value.trim().toUpperCase().replace(/\s+/g, ' '); 318 gSearchTerms.andField = document.getElementById("andSearchField").value; 319 gSearchTerms.andType = document.getElementById("searchAndWordMatch").value; 320 firstPass = []; 321 searchResults = []; 322 323 324 // get whether to search for whole/part/start of word 325 var wholeWordStartCode = "\\b"; 326 var wholeWordEndCode = "\\b"; 327 328 329 if (gSearchTerms.type == "start") 330 { 331 wholeWordStartCode = "\\b"; 332 wholeWordEndCode = ""; 333 334 }else if (gSearchTerms.type == "part"){ 335 wholeWordStartCode = ""; 336 wholeWordEndCode = ""; 337 } 338 339 // get whether to search for whole/part/start of word 340 var wholeAndWordStartCode = "\\b"; 341 var wholeAndWordEndCode = "\\b"; 342 if (gSearchTerms.andType == "start") 343 { 344 wholeAndWordStartCode = "\\b"; 345 wholeAndWordEndCode = ""; 346 347 }else if (gSearchTerms.andType == "part"){ 348 wholeAndWordStartCode = ""; 349 wholeAndWordEndCode = ""; 350 } 351 352 353 // if the search terms have ' or ' in them 354 // make a more complicated expression 355 var regex = new RegExp(wholeWordStartCode + gSearchTerms.words + wholeWordEndCode, "gi"); 356 var wordParts = gSearchTerms.words.split(" OR ") 357 if (wordParts.length > 1) 358 { 359 regex = new RegExp(wholeWordStartCode + "(?:" + wordParts.join("|") +")" + wholeWordEndCode, "gi"); 360 } 361 362 // same for and search 363 var andRegex = new RegExp(wholeAndWordStartCode + gSearchTerms.andWords + wholeAndWordEndCode, "gi"); 364 var andWordParts = gSearchTerms.andWords.split(" OR ") 365 if (andWordParts.length > 1) 366 { 367 andRegex = new RegExp(wholeAndWordStartCode + "(?:" + andWordParts.join("|")+")"+ wholeAndWordEndCode, "gi"); 368 } 369 370 // console.log(gSearchTerms) 371 // console.log(gSearchTerms.words,gSearchTerms.andWords) 372 // console.log(regex, andRegex) 373 374 // if both boxes are empty, return an error 375 if (gSearchTerms.words == "" && gSearchTerms.andWords == "") 376 { 377 showMessage("Please enter something to search for."); 378 return; 379 } 380 // if the first box is empty but the second one is not, swap them round 381 if (gSearchTerms.words == "" && gSearchTerms.andWords != "") 382 { 383 gSearchTerms.words = gSearchTerms.andWords; 384 gSearchTerms.field = gSearchTerms.andField; 385 gSearchTerms.andWords = "" 386 gSearchTerms.andField = ""; 387 } 388 389 // do the first pass, match anything from the first box 390 for(var i=0; i< g_viewableDials.length; i++) 391 { 392 var rec = g_viewableDials[i]; 393 if (gSearchTerms.field == "all" || gSearchTerms.andField == "all" ) 394 { 395 396 all = "";
397 Object.keys(possibleFields).forEach((f) => {all += " " + rec[f]}); 398 rec.all = all; 399 } 400 if (rec[gSearchTerms.field].search(regex) >= 0) 401 { 402 firstPass.push(rec) 403 } 404 } 405 406 // do a second pass if necessary 407 if (gSearchTerms.andWords != "") 408 { 409 for(var i=0; i< firstPass.length; i++) 410 { 411 var rec = firstPass[i]; 412 // no need to calculatr 'all' as it was done on the first pass 413 if (rec[gSearchTerms.andField].search(andRegex) >= 0) 414 { 415 searchResults.push(rec) 416 } 417 } 418 }else{ 419 // no second pass needed 420 searchResults = firstPass; 421 } 422 showListing(searchResults) 423} 424 425function searchTermDescription() 426{ 427 text = "'<B>" + gSearchTerms.words + "</B>' in " + possibleFields[gSearchTerms.field]; 428 if (gSearchTerms.andWords != ""){ 429 text += " and '<B>" + gSearchTerms.andWords + "</B>' in " + possibleFields[gSearchTerms.andField] 430 } 431 return (text) 432} 433 434function showMessage(text) 435{ 436 document.getElementById("bridol_message_bar").innerHTML = `<div class="row"> 437 <div class="col"> 438 <h2>${text}</h2> 439 </div> 440 </div>` 441 document.getElementById("bridol").innerHTML = ""; 442} 443 444function latestDials() 445{ 446 var latest = []; 447 // go through then list and get all those with a latest number 448 for(var i=0; i< g_viewableDials.length; i++) 449 { 450 if (g_viewableDials[i].la != "") 451 { 452 latest.push([g_viewableDials[i].la,g_viewableDials[i].id]) 453 } 454 } 455 456 457 // sort ascending 458 latest.sort(function(a, b) { 459 return a[0] - b[0]; 460 }); 461 462 // make a list for showPageFromButton 463 var latestList = "" 464 latest.forEach(item=>{ 465 latestList += item[1] + "," 466 }); 467 // trim last comma 468 latestList = latestList.replace(/,\s*$/, ""); 469 showPageFromButton(latestList); 470 471} 472 473 474function showListing(recordList) 475{ 476 // make buttons for the pages if nexessary 477 const perPage = 20; 478 var html = "" 479 var filterText = [] 480 if (gViewType == 'A') 481 { 482 filterText.push("all dials"); 483 }else if (gViewType == 'M'){ 484 filterText.push("Mass dials only"); 485 }else if (gViewType == 'S'){ 486 filterText.push("Fixed dials only"); 487 } 488 if (gLocationOnly) filterText.push ("with location information") 489 if (gPicsOnly) filterText.push ("with pictures") 490 491 var buttons = "<P>" + recordList.length + " dials matched your search for " + searchTermDescription() + " for " + filterText.join(", ") + ".</P>" 492 493 function makeButton(dials,name) 494 { 495 return `<button type="button" style="width:100px" class="btn btn-outline-secondary" 496 onClick="showPageFromButton('${dials.join(',')}')"> 497 <span style="font-size:8pt;">${name[0]} - ${name[1]}</span></Button> `; 498 } 499 500 501 if (recordList.length > perPage) 502 { 503 var current = 0; 504 var dials = []; 505 var pageName = [0,0] 506 while (current < recordList.length){ 507 if (current % perPage == 0 && current > 0){ 508 // write out any existing button 509 pageName[1] = current - 1 510 buttons += makeButton(dials,pageName); 511 // start a new button 512 dials = []; 513 pageName = [current,0] 514 } 515 dials.push(recordList[current].id); 516 current++; 517 } 518 // write out last one 519 pageName[1] = current - 1 520 521 522 523 buttons += makeButton(dials,pageName); 524 525 buttons += "<hr>"; 526 527 528 } 529 // make a link in html comments which would save these results 530 var link = "https://sundialsoc.org.uk/bridol?SRNLIST=" + recordList.map(dial=>dial.id).join(",") 531 buttons += `<!-- 532 You can copy the link below and paste it into your browser to easily get back to this set of dials. 533 ${link} 534 -->` 535 536 537 document.getElementById("bridol_message_bar").innerHTML = buttons; 538 document.getElementById("bridol").innerHTML = listingHTML(recordList.slice(0,perPage)); 539} 540 541// given a comma separated list of SRNs, pick out the 542// records and pass to the listing function 543// viewing all dials regardless of pic or location settings 544function showPageFromButton(skus) 545{ 546 var wantedDials = skus.split(",").map(item => item.trim()); 547 var results = [] 548 var temp = [] 549 for(var i=0; i< g_bridol_list.length; i++) 550 { 551 if (wantedDials.includes(g_bridol_list[i].id )) 552 { 553 // add to a list of ones wanted, together with what order it came in 554 temp.push([wantedDials.indexOf(g_bridol_list[i].id), g_bridol_list[i]]) 555 } 556 } 557 558 // sort the temp list by wanted order 559 temp.sort(function(a, b){return a[0] -b[0]}); 560 561 // copy to the results 562 temp.forEach(item=>{ 563 results.push(item[1]) 564 565 }); 566 567 document.getElementById("bridol").innerHTML = listingHTML(results); 568} 569 570function showMap() 571{ 572 html = ` 573<div class="row"> 574 <div class="col"> 575 <div id = "map" style = "width: 100%; height: 600"></div> 576 </div> 577</div>` 578 document.getElementById("bridol_message_bar").innerHTML = ""; 579 document.getElementById("bridol").innerHTML = html; 580 showBigMap(g_viewableDials); 581 582} 583 584 585function showGallery() 586{ 587 var recordList = [] 588 var cols = 6; // must be a factor of 12 589 var rows = 6; // must be a factor of 12 590 var size = cols * rows; 591 var numRecs = g_viewableDials.length; 592 var chosen = []; 593 594 // if during testing the list is small forr debugging 595 // dont go into an infinite loop 596 if (numRecs < size) 597 { 598 for( var c = 0; c< g_viewableDials.length; c++) 599 { 600 chosen.push(c); 601 } 602 size = g_viewableDials.length; 603 }else{ 604 while (chosen.length < size) 605 { 606 var c = Math.floor( Math.random() * numRecs ); 607 if (!chosen.includes(c)) 608 { 609 chosen.push(c) 610 } 611 } 612 } 613 614 615 for(var i=0; i< size; i++) 616 { 617 recordList.push(g_viewableDials[chosen[i]]); 618 } 619 document.getElementById("bridol_message_bar").innerHTML = ""; 620 document.getElementById("bridol").innerHTML = galleryHTML(recordList,rows,cols); 621} 622 623// get query string 624const params = new URLSearchParams(window.location.search); 625if (params.has('SRN')){ 626 showSinglePage(params.get('SRN')) 627}else if (params.has('SRNLIST')){ 628 showPageFromButton(decodeURI(params.get('SRNLIST'))) 629}else{ 630 showGallery(); 631}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.