PageSourceSearch

https://sundialsoc.org.uk/wp_bridol/bridol.js

js sundialsoc.org.uk collected 2026-10-02 07:15:33 UTC 20,284 bytes, 631 lines download raw bytes

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 != "&nbsp"){
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.