Forms

Standard forms, exhibits and associated instructions to be used by contractors and consultants in preparing proposals and agreements with the Tollway can be found below.

An error occurred while processing the template.
The following has evaluated to null or missing:
==> types[0]  [in template "20157#20197#1588857" at line 70, column 40]

----
Tip: It's the final [] step that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
	- Failed at: #assign defaultType = types[0]  [in template "20157#20197#1588857" at line 70, column 17]
----
1<#assign dlFileEntryUtil=serviceLocator.findService("com.liferay.document.library.kernel.service.DLFileEntryLocalService")> 
2<#assign dlFileEntryClass="com.liferay.document.library.kernel.model.DLFileEntry"> 
3<#assign portletID="${themeDisplay.getPortletDisplay().getId()}"> 
4<#assign customTagID="ap_"> 
5<#assign currentUrl = themeDisplay.getURLCurrent()!""> 
6<#if currentUrl?matches(".*[?&]type=([^&]+).*")> 
7    <#assign urlType = "ap_" + currentUrl?matches(".*[?&]type=([^&]+).*")?groups[1]!""/> 
8<#else> 
9    <#assign urlType = "ap_"/> 
10</#if> 
11							 
12<!--Type of Document--> 
13<div class="container no-padding"> 
14    <div class="row"> 
15        <div class="col-md-12" style="padding: 0px;"> 
16            <#assign types=[]> 
17            <#assign categories={}> 
18            <#assign categoriesByType={}> 
19 
20            <#list entries as curEntry> 
21                <#if curEntry.getClassName()==dlFileEntryClass> 
22                    <#assign dlFileEntry=dlFileEntryUtil.getFileEntry(curEntry.getClassPK())> 
23                    <#assign entryTypes=curEntry.getTags()> 
24                    <#assign entryCategories=curEntry.getCategories()> 
25 
26                    <!-- Type --> 
27                    <#list entryTypes as type> 
28                        <#list type.name?split(",") as typeName> 
29                            <#if typeName?matches("^" + customTagID + ".*")> 
30                                <#assign typeTag=typeName?trim> 
31                                <#if !types?seq_contains(typeTag)> 
32                                    <#assign types=(types + [typeTag])> 
33                                </#if> 
34                            </#if> 
35                        </#list> 
36                    </#list> 
37 
38                    <!-- Category --> 
39                    <#list entryCategories as category> 
40                        <#list category.name?split(",") as categoryName> 
41							<#assign categoryTag=categoryName?trim>    
42							<#if !categoryTag?matches("^LH.*")> 
43                                <#if categoriesByType[typeTag]?? && !categoriesByType[typeTag]?seq_contains(categoryTag)> 
44                                    <#assign categoriesByType = categoriesByType + {typeTag: categoriesByType[typeTag] + [categoryTag]} /> 
45                                <#elseif !(categoriesByType[typeTag]??)> 
46                                    <#assign categoriesByType = categoriesByType + {typeTag: [categoryTag]} /> 
47                                </#if>  
48							    <#if (category.description?split(",")[0]?trim?length gt 0)> 
49                                    <#assign categoryDescription=category.description?split(",")[0]?trim?substring(132, category.description?length - 28)?trim> 
50                                <#else> 
51								    <#assign categoryDescription=categoryTag> 
52								</#if> 
53                                <#assign categories = categories + {categoryTag: categoryDescription} /> 
54                            </#if> 
55                        </#list> 
56                    </#list>                       
57                </#if> 
58            </#list> 
59 
60            <!-- Sort Categories --> 
61            <#assign sortedCategoriesByType = {}> 
62            <#list categoriesByType?keys?sort as typeTag> 
63                <#assign sortedCategories = categoriesByType[typeTag]?sort> 
64                <#assign sortedCategoriesByType = sortedCategoriesByType + {typeTag: sortedCategories} /> 
65            </#list> 
66			<#assign categoriesByType = sortedCategoriesByType> 
67 
68            <!-- Default Type and Category --> 
69            <#if urlType == 'ap_' || !types?seq_contains(urlType)> 
70                <#assign defaultType = types[0]> 
71            <#else> 
72                <#assign defaultType = urlType> 
73			</#if> 
74            <#assign defaultCategory=categoriesByType[defaultType]?first> 
75 
76            <!-- Type of Documents Filters --> 
77            <div class="type-buttons"> 
78                <#list types as type> 
79                    <#assign typeTitle = type?replace(customTagID, "")?replace("_", " ")?split(" ")?map(x -> (x == 'and')?string("and", x?cap_first))?join(" ")> 
80                    <#if type == defaultType> 
81                        <button class="type-btn active" id="typeBtn_${type}" data-type="${type}"> 
82                            ${typeTitle} 
83                        </button> 
84                    <#else> 
85                        <button class="type-btn" id="typeBtn_${type}" data-type="${type}"> 
86                            ${typeTitle} 
87                        </button> 
88                    </#if> 
89                </#list> 
90            </div>             
91        </div> 
92    </div> 
93</div> 
94 
95<!-- Type of Document Title --> 
96<div class="container"> 
97    <div class="row"> 
98        <a class="col-md-12 main-title type-title" id="type-title">${defaultType?replace(customTagID, "")?replace("_", " ")?split(" ")?map(x -> (x == 'and')?string("and", x?cap_first))?join(" ")} 
99        </a> 
100    </div> 
101</div> 
102 
103<!-- Category of Document || Documents --> 
104<div class="container"> 
105    <div class="row"> 
106        <div class="filters-title"> 
107            Filters 
108        </div> 
109        <!-- Category of Documents Filters --> 
110        <div class="col-md-3 category-filters" id="categories_${portletID}">             
111        <#list categoriesByType[defaultType] as category> 
112            <#assign categoryTitle = categories[category]> 
113            <label class="category-label custom-checkbox"> 
114                <#if category == defaultCategory>  
115                    <input class="category-btn" type="checkbox" value="${category}" checked> 
116                <#else> 
117                    <input class="category-btn" type="checkbox" value="${category}"> 
118                </#if> 
119                <span class="custom-checkmark"></span> 
120                <a class="category-text">${categoryTitle}</a> 
121            </label> 
122        </#list> 
123    </div> 
124        <!-- Documents --> 
125        <div class="col-md-7 multiple-documents-files" style="padding-bottom: 30px">   
126            <div class="tiles-container"> 
127                <#if entries?has_content> 
128                    <#list entries as curEntry> 
129                        <#if curEntry.getClassName()==dlFileEntryClass> 
130                            <#assign dlFileEntry=dlFileEntryUtil.getFileEntry(curEntry.getClassPK())> 
131                            <#assign entryTypes=curEntry.getTags()> 
132                            <#assign entryCategories=curEntry.getCategories()> 
133                            <#assign classTypes=""> 
134                            <#assign classCategories=""> 
135                            <#list entryTypes as type> 
136                                <#list type.name?split(",") as typeName> 
137                                    <#assign classTypes=classTypes + " " + typeName?trim> 
138                                </#list> 
139                            </#list> 
140                                <#list entryCategories as category> 
141                                <#list category.name?split(",") as categoryName> 
142                                    <#assign classCategories=classCategories + " " + categoryName?trim> 
143                                </#list> 
144                            </#list> 
145                            <div class="multiple-documents-tile small-box-shadow-containers ${portletID} ${classTypes} ${classCategories}"> 
146                                <div class="multiple-documents-tile-text">                                  
147                                    <#if (curEntry.getDescription(locale)?split(",")[0]?trim?length gt 0)> 
148                                        ${curEntry.getDescription(locale)} 
149                                    <#else> 
150								        ${curEntry.getTitle(locale)} 
151								    </#if> 
152                                </div> 
153                                <div class="multiple-documents-tile-download"> 
154                                    <a href="${curEntry.getAssetRenderer().getURLDownload(themeDisplay)}" target="_blank"> 
155                                        Download 
156                                    </a> 
157                                </div> 
158                            </div> 
159                        </#if> 
160                    </#list> 
161                </#if> 
162                <a class="anchor" id="${themeDisplay.getPortletDisplay().title}" name="${themeDisplay.getPortletDisplay().title}"></a> 
163            </div> 
164            <!--PAGINATION--> 
165            <div class="asset-pagination"> 
166                <div class="asset-pagination-showing"> 
167                    Showing <span id="startResults_${portletID}">1</span> to <span id="endResults_${portletID}">10</span> of <span id="totalResults_${portletID}">25</span> results 
168                </div> 
169                <div class="asset-pagination-btns"> 
170                    <button class="asset-pagination-btn" id="prevBtn_${portletID}"> <i class="fa-solid fa-arrow-left"></i> </button> 
171                    <button class="asset-pagination-btn active" id="firstBtn_${portletID}">1</button> 
172                    <button class="asset-pagination-btn" id="secondBtn_${portletID}">2</button> 
173                    <button class="asset-pagination-btn" id="thirdBtn_${portletID}">3</button> 
174                    <button class="asset-pagination-btn" id="fourthBtn_${portletID}">4</button> 
175                    <button class="asset-pagination-btn" id="nextBtn_${portletID}"><i class="fa-solid fa-arrow-right"></i></button> 
176                </div> 
177            </div> 
178        </div> 
179    </div> 
180</div> 
181 
182 
183<script> 
184(function() { 
185    var titleID = "${portletID}"; 
186    var currentType = "${defaultType}"; 
187    var currentCategory = "${defaultCategory}"; 
188    var itemsPerPage = isMobileDevice() ? 3 : 10; 
189    var currentPage = 0; 
190    var nextPage = 0; 
191    var taggedRows = []; 
192    var startPagination = 1; 
193    var rows = []; 
194    var totalTaggedRows = 0; 
195    var totalPages = 0; 
196    var nextStart = 0; 
197    var nextEnd = 0; 
198    var allCategoriesByType = { 
199        <#list categoriesByType as type, typeCategories> 
200            "${type}": [<#list typeCategories as category>"${category}"<#if category_has_next>,</#if></#list>] 
201            <#if type_has_next>,</#if> 
202        </#list> 
203    }; 
204    var allCategories = { 
205        <#list categories as categoryName, categoryDescription> 
206            "${categoryName}": "${categoryDescription}" 
207            <#if categoryName_has_next>,</#if> 
208        </#list> 
209    }; 
210 
211    function isMobileDevice() { 
212        const userAgent = navigator.userAgent || navigator.vendor || window.opera; 
213 
214        if (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||  
215        ('ontouchstart' in window && (window.innerWidth <= 800 || window.innerHeight <= 600))) { 
216            return true; 
217
218 
219        return false; 
220    }    
221 
222    // Update Type 
223    function updateType(event) { 
224        var selectedType = event.target.getAttribute('data-type'); 
225        currentType = selectedType; 
226         
227        document.querySelectorAll('.type-btn').forEach(function(button) { 
228            button.classList.remove('active'); 
229        }); 
230        event.target.classList.add('active'); 
231 
232        var currentTypeTitle = currentType.replace("ap_", "").replace(/_/g, " ").split(" ")    .map(x => x.toLowerCase() === "and" ? "and" : x.charAt(0).toUpperCase() + x.slice(1)).join(" "); 
233        document.getElementById("type-title").text = currentTypeTitle; 
234 
235        updateCategories(currentType); 
236
237    
238    // Update Category based on type 
239    function updateCategories(type) { 
240        var categoryContainer = document.getElementById(`categories_${portletID}`); 
241        categoryContainer.innerHTML = ''; 
242 
243        var categories = allCategoriesByType[type] || []; 
244        categories.forEach(function(category) { 
245            var label = document.createElement("label"); 
246            label.classList.add("category-label", "custom-checkbox"); 
247 
248            var checkbox = document.createElement("input"); 
249            checkbox.type = 'checkbox'; 
250            checkbox.name = 'category'; 
251            checkbox.value = category; 
252            checkbox.classList.add("category-btn"); 
253 
254            if (category === categories[0]) { 
255                checkbox.checked = true; 
256                currentCategory = category; 
257
258 
259            var customCheckmark = document.createElement("span"); 
260            customCheckmark.classList.add("custom-checkmark"); 
261 
262            var categoryTitle = allCategories[category]; 
263            var categoryText = document.createElement("a"); 
264            categoryText.classList.add("category-text"); 
265            categoryText.textContent = categoryTitle; 
266 
267            label.appendChild(checkbox); 
268            label.appendChild(customCheckmark); 
269            label.appendChild(categoryText); 
270 
271            categoryContainer.appendChild(label); 
272        }); 
273 
274        document.querySelectorAll('.category-btn').forEach(function(checkbox) { 
275            checkbox.addEventListener('click', updateCategory); 
276        }); 
277 
278        updateRows(currentCategory); 
279
280 
281    function updateCategory(event) { 
282        var selectedCategory = event.target.getAttribute('value'); 
283        currentCategory = selectedCategory; 
284         
285        document.querySelectorAll('.category-btn').forEach(function(button) { 
286            button.checked = false; 
287        }); 
288        event.target.checked = true; 
289        updateRows(currentCategory); 
290
291    
292 
293     // Update Rows based on Type and Category 
294    function updateRows(category) { 
295        currentCategory = category; 
296         
297        rows = document.querySelectorAll(".multiple-documents-tile." + titleID); 
298        taggedRows = []; 
299        rows.forEach(function(row) { 
300            var tags = Array.from(row.classList); 
301            var categories = Array.from(row.classList); 
302 
303            // Check if row matches selected type and category 
304            if (tags.includes(currentType) && categories.includes(currentCategory)) { 
305                taggedRows.push(row); 
306
307 
308            // Hide all -> pagination will show 
309            if (!row.classList.contains("assetHidden")) { 
310                row.classList.add("assetHidden"); 
311
312        }); 
313        handlePaginationChange("0"); 
314
315 
316    //EVENT LISTENERS // 
317    // On load 
318    function initialHandle() { 
319        updateCategories("${defaultType}"); 
320
321    window.onload = initialHandle(); 
322    // Type 
323    document.querySelectorAll('.type-btn').forEach(function(button) { 
324        button.addEventListener('click', updateType); 
325    }); 
326    // Category 
327     document.addEventListener('DOMContentLoaded', function() { 
328        document.querySelectorAll('.category-btn').forEach(function(checkbox) { 
329            checkbox.addEventListener('click', updateCategory); 
330        }); 
331    }); 
332    // 
333 
334    //Pagination 
335    function handlePaginationChange(page) { 
336        totalTaggedRows = taggedRows.length; 
337        totalPages = Math.ceil((totalTaggedRows / itemsPerPage)); 
338        nextPage = 0; 
339        // defines what is the next page 
340        if (page === 'prev') { 
341            nextPage = currentPage - 1 < 0 ? currentPage : currentPage - 1; 
342            if (currentPage == nextPage) return; 
343        } else if (page === 'next') { 
344            nextPage = currentPage + 1 < totalPages ? currentPage + 1 : currentPage; 
345            if (currentPage == nextPage) return; 
346        } else { 
347            nextPage = parseInt(page, 10) + (startPagination - 1); 
348
349        nextStart = (nextPage) * itemsPerPage; 
350        nextEnd = nextStart + itemsPerPage; 
351        //hides current reports and shows reports for the next page 
352        handlePaginationReports(); 
353        //changes numbers in Showing x to y of z results 
354        handleShowingResults(); 
355        //updates the pagination labels 
356        handlePaginationButtons(); 
357        //update current page 
358        currentPage = nextPage; 
359
360 
361    function handlePaginationReports() { 
362        //hides current page reports 
363        var curStart = (currentPage) * itemsPerPage; 
364        var curEnd = curStart + itemsPerPage; 
365        for (let r = curStart; r < curEnd && r < totalTaggedRows; r++) { 
366            if (!taggedRows[r].classList.contains("assetHidden")) { 
367                taggedRows[r].classList.add("assetHidden"); 
368
369
370        //shows next page reports 
371        var nextStart = (nextPage) * itemsPerPage; 
372        var nextEnd = nextStart + itemsPerPage; 
373        for (let r = nextStart; r < nextEnd && r < totalTaggedRows; r++) { 
374            if (taggedRows[r].classList.contains("assetHidden")) { 
375                taggedRows[r].classList.remove("assetHidden"); 
376
377
378
379 
380    function handleShowingResults() { 
381        const startResultsLabel = document.getElementById('startResults_${portletID}'); 
382        startResultsLabel.innerText = nextStart + 1; 
383        const endResultsLabel = document.getElementById('endResults_${portletID}'); 
384        endResultsLabel.innerText = nextEnd < totalTaggedRows ? nextEnd : totalTaggedRows; 
385        const totalResultsLabel = document.getElementById('totalResults_${portletID}'); 
386        totalResultsLabel.innerText = totalTaggedRows; 
387
388 
389    function handlePaginationButtons() { 
390        var currentPageNumber = nextPage + 1; 
391        // numbered buttons 
392        const firstBtn = document.getElementById('firstBtn_${portletID}'); 
393        const secondBtn = document.getElementById('secondBtn_${portletID}'); 
394        const thirdBtn = document.getElementById('thirdBtn_${portletID}'); 
395        const fourthBtn = document.getElementById('fourthBtn_${portletID}'); 
396        if (totalPages <= 4 || currentPageNumber == 1 || currentPageNumber == 2 || currentPageNumber == 3) { 
397            updateButton(firstBtn, currentPageNumber, 1); 
398            updateButton(secondBtn, currentPageNumber, 2); 
399            updateButton(thirdBtn, currentPageNumber, 3); 
400            updateButton(fourthBtn, currentPageNumber, 4); 
401            startPagination = 1; 
402        } else if (currentPageNumber == totalPages) { 
403            updateButton(firstBtn, currentPageNumber, currentPageNumber - 3); 
404            updateButton(secondBtn, currentPageNumber, currentPageNumber - 2); 
405            updateButton(thirdBtn, currentPageNumber, currentPageNumber - 1); 
406            updateButton(fourthBtn, currentPageNumber, currentPageNumber); 
407            startPagination = currentPageNumber - 3; 
408        } else if (currentPageNumber == totalPages - 1) { 
409            updateButton(firstBtn, currentPageNumber, currentPageNumber - 2); 
410            updateButton(secondBtn, currentPageNumber, currentPageNumber - 1); 
411            updateButton(thirdBtn, currentPageNumber, currentPageNumber); 
412            updateButton(fourthBtn, currentPageNumber, currentPageNumber + 1); 
413            startPagination = currentPageNumber - 2; 
414        } else if (currentPageNumber > 3) { 
415            updateButton(firstBtn, currentPageNumber, currentPageNumber - 2); 
416            updateButton(secondBtn, currentPageNumber, currentPageNumber - 1); 
417            updateButton(thirdBtn, currentPageNumber, currentPageNumber); 
418            updateButton(fourthBtn, currentPageNumber, currentPageNumber + 1); 
419            startPagination = currentPageNumber - 2; 
420
421        // prev and next buttons 
422        const prevBtn = document.getElementById('prevBtn_${portletID}'); 
423        const nextBtn = document.getElementById('nextBtn_${portletID}'); 
424        if (currentPageNumber == 1) { 
425            prevBtn.classList.add('disabled'); 
426        } else { 
427            prevBtn.classList.remove('disabled'); 
428
429        if (currentPageNumber == totalPages) { 
430            nextBtn.classList.add('disabled'); 
431        } else { 
432            nextBtn.classList.remove('disabled'); 
433
434
435 
436    function updateButton(button, currentPageNumber, label) { 
437        // active 
438        if (currentPageNumber == label) { 
439            button.classList.add('active'); 
440        } else { 
441            button.classList.remove('active'); 
442
443        // label + visibility 
444        if (totalPages >= label && currentPageNumber <= totalPages) { 
445            button.classList.remove('assetHidden'); 
446            button.innerText = label; 
447        } else { 
448            button.classList.add('assetHidden'); 
449
450
451    // 
452    //EVENT LISTENERS // 
453    // Pagination event listener 
454    var prevBtn = document.getElementById(`prevBtn_${portletID}`); 
455    prevBtn.addEventListener('click', function(event) { 
456        handlePaginationChange("prev"); 
457    }); 
458    var firstBtn = document.getElementById(`firstBtn_${portletID}`); 
459    firstBtn.addEventListener('click', function(event) { 
460        handlePaginationChange("0"); 
461    }); 
462    var secondBtn = document.getElementById(`secondBtn_${portletID}`); 
463    secondBtn.addEventListener('click', function(event) { 
464        handlePaginationChange("1"); 
465    }); 
466    var thirdBtn = document.getElementById(`thirdBtn_${portletID}`); 
467    thirdBtn.addEventListener('click', function(event) { 
468        handlePaginationChange("2"); 
469    }); 
470    var fourthBtn = document.getElementById(`fourthBtn_${portletID}`); 
471    fourthBtn.addEventListener('click', function(event) { 
472        handlePaginationChange("3"); 
473    }); 
474    var nextBtn = document.getElementById(`nextBtn_${portletID}`); 
475    nextBtn.addEventListener('click', function(event) { 
476        handlePaginationChange("next"); 
477    }); 
478    /////////////////////// 
479})(); 
480</script>