1(function ($) { 2 3 var TOURDATATIMETABLE = TOURDATATIMETABLE || {}, 4 5 $win = $(window), 6 $doc = $(document), 7 $body = $(document.body), 8 extentionClass = '.tx-tourdata-timetable'; 9 10 11 TOURDATATIMETABLE = { 12 13 _: function () { 14 15 this.query.select._(); 16 this.query.tripTypeToggler._(); 17 this.query.dropdown._(); 18 this.query.datepicker._(); 19 this.result.timeline._(); 20 this.result.accordion._(); 21 }, 22 23 query: { 24 25 select: { 26 27 _: function () { 28 29 var self = this; 30 31 // prevent submit form on enter 32 $(window).keydown(function (event) { 33 34 var key = event.key.toLowerCase(); 35 if (key == 'enter') { 36 37 event.preventDefault(); 38 return false; 39 } 40 }); 41 42 // observe form elements and start action on change 43 $('#trip-type [type="radio"], #departure-place select, #arrival-place input[type=hidden], #datepicker', extentionClass).on('change', function () { 44 45 self.submitQuery(); 46 47 return false; 48 }); 49 50 $('#rotate-places button, #datepicker-prev button, #datepicker-next button', extentionClass).on('click', function () { 51 52 var clickedItem = $(this); 53 54 self.submitQuery(clickedItem.attr('name') + '=' + clickedItem.val()); 55 56 return false; 57 }); 58 }, 59 60 submitQuery: function (param) { 61 62 var formData = $('form', extentionClass).serialize(); 63 64 if (param) { 65 formData = decodeURI(formData); 66 formData = formData.split('&'); 67 formData.push(param); 68 formData = formData.join('&'); 69 formData = encodeURI(formData); 70 } 71 72 $('.query', extentionClass).addClass('loading'); 73 74 $.ajax({ 75 type: 'POST', 76 url: $('form', extentionClass).data('ajax'), 77 data: formData, 78 success: function (data) { 79 $('.query', extentionClass).html($(data).find('form')); 80 $('.query', extentionClass).removeClass('loading'); 81 history.pushState({}, document.title, $('form', extentionClass).data('url')); 82 TOURDATATIMETABLE._(); 83 hideKeSearchForm(); 84 }, 85 error: function (data) { 86 $('.query', extentionClass).removeClass('loading'); 87 alert('Something went wrong'); 88 } 89 }); 90 } 91 }, 92 93 tripTypeToggler: { 94 95 _: function () { 96 97 var tripTypeToggler = $('.trip-type-toggler', extentionClass), 98 tripTypeTogglerItems = tripTypeToggler.children('.trip-type-toggler-item'); 99 100 tripTypeTogglerItems.on('click', function () { 101 102 var togglerItem = $(this), 103 onewayRadio = tripTypeToggler.find('input[id="oneway"]'), 104 roundtripRadio = tripTypeToggler.find('input[id="roundtrip"]'); 105 106 if (togglerItem.data('type') == 'oneway') { 107 onewayRadio.attr('checked', true); 108 roundtripRadio.attr('checked', false); 109 } else { 110 onewayRadio.attr('checked', false); 111 roundtripRadio.attr('checked', true); 112 } 113 114 TOURDATATIMETABLE.query.select.submitQuery(); 115 }); 116 } 117 }, 118 119 dropdown: { 120 121 dropdowns: [], 122 123 _: function () { 124 125 var dropdowns = $('.dropdown', extentionClass), 126 self = this; 127 128 dropdowns.each(function (index) { 129 130 self.dropdowns[index] = $(this); 131 132 var dropdown = self.dropdowns[index], 133 resultList = dropdown.find('.results ul'), 134 queryField = dropdown.find('input[type=text]'), 135 places = window['tx_tourdata_timetable_' + dropdown.parent().attr('id').replace('-', '')]; 136 137 // handle action on field focus in 138 queryField.on('focusin', function () { 139 140 var fieldValue = queryField.val().toLowerCase(); 141 142 self.updatePlaces(index); 143 dropdown.addClass('active'); 144 145 // close all other dropdowns 146 dropdowns.each(function (innerIndex) { 147 148 if (innerIndex != index) { 149 $(this).removeClass('active'); 150 } 151 }); 152 }); 153 154 // check each pushed key 155 queryField.on('keyup', function (event) { 156 157 var queryValue = $(this).val().toLowerCase(), 158 key = event.key.toLowerCase(), 159 foundStops = []; 160 161 if (key == 'backspace') { 162 queryField.val(''); 163 self.unselectListItem(index); 164 self.updatePlaces(index); 165 dropdown.addClass('active'); 166 queryValue = ''; 167 } 168 169 if (key == 'enter') { 170 if (resultList.find('li:not(.error):first').length > 0) { 171 self.selectListItem(index, resultList.find('li:not(.error):first')); 172 dropdown.removeClass('active'); 173 } 174 175 return false;
176 } 177 178 if (queryValue.length < 1) { 179 180 foundStops = self.showAllPlaces(places); 181 } else { 182 $.each(places, function (key, stop) { 183 184 var cleanQueryValue = queryValue.toLowerCase(), 185 cleanStop = stop.toLowerCase(); 186 187 cleanQueryValue = cleanQueryValue.replace(' ', ''); 188 cleanStop = cleanStop.replace(' ', ''); 189 190 var distance = self.getDistance(cleanQueryValue, cleanStop.substr(0, cleanQueryValue.length)); 191 192 if (distance / cleanQueryValue.length < 0.1) { 193 194 foundStops.push({id: key, name: stop, distance: distance}); 195 } 196 }); 197 198 foundStops.sort(function (a, b) { 199 200 return a.distance - b.distance; 201 }); 202 } 203 204 self.updatePlaces(index, foundStops); 205 dropdown.addClass('active'); 206 }) 207 208 // remove all-result class on scrolling 209 resultList.on('scroll', function () { 210 211 resultList.closest('.results').removeClass('all-results'); 212 }); 213 }); 214 }, 215 216 showAllPlaces: function (places) { 217 218 var foundStops = []; 219 220 $.each(places, function (key, stop) { 221 222 foundStops.push({id: key, name: stop, distance: 0}); 223 }); 224 225 foundStops.sort(function (a, b) { 226 227 var nameA = a.name.toLowerCase(); 228 var nameB = b.name.toLowerCase(); 229 if (nameA < nameB) { 230 231 return -1; 232 } 233 234 if (nameA > nameB) { 235 236 return 1; 237 } 238 239 return 0; 240 }); 241 242 return foundStops; 243 }, 244 245 updatePlaces: function (index, foundStops) { 246 247 var self = this, 248 dropdown = $(this.dropdowns[index]), 249 resultList = dropdown.find('.results ul'), 250 resultListItems = resultList.find('li:not(.error)'), 251 resultContainer = resultList.closest('.results'); 252 253 // update list if there are some found stops 254 if (foundStops) { 255 resultListItems.remove(); 256 257 $.each(foundStops, function () { 258 resultList.append('<li data-value="' + this.id + '">' + this.name + '</li>'); 259 }); 260 } 261 262 // initialize list variable after rebuild 263 resultListItems = resultList.find('li:not(.error)'); 264 265 // on click on result list item 266 resultListItems.on('click', function () { 267 268 self.selectListItem(index, $(this)); 269 dropdown.removeClass('active'); 270 }); 271 272 // handle message if no result is found 273 if (resultListItems.length == 0) { 274 resultContainer.addClass('no-results'); 275 } else { 276 resultContainer.removeClass('no-results'); 277 } 278 279 // handle all-result class to add some special styles in this case 280 if (!foundStops && resultList.height() >= 290) { 281 resultContainer.addClass('all-results'); 282 } else { 283 resultContainer.removeClass('all-results'); 284 } 285 }, 286 287 unselectListItem: function (index) { 288 289 this.dropdowns[index].find('input[type=hidden]').val(''); 290 }, 291 292 selectListItem: function (index, selectedItem) { 293 294 var dropdown = this.dropdowns[index]; 295 296 dropdown.find('input[type=text]').val(selectedItem.text()); 297 dropdown.find('input[type=hidden]').val(selectedItem.data('value')); 298 299 TOURDATATIMETABLE.query.select.submitQuery(); 300 }, 301 302 // found @ http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance 303 // Compute the edit distance between the two given strings 304 getDistance: function (a, b) { 305 306 if (a.length === 0) return b.length; 307 if (b.length === 0) return a.length; 308 309 var matrix = []; 310 311 // increment along the first column of each row 312 var i; 313 for (i = 0; i <= b.length; i++) { 314 matrix[i] = [i]; 315 } 316 317 // increment each column in the first row 318 var j; 319 for (j = 0; j <= a.length; j++) { 320 matrix[0][j] = j; 321 } 322 323 // Fill in the rest of the matrix 324 for (i = 1; i <= b.length; i++) { 325 for (j = 1; j <= a.length; j++) { 326 if (b.charAt(i - 1) == a.charAt(j - 1)) { 327 matrix[i][j] = matrix[i - 1][j - 1]; 328 } else { 329 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution 330 Math.min(matrix[i][j - 1] + 1, // insertion 331 matrix[i - 1][j] + 1)); // deletion 332 } 333 } 334 } 335 336 return matrix[b.length][a.length]; 337 } 338 }, 339 340 datepicker: { 341 342 _: function () { 343 344 $.datepicker.setDefaults({ 345 beforeShowDay: function (date) { 346 var dateString = $.datepicker.formatDate('dd.mm.yy', date); 347 return [dates.indexOf(dateString) != -1] 348 }, 349 inline: true, 350 altField: '#datepicker-input', 351 defaultDate: $('#datepicker-input').val() 352 }); 353 354 $('#datepicker-trigger').on('click', function () { 355 356 $('#datepicker').datepicker($.datepicker.regional[$('html').attr('lang').split('-')[0]]); 357 $(this).addClass('open'); 358 return false;
359 }); 360 } 361 } 362 }, 363 364 result: { 365 366 timeline: { 367 368 _: function () { 369 370 var timelines = $('.timeline', extentionClass), 371 self = this; 372 373 timelines.each(function (index) { 374 375 var timeline = $(this), 376 togglerWrapper = timeline.find('li.toggler'), 377 toggler = togglerWrapper.find('.line span, .place'), 378 stops = togglerWrapper.find('.stops'); 379 380 // init the accordion 381 timeline.closest('.accordion-collapse').on('shown.bs.collapse', function () { 382 383 if (!stops.hasClass('init-done')) { 384 var stopsHeight = stops.height(); 385 stops.css('height', 0); 386 stops.addClass('init-done', function () { 387 388 stops.css('height', stopsHeight); 389 }); 390 } 391 }); 392 393 toggler.on('click', function () { 394 395 // toggle of accordion 396 if (!togglerWrapper.hasClass('minimized')) { 397 togglerWrapper.addClass('minimized'); 398 } else if (stops.hasClass('init-done')) { 399 togglerWrapper.removeClass('minimized'); 400 } 401 }); 402 }); 403 } 404 }, 405 406 accordion: { 407 408 _: function () { 409 410 var self = this; 411 412 $('.main-accordion > div > div > .accordion-trigger', extentionClass).on('click', function () { 413 414 self.accordionToggler($(this)) 415 }); 416 417 $('.sub-accordion > div > div > .accordion-trigger', extentionClass).on('click', function () { 418 419 self.accordionToggler($(this)) 420 }); 421 }, 422 423 accordionToggler: function (clickedAccordion) { 424 425 var accordion = clickedAccordion.closest('.accordion-item'); 426 427 if (accordion.hasClass('show')) { 428 accordion.removeClass('show'); 429 } else { 430 clickedAccordion.closest('.accordion').children().each(function () { 431 432 var otherAccordion = $(this); 433 434 otherAccordion.removeClass('show'); 435 }); 436 437 accordion.addClass('show'); 438 } 439 } 440 } 441 } 442 } 443 444 // Formular mit id "form_kesearch_pi1" im div mit Klasse "query" ausblenden 445 function hideKeSearchForm() { 446 const queryDiv = document.querySelector('.query'); 447 if (queryDiv) { 448 const form = queryDiv.querySelector('#form_kesearch_pi1'); 449 if (form) { 450 form.style.display = 'none'; 451 } 452 } 453 } 454 455 TOURDATATIMETABLE._(); 456 hideKeSearchForm(); 457 458})(jQuery);
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.