PageSourceSearch

https://mainelegislature.org/mrs-search/billtext.js

js mainelegislature.org collected 2026-09-24 08:36:13 UTC 17,455 bytes, 381 lines download raw bytes

1const urlPath = window.location.pathname.match(/\/.*\//) || '/';  // need only the path without trailing file names
2
3const vm = new Vue({
4    el: "#vue-instance",
5    data() {
6        return {
7            // can set default values here
8            //baseUrl: `//${window.location.hostname}:3000`, // LOCAL MAC API url
9            //baseUrl: 'https://legislature.maine.gov/mrs-search/api',   // frontend only, public only testing
10            baseUrl: `//${window.location.host}${urlPath}api`, // API url
11            //apiUrl: 'https://legislature.maine.gov/api', // frontend only testing
12            //apiUrl: `//${window.location.hostname}:5000`,   // LOCAL testing 
13            apiUrl: `//${window.location.hostname}/api`, //DEPLOYED URL
14            searchTerm: "", // Default search term
15            title: "",
16            ldNumberLow: null,
17            ldNumberHigh: null,
18            legislature: null,
19            paperNumberType: null,
20            paperNumberLow: null,
21            paperNumberHigh: null,
22            requestItemType: null,
23            summary: null,
24            // lawmaker and LEAP search items from form
25            lmSponsor: null,
26            lmSponsorPrimary: false,
27            lmIntroducer: null,
28            lmRollCallBody: null,
29            lmRollCallNumber: null,
30            lmRollCallStartDate: null,
31            lmRollCallEndDate: null,
32            reqHouseAction: null,
33            reqSenateAction: null,
34            reqAmendBody: null,
35            reqAmendFiling: null,
36            reqSeqBody1: null,
37            reqSeqBody2: null,
38            reqAmendSeq1: null,
39            reqAmendSeq2: null,
40            reqAmendExists: false,
41            reqAmendAdoptH: false,
42            reqAmendAdoptS: false,
43            reqLawType: null,
44            reqChapter: null,
45            reqChapterExists: false,
46            reqCommittee: null,
47            reqCommitteeAction: null,
48            reqPublicHearingStartDate: null,
49            reqPublicHearingEndDate: null,
50            reqWorkSessionStartDate: null,
51            reqWorkSessionEndDate: null,
52            reqTestimonyStartDate: null,
53            reqTestimonyEndDate: null,
54            reqTestimonyNameFirst: null,
55            reqTestimonyNameLast: null,    
56            reqFinalDisposition: null,
57            reqFinalDispositionActionId: null,
58            reqGovernorAction: null,
59            reqTSTitle: null,
60            reqTSSection: null,
61            reqTSSubsection: null,
62            reqTSParagraph: null,
63            reqFiscalImpact: null,
64            reqFNRequired: false,
65            reqEmergency: false,
66            reqGovernor: false,
67            reqBond: false,
68            reqMandate: false,
69            reqPublicLand: false,
70            // end lawmaker/leap
71            showExtraParameters: false,
72            mustHave: "", // filters - must have these exactly?
73            mustNotHave: "", // filters - results not have these
74            searchOffset: 0, // Search result pagination offset
75            pageSize: 12,
76            sortByScore: false,
77            showBillText: false,
78            sortAscending: false,
79            excludeOrders: false,
80            listAllResults: false, // change page size to be equal to number of hits, up to pageMaxLimit below.
81            searchDebounce: null, // Timeout for search bar debounce
82            searchResults: [], // Displayed search results
83            numHits: null, // Total search results found
84            pageMaxLimit: 500,  // maximum page size
85            pageMinLimit: 10, // minimum page size - could probably be smaller, but at some point why would you bother
86            displayPageCount: 0, // actual number of results on the page - sometimes less than pagesize if separateSections is false
87            displayOffset: 0,  //display offset that takes into account the results of the separateSections consolidation
88            showInstructions: false,  // show instructions on search page
89            timestamp: null,
90            committeeActions: [],
91            floorActions:[],
92            governorActions: [],
93            finalActions: [],
94            committees: [],
95            sponsors: [],
96            legislatures: [],
97            err: null
98        };
99    },
100    async created() {
101        axios.get(`${this.apiUrl}/QRY-LEG-LST`)
102        .then(legList => {
103            this.legislatures = legList.data;
104        })
105        .catch(err => {
106            console.log(`Error fetching legislature list - ${JSON.stringify(err)}`);
107        });
108        axios.get(`${this.apiUrl}/QRY-CUR-LEG`)
109        .then(currentLeg => {
110            this.legislature = currentLeg.data;
111            this.getMetadata(this.legislature);
112            this.search()
113            .then(results => {
114                this.searchResults = results;
115            })
116            .catch(err => {
117                console.log(`Error fetching default search - ${JSON.stringify(err)}`);
118            });
119        })
120        .catch(err => {
121            console.log(`Error fetching current Legislature - ${JSON.stringify(err)}`);
122        });
123
124        // retrieve Action tables - no parameters!
125        axios.get(`${this.apiUrl}/QRY-META-ACTION`)
126        .then(response => {
127            this.committeeActions = response.data.COM;
128            this.floorActions = response.data.FLR;
129            this.governorActions = response.data.GOV;
130            this.finalActions = response.data.FD;    
131            //console.log(`response: ${JSON.stringify(response)}`)
132            console.log(`found action metadata, status ${response.status}`)
133        })
134        .catch(err => {
135            console.log(`Error fetching action metadata - ${JSON.stringify(err)}`);
136        });
137        //this.searchResults = await this.search(); // Search for default term
138    },
139    computed:{
140    },
141    methods: {
142        /** Debounce search input by ms */
143        onSearchInput() {
144            clearTimeout(this.searchDebounce);
145            this.searchDebounce = setTimeout(async () => {
146                this.searchOffset = 0;
147                this.displayOffset = 0;
148                this.getMetadata(this.legislature);
149                this.searchResults = await this.search();
150            }, 500);
151        },
152        getNow: function () {  //Date time US style
153            const today = new Date();
154            const todayDate = (today.getMonth() + 1) + '/' + today.getDate() + '/' + today.getFullYear();
155            const todayTime = today.getHours() + ":" + new String("00" + today.getMinutes()).slice(-2);
156            this.timestamp = todayDate + ' ' + todayTime;
157        },
158
159        toggleInstructionDisplay() {
160            this.showInstructions = !this.showInstructions;
161        },
162        toggleParameterDisplay() {
163            this.showExtraParameters = !this.showExtraParameters;
164        },
165        setListFormat(val) {
166            this.listAllResults = (val == 1) ?  true: false;
167        },
168        cmteLegislatureRange: function(from, to) {
169            return !this.legislature ?  `${this.ordinal(from)} - ${this.ordinal(to)}` : '';
170        },
171        resetPage: function() {
172            window.location.reload();
173        },
174        ordinal(number) {
175            if (isNaN(parseInt(number,10))) return '';
176
177            if (number % 100 > 9 && number % 100 < 21 ) return number + 'th';
178            switch (number % 10) {
179                case 1:
180                    return number + 'st';
181                case 2:
182                    return number + 'nd';
183                case 3:
184                    return number + 'rd';
185                default:
186                    return number + 'th';
187            }
188        },
189        itemTypeDisplay(requestItemType) {
190            return (({
191                'O': ' ',  //Original
192                'A': ', Amendment ',
193                'K': ', Concept Draft ', //Concept Draft
194                'C': ', ',  // was "Chaptered Law " but phrase not needed with law type and chapter number
195                'E': ' ', //Engrosser
196                'N': ' ', //New Draft
197                'W': ' ', //Working Draft
198                'F': ' ', //Fiscal Working Draft
199                'JR': ', Joint Resolution ',
200                'ES': ', Summary ' //Enacted Law Summary - don't think we have any of these.
201            })[requestItemType] ?? '');
202        },
203        lawTypeDisplay(lawType){
204            return (({
205                    "ACTPUB": 'Public Law',
206                    "ACTPS": 'Private &amp; Special',
207                    "CONRES": 'Constitutional Resolution',
208                    "RESLV": 'Resolve',
209                    "IB": 'Initiated Bill',
210                    "JSO": 'Joint Study Order',
211                    "JTRES": 'Joint Resolution',
212                    "CMR": 'Competing Mesure Resolution',
213            })[lawType] ?? '');
214        },
215        billStatusUrl(legislature, paperNumber) {
216            return (`https://legislature.maine.gov/billtracker/#Paper/${paperNumber}?legislature=${legislature}`);
217        },
218
219        /** Call API to search */
220        async search() {
221            this.getNow();  //update time on each search
222            if (isNaN(parseInt(this.pageSize, 10))) { return; }  // do this first to prevent NaN from appearing if user deletes entire number
223            this.pageSize = parseInt(this.pageSize, 10);
224            if (this.pageSize > this.pageMaxLimit) { this.pageSize = this.pageMaxLimit }
225            if (this.pageSize < this.pageMinLimit) { this.pageSize = this.pageMinLimit }
226            let response = null;
227            try {
228                this.err = null;
229                response = await axios.get(`${this.baseUrl}/billtext`, {
230                    params: {
231                        term: this.searchTerm,
232                        title: this.title,
233                        ldNumberLow: this.ldNumberLow,
234                        ldNumberHigh: this.ldNumberHigh,
235                        legislature: this.legislature,
236                        paperNumberType: this.paperNumberType,
237                        paperNumberLow: this.paperNumberLow,
238                        paperNumberHigh: this.paperNumberHigh,
239                        requestItemType: this.requestItemType,
240                        summary: this.summary,
241                        // lawmaker and LEAP search items from form
242                        lmSponsor: this.lmSponsor,
243                        lmSponsorPrimary: this.lmSponsorPrimary,
244                        lmIntroducer: this.lmIntroducer,
245                        lmRollCallBody: this.lmRollCallBody,
246                        lmRollCallNumber: this.lmRollCallNumber,
247                        lmRollCallStartDate: this.lmRollCallStartDate,
248                        lmRollCallEndDate: this.lmRollCallEndDate,
249                        reqHouseAction: this.reqHouseAction,
250                        reqSenateAction: this.reqSenateAction,
251                        reqAmendBody: this.reqAmendBody,
252                        reqAmendFiling: this.reqAmendFiling,
253                        reqSeqBody1: this.reqSeqBody1,
254                        reqSeqBody2: this.reqSeqBody2,
255                        reqAmendSeq1: this.reqAmendSeq1,
256                        reqAmendSeq2: this.reqAmendSeq2,
257                        reqAmendExists: this.reqAmendExists,
258                        reqAmendAdoptH: this.reqAmendAdoptH,
259                        reqAmendAdoptS: this.reqAmendAdoptS,
260                        reqLawType: this.reqLawType,
261                        reqChapter: this.reqChapter,
262                        reqChapterExists: this.reqChapterExists,
263                        reqCommittee: this.reqCommittee,
264                        reqCommitteeAction: this.reqCommitteeAction,
265                        reqPublicHearingStartDate: this.reqPublicHearingStartDate,
266                        reqPublicHearingEndDate: this.reqPublicHearingEndDate,
267                        reqWorkSessionStartDate: this.reqWorkSessionStartDate,
268                        reqWorkSessionEndDate: this.reqWorkSessionEndDate,
269                        reqTestimonyStartDate: this.reqTestimonyStartDate,
270                        reqTestimonyEndDate: this.reqTestimonyEndDate,
271                        reqTestimonyNameFirst: this.reqTestimonyNameFirst,
272                        reqTestimonyNameLast: this.reqTestimonyNameLast,
273                        reqFinalDisposition: this.reqFinalDisposition,
274                        reqFinalDispositionActionId: this.reqFinalDispositionActionId,
275                        reqGovernorAction: this.reqGovernorAction,
276                        reqTSTitle: this.reqTSTitle,
277                        reqTSSection: this.reqTSSection,
278                        reqTSSubsection: this.reqTSSubsection,
279                        reqTSParagraph: this.reqTSParagraph,
280                        reqFiscalImpact: this.reqFiscalImpact,
281                        reqFNRequired: this.reqFNRequired,
282                        reqEmergency: this.reqEmergency,
283                        reqGovernor: this.reqGovernor,
284                        reqBond: this.reqBond,
285                        reqMandate: this.reqMandate,
286                        reqPublicLand: this.reqPublicLand,
287                        // end lawmaker/leap
288                        showExtraParameters: this.showExtraParameters,
289                        mustHave: this.mustHave,
290                        mustNotHave: this.mustNotHave,
291                        offset: this.searchOffset,
292                        pageSize: this.pageSize, // page size for Elasticsearch defaults to 10, need to specifiy somewhere (code for server end set to default to 12)
293                        sortByScore: this.sortByScore,
294                        showBillText: this.showBillText,
295                        sortAscending: this.sortAscending,
296                        excludeOrders: this.excludeOrders,
297                        separateSections: this.separateSections,  // no point in passing this yet, but it would be good to figure out how to get ES to do the consolidation
298                    }
299                });
300    
301            } 
302            catch (err) {
303                this.err = {'message' : err.message};
304            }
305            let results = [];
306            this.displayPageCount = 0;
307            if (response) {
308                this.numHits = response.data.hits.total.value; // numhits becomes obj with value and relation properties
309                results = response.data.hits.hits;
310                this.displayPageCount = this.pageSize;
311            }
312
313            return results;
314        },
315        /** Get next page of search results */
316        async nextResultsPage() {
317            if (this.numHits > this.pageSize) {
318                if (!this.err) {
319                    this.searchOffset += this.pageSize;
320                }
321                this.displayOffset = this.searchOffset;
322                console.log(`searching NEXT ${this.searchOffset} of ${this.numHits}`);
323                this.searchResults = await this.search();
324                document.documentElement.scrollTop = 0;
325            }
326        },
327        /** Get previous page of search results */
328        async prevResultsPage() {
329            if (!this.err) {
330                this.searchOffset -= this.pageSize;
331                if (this.searchOffset < 0) {
332                    this.searchOffset = 0;
333                }    
334            }
335            this.displayOffset = this.searchOffset;
336            console.log(`searching PREVIOUS ${this.searchOffset} of ${this.numHits}`);
337            this.searchResults = await this.search();
338            document.documentElement.scrollTop = 0;
339        },
340        showStatutePage(hit) {
341            window.open(hit._source.url, "_self");
342        },
343        async getMetadata(legislature)
344        {
345            let result = {}
346            result.committeeStatus = 1;
347            result.sponsorStatus = 1;
348            try {
349                const url = (legislature ? `${this.apiUrl}/QRY-CMTG-BY-LEG/${legislature}`: `${this.apiUrl}/QRY-CMTG-BY-LEG/`);
350                const committees = await axios.get(url);
351                this.committees = committees.data;
352                result.committeeStatus = committees.status;
353                //console.log(`committees: ${JSON.stringify(committees)}`)
354                
355                const lmUrl = (legislature ? `${this.apiUrl}/QRY-SPNSR-LST/${legislature}`: `${this.apiUrl}/QRY-SPNSR-LST/`);
356                const sponsors = await axios.get(lmUrl);
357                //this.sponsors = sponsors.data;
358                this.sponsors = [];
359                sponsors.data.forEach(member => {
360                    let memberRecord = this.sponsors.find(e => e.MemberCode == member.MemberCode);
361                    if (memberRecord) {
362                        memberRecord.legislatures.push(this.ordinal(member.SessionID + 116));
363                    } else {
364                        member.legislatures = [this.ordinal(member.SessionID + 116) ];
365                        this.sponsors.push(member);
366                    }
367                
368                })
369                result.sponsorStatus = sponsors.status;
370                //console.log(`members: ${JSON.stringify(this.sponsors)}`)
371
372            } catch (e) {
373                console.log(`ERROR, API retrieval, action metadata \n${e}`);
374            } finally {
375                    console.log(`found committee list metadata, status ${result.committeeStatus}`)
376                    console.log(`found ${this.sponsors.length} members, status ${result.sponsorStatus}`)
377            }
378        }
379
380    }
381});

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.