1/** 2 * ФилÑÑÑ Ð²Ð°ÐºÐ°Ð½Ñий 3 * @param {{counterSelector: string = '.i-vacancy-page-items', loadUri: string = './', timeoutInMs: number = 60, itemsContainerSelector: string = '', form: Object, preloaderSelector: string = ''}} options 4 * @constructor 5 */ 6function VacancyFilter(options) { 7 // ALLOWED OPTIONS 8 9 /** 10 * CелекÑÐ¾Ñ ÑпиÑка ÑлеменÑов 11 * @type {string} 12 */ 13 this.itemsContainerSelector = options.itemsContainerSelector || '.i-vacancy-page-items'; 14 /** 15 * CелекÑÐ¾Ñ ÐºÐ¾Ð»Ð¸ÑеÑÑва запиÑей 16 * @type {string} 17 */ 18 this.counterSelector = options.counterSelector || ''; 19 /** 20 * URI Ð´Ð»Ñ ajax запÑоÑа 21 * @type {string} 22 */ 23 this.loadUri = options.loadUri || './'; 24 /** 25 * ÐинималÑное вÑÐµÐ¼Ñ Ð¿ÐµÑеÑÑва Ð¼ÐµÐ¶Ð´Ñ Ð·Ð°Ð¿ÑоÑами 26 * @type {number} 27 */ 28 this.timeoutInMs = options.timeoutInMs || 60; 29 /** 30 * ÐбÑекÑÑ ÑоÑÐ¼Ñ 31 * @type {Object} 32 */ 33 this.form = options.form || {}; 34 /** 35 * СелекÑÐ¾Ñ Ð¿ÑелоадеÑа 36 * @type {string} 37 */ 38 this.preloaderSelector = options.preloaderSelector || ''; 39 40 // <-- ALLOWED OPTIONS 41 42 /** 43 * СпиÑок ÑлеменÑов 44 * @type {jQuery} 45 */ 46 this.$itemsListObject = $(this.itemsContainerSelector); 47 /** 48 * СÑÑÑÑик ÑлеменÑов 49 * @type {jQuery|undefined} 50 */ 51 this.$counterObject = this.counterSelector.length > 0 ? $(this.counterSelector) : undefined; 52 /** 53 * ТÑебÑеÑÑÑ Ð»Ð¸ инÑегÑаÑÐ¸Ñ Ñ ShowMoreComponent 54 * @type {boolean} 55 */ 56 this.isWithShowMoreComponent = false; 57 58 this.Preloader = this.createPreloaderInstance(this.preloaderSelector); 59 this.Filter = this.createFilterInstance(); 60 this.FilterRequest = this.createFilterRequestInstance(this.timeoutInMs); 61} 62 63VacancyFilter.prototype.removeParamsFromUrl = function() { 64 if (typeof window.history.replaceState === 'function') { 65 // window.history.replaceState('', document.title, './'); 66 } 67}; 68 69/** @param {*} filter */ 70VacancyFilter.prototype.setFilter = function(filter) { 71 this.Filter.set(filter); 72}; 73 74VacancyFilter.prototype.saveToBrowserHistory = function(url) { 75 function queryStringToObject (queryString) { 76 let a = queryString.split('&') 77 let so = {} 78 a.forEach(item => { 79 let p = item.split('=') 80 if (p.length === 2) { 81 so[p[0]] = so[p[0]] === undefined ? [] : so[p[0]] 82 so[p[0]].push(p[1]) 83 } 84 }) 85 86 return so 87 } 88 function objectToQueryString (p) { 89 const d = [] 90 for (const k in p) { 91 if (Array.isArray(p[k])) { 92 p[k].forEach(v => { 93 d.push(k + '=' + v) 94 }) 95 } else { 96 d.push(k + '=' + p[k]) 97 } 98 } 99 100 return d.join('&') 101 } 102 103 const $form = $('<form></form>') 104 $('.i-filterSearch input, .i-filterSearch select').each(function() { 105 const name = $(this).attr('name') 106 const value = $(this).val() 107 if (name) { 108 if (typeof value === 'object' && value.length) { 109 const c = $(this).clone() 110 c.appendTo($form) 111 c.val($(this).val()) 112 } else if (value) { 113 const c = $(this).clone() 114 c.appendTo($form) 115 c.val($(this).val()) 116 } 117 } 118 }) 119 const up = url.split('?') 120 const n = queryStringToObject($form.serialize()) 121 122 if (typeof n[encodeURIComponent('cityId[]')] === 'undefined') { 123 // n['nocity'] = '1' 124 } else if (typeof n['nocity'] !== 'undefined') {
125 // delete n['nocity'] 126 } 127 if (up.length === 2) { 128 const a = queryStringToObject(up[1]) 129 for (var l in a) { 130 if (typeof n[l] === 'undefined' && l) 131 n[l] = a[l] 132 } 133 } 134 135 window.history.pushState(null, null, up[0] + '?' + objectToQueryString(n)); 136} 137 138/** 139 * @param {{customLoadUri: string = '', saveItemsList: boolean = false, ignoreEmptyFilter: boolean = false}} options 140 * @returns {boolean} 141 */ 142VacancyFilter.prototype.loadByFilter = function(options = {}) { 143 var customLoadUri = options.customLoadUri || undefined; 144 var ignoreEmptyFilter = options.ignoreEmptyFilter || false; 145 var saveItemsList = options.saveItemsList || false; 146 147 if (!this.FilterRequest.canSend()) { 148 return false; 149 } 150 151 if (!ignoreEmptyFilter && this.Filter.isEmpty()) { 152 return true; 153 } 154 155 this.Preloader.show(); 156 this.FilterRequest.start(); 157 this.removeParamsFromUrl(); 158 159 var currentPostData = this.Filter.getHttpRequestData(); 160 this.Filter.clear(); 161 162 var globalThis = this; 163 var intervalId; 164 165 let url = customLoadUri || this.loadUri; 166 167 168 $.ajax({ 169 url: url, 170 data: currentPostData, 171 type: 'POST', 172 success: function(data) { 173 if (typeof window.history.pushState === 'function') { 174 globalThis.saveToBrowserHistory(url) 175 } 176 177 var $data = $(data); 178 var $counter = $data.find(globalThis.counterSelector); 179 var $itemsList = $data.find(globalThis.itemsContainerSelector); 180 181 if (!saveItemsList) { 182 globalThis.$itemsListObject.empty(); 183 } 184 if ($counter.length > 0) { 185 globalThis.setCounterValue($counter.html()); 186 } 187 globalThis.$itemsListObject.append($itemsList.execInit().children()); 188 189 if (globalThis.isWithShowMoreComponent) { 190 globalThis.withShowMoreComponent({ 191 $loadedData: $data, 192 }); 193 } 194 195 globalThis.FilterRequest.cancel(); 196 globalThis.Preloader.hide(); 197 198 intervalId = setInterval(function() { 199 if (globalThis.loadByFilter({ customLoadUri: customLoadUri })) { 200 clearInterval(intervalId); 201 } 202 }, globalThis.timeoutInMs); 203 }, 204 }); 205 206 return true; 207}; 208 209/** @param {*} counterValue */ 210VacancyFilter.prototype.setCounterValue = function(counterValue) { 211 if (typeof this.$counterObject !== undefined) { 212 this.$counterObject.val(counterValue) 213 } 214}; 215 216/** 217 * ÐеÑеназнаÑÐ°ÐµÑ ÑобÑÑÐ¸Ñ ÑлеменÑов пагинаÑии компоненÑа ShowMoreComponent. 218 * ÐÐµÐ¾Ð±Ñ Ð¾Ð´Ð¸Ð¼Ð¾ пÑи Ñоздании обÑекÑа VacancyFilter вÑзваÑÑ ÑÑÐ¾Ñ Ð¼ÐµÑод, 219 * благодаÑÑ ÑÑÐ¾Ð¼Ñ Ð¿Ð¾Ñле каждого ajax запÑоÑа ShowMoreComponent 220 * бÑÐ´ÐµÑ Ð¿ÐµÑеиниÑиализиÑован ÑÑим меÑодом. 221 * 222 * @returns {VacancyFilter} 223 */ 224VacancyFilter.prototype.withShowMoreComponent = function(options = {}) { 225 // user options 226 this.paginationContainer = options.paginationContainer || this.paginationContainer || ''; 227 if (this.paginationContainer.length === 0) { 228 return this; 229 } 230 231 // component options (inside VacancyFilter) 232 var $loadedData = options.$loadedData || undefined; 233 this.isWithShowMoreComponent = true; 234 235 var $paginationContainer = $(this.paginationContainer); 236 var $loadedPaginationContainer = undefined; 237 if ($loadedData !== undefined) { 238 $loadedPaginationContainer = $loadedData.find(this.paginationContainer); 239 } 240 241 if ($loadedPaginationContainer !== undefined) { 242 var html = $loadedPaginationContainer.length > 0 ? $loadedPaginationContainer.html() : ''; 243 $paginationContainer.html(html); 244 $paginationContainer.execInit(); 245 } 246 247 var $showMoreBtn = $paginationContainer.find('.i-showMore'); 248 var $pageNavItems = $paginationContainer.find('.i-pageNavigation-item'); 249 var globalThis = this; 250 251 $showMoreBtn.off().on('click', function(e) { 252 e.preventDefault(); 253 globalThis.setCurrentFilter(); 254 globalThis.loadByFilter({ 255 customLoadUri: $showMoreBtn.data('href') || '', 256 ignoreEmptyFilter: true, 257 saveItemsList: true, 258 }); 259 }); 260 261 $pageNavItems.each(function() { 262 var $currentItem = $(this); 263 $currentItem.on('click', function(e) { 264 e.preventDefault(); 265 globalThis.setCurrentFilter(); 266 globalThis.loadByFilter({ 267 customLoadUri: $currentItem.attr('href'), 268 ignoreEmptyFilter: true, 269 }); 270 }); 271 }); 272 273 return this; 274}; 275 276/** 277 * УÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ ÐºÐ¾ÑÑекÑнÑй ÑилÑÑÑ Ñо ÑÑÑаниÑÑ 278 */ 279VacancyFilter.prototype.setCurrentFilter = function () { 280 this.setFilter(this.form.serializeArray()); 281}; 282 283/** 284 * СоздаÑÑ Ð½Ð¾Ð²Ñй обÑÐµÐºÑ ÑилÑÑÑа 285 * @returns {{getHttpRequestData: (function(): {}), set: set, get: (function(): []), currentFilter: [], clear: clear, isEmpty: (function(): boolean)}} 286 */ 287VacancyFilter.prototype.createFilterInstance = function() { 288 return { 289 currentFilter: [], 290 291 set: function(filter) {
292 this.currentFilter = filter; 293 }, 294 get: function() { 295 return this.currentFilter; 296 }, 297 clear: function() { 298 this.currentFilter = []; 299 }, 300 isEmpty: function() { 301 return this.currentFilter.length === 0; 302 }, 303 getHttpRequestData: function() { 304 var data = {}; 305 this.get().forEach(function(filterItem) { 306 if (filterItem.name.endsWith('[]')) { 307 if (data[filterItem.name] === undefined) { 308 data[filterItem.name] = []; 309 } 310 data[filterItem.name].push(filterItem.value); 311 } else { 312 data[filterItem.name] = filterItem.value; 313 } 314 }); 315 316 return data; 317 }, 318 } 319}; 320 321/** 322 * СоздаÑÑ Ð½Ð¾Ð²Ñй обÑÐµÐºÑ Ð·Ð°Ð¿ÑоÑа ÑилÑÑÑа 323 * @param timeoutInMs - минималÑное вÑÐµÐ¼Ñ Ð¿ÐµÑеÑÑва Ð¼ÐµÐ¶Ð´Ñ Ð·Ð°Ð¿ÑоÑами в миллиÑекÑÐ½Ð´Ð°Ñ 324 * @returns {{cancel: cancel, blocked: boolean, start: start, lastRequestWas: number, canSend: (function(): boolean)}} 325 */ 326VacancyFilter.prototype.createFilterRequestInstance = function(timeoutInMs = 0) { 327 return { 328 blocked: false, 329 lastRequestWas: Date.now(), 330 331 start: function() { 332 this.blocked = true; 333 }, 334 cancel: function() { 335 this.lastRequestWas = Date.now(); 336 this.blocked = false; 337 }, 338 canSend: function() { 339 if (timeoutInMs > 0 && Date.now() - this.lastRequestWas < timeoutInMs) { 340 return false; 341 } 342 return this.blocked === false; 343 }, 344 } 345}; 346 347/** 348 * СоздаÑÑ Ð½Ð¾Ð²Ñй обÑÐµÐºÑ Ð¿ÑелоадеÑа 349 * @param {string} preloaderSelector 350 * @returns {{hide: (function(): undefined), show: (function(): undefined), $instance: (*|jQuery|HTMLElement|undefined)}} 351 */ 352VacancyFilter.prototype.createPreloaderInstance = function(preloaderSelector) { 353 return { 354 $instance: preloaderSelector.length > 0 ? $(preloaderSelector) : undefined, 355 356 show: function() { 357 if (this.$instance === undefined) { 358 return; 359 } 360 this.$instance.css('display', 'block'); 361 }, 362 hide: function() { 363 if (this.$instance === undefined) { 364 return; 365 } 366 this.$instance.css('display', 'none'); 367 }, 368 } 369}
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.