PageSourceSearch

https://www.sanger.k12.ca.us/wp-content/plugins/wp_google_drive_in…/elementor-script.js?ver=1789584470

js sanger.k12.ca.us collected 2026-09-28 17:18:38 UTC 33,139 bytes, 756 lines download raw bytes

1var imgPath = '/wp-content/plugins/wp_google_drive_integration/blocks/images';
2var storagePrefix = 'wpgdi-';
3__wgdi__clearStorage(storagePrefix);
4
5function __wpgdi__googleDriveFolderPopulate( e )
6{
7    if( e != 'undefined' || e != '' || e != false )
8    {
9        var view = e.find('.google-drive-folder-list');
10        var sectionId = e.parents('section').data('id')
11        view.attr('storageid', sectionId).append('<div class="view-loading">Loading...</div>');
12        var gDriveFolderId = e.attr("fid");
13        var listStyle = view.attr("liststyle");
14        switch (listStyle) {
15            case "file-tree":
16                __wgdi__populateFileTree(gDriveFolderId, view);
17                __wgdi__toggleSearchable(view, true);
18                break;
19            case "file-buttons":
20                __wgdi__populateFileButtons(gDriveFolderId, view);
21                __wgdi__toggleSearchable(view, true);
22                break;
23            case "table-list":
24                __wgdi__populateTableList(gDriveFolderId, view);
25                break;
26            case "board-policy":
27                __wgdi__populateBoardPolicy(gDriveFolderId, view);
28                break;
29        }
30    } else {
31        console.log("init")
32    }
33}
34
35function __wgdi__checkFolderIdChanged(gDriveFolderId, sectionId)
36{
37    if(sessionStorage.getItem('gid-' + sectionId) != gDriveFolderId){
38        //changed
39        sessionStorage.setItem('gid-' + sectionId, gDriveFolderId);
40        return true;
41    }else{
42        return false;
43    }
44}
45
46function __wgdi__populateFileTree(gDriveFolderId, view, notRoot, firstFolder = true)
47{
48    var id = view.attr('storageid');
49    var storageName = 'file-tree' + id;
50    if(gDriveFolderId){
51        let googleAPIkey = gdi_drive_api_key;
52        let url = "https://www.googleapis.com/drive/v3/files?q='" + gDriveFolderId + "'+in+parents&fields=files(contentHints(indexableText,thumbnail/mimeType),createdTime,description,fileExtension,id,kind,mimeType,shortcutDetails,modifiedTime,name,parents,properties,size),kind&pageSize=1000&key=" + googleAPIkey;
53        //stores Google URL fetch result into storage so when
54        //properties are changed don't spam the Google API.
55        //sessionStorage is reseted after page reload
56        if((!sessionStorage.getItem(storagePrefix + storageName) || (!sessionStorage.getItem(storagePrefix + storageName) && !notRoot)) || (__wgdi__checkFolderIdChanged(gDriveFolderId, id) && !notRoot)){
57            fetch(url)
58            .then(res => res.json())
59            .then((object) => {
60                sessionStorage.setItem(storagePrefix + storageName, JSON.stringify(object));
61                __wgdi__populateFileTree__output(gDriveFolderId, object, view, notRoot, firstFolder)
62            })
63            .catch(err => {
64                //console.log(err);
65                if(view){view.innerHTML = 'Error loading Google Folder. Please enter a valid ID or make sure the folder has public access.'}
66            });
67        }else{
68            var object = JSON.parse(sessionStorage.getItem(storagePrefix + storageName));
69            __wgdi__populateFileTree__output(gDriveFolderId, object, view, notRoot, firstFolder);
70        }
71    }
72}
73
74function __wgdi__populateFileTree__output(gDriveFolderId, object, view, notRoot, firstFolder)
75{
76    let ul;
77    view.find('.view-loading').remove();
78    if(view.find(".files-list").length < 1){
79        ul = jQuery("<ul/>", {class: "files-list"});
80    }else{
81        view.find(".files-list").remove();
82        ul = jQuery("<ul/>", {class: "files-list"});
83        ul.empty();
84    }
85    view.append(ul);
86    if(!notRoot){
87        ul.addClass("list");
88    }
89    ul.attr({id: gDriveFolderId, "by": view.attr("by"), "direction": view.attr("direction")});
90
91    var showIcon = view.attr("showicon") != '' && view.attr("showicon") != 'false' ? true : false;
92    var showFilesize = view.attr("showfilesize") != '' && view.attr("showfilesize") != 'false' ? true : false;
93    var by = view.attr("by") != "date" ? view.attr("by") : "modifiedTime";
94    var direction = view.attr("direction");
95
96    for(let i in object){
97        if(typeof object[i] === "object"){
98            let files = object[i];
99            //sort
100            if(direction == "asc"){
101                files.sort((a, b) => (a[by] > b[by]) ? 1 : -1);
102            }
102else if(direction == "desc"){
103                files.sort((a, b) => (a[by] < b[by]) ? 1 : -1);
104            }
105            for(let j in files){
106                let file = files[j];
107                let fileName = file.name;
108                if(fileName != ".DS_Store"){
109                    let mimeType = file.mimeType;
110                    let fileSize = '';
111                    if(showFilesize) {
112                        fileSize = file.size / 1000;
113                        fileSize = fileSize < 1000 ? fileSize.toFixed(0) + " KB" : (fileSize / 1000).toFixed(1) + " MB";
114                        fileSize = " <span>.......... " + fileSize + "</span>";
115                        file.size = fileSize;
116                    }else{
117                        file.size = '';
118                    }
119                    file.customName = fileName + fileSize;
120                    let li = jQuery("<li/>");
121                    const mimeFiles = ["image/png", "image/jpg", "image/jpeg", "application/pdf", "application/vnd.google-apps.file", "application/vnd.google-apps.document", "application/vnd.google-apps.presentation", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"];
122                    
123                    if(mimeFiles.includes(file.mimeType)){ //if is file
124                        genFileIcon(file,li,ul);
125                    }else if(file.mimeType == "application/vnd.google-apps.folder"){ //if is folder
126                        genFolderIcon(view, file, li, ul, firstFolder);
127                    }else if(file.mimeType == "application/vnd.google-apps.shortcut") {//if is a shortcut
128                        const mimeType = file.shortcutDetails.targetMimeType;
129                        if(mimeFiles.includes(mimeType)) {
130                            file.mimeType = mimeType;
131                            genFileIcon(file, li, ul);
132                        }else{
133                            file.id = file.shortcutDetails.targetId;
134                            genFolderIcon(view, file, li, ul, firstFolder)
135                        }
136                    }
137                }
138            }
139        }
140    }
141
142    function genFileIcon(file, li, ul) {
143        let iconDom;
144        let label = file.customName;
145        if(file.fileExtension){
146            iconDom = showIcon ? __makeFileIcon(file.fileExtension, 'raw') : '';
147        }else{
148            iconDom = showIcon ? __makeFileIcon(file.mimeType, 'raw') : '';
149        }
150        li.attr({type: "file", name: file.customName, date: file.modifiedTime, tabindex: 0}).append(iconDom, label).on("click", function(){
151            window.open('https://drive.google.com/file/d/' + file.id + '/view');
152        });
153        ul.append(li);
154    }
155
156    function genFolderIcon(view, file, li, ul, firstFolder) {
157        let iconFolder = jQuery("<img/>", {class: "list-icon", src: imgPath + "/icon-folder-closed.svg"});
158        li.attr({type: "folder", name: file.customName, date: file.modifiedTime, tabindex: 0, by: by, direction: direction}).append(iconFolder, file.name);
159        if(firstFolder) {
160            li.addClass('first-folder');
161        }
162        ul.append(li);
163        if(parseInt(view.attr("expandfolder")) == 1){
164            li.addClass("expand");
165            iconFolder.attr("src", imgPath + "/icon-folder-open.svg");
166        }
167        li.on('click', function(event){
168            let li = jQuery(this);
169            li.find('ul').on('click', function(j){
170                j.stopPropagation();
171            })
172            let expandClass = "expand";
173            const empty = jQuery('<li/>', {class: 'empty-label'}).append('<small><em>(Empty)</em></small>');
174            if(!li.hasClass(expandClass)){
175                iconFolder.attr("src", imgPath + "/icon-folder-open.svg");
176                li.addClass(expandClass);
177                if(li.find('ul').children().length < 1) {
178                    li.find('ul').append(empty)
179                }else{
180                    if(li.find('ul > [type="folder"]').length > 0) {
181                        const expandCollapseWrapper= jQuery('<div/>', {class: 'expandCollapseWrapper'});
182                        const expandBtn = jQuery('<button/>', {class: 'closed'}).append('+ Expand All').on('click', function(event) { expandCollapse(event, 'e', li); });
183                        const collapseBtn = jQuery('<button/>', {class: 'closed'}).append('- Collapse All').on('click', function(event) { expandCollapse(event, 'c', li); });
184                        if(li.parents('.first-folder').find('> .expandCollapseWrapper').length < 1) {
185                            li.append(expandCollapseWrapper.append(expandBtn, collapseBtn));
186                        }
187                    }
188                }
189            }else{
190                iconFolder.attr("src", imgPath + "/icon-folder-closed.svg");
191                li.removeClass(expandClass);
192                li.find('.empty-label, .expandCollapseWrapper').remove();
193            }
194        });
195        li.attr({'storageid': view.parents('section').data('id') + file.name, 'showicon': showIcon, 'showFilesize': file.size});
196        __wgdi__populateFileTree(file.id, li, true, false);
197    }
198
199    function expandCollapse(e, action, li) {
200        e.stopPropagation();
201        if(action == 'e') {
202            li.find('[type="folder"]').not('.expand').trigger('click');
203        }else{
204            li.find('[type="folder"]').filter('.expand').trigger('click');
205        }
206    }
207}
208
209//File Buttons
210function __wgdi__populateFileButtons(gDriveFolderId, view, notRoot)
211{
212    var id = view.attr('storageid');
213    var storageName = 'file-buttons' + id;
214    if(gDriveFolderId){
215        let googleAPIkey = gdi_drive_api_key;
216        let url = "https://www.googleapis.com/drive/v3/files?q='" + gDriveFolderId + "'+in+parents&fields=files(contentHints(indexableText,thumbnail/mimeType),createdTime,description,fileExtension,id,kind,thumbnailLink,mimeType,modifiedTime,name,parents,properties,size),kind&pageSize=1000&key=" + googleAPIkey;
217
218        //stores Google URL fetch result into storage so when
219        //properties are changed don't spam the Google API.
220        //sessionStorage is reseted after page reload
221        if((!sessionStorage.getItem(storagePrefix + storageName) || (!sessionStorage.getItem(storagePrefix + storageName) && !notRoot)) || (__wgdi__checkFolderIdChanged(gDriveFolderId, id) && !notRoot)){
222            fetch(url)
223            .then(res => res.json())
224            .then((object) => {
225                sessionStorage.setItem(storagePrefix + storageName, JSON.stringify(object));
226                __wgdi__populateFileButtons__output(gDriveFolderId, object, view, notRoot)
227            })
228            .catch(err => {
229                //console.log(err);
230                if(view){view.innerHTML = 'Error loading Google Folder. Please enter a valid ID or make sure the folder has public access.'}
231            });
232        }else{
233            var object = JSON.parse(sessionStorage.getItem(storagePrefix + storageName));
234            __wgdi__populateFileButtons__output(gDriveFolderId, object, view, notRoot);
235        }
236    }
237}
238
239function __wgdi__populateFileButtons__output(gDriveFolderId, object, view, notRoot)
240{
241    let div;
242    view.find('.view-loading').remove();
243    if(view.find(".file-buttons").length < 1){
244        div = jQuery("<div/>", {class: "file-buttons"});
245    }else{
246        view.find(".file-buttons").remove();
247        div = jQuery("<div/>", {class: "file-buttons"});
248        div.empty();
249    }
250    view.append(div);
251    if(!notRoot){
252        div.addClass("list");
253    }
254    div.attr({id: gDriveFolderId, "by": view.attr("by"), "direction": view.attr("direction")});
255
256    var showIcon = view.attr("showicon") != '' && view.attr("showicon") != 'false' ? true : false;
257    var showThumb = view.attr("showthumb") != '' && view.attr("showthumb") != 'false' ? true : false;
258    var thumbSize = view.attr("thumbsize");
259    var showFilename = view.attr("showfilename") != '' && view.attr("showfilename") != 'false' ? true : false;
260    var showFilesize = view.attr("showfilesize") != '' && view.attr("showfilesize") != 'false' ? true : false;
261    var by = view.attr("by") != "date" ? view.attr("by") : "modifiedTime";
262    var direction = view.attr("direction");
263    var showToolTip = view.attr("showtip") != '' && view.attr("showtip") != 'false' ? true : false;
264
265    for(let i in object){
266        if(typeof object[i] === "object"){
267            let files = object[i];
268            //sort
269            if(direction == "asc"){
270                files.sort((a, b) => (a[by] > b[by]) ? 1 : -1);
271            }else if(direction == "desc"){
272                files.sort((a, b) => (a[by] < b[by]) ? 1 : -1);
273            }
274
275            for(let j in files){
276                let file = files[j];
277                let fileName = file.name;
278                if(fileName != ".DS_Store"){
279                    let fileId = file.id;
280                    let fileExt = file.fileExtension;
281                    let mimeType = file.mimeType;
282                    let fileModified = file.modifiedTime;
283                    let fileSize = '';
284                    if(showFilesize) {
285                        fileSize = file.size / 1000;
286                        fileSize = fileSize < 1000 ? fileSize.toFixed(0) + " KB" : (fileSize / 1000).toFixed(1) + " MB";
287                        fileSize = " <span>(" + fileSize + ")</span>";
288                    }else{
289                        fileSize = '';
290                    }
291                    const parseTitle = __wdgi__fileButtonParser(fileName);
292                    let label = showFilename ? parseTitle.display + fileSize: '';
293                    
294                    
295                    if(mimeType != "application/vnd.google-apps.folder" || mimeType != "application/zip"){//if is file
296                        if(!showThumb) {
297                            let iconDom;
298                            if(fileExt){
299                                iconDom = showIcon ? __makeFileIcon(fileExt, 'raw') : '';
300                            }else{
301                                iconDom = showIcon ? __makeFileIcon(mimeType, 'raw') : '';
302                            }
303                            let buttonWrapper = jQuery("<div/>", {class: "file-button-wrapper"});
304                            let button = jQuery("<button/>", {class: "file-button", tabindex: "0"});
305                            let toolTip = jQuery("<div/>", {class: "tooltip"}).append(parseTitle.tip);
306                            button.attr({type: "file", name: fileName, date: fileModified, tabindex: 0});
307                            if(showToolTip && parseTitle?.tip) {
308                                buttonWrapper.append(toolTip);
309                            }
310                            buttonWrapper.append(button.append(iconDom, label));
311                            button.on("click", function(){
312                                window.open('https://drive.google.com/file/d/' + fileId + '/view');
313                            });
314                            div.append(buttonWrapper);
315                        }else{
316                            let thumbnailWrapper;
317                            let thumbLink;
318                            let thumbnail;
319                            if(file?.thumbnailLink) {
320
321                                thumbnailWrapper = jQuery("<div/>", {class: "thumbnail"});
322                                thumbLink = thumbSize == 'thumb-small' ? file.thumbnailLink.replace(/=s\d+$/, '=s400') : thumbSize == 'thumb-medium' ? file.thumbnailLink.replace(/=s\d+$/, '=s600') : thumbSize == 'thumb-large' ? file.thumbnailLink.replace(/=s\d+$/, '=s800') : file.thumbnailLink;
323                                if(showThumb ) {
324                                    //loader
325                                    let loader = jQuery("<small/>", {class: ""}).append('Loading preview...');
326                                    thumbnailWrapper.append(loader);
327                                    thumbnail = jQuery("<img/>", {src: thumbLink}).on('load', () => {
328                                        loader.remove();
329                                        thumbnailWrapper.append(thumbnail)
330                                    })
331                                }
332
333
334                                thumbnailWrapper.on("click", function(){
335                                    window.open('https://drive.google.com/file/d/' + fileId + '/view');
336                                });
337                                label = jQuery("<div/>").append(label);
338                                div.append(thumbnailWrapper.append(label));
339                            }
340                        }
341                    }
342                }
343            }
344        }
345    }
346}
347
348function __wdgi__fileButtonParser(text) {
349    const regex = /^(.*?)\s*\((.*?)\)(\..*)$/;
350    const matches = text.match(regex);
351
352    if (matches) {
353        // const outsideText = matches[1]; // "long file name title"
354        // const insideText = matches[2];  // "short title"
355        // const extension = matches[3];   // ".pdf"
356        return {display: matches[1], tip: matches[2]}
357    } else {
358        return {display: text}
359    }
360}
361
362function __wgdi__populateTableList(gDriveFolderId, view)
363{
364    view.find('.view-loading').remove();
365    var id = view.attr('storageid');
366    var storageName = 'file-tree' + id;
367    if(gDriveFolderId){
368        let googleAPIkey = gdi_drive_api_key;
369        let url = "https://www.googleapis.com/drive/v3/files?pageSize=1000&q='" + gDriveFolderId + "'+in+parents&fields=files(contentHints(indexableText,thumbnail/mimeType),createdTime,description,fileExtension,id,kind,mimeType,modifiedTime,name,parents,properties,size),kind&pageSize=1000&key=" + googleAPIkey;
370
371        //stores Google URL fetch result into storage so when
372        //properties are changed don't spam the Google API.
373        //sessionStorage is reseted after page reload
374        if(!sessionStorage.getItem(storagePrefix + storageName) || __wgdi__checkFolderIdChanged(gDriveFolderId, id)){
375            fetch(url)
376            .then(res => res.json())
377            .then((object) => {
378                sessionStorage.setItem(storagePrefix + storageName, JSON.stringify(object));
379                __wgdi__populateTableList__output(object, view);
380            })
381            .catch(err => {
382                console.log(err);
383                //if(view){ul.innerHTML = 'Error loading Google Folder. Please enter a valid ID or make sure the folder has public access.'}
384            });
385        }else{
386            var object = JSON.parse(sessionStorage.getItem(storagePrefix + storageName));
387            __wgdi__populateTableList__output(object, view);
388        }
389    }
390}
391
392function __wgdi__populateTableList__output(object, view) {
393    view.find('.view-loading').remove();
394    var headers = [
395        {
396            name: 'Filename',
397            sort: {
398                compare: (a, b) => {
399                    const getText = function(x) {
400                        return jQuery(x.props.content).find('span').html();
401                    }
402                    if( getText(a) > getText(b)) {
403                        return 1;
404                    }else if( getText(b) > getText(a) ) {
405                        return -1;
406                    }else{
407                        return 0;
408                    }
409                }
410            }
411        },
412        {
413            name: 'Date',
414            sort: {
415                compare: (a, b) => {
416                    const stamp = function(x) {
417                        return __wgdi__toTimestamp(x);
418                    }
419                    if( stamp(a) > stamp(b)) {
420                        return 1;
421                    }else if( stamp(b) > stamp(a) ) {
422                        return -1;
423                    }else{
424                        return 0;
425                    }
426                }
427            },
428            hidden: view.attr('showdate') ? false : true
429        },
430        {
431            name: 'Size',
432            sort: {
433                compare: (a, b) => {
434                    const base = function(x) {
435                        var splice = x.split(' ');
436                        if(splice[1] == 'KB') {
437                            return splice[0] * 1000;
438                        }else if(splice[1] == 'MB') {
439                            return splice[0] * 1000000;
440                        }
441                    }
442                    if( base(a) > base(b)) {
443                        return 1;
444                    }else if( base(b) > base(a) ) {
445                        return -1;
446                    }else{
447                        return 0;
448                    }
449                }
450            },
451            hidden: view.attr('showFilesize') ? false : true
452        }];
453    var data = [];
454    var row = [];
455
456    var showIcon = view.attr("showicon") ? true : false;
457
458    //presort data based on settings
459    if(view.attr('by') != '' || typeof view.attr('by') != 'undefined') {
460        object.files.sort(function (a, b) {
461            var by = view.attr('by') ? view.attr('by') : 'name';
462            if(view.attr('direction') == 'asc') {
463                return a[by].localeCompare(b[by]);
464            }else{
465                return b[by].localeCompare(a[by]);
466            }
467        });
468    }
469
470    jQuery.each(object.files, function(i, val){
471        var itemId = val.id;
472        var itemName = val.name;
473        var itemExt = val.fileExtension;
474        var itemLink = "https://drive.google.com/file/d/" + itemId + "/view";
475        var mimeType = val.mimeType;
476        var fileSize = val.size / 1000;
477        fileSize = fileSize < 1000 ? fileSize.toFixed(0) + " KB" : (fileSize / 1000).toFixed(1) + " MB";
478        var fileDateTimestamp = __wgdi__toTimestamp(val.createdTime);
479        var fileDateLamens = __wgdi__timestampToDate(val.createdTime, "MMM DD, YYYY");
480        if(mimeType != "application/vnd.google-apps.folder"){
481            var icon = showIcon ? __makeFileIcon(itemExt, 'raw') : '';
482            var link = gridjs.html("<a href='" + itemLink + "' target='_blank'>" + icon + "<span>" + itemName + "</span></a>");
483            var row = [link, fileDateLamens, fileSize];
484            data.push(row);
485        }
486    });
487
488    var options = {
489        columns: headers,
490        data: data,
491        sort: true,
492        resizable: false,
493        search: {
494            enabled: view.attr('searchable')
495        },
496        pagination: {
497            enabled: view.attr('paginate'),
498            limit: view.attr('paginate-limit'),
499            summary: true
500        }
501    }
502
503    var tableWrapper = jQuery("<div/>", {class: "datatable-wrapper"});
504    view.append(tableWrapper);
505    var table = new gridjs.Grid(options).render(tableWrapper[0]);
506}
507
508function __wgdi__populateBoardPolicy(gDriveFolderId, view, notRoot)
509{
510    var id = view.attr('storageid');
511    var storageName = 'file-tree' + id;
512    if(gDriveFolderId){
513        let googleAPIkey = gdi_drive_api_key;
514        let url = "https://www.googleapis.com/drive/v3/files?q='" + gDriveFolderId + "'+in+parents&fields=files(contentHints(indexableText,thumbnail/mimeType),createdTime,description,fileExtension,id,kind,mimeType,modifiedTime,name,parents,properties,size),kind&pageSize=1000&key=" + googleAPIkey;
515        //stores Google URL fetch result into storage so when
516        //properties are changed don't spam the Google API.
517        //sessionStorage is reseted after page reload
518        if(!sessionStorage.getItem(storagePrefix + storageName) || __wgdi__checkFolderIdChanged(gDriveFolderId, id)){
519            fetch(url)
520            .then(res => res.json())
521            .then((object) => {
522                sessionStorage.setItem(storagePrefix + storageName, JSON.stringify(object));
523                __wgdi__populateBoardPolicy__output(gDriveFolderId, object, view)
524            })
525            .catch(err => {
526                //console.log(err);
527                var message = "ERROR: The files below have incorrect Board Policy formatting and will display incorrectly. Please follow <a href='https://drive.google.com/open?id=1I4XdmS2pW8Tu0T8ROkvyXo94cUOmXYftcOP3-TUj7ig' target='_blank'>this guide.</a><br/><br/>";
528            });
529            //return false;
530        }else{
531            var object = JSON.parse(sessionStorage.getItem(storagePrefix + storageName));
532            __wgdi__populateBoardPolicy__output(gDriveFolderId, object, view);
533        }
534    }
535}
536
537function __wgdi__populateBoardPolicy__output(gDriveFolderId, object, view)
538{
539    view.find('.view-loading').remove();
540    if(view.find(".files-list").length > 0){
541        view.find(".files-list").remove()
542    };
543
544    var table = jQuery("<table/>", {class: "files-list board-policy"});
545    var heading = "<thead><tr>\
546        <th>Series Number</th>\
547        <th>Article Title</th>\
548    </tr></thead>";
549
550    var body = jQuery("<tbody/>");
551
552    var fileArray = {};
553    var error = "";
554    jQuery.each(object.files, function(key, val){
555        var itemId = val.id;
556        var itemName = val.name;
557        var itemExt = val.fileExtension;
558        var mimeType = val.mimeType;
559        if(mimeType != "application/vnd.google-apps.folder"){
560            //var itemLink = "/file/drive/" + itemId + "/" + encodeURIComponent(string_to_slug(itemName)).replace(/\(/g, "%28").replace(/\)/g, "%29");
561            var itemLink = "https://drive.google.com/file/d/" + itemId + "/view";
562            //checkfor correct regex format
563            var regex = /^(\w)+(\s){1}([\w\d\.])+(\s){1}(-)/;
564            if(!itemName.match(regex)){
565                error += "<li>" + itemName + "</li>";
566            }else{
567                //process arrays
568                var nameSplit = itemName.split(/\s-(.+)/);//split filename into Series Number & Filename
569
570                var seriesNumber = nameSplit[0];
571                    var seriesNumberSplit = seriesNumber.split(" ");//split the Series Number to Letter & Number
572                    var seriesNumberOnly = seriesNumberSplit[1].match(/[a-z]+|[^a-z]+/gi);
573                        var version = typeof seriesNumberOnly[1] == "undefined" ? "" : seriesNumberOnly[1];
574                        seriesNumberOnly = seriesNumberOnly[0];
575                    var seriesLetterOnly = seriesNumberSplit[0];
576                var articleName = nameSplit[1].trim();
577                var subArray = {itemId, articleName, seriesLetterOnly, seriesNumberOnly, version, itemLink};
578
579                if(!(seriesNumberOnly in fileArray)){
580                    fileArray[seriesNumberOnly] = [];
581                    fileArray[seriesNumberOnly].unshift(subArray);
582                }else{
583                    fileArray[seriesNumberOnly].unshift(subArray);
584                }
585            }
586        }
587
588    });
589    var fileArraySorted = (Object.keys(fileArray)).sort(__wgdi__sortNumerically);
590    var groupClass = "group-a";
591    for(const key of fileArraySorted){
592        var val = fileArray[key];
593        var seriesNumber = "";
594        var articleName;
595
596        //split each series letter into their own groups so they can be sorted correctly
597        var bpArray = [];
598        var arArray = [];
599        var verArray = [];
600        jQuery.each(val, function(i, val){
601            switch (val.seriesLetterOnly.__wgdi__capitalize()) {
602                case "BP":
603                case "BB":
604                    bpArray.push(val);
605                    break;
606                case "AR":
607                    arArray.push(val);
608                    break;
609                default:
610                    verArray.push(val);
611                    break;
612            }
613        });
614
615        //sorting each array group
616        var orderedArray = [];
617        bpArray.sort(function(a, b){
618            if(a.articleName > b.articleName) { return -1; }
619            if(a.articleName < b.articleName) { return 1; }
620            return 0;
621        });
622        jQuery.each(bpArray, function(i, row){
623            orderedArray.push(row);
624        });
625        arArray.sort(function(a, b){
626            if(a.articleName > b.articleName) { return -1; }
627            if(a.articleName < b.articleName) { return 1; }
628            return 0;
629        });
630        jQuery.each(arArray, function(i, row){
631            orderedArray.push(row);
632        });
633        verArray.sort(function(a, b){
634            if(a.version < b.version) { return -1; }
635            if(a.version > b.version) { return 1; }
636            return 0;
637        });
638        jQuery.each(verArray, function(i, row){
639            orderedArray.push(row);
640        });
641
642        var tempNumber = "";
643        var tempName = "";
644        var row = "";
645        jQuery.each(orderedArray, function(i, val){
646            articleName = val.articleName.split(/\.(.+)/)[0];
647            if(tempName == articleName){
648                row += "<tr class='" + groupClass + "' data-id='" + val.itemId + "' onclick='window.open(\"" + val.itemLink + "\")'><td>" + val.seriesLetterOnly + " " + val.seriesNumberOnly + val.version + "</td><td> " + articleName + "</td></tr>";
649            }else if(tempName != articleName && tempNumber == val.seriesNumberOnly){
650                row += "<tr class='" + groupClass + " no-border' data-id='" + val.itemId + "' onclick='window.open(\"" + val.itemLink + "\")'><td>" + val.seriesLetterOnly + " " + val.seriesNumberOnly + val.version + "</td><td> " + articleName + "</td></tr>";
651            }else{
652                row += "<tr class='" + groupClass + " new' data-id='" + val.itemId + "' onclick='window.open(\"" + val.itemLink + "\")'><td>" + val.seriesLetterOnly + " " + val.seriesNumberOnly + val.version + "</td><td> " + articleName + "</td></tr>";
653            }
654            tempNumber = val.seriesNumberOnly;
655            tempName = articleName;
656        });
657
658        body.append(row);
659        groupClass = groupClass == "group-a" ? "group-b" : "group-a";
660    };
661    var tableWrapper = jQuery("<div/>", {class: "datatable-wrapper"});
662    table.append(heading, body);
663    view.append(tableWrapper.append(table));
664    table.dataTable({
665        "bPaginate": false,
666        "sPaginationType": "full_numbers",
667        "pageLength": 1000,
668        "asStripeClasses": [],
669        "bSort": false
670    });
671    if(error != ""){
672        var message = "ERROR: The files below have incorrect Board Policy formatting and will display incorrectly. Please follow <a href='https://drive.google.com/open?id=1I4XdmS2pW8Tu0T8ROkvyXo94cUOmXYftcOP3-TUj7ig' target='_blank'>this guide.</a><br/><br/>";
673        alertify.alert(message + "<ul>" + error + "</ul>");
674    }
675    //__wgdi__sortDocuments(ul);
676}
677
678function __wgdi__toggleSearchable(parentDom, val)
679{
680    val = !val ? parentDom.attr("searchable") : val;
681    val = parseInt(val);
682    var searchBox = parentDom.find('input.searchbox').length > 0 ? parentDom.find('.searchbox') : jQuery('<input/>', {type: 'text', class: 'searchbox', placeholder: 'type to search'});
683
684    if(val){
685        parentDom.find("ul.list").before(searchBox);
686        __wgdi__makeSearchable(searchBox, parentDom);
687    }else{
688        searchBox.remove();
689    }
690}
691
692function __wgdi__makeSearchable(search, view)
693{
694    search.keyup(function(){
695        var e = jQuery(this);
696        var searchTerms = e.val();
697        view.find('li').each(function() {
698            var e = jQuery(this);
699            var hasMatch = searchTerms.length == 0 || e.html().toLowerCase().indexOf(searchTerms.toLowerCase()) > 0;
700            e.toggle(hasMatch);
701            hasMatch ? e.addClass("search-open") : e.removeClass("search-open");
702            if(searchTerms.length == 0){
703                e.removeClass("search-open");
704            }
705        });
706    });
707}
708
709function __wgdi__makeId(range)
710{
711  var text = "";
712  var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
713
714  for (var i = 0; i < range; i++)
715    text += possible.charAt(Math.floor(Math.random() * possible.length));
716
717  return text;
718}
719
720function __makeFileIcon(fileExt, exportType)
721{
722    let icon = "icon-file.svg"; //generic
723    switch (fileExt) {
724        case "pdf":
725        case "application/pdf":
726            icon = "icon-pdf.svg";
727            break;
728        case "docx":
729        case "doc":
730        case "application/vnd.google-apps.document":
731            icon = "icon-word.svg";
732            break;
733        case "xls":
734        case "xlsx":
735        case "application/vnd.google-apps.spreadsheet":
736            icon = "icon-excel.svg";
737            break;
738        case "zip":
739            icon = "icon-zip.svg";
740            break;
741        case "pptx":
742        case "application/vnd.google-apps.presentation":
743            icon = "icon-pptx.svg";
744            break;
745        case "jpeg":
746        case "jpg":
747        case "png":
748            icon = "icon-img.svg";
749            break;
750    }
751    if(exportType == 'raw') {
752        return iconRaw = "<img class='list-icon' src='" + imgPath + "/" + icon + "'/>";
753    }else if(exportType == 'dom') {
754        return iconDom = jQuery("<img/>", {class: "list-icon", src: imgPath + "/" + icon});
755    }
756}

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.