Forms
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#1572726" 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#1572726" 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>