1import NiuBase from "niu-base"; 2 3class NiuLoadMore extends NiuBase{ 4 static Selectors = { 5 wrapper:()=>{return'[data-niu-load-more]'}, 6 maxPages:'[data-niu-load-more-max-pages]', 7 productsCount:'[data-niu-load-more-products-count]', 8 productsShown:'[data-niu-load-more-products-shown]', 9 productsOffset:'[data-niu-load-more-products-offset]', 10 currentPage:'[data-niu-load-more-current-page]', 11 currentProductsText:'[data-niu-load-more-current-products-text]', 12 maxProductsText:'[data-niu-load-more-max-products-text]', 13 progressBar:'[data-niu-load-more-progress-bar]', 14 btn:'[data-niu-load-more-btn]', 15 btnText:'[data-niu-load-more-btn-text]', 16 btnSpinner:'[data-niu-load-more-btn-spinner]', 17 prevBtn:'[data-niu-load-prev-btn]', 18 prevBtnText:'[data-niu-load-prev-btn-text]', 19 prevBtnSpinner:'[data-niu-load-prev-btn-spinner]', 20 next:'[data-niu-load-more-next]', 21 prev:'[data-niu-load-more-prev]', 22 current:'[data-niu-load-more-current]', 23 24 25 26 pages:'[data-niu-load-more-pages]', 27 28 elements:'.niu-box', 29 targetList:'product-list' 30 31 } 32 33 constructor(wrapper) { 34 super(wrapper); 35 this.currentPage; 36 this.maxPages; 37 this.productsCount; 38 this.productsShown; 39 this.percentageShown; 40 this.productsOffset; 41 42 this.productsPerPage; 43 44 this.maxProductsText; 45 this.currentProductsText; 46 this.progressBar; 47 this.btn; 48 this.btnText; 49 this.btnSpinner; 50 51 this.prevBtn; 52 this.prevBtnText; 53 this.prevBtnSpinner; 54 55 this.nextPageURL; 56 this.prevPageURL; 57 this.currentPageURL; 58 59 this.targetList; 60 61 this.pages=[]; 62 63 this.itemsStack = []; 64 65 this.loading = false; 66 this.loadingPrev = false; 67 } 68 setError(msg){ 69 this.wrapper.classList.add('d-none'); 70 console.error(msg) 71 72 } 73 init(){ 74 /*console.log('init load ore')*/ 75 if(!this.wrapper.querySelector(NiuLoadMore.Selectors.current)) 76 return; 77 this.currentPage = parseInt(this.wrapper.dataset[NiuBase.select(NiuLoadMore.Selectors.currentPage)]); 78 this.maxPages = parseInt(this.wrapper.dataset[NiuBase.select(NiuLoadMore.Selectors.maxPages)]); 79 80 if(this.currentPage<this.maxPages){ 81 this.wrapper.classList.remove('d-none') 82 } 83 84 this.targetList = document.getElementById(NiuLoadMore.Selectors.targetList); 85 if(!this.targetList){ 86 this.setError('no targetlist') 87 return; 88 } 89 this.nextPageURL = this.wrapper.querySelector(NiuLoadMore.Selectors.next).dataset.niuLoadMoreNext; 90 this.prevPageURL = this.wrapper.querySelector(NiuLoadMore.Selectors.prev).dataset.niuLoadMorePrev; 91 this.currentPageURL = this.wrapper.querySelector(NiuLoadMore.Selectors.current).dataset.niuLoadMoreCurrent; 92 93 this.productsCount = parseInt(this.wrapper.dataset[NiuBase.select(NiuLoadMore.Selectors.productsCount)]); 94 this.productsShown = parseInt(this.wrapper.dataset[NiuBase.select(NiuLoadMore.Selectors.productsShown)]); 95 this.productsOffset = parseInt(this.wrapper.dataset[NiuBase.select(NiuLoadMore.Selectors.productsOffset)]); 96 this.productsPerPage = this.productsShown - this.productsOffset + 1; 97 98 this.maxProductsText = this.wrapper.querySelector(NiuLoadMore.Selectors.maxProductsText) 99 this.currentProductsText = this.wrapper.querySelector(NiuLoadMore.Selectors.currentProductsText) 100 this.progressBar = this.wrapper.querySelector(NiuLoadMore.Selectors.progressBar) 101 102 103 104 this.btn = this.wrapper.querySelector(NiuLoadMore.Selectors.btn) 105 this.btnText = this.wrapper.querySelector(NiuLoadMore.Selectors.btnText) 106 this.btnSpinner = this.wrapper.querySelector(NiuLoadMore.Selectors.btnSpinner) 107 108 this.setStatus() 109 this.wrapper.querySelectorAll(NiuLoadMore.Selectors.pages).forEach(p=>{ 110 this.pages.push(p.dataset[NiuBase.select(NiuLoadMore.Selectors.pages)]) 111 }) 112 113 114 this.btn.addEventListener('click',()=>{ 115 this.loadMore() 116 }); 117 118 119 this.prevBtn = document.querySelector(NiuLoadMore.Selectors.prevBtn) 120 if(this.prevBtn){ 121 this.prevBtnText = this.prevBtn.querySelector(NiuLoadMore.Selectors.prevBtnText) 122 this.prevBtnSpinner = this.prevBtn.querySelector(NiuLoadMore.Selectors.prevBtnSpinner) 123 124 this.prevBtn.addEventListener('click',()=>{ 125 this.loadPrev(); 126 }) 127 } 128 129 this.loadMore(); 130 131 } 132 133 triggerLoading(){ 134 this.loading = !this.loading; 135 if(this.loading){ 136 this.btn.disabled = true; 137 this.btnText.classList.add('d-none') 138 this.btnSpinner.classList.remove('d-none') 139 }else{ 140 this.btnText.classList.remove('d-none') 141 this.btnSpinner.classList.add('d-none') 142 this.btn.disabled = false;
143 } 144 } 145 146 triggerPrevLoading(){ 147 this.loadingPrev = !this.loadingPrev; 148 if(this.loadingPrev){ 149 this.prevBtn.disabled = true; 150 this.prevBtnText.classList.add('d-none') 151 this.prevBtnSpinner.classList.remove('d-none') 152 }else{ 153 this.prevBtnText.classList.remove('d-none') 154 this.prevBtnSpinner.classList.add('d-none') 155 this.prevBtn.disabled = false; 156 } 157 } 158 159 getColumnsCount(){ 160 let cs = window.getComputedStyle(this.targetList); 161 return cs.getPropertyValue('grid-template-columns').split(" ").length; 162 } 163 getEmptySlots(){ 164 let columnCount = this.getColumnsCount() 165 166 let items = this.targetList.querySelectorAll(NiuLoadMore.Selectors.elements); 167 let itemsSpace = 0; 168 items.forEach(item=>{ 169 let cs = window.getComputedStyle(item); 170 let colSpan = cs.getPropertyValue('grid-column-start').split(" "); 171 if(colSpan.length > 1){ 172 itemsSpace += parseInt(colSpan[1]); 173 }else{ 174 itemsSpace++; 175 } 176 /*console.log(cs.getPropertyValue('grid-column-start'))*/ 177 }) 178 let itemsInLastRow = itemsSpace % columnCount; 179 180 return itemsInLastRow === 0 ? 0 : columnCount - itemsInLastRow; 181 } 182 183 fillUp(){ 184 let emptySlots = this.getEmptySlots(); 185 let i = 0; 186 while(emptySlots > 0 && this.itemsStack.length > 0){ 187 emptySlots--; 188 this.pushItem(this.itemsStack.shift(),i) 189 190 i++; 191 } 192 } 193 194 emptyStack(){ 195 let i = 0; 196 let columnsCount = this.getColumnsCount(); 197 let currentFilled = columnsCount; 198 while(this.itemsStack.length > 0){ 199 if(currentFilled === columnsCount && this.itemsStack.length < columnsCount && this.productsCount-this.productsShown > columnsCount){ 200 break; 201 } 202 203 this.pushItem(this.itemsStack.shift(),i) 204 205 206 if(currentFilled === columnsCount){ 207 currentFilled = 1; 208 }else{ 209 currentFilled++; 210 } 211 212 i++; 213 214 } 215 } 216 217 pushItem(element,i){ 218 element.classList.remove('niu-sub-ani','animate__faster') 219 element.classList.add('animate__animated', 'animate__fadeInUp'); 220 221 222 element.style.animationDelay = `${ i * 0.1}s`; 223 224 225 element.style.animationDuration = '0.3s'; 226 this.productsShown++; 227 this.setStatus() 228 this.targetList.appendChild(element); 229 } 230 231 setStatus(){ 232 this.percentageShown = this.productsShown * 100 / this.productsCount; 233 234 235 this.maxProductsText.innerText = this.productsCount; 236 this.currentProductsText.innerText = this.productsShown; 237 this.progressBar.style.width = (this.percentageShown)+'%'; 238 if(this.productsShown >= this.productsCount){ 239 this.btn.classList.add('d-none') 240 } 241 } 242 243 loadMore(){ 244 if(this.loading){ 245 /*console.log('LoadMoreBtn: Mach ma nich so schnell hier...')*/ 246 return; 247 } 248 /*console.log('loadMore')*/ 249 this.emptyStack() 250 /*if(this.currentPage+1 > this.pages.length){ 251 if(this.itemsStack.length > 0){ 252 this.fillUp() 253 } 254 this.dispatch(dzm.events.productList.updated) 255 return; 256 }*/ 257 this.triggerLoading() 258 259 const controller = new AbortController(); 260 261 const timeout = setTimeout(()=>{ 262 controller.abort(); 263 const link = document.createElement('a'); 264 link.href = this.pages[this.currentPage]; // Ziel-URL 265 link.innerText = this.btnText.innerText; 266 link.className = this.btn.className; // übernimmt CSS-Klassen vom Button 267 this.btn.replaceWith(link); 268 269 270 },10000); 271 if(this.currentPage > 1){ 272 history.pushState({}, "", this.currentPageURL); 273 } 274 if(this.nextPageURL === ""){ 275 this.triggerLoading() 276 return; 277 } 278 fetch(this.nextPageURL+'?isAjax',{signal:controller.signal}) 279 .then(res=> { 280 clearTimeout(timeout); 281 return res.text() 282 }) 283 .then(result=>{ 284 285 this.currentPage++; 286 287 288 const tempContainer = document.createElement('div'); 289 tempContainer.innerHTML = result; 290 const elements = tempContainer.querySelectorAll(NiuLoadMore.Selectors.elements); 291 const next = tempContainer.querySelector(NiuLoadMore.Selectors.next).dataset.niuLoadMoreNext; 292 293 /*console.log('next',next);*/ 294 this.currentPageURL = this.nextPageURL; 295 this.nextPageURL = next; 296 297 298
299 elements.forEach((element) => { 300 if(!element.classList.contains('niu-disturber')) 301 this.itemsStack.push(element) 302 303 }); 304 305 }).catch(err=>{ 306 console.error('loadMore error', err) 307 }).finally(()=>{ 308 this.triggerLoading() 309 this.fillUp() 310 this.dispatch(dzm.events.productList.updated) 311 }) 312 } 313 314 315 loadPrev(){ 316 /*console.log(this.prevPageURL)*/ 317 if(this.loadingPrev){ 318 /*console.log('LoadPrevBtn: Mach ma nich so schnell hier...')*/ 319 return; 320 } 321 /*console.log('loadPrev')*/ 322 /*if(this.currentPage+1 > this.pages.length){ 323 if(this.itemsStack.length > 0){ 324 this.fillUp() 325 } 326 this.dispatch(dzm.events.productList.updated) 327 return; 328 }*/ 329 this.triggerPrevLoading() 330 331 const controller = new AbortController(); 332 333 const timeout = setTimeout(()=>{ 334 controller.abort(); 335 const link = document.createElement('a'); 336 link.href = this.prevPageURL // Ziel-URL 337 link.innerText = this.prevBtnText.innerText; 338 link.className = this.prevBtn.className; // übernimmt CSS-Klassen vom Button 339 this.prevBtn.replaceWith(link); 340 341 342 },10000); 343 history.pushState({}, "", this.prevPageURL); 344 345 346 fetch(this.prevPageURL+'?isAjax',{signal:controller.signal}) 347 .then(res=> { 348 clearTimeout(timeout); 349 return res.text() 350 }) 351 .then(result=>{ 352 353 //this.currentPage++; 354 355 356 const tempContainer = document.createElement('div'); 357 tempContainer.innerHTML = result; 358 const elements = [...tempContainer.querySelectorAll(NiuLoadMore.Selectors.elements)].reverse(); 359 const prev = tempContainer.querySelector(NiuLoadMore.Selectors.prev).dataset.niuLoadMorePrev; 360 /*console.log('prev',prev);*/ 361 this.prevPageURL = prev; 362 if(this.prevPageURL === ""){ 363 this.prevBtn.classList.add('d-none') 364 } 365 366 elements.forEach((element) => { 367 element.classList.remove('niu-sub-ani','animate__faster','animate__animated', 'animate__fadeInUp') 368 369 this.targetList.prepend(element); 370 371 372 373 }); 374 375 }).catch(err=>{ 376 console.error('loadPrev error', err) 377 }).finally(()=>{ 378 this.triggerPrevLoading() 379 this.dispatch(dzm.events.productList.updated) 380 }) 381 } 382} 383 384 385NiuBase.listenTo([ 386 dzm.events.controller.initialized 387],()=>{ 388 NiuLoadMore.Scan(); 389})
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.