1var SWU__currentScript = document.currentScript || {}; 2var SWU__DOMReady = function(callback) { 3 document.readyState === 'interactive' || document.readyState === 'complete' ? callback() : document.addEventListener('DOMContentLoaded', callback); 4}; 5SWU__DOMReady(function() { 6 const el = document.createElement('div'); 7 8 // Options 9 const url = 'https://www.fork-cms.com/blog/detail/fork-stands-with-ukraine'; 10 const title = SWU__currentScript.getAttribute('title'); 11 const position = SWU__currentScript.getAttribute('position'); 12 13 // Set custom title 14 el.title = title || 'We stand with Ukraine'; 15 16 // Set custom URL 17 if (url) { 18 el.style.cursor = 'pointer' 19 el.target = '_blank' 20 el.addEventListener('click', function() { 21 window.open(url, '_blank') 22 }) 23 } 24 25 // Set custom position (left, right) 26 if (position === 'right') { 27 el.style.right = '-80px' 28 el.style.bottom = '20px' 29 el.style.transform = 'rotate(135deg)' 30 el.style.background = 'linear-gradient(-360deg, #005BBB 50%, #FFD500 50%)' 31 } else { 32 el.style.left = '-80px' 33 el.style.bottom = '20px' 34 el.style.transform = 'rotate(45deg)' 35 el.style.background = 'linear-gradient(-180deg, #005BBB 50%, #FFD500 50%)' 36 } 37 38 el.style.width = '300px' 39 el.style.height = '84px' 40 el.style.position = 'fixed'; 41 el.style.zIndex = '999'; 42 43 el.setAttribute('id', 'we-stand-with-ukraine') 44 document.body.appendChild(el); 45}); 46 47/** 48 * Frontend related objects 49 * 50 * @author Tijs Verkoyen <[email protected]> 51 * @author Thomas Deceuninck <[email protected]> 52 */ 53var jsFrontend = 54{ 55 debug: false, 56 current: {}, 57 58 // init, something like a constructor 59 init: function() 60 { 61 jsFrontend.current.language = jsFrontend.data.get('FRONTEND_LANGUAGE'); 62 63 // init stuff 64 jsFrontend.initAjax(); 65 66 // init controls 67 jsFrontend.controls.init(); 68 69 // init form 70 jsFrontend.forms.init(); 71 72 // init gravatar 73 jsFrontend.gravatar.init(); 74 75 // init search 76 jsFrontend.search.init(); 77 78 // init statistics 79 jsFrontend.statistics.init(); 80 81 // init twitter 82 jsFrontend.twitter.init(); 83 }, 84 85 // init 86 initAjax: function() 87 { 88 // set defaults for AJAX 89 $.ajaxSetup( 90 { 91 url: '/frontend/ajax.php', 92 cache: false, 93 type: 'POST', 94 dataType: 'json', 95 timeout: 10000, 96 data: { fork: { module: null, action: null, language: jsFrontend.current.language } } 97 }); 98 } 99} 100 101/** 102 * Controls related javascript 103 * 104 * @author Tijs Verkoyen <[email protected]> 105 */ 106jsFrontend.controls = 107{ 108 // init, something like a constructor 109 init: function() 110 { 111 jsFrontend.controls.bindTargetBlank(); 112 }, 113 114 // bind target blank 115 bindTargetBlank: function() 116 { 117 $('a.targetBlank').attr('target', '_blank'); 118 } 119} 120 121/** 122 * Data related methods 123 * 124 * @author Tijs Verkoyen <[email protected]> 125 */ 126jsFrontend.data = 127{ 128 initialized: false, 129 data: {}, 130 131 init: function() 132 { 133 // check if var is available 134 if(typeof jsData == 'undefined') throw 'jsData is not available'; 135 136 // populate 137 jsFrontend.data.data = jsData; 138 jsFrontend.data.initialized = true; 139 }, 140 141 exists: function(key) 142 { 143 return (typeof eval('jsFrontend.data.data.' + key) != 'undefined'); 144 }, 145 146 get: function(key) 147 { 148 // init if needed 149 if(!jsFrontend.data.initialized) jsFrontend.data.init(); 150 151 // return 152 return eval('jsFrontend.data.data.' + key); 153 } 154} 155 156/** 157 * Facebook related 158 * 159 * @author Tijs Verkoyen <[email protected]> 160 */ 161jsFrontend.facebook = 162{ 163 // will be called after Facebook is initialized 164 afterInit: function() 165 { 166 // is GA available? 167 if(typeof _gaq == 'object') 168 { 169 // subscribe and track like 170 FB.Event.subscribe('edge.create', function(targetUrl) { _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]); }); 171 172 // subscribe and track unlike 173 FB.Event.subscribe('edge.remove', function(targetUrl) { _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]); }); 174 175 // subscribe and track message 176 FB.Event.subscribe('message.send', function(targetUrl) { _gaq.push(['_trackSocial', 'facebook', 'send', targetUrl]); }); 177 } 178 } 179} 180 181/** 182 * Form related javascript 183 * 184 * @author Tijs Verkoyen <[email protected]> 185 */ 186jsFrontend.forms = 187{ 188 // init, something like a constructor 189 init: function() 190 { 191 jsFrontend.forms.placeholders(); 192 jsFrontend.forms.datefields(); 193 jsFrontend.forms.filled(); 194 }, 195 196 // once text has been filled add another class to it (so it's possible to style it differently) 197 filled: function() 198 { 199 $(document).on('blur', 'form input, form textarea, form select', function() 200 { 201 if($(this).val() == '') $(this).removeClass('filled'); 202 else $(this).addClass('filled'); 203 }); 204 }, 205 206 // initialize the date fields 207 datefields: function() 208 { 209 var $inputDatefields = $('.inputDatefieldNormal, .inputDatefieldFrom, .inputDatefieldTill, .inputDatefieldRange') 210 var $inputDatefieldNormal = $('.inputDatefieldNormal'); 211 var $inputDatefieldFrom = $('.inputDatefieldFrom'); 212 var $inputDatefieldTill = $('.inputDatefieldTill'); 213 var $inputDatefieldRange = $('.inputDatefieldRange'); 214 215 if($inputDatefields.length > 0) 216 { 217 var dayNames = [jsFrontend.locale.loc('DayLongSun'), jsFrontend.locale.loc('DayLongMon'), jsFrontend.locale.loc('DayLongTue'), jsFrontend.locale.loc('DayLongWed'), jsFrontend.locale.loc('DayLongThu'), jsFrontend.locale.loc('DayLongFri'), jsFrontend.locale.loc('DayLongSat')]; 218 var dayNamesMin = [jsFrontend.locale.loc('DayShortSun'), jsFrontend.locale.loc('DayShortMon'), jsFrontend.locale.loc('DayShortTue'), jsFrontend.locale.loc('DayShortWed'), jsFrontend.locale.loc('DayShortThu'), jsFrontend.locale.loc('DayShortFri'), jsFrontend.locale.loc('DayShortSat')]; 219 var dayNamesShort = [jsFrontend.locale.loc('DayShortSun'), jsFrontend.locale.loc('DayShortMon'), jsFrontend.locale.loc('DayShortTue'), jsFrontend.locale.loc('DayShortWed'), jsFrontend.locale.loc('DayShortThu'), jsFrontend.locale.loc('DayShortFri'), jsFrontend.locale.loc('DayShortSat')]; 220 var monthNames = [jsFrontend.locale.loc('MonthLong1'), jsFrontend.locale.loc('MonthLong2'), jsFrontend.locale.loc('MonthLong3'), jsFrontend.locale.loc('MonthLong4'), jsFrontend.locale.loc('MonthLong5'), jsFrontend.locale.loc('MonthLong6'), jsFrontend.locale.loc('MonthLong7'), jsFrontend.locale.loc('MonthLong8'), jsFrontend.locale.loc('MonthLong9'), jsFrontend.locale.loc('MonthLong10'), jsFrontend.locale.loc('MonthLong11'), jsFrontend.locale.loc('MonthLong12')]; 221 var monthNamesShort = [jsFrontend.locale.loc('MonthShort1'), jsFrontend.locale.loc('MonthShort2'), jsFrontend.locale.loc('MonthShort3'), jsFrontend.locale.loc('MonthShort4'), jsFrontend.locale.loc('MonthShort5'), jsFrontend.locale.loc('MonthShort6'), jsFrontend.locale.loc('MonthShort7'), jsFrontend.locale.loc('MonthShort8'), jsFrontend.locale.loc('MonthShort9'), jsFrontend.locale.loc('MonthShort10'), jsFrontend.locale.loc('MonthShort11'), jsFrontend.locale.loc('MonthShort12')]; 222 223 $inputDatefields.datepicker({ 224 dayNames: dayNames, 225 dayNamesMin: dayNamesMin, 226 dayNamesShort: dayNamesShort, 227 hideIfNoPrevNext: true, 228 monthNames: monthNames, 229 monthNamesShort: monthNamesShort, 230 nextText: jsFrontend.locale.lbl('Next'), 231 prevText: jsFrontend.locale.lbl('Previous'),
232 showAnim: 'slideDown' 233 }); 234 235 // the default, nothing special 236 $inputDatefieldNormal.each(function() 237 { 238 // get data 239 var data = $(this).data(); 240 var value = $(this).val(); 241 242 // set options 243 $(this).datepicker('option', { 244 dateFormat: data.mask, 245 firstDay: data.firstday 246 }).datepicker('setDate', value); 247 }); 248 249 // date fields that have a certain start date 250 $inputDatefieldFrom.each(function() 251 { 252 // get data 253 var data = $(this).data(); 254 var value = $(this).val(); 255 256 // set options 257 $(this).datepicker('option', { 258 dateFormat: data.mask, firstDay: data.firstday, 259 minDate: new Date(parseInt(data.startdate.split('-')[0], 10), parseInt(data.startdate.split('-')[1], 10) - 1, parseInt(data.startdate.split('-')[2], 10)) 260 }).datepicker('setDate', value); 261 }); 262 263 // date fields that have a certain enddate 264 $inputDatefieldTill.each(function() 265 { 266 // get data 267 var data = $(this).data(); 268 var value = $(this).val(); 269 270 // set options 271 $(this).datepicker('option', 272 { 273 dateFormat: data.mask, 274 firstDay: data.firstday, 275 maxDate: new Date(parseInt(data.enddate.split('-')[0], 10), parseInt(data.enddate.split('-')[1], 10) -1, parseInt(data.enddate.split('-')[2], 10)) 276 }).datepicker('setDate', value); 277 }); 278 279 // date fields that have a certain range 280 $inputDatefieldRange.each(function() 281 { 282 // get data 283 var data = $(this).data(); 284 var value = $(this).val(); 285 286 // set options 287 $(this).datepicker('option', 288 { 289 dateFormat: data.mask, 290 firstDay: data.firstday, 291 minDate: new Date(parseInt(data.startdate.split('-')[0], 10), parseInt(data.startdate.split('-')[1], 10) - 1, parseInt(data.startdate.split('-')[2], 10), 0, 0, 0, 0), 292 maxDate: new Date(parseInt(data.enddate.split('-')[0], 10), parseInt(data.enddate.split('-')[1], 10) - 1, parseInt(data.enddate.split('-')[2], 10), 23, 59, 59) 293 }).datepicker('setDate', value); 294 }); 295 } 296 }, 297 298 // placeholder fallback for browsers that don't support placeholder 299 placeholders: function() 300 { 301 // detect if placeholder-attribute is supported 302 jQuery.support.placeholder = ('placeholder' in document.createElement('input')); 303 304 if(!jQuery.support.placeholder) 305 { 306 // bind focus 307 $('input[placeholder], textarea[placeholder]').on('focus', function() 308 { 309 // grab element 310 var input = $(this); 311 312 // only do something when the current value and the placeholder are the same 313 if(input.val() == input.attr('placeholder')) 314 { 315 // clear 316 input.val(''); 317 318 // remove class 319 input.removeClass('placeholder'); 320 } 321 }); 322 323 $('input[placeholder], textarea[placeholder]').on('blur', function() 324 { 325 // grab element 326 var input = $(this); 327 328 // only do something when the input is empty or the value is the same as the placeholder 329 if(input.val() == '' || input.val() == input.attr('placeholder')) 330 { 331 // set placeholder 332 input.val(input.attr('placeholder')); 333 334 // add class 335 input.addClass('placeholder'); 336 } 337 }); 338 339 // call blur to initialize 340 $('input[placeholder], textarea[placeholder]').blur(); 341 342 // hijack the form so placeholders aren't submitted as values 343 $('input[placeholder], textarea[placeholder]').parents('form').submit(function() 344 { 345 // find elements with placeholders 346 $(this).find('input[placeholder]').each(function() 347 { 348 // grab element 349 var input = $(this); 350 351 // if the value and the placeholder are the same reset the value 352 if(input.val() == input.attr('placeholder')) input.val(''); 353 }); 354 }); 355 } 356 } 357} 358 359/** 360 * Gravatar related javascript 361 * 362 * @author Tijs Verkoyen <[email protected]> 363 */ 364jsFrontend.gravatar = 365{ 366 // init, something like a constructor 367 init: function() 368 { 369 $('.replaceWithGravatar').each(function() 370 { 371 var element = $(this); 372 var gravatarId = element.data('gravatarId'); 373 var size = element.attr('height'); 374 375 // valid gravatar id 376 if(gravatarId != '') 377 { 378 // build url 379 var url = 'http://www.gravatar.com/avatar/' + gravatarId + '?r=g&d=404'; 380 381 // add size if set before 382 if(size != '') url += '&s=' + size; 383 384 // create new image 385 var gravatar = new Image(); 386 gravatar.src = url; 387 388 // reset src 389 gravatar.onload = function() 390 { 391 element.attr('src', url).addClass('gravatarLoaded'); 392 } 393 } 394 }); 395 } 396} 397 398/** 399 * Locale 400 * 401 * @author Tijs Verkoyen <[email protected]> 402 */ 403jsFrontend.locale = 404{ 405 initialized: false, 406 data: {}, 407 408 // init, something like a constructor 409 init: function() 410 { 411 $.ajax({ 412 url: '/frontend/cache/locale/' + jsFrontend.current.language + '.json', 413 type: 'GET', 414 dataType: 'json', 415 async: false, 416 success: function(data) 417 { 418 jsFrontend.locale.data = data; 419 jsFrontend.locale.initialized = true; 420 }, 421 error: function(jqXHR, textStatus, errorThrown) 422 { 423 throw 'Regenerate your locale-files.'; 424 } 425 }); 426 }, 427 428 // get an item from the locale 429 get: function(type, key) 430 { 431 // initialize if needed 432 if(!jsFrontend.locale.initialized) jsFrontend.locale.init(); 433 434 // validate 435 if(typeof jsFrontend.locale.data[type][key] == 'undefined') return '{$' + type + key + '}'; 436 437 return jsFrontend.locale.data[type][key]; 438 }, 439 440 // get an action 441 act: function(key) 442 { 443 return jsFrontend.locale.get('act', key); 444 }, 445 446 // get an error 447 err: function(key) 448 { 449 return jsFrontend.locale.get('err', key); 450 }, 451 452 // get a label 453 lbl: function(key) 454 { 455 return jsFrontend.locale.get('lbl', key); 456 }, 457 458 // get localization 459 loc: function(key) 460 { 461 return jsFrontend.locale.get('loc', key); 462 }, 463 464 // get a message 465 msg: function(key) 466 { 467 return jsFrontend.locale.get('msg', key); 468 } 469} 470 471/** 472 * Search controls 473 * 474 * @author Matthias Mullie <[email protected]> 475 */ 476jsFrontend.search = 477{ 478 // init, something like a constructor 479 init: function() 480 { 481 // auto suggest (search widget) 482 if($('input.autoSuggest').length > 0) jsFrontend.search.autosuggest(55); 483 484 // autocomplete (search results page: autocomplete based on known search terms) 485 if($('input.autoComplete').length > 0) jsFrontend.search.autocomplete(); 486 487 // live suggest (search results page: live feed of matches) 488 if($('input.liveSuggest').length > 0 && $('#searchContainer').length > 0) jsFrontend.search.livesuggest(); 489 }, 490 491 // autocomplete (search results page: autocomplete based on known search terms) 492 autocomplete: function() 493 { 494 // grab element 495 var $input = $('input.autoComplete'); 496 497 // autocomplete (based on saved search terms) on results page 498 $input.autocomplete( 499 { 500 minLength: 1, 501 source: function(request, response) 502 { 503 // ajax call! 504 $.ajax( 505 { 506 data: 507 { 508 fork: { module: 'search', action: 'autocomplete' }, 509 term: request.term 510 }, 511 success: function(data, textStatus) 512 { 513 // init var 514 var realData = []; 515 516 // alert the user 517 if(data.code != 200 && jsFrontend.debug) { alert(data.message); } 518 519 if(data.code == 200) 520 { 521 for(var i in data.data) realData.push({ label: data.data[i].term, value: data.data[i].term, url: data.data[i].url }); 522 } 523 524 // set response 525 response(realData); 526 } 527 }); 528 }, 529 select: function(e, ui) 530 { 531 window.location.href = ui.item.url 532 } 533 }) 534 // when we have been typing in the search textfield and we blur out of it, we're ready to save it 535 .on('blur', function() 536 { 537 if($(this).val() != '') 538 { 539 // ajax call! 540 $.ajax( 541 { 542 data: 543 { 544 fork: { module: 'search', action: 'save' }, 545 term: $(this).val() 546 } 547 }); 548 } 549 }); 550 }, 551 552 // auto suggest (search widget) 553 autosuggest: function(length) 554 { 555 // set default values 556 if(typeof length == 'undefined') length = 100; 557 558 // grab element 559 var $input = $('input.autoSuggest'); 560 561 // search widget suggestions 562 $input.autocomplete( 563 { 564 minLength: 1, 565 source: function(request, response) 566 { 567 // ajax call! 568 $.ajax( 569 { 570 data: 571 { 572 fork: { module: 'search', action: 'autosuggest' }, 573 term: request.term, 574 length: length 575 }, 576 success: function(data, textStatus) 577 { 578 // init var 579 var realData = []; 580 581 // alert the user 582 if(data.code != 200 && jsFrontend.debug) { alert(data.message); } 583 584 if(data.code == 200) 585 { 586 for(var i in data.data) realData.push({ label: data.data[i].title, value: data.data[i].title, url: data.data[i].full_url, desc: data.data[i].text }); 587 } 588 589 // set response 590 response(realData); 591 } 592 }); 593 }, 594 select: function(e, ui) 595 { 596 window.location.href = ui.item.url 597 } 598 }) 599 // when we have been typing in the search textfield and we blur out of it, we're ready to save it 600 .on('blur', function() 601 { 602 if($(this).val() != '') 603 { 604 // ajax call! 605 $.ajax( 606 { 607 data: 608 { 609 fork: { module: 'search', action: 'save' }, 610 term: $(this).val() 611 } 612 }); 613 } 614 }) 615 // and also: alter the autocomplete style: add description! 616 .data('autocomplete')._renderItem = function(ul, item) 617 { 618 return $('<li></li>') 619 .data('item.autocomplete', item) 620 .append('<a><strong>' + item.label + '</strong><br \>' + item.desc + '</a>' ) 621 .appendTo(ul); 622 }; 623 }, 624 625 // livesuggest (search results page: live feed of matches) 626 livesuggest: function() 627 { 628 // check if calls for live suggest are allowed 629 var allowCall = true; 630 631 // grab element 632 var $input = $('input.liveSuggest'); 633 634 // change in input = do the dance: live search results completion 635 $input.on('keyup', function() 636 { 637 var $searchContainer = $('#searchContainer'); 638 639 // make sure we're allowed to do the call (= previous call is no longer processing) 640 if(allowCall) 641 { 642 // temporarily allow no more calls 643 allowCall = false;
644 645 // fade out 646 $searchContainer.fadeTo(0, 0.5); 647 648 // ajax call! 649 $.ajax( 650 { 651 data: 652 { 653 fork: { module: 'search', action: 'livesuggest' }, 654 term: $(this).val() 655 }, 656 success: function(data, textStatus) 657 { 658 // allow for new calls 659 allowCall = true; 660 661 // alert the user 662 if(data.code != 200 && jsFrontend.debug) { alert(data.message); } 663 664 if(data.code == 200) 665 { 666 // replace search results 667 $searchContainer.html(utils.string.html5(data.data)); 668 669 // fade in 670 $searchContainer.fadeTo(0, 1); 671 } 672 }, 673 error: function() 674 { 675 // allow for new calls 676 allowCall = true; 677 678 // replace search results 679 $searchContainer.html(''); 680 681 // fade in 682 $searchContainer.fadeTo(0, 1); 683 } 684 }); 685 } 686 }); 687 } 688} 689 690/** 691 * Gravatar related javascript 692 * 693 * @author Tijs Verkoyen <[email protected]> 694 */ 695jsFrontend.statistics = 696{ 697 // init, something like a constructor 698 init: function() 699 { 700 jsFrontend.statistics.trackOutboundLinks(); 701 }, 702 703 // track all outbound links 704 trackOutboundLinks: function() 705 { 706 // check if Google Analytics is available 707 if(typeof _gaq == 'object') 708 { 709 // create a new selector 710 $.expr[':'].external = function(obj) { 711 return (typeof obj.href != 'undefined' && !obj.href.match(/^mailto\:/) && (obj.hostname != location.hostname)); 712 }; 713 714 // bind on all links that don't have the class noTracking 715 $(document).on('click', 'a:external:not(.noTracking)', function(e) 716 { 717 var $this = $(this); 718 var link = $this.attr('href'); 719 var title = $this.attr('title'); 720 if(typeof title == 'undefined' || title == '') title = $this.html(); 721 722 // track in Google Analytics 723 _gaq.push(['_trackEvent', 'Outbound Links', link, title]); 724 }); 725 } 726 } 727} 728 729/** 730 * Twitter related stuff 731 * 732 * @author Tijs Verkoyen <[email protected]> 733 */ 734jsFrontend.twitter = 735{ 736 init: function() 737 { 738 // if GA is integrated and a tweet button is used 739 if(typeof _gaq == 'object' && typeof twttr == 'object') 740 { 741 // bind event, so we can track the tweets 742 twttr.events.on('tweet', function(e) 743 { 744 // valid event? 745 if(e) 746 { 747 // init var 748 var targetUrl = null; 749 750 // get url 751 if(e.target && e.target.nodeName == 'IFRAME') targetUrl = utils.url.extractParamFromUri(e.target.src, 'url'); 752 753 // push to GA 754 _gaq.push(['_trackSocial', 'twitter', 'tweet', targetUrl]); 755 } 756 }); 757 } 758 } 759} 760 761$(jsFrontend.init());
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.