1/* global document */ 2/* global jQuery */ 3/* global snax */ 4/* global alert */ 5/* global confirm */ 6/* global console */ 7/* global grecaptcha */ 8 9// globa namespace 10if ( typeof window.snax === 'undefined' ) { 11 window.snax = {}; 12} 13 14/******** 15 * 16 * Core 17 * 18 *******/ 19 20(function ($, ctx) { 21 22 'use strict'; 23 24 /** VARS *************************/ 25 26 ctx.config = $.parseJSON(window.snax_front_config); 27 28 if (!ctx.config) { 29 throw 'Snax Error: Global config is not defined!'; 30 } 31 32 /** FUNCTIONS ********************/ 33 34 ctx.log = function(msg) { 35 if (typeof console !== 'undefined') { 36 console.log(msg); 37 } 38 }; 39 40 ctx.inDebugMode = function() { 41 return (typeof ctx.config.debug_mode !== 'undefined' && ctx.config.debug_mode); 42 }; 43 44 ctx.isTouchDevice = function () { 45 return ('ontouchstart' in window) || navigator.msMaxTouchPoints; 46 }; 47 48 ctx.createCookie = function (name, value, hours) { 49 var expires; 50 51 if (hours) { 52 var date = new Date(); 53 date.setTime(date.getTime() + (hours * 60 * 60 * 1000)); 54 expires = '; expires=' + date.toUTCString(); 55 } 56 else { 57 expires = ''; 58 } 59 60 document.cookie = name.concat('=', value, expires, '; path=/'); 61 }; 62 63 ctx.readCookie = function (name) { 64 var nameEQ = name + '='; 65 var ca = document.cookie.split(';'); 66 67 for(var i = 0; i < ca.length; i += 1) { 68 var c = ca[i]; 69 while (c.charAt(0) === ' ') { 70 c = c.substring(1,c.length); 71 } 72 73 if (c.indexOf(nameEQ) === 0) { 74 return c.substring(nameEQ.length,c.length); 75 } 76 } 77 78 return null; 79 }; 80 81 ctx.deleteCookie = function (name) { 82 ctx.createCookie(name, '', -1); 83 }; 84 85 ctx.getUrlParameter = function (param) { 86 var sPageURL = decodeURIComponent(window.location.search.substring(1)), 87 sURLVariables = sPageURL.split('&'), 88 sParameterName, 89 i; 90 91 for (i = 0; i < sURLVariables.length; i++) { 92 sParameterName = sURLVariables[i].split('='); 93 94 if (sParameterName[0] === param) { 95 return sParameterName[1] === undefined ? true : sParameterName[1]; 96 } 97 } 98}; 99 100})(jQuery, snax); 101 102 103/*********** 104 * 105 * Helpers 106 * 107 ***********/ 108 109(function ($, ctx) { 110 111 /* Media Item Class (image, audio, video) */ 112 113 ctx.MediaItem = function (data) { 114 // Public scope. 115 var instance = {}; 116 117 // Constructor. 118 function init() { 119 data = data || {}; 120 121 data = $.extend({ 122 'type': 'image', 123 'title': '', 124 'source': '', 125 'refLink': '', 126 'description': '', 127 'mediaId': '', 128 'postId': 0, 129 'authorId': '', 130 'status': '', 131 'parentFormat': 'list', 132 'origin': 'post', 133 'legal': false, 134 'memeTemplate': '' 135 }, data); 136 137 return instance; 138 } 139 140 instance.save = function(callback) { 141 callback = callback || function() {}; 142 143 var ajaxData = { 144 'action': 'snax_add_media_item', 145 'security': $('input[name=snax-add-media-item-nonce]').val(), 146 'snax_title': data.title, 147 'snax_source': data.source, 148 'snax_ref_link': data.refLink, 149 'snax_description': data.description, 150 'snax_media_id': data.mediaId, 151 'snax_post_id': data.postId, 152 'snax_author_id': data.authorId, 153 'snax_status': data.status, 154 'snax_parent_format': data.parentFormat, 155 'snax_origin': data.origin, 156 'snax_legal': data.legal ? 'accepted' : '', 157 'snax_type': data.type, 158 'snax_meme_template': data.memeTemplate 159 }; 160 161 if (typeof ctx.saveItemImageDataFilter === 'function') { 162 ajaxData = ctx.saveItemImageDataFilter(ajaxData, data); 163 } 164 165 var xhr = $.ajax({ 166 'type': 'POST', 167 'url': ctx.config.ajax_url, 168 'dataType': 'json', 169 'data': ajaxData 170 }); 171 172 xhr.done(function (res) { 173 callback(res); 174 }); 175 }; 176 177 return init(); 178 }; 179 180 /* Embed Item Class */ 181 182 ctx.EmbedItem = function (data) { 183 // Public scope. 184 var instance = {}; 185 186 // Constructor.
187 function init() { 188 data = data || {}; 189 190 data = $.extend({ 191 'type': 'embed', 192 'title': '', 193 'source': '', 194 'refLink': '', 195 'description': '', 196 'embedCode': '', 197 'postId': 0, 198 'authorId': '', 199 'status': '', 200 'parentFormat': 'list', 201 'origin': 'post', 202 'legal': false 203 }, data); 204 205 return instance; 206 } 207 208 instance.save = function(callback) { 209 callback = callback || function() {}; 210 211 var ajaxData = { 212 'action': 'snax_add_embed_item', 213 'security': $('input[name=snax-add-embed-item-nonce]').val(), 214 'snax_title': data.title, 215 'snax_source': data.source, 216 'snax_ref_link': data.refLink, 217 'snax_embed_code': data.embedCode, 218 'snax_description': data.description, 219 'snax_post_id': data.postId, 220 'snax_author_id': data.authorId, 221 'snax_status': data.status, 222 'snax_parent_format': data.parentFormat, 223 'snax_origin': data.origin, 224 'snax_legal': data.legal ? 'accepted' : '' 225 }; 226 227 if (typeof ctx.saveItemEmbedDataFilter === 'function') { 228 ajaxData = ctx.saveItemEmbedDataFilter(ajaxData, data); 229 } 230 231 var xhr = $.ajax({ 232 'type': 'POST', 233 'url': ctx.config.ajax_url, 234 'dataType': 'json', 235 'data': ajaxData 236 }); 237 238 xhr.done(function (res) { 239 callback(res); 240 }); 241 }; 242 243 return init(); 244 }; 245 246 /* Embed Item Class */ 247 248 ctx.TextItem = function (data) { 249 // Public scope. 250 var instance = {}; 251 252 // Constructor. 253 function init() { 254 data = data || {}; 255 256 data = $.extend({ 257 'type': 'text', 258 'title': '', 259 'refLink': '', 260 'description': '', 261 'postId': 0, 262 'authorId': '', 263 'status': '', 264 'parentFormat': 'list', 265 'origin': 'post', 266 'legal': false 267 }, data); 268 269 return instance; 270 } 271 272 instance.save = function(callback) { 273 callback = callback || function() {}; 274 275 var ajaxData = { 276 'action': 'snax_add_text_item', 277 'security': $('input[name=snax-add-text-item-nonce]').val(), 278 'snax_title': data.title, 279 'snax_ref_link': data.refLink, 280 'snax_description': data.description, 281 'snax_post_id': data.postId, 282 'snax_author_id': data.authorId, 283 'snax_status': data.status, 284 'snax_parent_format': data.parentFormat, 285 'snax_origin': data.origin, 286 'snax_legal': data.legal ? 'accepted' : '' 287 }; 288 289 if (typeof ctx.saveItemTextDataFilter === 'function') { 290 ajaxData = ctx.saveItemTextDataFilter(ajaxData, data); 291 } 292 293 var xhr = $.ajax({ 294 'type': 'POST', 295 'url': ctx.config.ajax_url, 296 'dataType': 'json', 297 'data': ajaxData 298 }); 299 300 xhr.done(function (res) { 301 callback(res); 302 }); 303 }; 304 305 return init(); 306 }; 307 308 ctx.deleteItem = function($link, callback) { 309 callback = callback || function() {}; 310 311 var nonce = $.trim($link.attr('data-snax-nonce')); 312 var itemId = parseInt($link.attr('data-snax-item-id'), 10); 313 var userId = snax.currentUserId; 314 315 var xhr = $.ajax({ 316 'type': 'POST', 317 'url': ctx.config.ajax_url, 318 'dataType': 'json', 319 'data': { 320 'action': 'snax_delete_item', 321 'security': nonce, 322 'snax_item_id': itemId, 323 'snax_user_id': userId 324 } 325 }); 326 327 xhr.done(function (res) { 328 callback(res); 329 }); 330 }; 331 332 ctx.setItemAsFeatured = function($link, callback) { 333 callback = callback || function() {}; 334 335 var nonce = $.trim($link.attr('data-snax-nonce')); 336 var itemId = parseInt($link.attr('data-snax-item-id'), 10); 337 var userId = snax.currentUserId; 338 339 var xhr = $.ajax({ 340 'type': 'POST', 341 'url': ctx.config.ajax_url, 342 'dataType': 'json', 343 'data': { 344 'action': 'snax_set_item_as_featured', 345 'security': nonce, 346 'snax_item_id': itemId, 347 'snax_user_id': userId 348 } 349 }); 350 351 xhr.done(function (res) { 352 callback(res); 353 }); 354 }; 355 356 ctx.updateItems = function(items, callback) { 357 callback = callback || function() {}; 358 359 var xhr = $.ajax({ 360 'type': 'POST', 361 'url': ctx.config.ajax_url, 362 'dataType': 'json', 363 'data': { 364 'action': 'snax_update_items', 365 'security': $('input[name=snax-frontend-submission-nonce]').val(), 366 'snax_items': items 367 } 368 }); 369 370 xhr.done(function (res) { 371 callback(res); 372 }); 373 }; 374 375 ctx.deletePost = function($link, callback) { 376 callback = callback || function() {}; 377 378 var nonce = $.trim($link.attr('data-snax-nonce')); 379 var postId = parseInt($link.attr('data-snax-post-id'), 10); 380 381 var xhr = $.ajax({ 382 'type': 'POST', 383 'url': ctx.config.ajax_url, 384 'dataType': 'json', 385 'data': { 386 'action': 'snax_delete_post', 387 'security': nonce, 388 'snax_post_id': postId 389 } 390 }); 391 392 xhr.done(function (res) { 393 callback(res); 394 }); 395 }; 396 397 ctx.loginRequired = function(blocked) { 398 $('body').trigger('snaxLoginRequired', [blocked]); 399 }; 400 401 ctx.getMediaHtmlTag = function(data, callback) { 402 var xhr = $.ajax({ 403 'type': 'GET', 404 'url': ctx.config.ajax_url, 405 'dataType': 'json', 406 'data': { 407 'action': 'snax_load_media_tpl', 408 'snax_media_id': data.mediaId, 409 'snax_post_id': data.postId, 410 'snax_type': data.type 411 } 412 }); 413 414 xhr.done(function (res) { 415 callback(res); 416 }); 417 }; 418 419 ctx.deleteMedia = function(data, callback) { 420 callback = callback || function() {}; 421 422 var xhr = $.ajax({ 423 'type': 'POST', 424 'url': ctx.config.ajax_url, 425 'dataType': 'json', 426 'data': { 427 'action': 'snax_delete_media', 428 'security': $('input[name=snax-delete-media-nonce]').val(), 429 'snax_media_id': data.mediaId, 430 'snax_author_id': data.authorId 431 } 432 }); 433 434 xhr.done(function (res) { 435 callback(res); 436 }); 437 }; 438 439 ctx.updateMediaMetadata = function(data, callback) { 440 callback = callback || function() {}; 441 442 var xhr = $.ajax({ 443 'type': 'POST', 444 'url': ctx.config.ajax_url, 445 'dataType': 'json', 446 'data': { 447 'action': 'snax_update_media_meta', 448 // @todo - use separate nonce or use generic one. 449 'security': $('input[name=snax-delete-media-nonce]').val(), 450 'snax_media_id': data.mediaId, 451 'snax_parent_format': data.parentFormat 452 } 453 }); 454 455 xhr.done(function (res) { 456 callback(res); 457 }); 458 }; 459 460 ctx.getEmbedPreview = function(embed_code, callback) { 461 var xhr = $.ajax({ 462 'type': 'POST', 463 'url': ctx.config.ajax_url, 464 'dataType': 'json', 465 'data': { 466 'action': 'snax_load_embed_tpl', 467 'snax_embed_code': embed_code 468 } 469 }); 470 471 xhr.done(function (res) { 472 callback(res); 473 }); 474 }; 475 476 ctx.displayFeedback = function(type) { 477 var feedbackTypeClass = 'snax-feedback-' + type; 478 479 // Try to get type specific feedback first. 480 var $feedback = $('.' + feedbackTypeClass); 481 482 if ($feedback.length === 0) { 483 return; 484 } 485 486 ctx.hideFeedback(); 487 488 // Activate. 489 $feedback.toggleClass('snax-feedback-off snax-feedback-on'); 490 491 // Show. 492 $('body').addClass('snax-show-feedback'); 493 }; 494 495 ctx.hideFeedback = function() { 496 // Deactivate all. 497 $('.snax-feedback-on').toggleClass('snax-feedback-on snax-feedback-off'); 498 499 // Hide all. 500 $('body').removeClass('snax-show-feedback'); 501 }; 502 503 ctx.isValidUrl = function(url) { 504 return url.match(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;
504:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/); 505 }; 506 507})(jQuery, snax); 508 509 510/********* 511 * 512 * Common 513 * 514 *********/ 515 516(function ($, ctx) { 517 518 'use strict'; 519 520 var selectors = { 521 'feedbackCloseButton': '.snax-close-button' 522 }; 523 524 // fire 525 $(document).ready(function () { 526 527 /* 528 * Close feedback button. 529 */ 530 $(selectors.feedbackCloseButton).on('click', function(e) { 531 e.preventDefault(); 532 533 snax.hideFeedback(); 534 }); 535 }); 536 537})(jQuery, snax); 538 539 540/**************** 541 * 542 * Facebook SDK 543 * 544 ****************/ 545 546(function ($, ctx) { 547 548 'use strict'; 549 550 ctx.resetFacebookSDK = function () { 551 $('script#facebook-jssdk').remove(); 552 $('#fb-root').remove(); 553 if (window.FB) { 554 delete window.FB; 555 } 556 }; 557 558 $('body').on( 'snaxBeforeNewContentReady', function ( e, $newContent ) { 559 if ($newContent.find('.fb-video')) { 560 ctx.resetFacebookSDK(); 561 } 562 } ); 563 564})(jQuery, snax); 565 566/**************************** 567 * 568 * Module: Media upload form 569 * 570 ***************************/ 571 572(function ($, ctx) { 573 574 'use strict'; 575 576 var selectors = { 577 'form': '.snax-media-upload-form', 578 'pluploadForm': '.snax-plupload-upload-ui', 579 'loadFormButton': '.snax-load-form-button' 580 }; 581 582 ctx.mediaUploadForm = function () { 583 if (typeof snaxPlupload === 'undefined') { 584 return; 585 } 586 587 $(selectors.form).each(function() { 588 snaxPlupload.initUploader($(this)); 589 }); 590 591 $(selectors.loadFormButton).on('click',function() { 592 var $link = $(this); 593 var $form = $link.parents(selectors.form); 594 var formClass = $link.attr('data-snax-rel-class'); 595 596 $form.find('.' + formClass).toggle(); 597 598 var $pluploadForm = $form.find(selectors.pluploadForm); 599 600 $pluploadForm.toggle(); 601 602 if ($pluploadForm.is(':visible')) { 603 $form.removeClass('snax-custom-form'); 604 } else { 605 $form.addClass('snax-custom-form'); 606 } 607 }); 608 }; 609 610 // fire 611 $(document).ready(function () { 612 ctx.mediaUploadForm(); 613 }); 614 615})(jQuery, snax); 616 617 618/************************* 619 * 620 * Module: Date > Time ago 621 * 622 *************************/ 623 624(function ($, ctx) { 625 626 'use strict'; 627 628 var selectors = { 629 'wrapper': '.snax-time-left', 630 'dateWrapper': '> .snax-date-wrapper', 631 'date': '> .snax-date', 632 'timeWrapper': '> .snax-time-wrapper', 633 'time': '> .snax-time' 634 }; 635 636 ctx.timeagoSelectors = selectors; 637 638 ctx.dateConstans = { 639 'day_ms': 1000 * 60 * 60 * 24, 640 'month_ms': 1000 * 60 * 60 * 24 * 30, 641 'year_ms': 1000 * 60 * 60 * 24 * 356 642 }; 643 644 ctx.dateToTimeago = function () { 645 if (!$.fn.timeago) { 646 return; 647 } 648 649 // store current settings, thanks to $.extend we have a copy without reference to original object 650 var origSettings = $.extend(true, {} , $.timeago.settings); 651 652 // override 653 $.extend($.timeago.settings, { 654 cutoff: ctx.dateConstans.year_ms, 655 allowFuture: true 656 }); 657 658 $.extend($.timeago.settings.strings, { 659 suffixFromNow: '' 660 }); 661 662 // apply 663 $(selectors.wrapper).each(function () { 664 var $wrapper = $(this); 665 var $dateWrapper = $wrapper.find(selectors.dateWrapper); 666 var $date = $dateWrapper.find(selectors.date); 667 var $timeWrapper = $wrapper.find(selectors.timeWrapper); 668 var $time = $timeWrapper.find(selectors.time); 669 670 var timeLeftText = $.timeago($date.text()); 671 672 $time.text(timeLeftText); 673 674 $dateWrapper.removeClass( '.snax-date-wrapper-unfriendly' ); 675 $timeWrapper.removeClass( 'snax-time-wrapper-unfriendly' ); 676 }); 677 678 // restore 679 $.timeago.settings = origSettings; 680 }; 681 682 // fire 683 $(document).ready(function () { 684 ctx.dateToTimeago(); 685 }); 686 687})(jQuery, snax); 688 689 690/************************** 691 * 692 * Module: Upvote/Downvote 693 * 694 *************************/ 695 696(function ($, ctx) { 697 698 'use strict'; 699 700 var locked = false;
701 702 var selectors = { 703 'wrapper': '.snax-voting', 704 'upvoteLink': '.snax-voting-upvote', 705 'downvoteLink': '.snax-voting-downvote', 706 'guestVoting': '.snax-guest-voting', 707 'voted': '.snax-user-voted', 708 'scoreWrapper': '.snax-voting-score', 709 'scoreValue': '.snax-voting-score strong' 710 }; 711 712 var classes = { 713 'voted': 'snax-user-voted' 714 }; 715 716 ctx.votesSelectors = selectors; 717 ctx.votesClasses = classes; 718 719 ctx.votes = function () { 720 // Catch event on wrapper to keep it working after box content reloading 721 $('body').on('click', selectors.upvoteLink + ', ' + selectors.downvoteLink, function (e) { 722 e.preventDefault(); 723 724 if (locked) { 725 return; 726 } 727 728 locked = true; 729 730 var $link = $(this); 731 732 var voteType = $link.is(selectors.upvoteLink) ? 'upvote' : 'downvote'; 733 var $wrapper = $link.parents(selectors.wrapper); 734 var nonce = $.trim($link.attr('data-snax-nonce')); 735 var itemId = parseInt($link.attr('data-snax-item-id'), 10); 736 var authorId = parseInt($link.attr('data-snax-author-id'), 10); 737 738 ctx.vote({ 739 'itemId': itemId, 740 'authorId': authorId, 741 'type': voteType 742 }, nonce, $wrapper); 743 }); 744 745 // Iterate over all voting boxes and update them based on cookie states. 746 $(selectors.wrapper).each(function () { 747 var $this = $(this); 748 var id = parseInt($this.attr('data-snax-item-id'), 10); 749 750 if (id <= 0) { 751 return; 752 } 753 754 var typeCookie = 'snax_vote_type_' + id; 755 var scoreCookie = 'snax_vote_score_' + id; 756 757 var type = ctx.readCookie(typeCookie); 758 var score = ctx.readCookie(scoreCookie); 759 760 if (!type && !score) { 761 return; 762 } 763 764 if (score) { 765 ctx.updateVoteScore($this.find(selectors.scoreWrapper), score); 766 } 767 768 if (type) { 769 var $upVoteLink = $this.find(selectors.upvoteLink); 770 var $downVoteLink = $this.find(selectors.downvoteLink); 771 772 if ('upvote' === type) {
773 $upVoteLink.addClass(classes.voted); 774 $downVoteLink.removeClass(classes.voted); 775 } else { 776 $downVoteLink.addClass(classes.voted); 777 $upVoteLink.removeClass(classes.voted); 778 } 779 } 780 }); 781 }; 782 783 ctx.vote = function (data, nonce, $box) { 784 var config = $.parseJSON(window.snax_front_config); 785 if ( $box.find('.snax-login-required').length > 0 ){ 786 return; 787 } 788 789 if (!config) { 790 ctx.log('Item voting failed. Global config is not defined!'); 791 return; 792 } 793 794 /* 795 * Apply new voting box state before ajax response. 796 */ 797 var $userVoted = $box.find('.snax-user-voted'); 798 var userUpvoted = $userVoted.length > 0 && $userVoted.is('.snax-voting-upvote'); 799 var userDownvoted = $userVoted.length > 0 && $userVoted.is('.snax-voting-downvote'); 800 var $score = $box.find('.snax-voting-score > strong'); 801 var score = parseInt($score.text(), 10); 802 var diff = 'upvote' === data.type ? 1 : -1; 803 804 // Remove all bubbles. 805 $box.find('.snax-voting-bubble').remove(); 806 807 // User reverted his vote. 808 if (userUpvoted && 'upvote' === data.type || userDownvoted && 'downvote' === data.type) { 809 diff *= -1; 810 811 $box.find('.snax-user-voted').removeClass('snax-user-voted'); 812 813 if ( 'upvote' === data.type ) { 814 $box.find('.snax-voting-upvote').append('<span class="snax-voting-bubble snax-voting-bubble-minus-back">-1</span>'); 815 } else { 816 $box.find('.snax-voting-downvote').append('<span class="snax-voting-bubble snax-voting-bubble-plus-back">+1</span>'); 817 } 818 819 // User voted opposite. 820 } else if (userUpvoted && 'downvote' === data.type || userDownvoted && 'upvote' === data.type) { 821 diff *= 2; 822 823 $box.find('.snax-user-voted').removeClass('snax-user-voted'); 824 $box.find('.snax-voting-' + data.type).addClass('snax-user-voted'); 825 826 if ( 'upvote' === data.type ) { 827 $box.find('.snax-voting-upvote').append('<span class="snax-voting-bubble snax-voting-bubble-plus">+2</span>'); 828 } else { 829 $box.find('.snax-voting-downvote').append('<span class="snax-voting-bubble snax-voting-bubble-minus">-2</span>'); 830 } 831 // User added new vote. 832 } else { 833 if ( 'upvote' === data.type ) { 834 $box.find('.snax-voting-upvote').addClass('snax-user-voted').append('<span class="snax-voting-bubble snax-voting-bubble-plus">+1</span>'); 835 } else { 836 $box.find('.snax-voting-downvote').addClass('snax-user-voted').append('<span class="snax-voting-bubble snax-voting-bubble-minus">-1</span>'); 837 } 838 } 839 840 // Update score. 841 ctx.updateVoteScore($box.find(selectors.scoreWrapper), score + diff); 842 843 // Send ajax. 844 var xhr = $.ajax({ 845 'type': 'POST', 846 'url': config.ajax_url, 847 'dataType': 'json', 848 'data': { 849 'action': 'snax_vote_item', 850 'security': nonce, 851 'snax_item_id': data.itemId, 852 'snax_author_id': data.authorId, 853 'snax_vote_type': data.type, 854 'snax_user_voted': ctx.readCookie( 'snax_vote_type_' + data.itemId ) 855 } 856 }); 857 858 // Update state immediately after sending ajax. Don't wait for response. 859 ctx.updateVoteState(data.itemId, data.type, $box); 860 861 xhr.done(function (res) { 862 locked = false; 863 }); 864 }; 865 866 ctx.updateVoteState = function(itemId, type, $box) { 867 var typeCookie = 'snax_vote_type_' + itemId; 868 var scoreCookie = 'snax_vote_score_' + itemId; 869 870 var currentValue = ctx.readCookie(typeCookie); 871 872 // Cookie can't be read immediately so we need to update CSS classes manually.
873 $box.find(selectors.voted).removeClass(classes.voted); 874 875 // User voted and now he wants to remove the vote. 876 if (currentValue === type) { 877 ctx.deleteCookie(typeCookie); 878 } else { 879 ctx.createCookie(typeCookie, type, 1); 880 881 // Cookie can't be read immediately so we need to update CSS classes manually. 882 $box.find('.snax-voting-' + type).addClass(classes.voted); 883 } 884 885 // Update score. 886 var score = parseInt($box.find(selectors.scoreValue).text(), 10); 887 888 ctx.createCookie(scoreCookie, score, 1); 889 }; 890 891 ctx.updateVoteScore = function($wrapper, score) { 892 var scoreHtml = ''; 893 var $container = $wrapper.parents('.snax-voting'); 894 895 // Singular? 896 if (1 === Math.abs(score)) { 897 scoreHtml = ctx.config.i18n.points_singular_tpl.replace('%d', score); 898 } else { 899 scoreHtml = ctx.config.i18n.points_plural_tpl.replace('%d', score); 900 } 901 902 // Remove all score related classes. 903 $container.removeClass('snax-voting-0 snax-voting-negative snax-voting-positive'); 904 905 // Add score related classes. 906 if (0 < score) { 907 $container.addClass( 'snax-voting-positive' ); 908 } else if (0 > score) { 909 $container.addClass( 'snax-voting-negative' ); 910 } else { 911 $container.addClass( 'snax-voting-0' ); 912 } 913 914 $wrapper.html(scoreHtml); 915 }; 916 917 // fire 918 $(document).ready(function () { 919 ctx.votes(); 920 }); 921 922})(jQuery, snax); 923 924/********************* 925 * 926 * Module: Login form 927 * 928 ********************/ 929 930(function ($, ctx) { 931 932 'use strict'; 933 934 var selectors = { 935 'loginTab': '.snax-login-tab', 936 'loginFormWrapper': '.snax-login-form', 937 'loginForm': '.snax-login-form #loginform-in-popup', 938 'forgotTab': '.snax-forgot-pass-tab', 939 'forgotFormWrapper': '.snax-forgot-pass-form', 940 'forgotForm': '.snax-forgot-pass-form #lostpasswordform', 941 'gdprTab': '.snax-gdpr-tab', 942 'backToLoginTab': '.snax-back-to-login-tab', 943 'loginErrorMessage': '.snax-login-form .snax-login-error-message', 944 'forgotErrorMessage': '.snax-forgot-pass-form .snax-forgot-pass-error-message', 945 'forgotSuccessMessage': '.snax-forgot-pass-form .snax-forgot-pass-success-message', 946 'user': { 947 'loginInput': '#user_login', 948 'emailInput': '#user_email', 949 'passwordInput': '#user_pass' 950 }, 951 'forgotPasswordLink': '#snax-popup-content .snax-link-forgot-pass', 952 'passwordWrapper': '#snax-popup-content .login-password', 953 'connectWithLabel': '#snax-popup-content .wp-social-login-connect-with', 954 'resetTab': '.snax-reset-tab' 955 }; 956 957 ctx.loginFormSelectors = selectors; 958 959 var useReCaptcha; 960 var reCaptchaToken; 961 var urlAction = ctx.getUrlParameter(ctx.config.login_popup_url_var); 962 963 ctx.loginForm = function () { 964 useReCaptcha = ctx.config.use_login_recaptcha; 965 966 // Add input placeholders. 967 $.each(selectors.user, function (id, selector) { 968 var $input = $(selector); 969 var $label = $input.prev('label'); 970 971 if ($label.length > 0) { 972 $input.attr('placeholder', $label.text()); 973 } 974 }); 975 976 // Move forgot link after password field. 977 $(selectors.passwordWrapper + ' input').after( $(selectors.forgotPasswordLink) ); 978 979 // Wrap label with <h4> tag. 980 $(selectors.connectWithLabel).wrapInner( '<h4>' ); 981 982 $('.wp-social-login-provider').on('click', function(e) { 983 var $that = $(this); 984 if ($('.snax-wpsl-gdpr-consent input').length > 0) { 985 if (!$('.snax-wpsl-gdpr-consent input').is(':checked')) { 986 e.stopPropagation(); 987 e.stopImmediatePropagation(); 988 e.preventDefault(); 989 handleLoginGDPR($that); 990 } 991 } 992 }); 993 994 handleLoginAction(); 995 handleForgotPassAction(); 996 handleTabsSwitch(); 997 998 if (urlAction==='reset_password'){ 999 $(selectors.loginTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1000 $(selectors.resetTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1001 } 1002 1003 if (urlAction==='forgot_password'){ 1004 $(selectors.loginTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1005 $(selectors.forgotTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1006 } 1007 1008 $('body').on('snaxPopupOpened', function(e, action) { 1009 if (useReCaptcha && 'login' === action) { 1010 loadReCaptcha(); 1011 } 1012 }); 1013 }; 1014 1015 var handleLoginGDPR = function($clickedProvider) { 1016 $(selectors.loginTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1017 $(selectors.gdprTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1018 $('.snax-login-gdpr-accept').on('click', function() { 1019 $('.snax-wpsl-gdpr-consent input').prop('checked', true); 1020 $(selectors.gdprTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1021 $(selectors.loginTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1022 var redirectTo = $clickedProvider.attr('href'); 1023 window.location.href = redirectTo; 1024 }); 1025 }; 1026 1027 var handleLoginAction = function() { 1028 $(selectors.loginForm).on('submit', function(e) { 1029 e.preventDefault(); 1030 1031 var $form = $(this); 1032 var $errorMessage = $(selectors.loginErrorMessage); 1033 1034 var requestData = { 1035 'action': 'snax_login' 1036 }; 1037 1038 $.each($form.serializeArray(), function(i, field) { 1039 requestData[field.name] = field.value; 1040 }); 1041 1042 // Read config directly, DO NOT use any reference. We change the value while auto logging. 1043 if (ctx.config.use_login_recaptcha) { 1044 if (!reCaptchaToken) { 1045 $errorMessage.html( '<p class="snax-validation-tip">' + ctx.config.i18n.recaptcha_invalid + '</p>'); 1046 return; 1047 } 1048 } 1049 1050 // Clear error message. 1051 $errorMessage.html('<p>' + ctx.config.i18n.user_is_logging + '</p>'); 1052 1053 var xhr = $.ajax({ 1054 'type': 'POST', 1055 'url': ctx.config.ajax_url, 1056 'dataType': 'json', 1057 'data': requestData 1058 }); 1059 1060 xhr.done(function (res) { 1061 if ('success' === res.status) { 1062 var redirectTo = res.args.redirect_url; 1063 1064 if (!redirectTo) { 1065 redirectTo = window.location.href; 1066 } 1067 1068 if (redirectTo.indexOf('?') > 0){ 1069 redirectTo += '&' + ctx.config.login_success_var + '=true'; 1070 }else{ 1071 redirectTo += '?' + ctx.config.login_success_var + '=true'; 1072 } 1073 1074 window.location.href = redirectTo; 1075 } else { 1076 if (res.message) { 1077 $errorMessage.html( '<p class="snax-validation-tip">' + res.message + '</p>'); 1078 } 1079 1080 if (useReCaptcha) { 1081 grecaptcha.reset(); 1082 } 1083 } 1084 }); 1085 1086 // Reload page if failed. 1087 xhr.fail(function() { 1088 var reloadUrl = window.location.href; 1089 1090 if (reloadUrl.indexOf('?') > 0){ 1091 reloadUrl += '&' + ctx.config.login_success_var + '=false'; 1092 }else{ 1093 reloadUrl += '?' + ctx.config.login_success_var + '=false'; 1094 } 1095 1096 window.location.href = reloadUrl; 1097 }); 1098 }); 1099 }; 1100 1101 var handleForgotPassAction = function() { 1102 $(selectors.forgotForm).on('submit', function(e) { 1103 e.preventDefault(); 1104 1105 var $form = $(this); 1106 var $errorMessage = $(selectors.forgotErrorMessage); 1107 var $successMessage = $(selectors.forgotSuccessMessage); 1108 1109 var requestData = { 1110 'action': 'snax_forgot_pass' 1111 }; 1112 1113 $.each($form.serializeArray(), function(i, field) { 1114 requestData[field.name] = field.value; 1115 }); 1116 1117 // Clear messages. 1118 $errorMessage.text(''); 1119 $successMessage.text(''); 1120 1121 var xhr = $.ajax({ 1122 'type': 'POST', 1123 'url': ctx.config.ajax_url, 1124 'dataType': 'json', 1125 'data': requestData 1126 }); 1127 1128 xhr.done(function (res) { 1129 if ('success' === res.status) { 1130 if (res.message) { 1131 $successMessage.html( '<p class="snax-validation-tip">' + res.message + '</p>'); 1132 } 1133 } else { 1134 if (res.message) { 1135 $errorMessage.html( '<p class="snax-validation-tip">' + res.message + '</p>'); 1136 } 1137 } 1138 }); 1139 }); 1140 }; 1141 1142 var handleTabsSwitch = function() { 1143 $(selectors.forgotPasswordLink).on('click', function(e) { 1144 e.preventDefault(); 1145 1146 $(selectors.loginTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1147 $(selectors.forgotTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1148 }); 1149 1150 $(selectors.backToLoginTab).on('click', function(e) { 1151 e.preventDefault(); 1152 1153 $(selectors.resetTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1154 $(selectors.forgotTab).removeClass('snax-tab-active').addClass('snax-tab-inactive'); 1155 $(selectors.loginTab).removeClass('snax-tab-inactive').addClass('snax-tab-active'); 1156 }); 1157 }; 1158 1159 var loadReCaptcha = function() { 1160 var apiUrl = ctx.config.recaptcha_api_url; 1161 var ver = ctx.config.recaptcha_version; 1162 var siteKey = ctx.config.recaptcha_site_key; 1163 1164 if (!siteKey || !apiUrl) { 1165 return; 1166 } 1167 1168 var renderLoginReCaptcha = function() { 1169 if ('30' === ver) { 1170 grecaptcha.execute( 1171 siteKey, 1172 { action: 'login' } 1173 ).then( function( token ) { 1174 $('#snax-login-recaptcha').html('<input type="hidden" name="g-recaptcha-response" value="'+ token +'">'); 1175 reCaptchaEnteredCorrectly(token); 1176 } ); 1177 } else { 1178 grecaptcha.render('snax-login-recaptcha', { 1179 'sitekey' : siteKey, 1180 'callback': reCaptchaEnteredCorrectly 1181 }); 1182 } 1183 }; 1184 1185 // Google reCaptcha API loaded. 1186 if (typeof grecaptcha !== 'undefined') { 1187 renderLoginReCaptcha(); 1188 } else { 1189 // API not loaded. Register callback and load script. 1190 window.snaxReCaptchaOnloadCallback = function() { 1191 renderLoginReCaptcha(); 1192 }; 1193 1194 if ('30' === ver) { 1195 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render='+siteKey+'" async defer>'); 1196 } else { 1197 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render=explicit" async defer>'); 1198 } 1199 } 1200 }; 1201 1202 var reCaptchaEnteredCorrectly = function(response) { 1203 reCaptchaToken = response; 1204 }; 1205 1206 // Fire. 1207 $(document).ready(function () { 1208 ctx.loginForm(); 1209 }); 1210 1211})(jQuery, snax); 1212 1213/**************** 1214 * 1215 * Module: Popup 1216 * 1217 ****************/ 1218 1219(function ($, ctx) { 1220 1221 'use strict'; 1222 1223 var selectors = { 1224 'popupContent': '#snax-popup-content', 1225 'loginRequired': '.snax-login-required', 1226 'usernameField': '#user_login' 1227 }; 1228 1229 ctx.popupSelectors = selectors; 1230 1231 ctx.popup = function () { 1232 if (!$.fn.magnificPopup) { 1233 return; 1234 } 1235 1236 $(selectors.loginRequired).click(function (e) { 1237 e.preventDefault(); 1238 1239 var $content = $(selectors.popupContent); 1240 1241 ctx.openPopup($content, {}, 'login'); 1242 1243 // Delay till popup opens. 1244 setTimeout(function() { 1245 $content.find(selectors.usernameField).focus(); 1246 }, 100); 1247 }); 1248 1249 $('body').on('snaxLoginRequired', function(e, blocked) { 1250 blocked = blocked || false; 1251 1252 var $content = $(selectors.popupContent); 1253 1254 ctx.openPopup($content, { 1255 'closeOnBgClick': blocked 1256 }, 'login'); 1257 1258 // Delay till popup opens. 1259 setTimeout(function() { 1260 $content.find(selectors.usernameField).focus(); 1261 }, 100); 1262 1263 // Prevent default close action to not show page under the overlay when redirecting. 1264 if (blocked) { 1265 $.magnificPopup.instance.close = function() { 1266 window.location.href = ctx.config.site_url; 1267 }; 1268 } 1269 }); 1270 }; 1271 1272 ctx.openPopup = function ($content, args, action) { 1273 if (!$.fn.magnificPopup) { 1274 return; 1275 } 1276 1277 args = args || {}; 1278 1279 args.items = { 1280 src: $content, 1281 type: 'inline' 1282 }; 1283 1284 args.callbacks = { 1285 'open': function () { 1286 $('body').trigger('snaxPopupOpened', [ action ]); 1287 } 1288 }; 1289 1290 $.magnificPopup.open(args); 1291 }; 1292 1293 ctx.closePopup = function () { 1294 if (!$.fn.magnificPopup) { 1295 return; 1296 } 1297 1298 $.magnificPopup.close(); 1299 }; 1300 1301 ctx.redirectToLogin = function () { 1302 $(selectors.loginRequired).click(function (e) { 1303 window.location.href = ctx.config.login_url; 1304 }); 1305 1306 if (ctx.forceLoginPopup) { 1307 window.location.href = ctx.config.login_url; 1308 } 1309 1310 $('body').on('snaxLoginRequired', function(e) { 1311 window.location.href = ctx.config.login_url; 1312 }); 1313 1314 if (ctx.getUrlParameter(ctx.config.login_popup_url_var)){ 1315 window.location.href = ctx.config.login_url; 1316 } 1317 }; 1318 1319 // fire 1320 $(document).ready(function () { 1321 if (ctx.config.enable_login_popup) { 1322 ctx.popup(); 1323 1324 if (ctx.getUrlParameter(ctx.config.login_popup_url_var) && !ctx.config.logged_in){ 1325 ctx.loginRequired(); 1326 } 1327 1328 if (ctx.forceLoginPopup) { 1329 ctx.loginRequired(ctx.forceLoginPopup.blocked); 1330 } 1331 } else { 1332 ctx.redirectToLogin(); 1333 } 1334 }); 1335 1336})(jQuery, snax); 1337 1338 1339/************************* 1340 * 1341 * Module: Actions Menu 1342 * 1343 ************************/ 1344 1345(function ($, ctx) { 1346 1347 'use strict'; 1348 1349 var selectors = { 1350 'actions' : '.snax-actions', 1351 'actionsToggle': '.snax-actions-toggle', 1352 'actionsExpanded': '.snax-actions-expanded' 1353 }; 1354 1355 var classes = { 1356 'expanded': 'snax-actions-expanded' 1357 }; 1358 1359 ctx.actionsMenuSelectors = selectors; 1360 ctx.actionsMenuClasses = classes; 1361 1362 ctx.actionsMenu = function () { 1363 var $body = $('body'); 1364 1365 $('body').on('click', selectors.actionsToggle, function(e) { 1366 e.preventDefault(); 1367 1368 var $toggle = $(e.target); 1369 1370 $toggle.parents(selectors.actions).toggleClass(classe
1370s.expanded); 1371 }); 1372 1373 // Hide on focus out. 1374 $body.on('click touchstart', function (e) { 1375 var $activeMenu = $(e.target).parents(selectors.actions); 1376 1377 // Collapse all expanded menus except active one. 1378 $(selectors.actionsExpanded).not($activeMenu).removeClass(classes.expanded); 1379 }); 1380 }; 1381 1382 // Fire. 1383 $(document).ready(function () { 1384 ctx.actionsMenu(); 1385 }); 1386 1387})(jQuery, snax); 1388 1389 1390/************************* 1391 * 1392 * Module: Item Share 1393 * 1394 ************************/ 1395 1396(function ($, ctx) { 1397 1398 'use strict'; 1399 1400 var selectors = { 1401 'wrapper' : '.snax-item-share', 1402 'toggle': '.snax-item-share-toggle', 1403 'expandedState': '.snax-item-share-expanded' 1404 }; 1405 1406 var classes = { 1407 'expanded': 'snax-item-share-expanded' 1408 }; 1409 1410 ctx.itemShareSelectors = selectors; 1411 ctx.itemShareClasses = classes; 1412 1413 ctx.itemShare = function () { 1414 // On none touchable devices, shares visibility is handled via css :hover. 1415 // On touch devices there is no "hover", so we emulate hover via CSS class toggle on click. 1416 $(selectors.toggle).on('click touchstart', function (e) { 1417 e.preventDefault(); 1418
1419 $(this).parents(selectors.wrapper).addClass(classes.expanded); 1420 }); 1421 1422 // Hide shares on focus out. 1423 $('body').on('click touchstart', function (e) { 1424 var $activeElem = $(e.target).parents(selectors.expandedState); 1425 1426 // Collapse all expanded micro shares except active one. 1427 $(selectors.expandedState).not($activeElem).removeClass(classes.expanded); 1428 }); 1429 1430 $('.snax-share-twitter').on('click', function (e) { 1431 e.preventDefault(); 1432 window.open($(this).attr('href'), 'Google', 'width=500,height=300'); 1433 }); 1434 $('.snax-share-pinterest').on('click', function (e) { 1435 e.preventDefault(); 1436 window.open($(this).attr('href'), 'Google', 'width=700,height=670'); 1437 }); 1438 }; 1439 1440 // Fire. 1441 $(document).ready(function () { 1442 if (ctx.isTouchDevice()) { 1443 $('body').removeClass('snax-hoverable'); 1444 1445 } 1446 1447 ctx.itemShare(); 1448 }); 1449 1450})(jQuery, snax); 1451 1452 1453/************************* 1454 * 1455 * Module: Delete Item 1456 * 1457 ************************/ 1458 1459(function ($, ctx) { 1460 1461 'use strict'; 1462 1463 /** CONFIG *******************************************/ 1464 1465 // Register new component. 1466 ctx.deleteItemModule = {}; 1467 1468 // Component namespace shortcut. 1469 var c = ctx.deleteItemModule; 1470 1471 // CSS selectors. 1472 var selectors = { 1473 'deleteLink': '.snax-delete-item' 1474 }; 1475 1476 var i18n = { 1477 'confirm': 'Are you sure?' 1478 }; 1479 1480 // Allow accessing 1481 c.selectors = selectors; 1482 c.i18n = i18n; 1483 1484 /** INIT *******************************************/ 1485 1486 c.init = function () { 1487 c.attachEventHandlers(); 1488 }; 1489 1490 /** EVENTS *****************************************/ 1491 1492 c.attachEventHandlers = function() { 1493 1494 /* Delete item */ 1495 1496 $(selectors.deleteLink).on('click', function (e) { 1497 e.preventDefault(); 1498 1499 if (!confirm(i18n.confirm)) { 1500 return; 1501 } 1502 1503 ctx.deleteItem($(this), function(res) { 1504 if (res.status === 'success') { 1505 location.href = res.args.redirect_url; 1506 } else { 1507 alert(res.message); 1508 } 1509 }); 1510 }); 1511 }; 1512 1513 // Fire. 1514 $(document).ready(function () { 1515 c.init(); 1516 }); 1517 1518})(jQuery, snax); 1519 1520 1521/******************************* 1522 * 1523 * Module: Set Item as Featured 1524 * 1525 ******************************/ 1526 1527(function ($, ctx) { 1528 1529 'use strict'; 1530 1531 /** CONFIG *******************************************/ 1532 1533 // Register new component. 1534 ctx.setItemAsFeaturedModule = {}; 1535 1536 // Component namespace shortcut. 1537 var c = ctx.setItemAsFeaturedModule; 1538 1539 // CSS selectors. 1540 var selectors = { 1541 'link': '.snax-set-item-as-featured' 1542 }; 1543 1544 // Allow accessing 1545 c.selectors = selectors; 1546 1547 /** INIT *******************************************/ 1548 1549 c.init = function () { 1550 c.attachEventHandlers(); 1551 }; 1552 1553 /** EVENTS *****************************************/ 1554 1555 c.attachEventHandlers = function() { 1556 1557 /* Delete item */ 1558 1559 $(selectors.link).on('click', function (e) { 1560 e.preventDefault(); 1561 1562 ctx.setItemAsFeatured($(this), function(res) { 1563 if (res.status === 'success') { 1564 location.reload(); 1565 } else { 1566 alert(res.message); 1567 } 1568 }); 1569 }); 1570 }; 1571 1572 // Fire. 1573 $(document).ready(function () { 1574 c.init(); 1575 }); 1576 1577})(jQuery, snax); 1578 1579/******************************* 1580 * 1581 * Module: Froala for items 1582 * 1583 ******************************/ 1584 1585(function ($, ctx) { 1586 1587 'use strict'; 1588 1589 /** CONFIG *******************************************/ 1590 1591 // Register new component. 1592 ctx.froalaSimple = {}; 1593 1594 // Component namespace shortcut. 1595 var c = ctx.froalaSimple; 1596 1597 // CSS selectors. 1598 var selectors = { 1599 'froalaEditor': '.froala-editor-simple' 1600 }; 1601 1602 // Allow accessing 1603 c.selectors = selectors; 1604 1605 /** INIT *******************************************/ 1606 1607 c.init = function () { 1608 c.attachEventHandlers(); 1609 c.applyFroala(); 1610 }; 1611 1612 /** EVENTS *****************************************/ 1613 1614 c.attachEventHandlers = function() { 1615 $( 'body' ).on( 'snaxNewCardAdded', function($card) { 1616 c.applyFroala(); 1617 }); 1618 }; 1619 1620 c.renderFroala = function() { 1621 var $textarea = $(this); 1622 var config = { 1623 'key': 'CMFIZJNKLDXIREJI==', 1624 'language': c.getFroalaEditorConfig('language'), 1625 'heightMin': 200, 1626 // Toolbar buttons on large devices (≥ 1200px). 1627 'toolbarButtons': ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1628 // On medium devices (≥ 992px). 1629 toolbarButtonsMD: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1630 // On small devices (≥ 768px). 1631 toolbarButtonsSM: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1632 // On extra small devices (< 768px). 1633 toolbarButtonsXS: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1634 charCounterMax: c.getFroalaEditorMaxCharacters($textarea) 1635 }; 1636 // Override Froala's config using this filter function. 1637 if (typeof c.froalaEditorConfig === 'function') { 1638 config = c.froalaEditorConfig(config); 1639 } 1640 if (snax.inDebugMode()) { 1641 snax.log(config); 1642 } 1643 // Init. 1644 $textarea.froalaEditor(config); 1645 }; 1646 1647 c.getFroalaEditorConfig = function(id) { 1648 var config = c.config.froala; 1649 1650 if (typeof config[id] !== 'undefined') { 1651 return config[id]; 1652 } 1653 1654 return null; 1655 }; 1656 1657 c.getFroalaEditorMaxCharacters = function($editor) { 1658 var maxCharacters = parseInt($editor.attr('maxlength'), 10); 1659 1660 return maxCharacters > 0 ? maxCharacters : -1; 1661 }; 1662 1663 c.applyFroala = function () { 1664 1665 // Check if Froals script is loaded. 1666 if (!$.fn.froalaEditor) { 1667 return; 1668 } 1669 $(selectors.froalaEditor).each(c.renderFroala); 1670 }; 1671 1672 // Fire. 1673 $(document).ready(function () { 1674 if (snax.frontendSubmission) { 1675 c.config = snax.frontendSubmission.config; 1676 } 1677 if (snax.post) { 1678 c.config = snax.post.config; 1679 } 1680 if (typeof c.config === 'undefined') { 1681 return; 1682 } 1683 c.init(); 1684 }); 1685 1686})(jQuery, snax); 1687 1688/******************************* 1689 * 1690 * Module: Single item comments 1691 * 1692 ******************************/ 1693 1694(function ($, ctx) { 1695 1696 'use strict'; 1697 1698 /** CONFIG *******************************************/ 1699 1700 // Register new component. 1701 ctx.singleItemComments = {}; 1702 1703 // Component namespace shortcut. 1704 var c = ctx.singleItemComments; 1705 1706 var seeMoreHtml = '<a class="snax-see-all-replies">' + ctx.config.i18n.see_all_replies + '</a>'; 1707 1708 // CSS selectors. 1709 var selectors = { 1710 'section': '.snax-item-comments', 1711 'topLevelComment': '.snax-item-comments .depth-1', 1712 'moreCommentsLink': '.snax-item-comments-more-link', 1713 'allRepliesLink': '.snax-see-all-replies', 1714 'list': '.comment-list', 1715 'commentForm': '.snax-item-comments .comment-form', 1716 'textarea': '.snax-item-comments .comment-form textarea', 1717 'submitButton': '.submit', 1718 'cancelReply': '#cancel-comment-reply-link', 1719 'respondSection': '#respond', 1720 'replyLogin': '.comment-reply-login', 1721 'respondLogin': '.snax-comment-login', 1722 'authorFields': '.snax-item-comment-autor', 1723 }; 1724
1725 var $spinner = $('<div class="snax-upload-icon"></div>'); 1726 1727 // Allow accessing 1728 c.selectors = selectors; 1729 1730 /** INIT *******************************************/ 1731 1732 c.init = function () { 1733 $(selectors.submitButton, selectors.commentForm).attr('disabled', true); 1734 c.renderSeeMore(); 1735 c.attachEventHandlers(); 1736 }; 1737 1738 /** EVENTS *****************************************/ 1739 1740 c.renderSeeMore = function() { 1741 $(selectors.topLevelComment).each(function( index ) { 1742 if($('.children li', this).length>1 && $(this).attr('appendedSeeMoreLink')!== 'true'){ 1743 $(this).append(seeMoreHtml); 1744 $(this).attr('appendedSeeMoreLink','true'); 1745 } 1746 }); 1747 $(selectors.allRepliesLink).on('click', function (e) { 1748 var $parent = $(this).closest(selectors.topLevelComment); 1749 $('.children', $parent).addClass('children-visible'); 1750 $(this).remove(); 1751 }); 1752 }; 1753 1754 //overrides core comment-reply.js to handle multiple forms 1755 c.overrideDefaultHandler = function() { 1756 if (typeof addComment === 'undefined') { 1757 return; 1758 } 1759 1760 addComment.moveForm = function( commId, parentId, respondId, postId ) { 1761 var div, element, style, cssHidden, 1762 t = this, 1763 comm = t.I( commId ), 1764 respond = t.I( respondId ), 1765 cancel = respond.querySelector( '#cancel-comment-reply-link' ), 1766 parent = respond.querySelector( '#comment_parent' ), 1767 post = respond.querySelector( '#comment_post_ID' ), 1768 commentForm = respond.getElementsByTagName( 'form' )[0]; 1769 1770 if ( ! comm || ! respond || ! cancel || ! parent || ! commentForm ) { 1771 return; 1772 } 1773 1774 t.respondId = respondId; 1775 postId = postId || false; 1776 1777 if ( ! t.I( 'wp-temp-form-div' ) ) { 1778 div = document.createElement( 'div' ); 1779 div.id = 'wp-temp-form-div'; 1780 div.style.display = 'none'; 1781 respond.parentNode.insertBefore( div, respond ); 1782 } 1783 1784 comm.parentNode.insertBefore( respond, comm.nextSibling ); 1785 if ( post && postId ) { 1786 post.value = postId; 1787 } 1788 parent.value = parentId; 1789 cancel.style.display = ''; 1790 1791 cancel.onclick = function() { 1792 var t = addComment, 1793 temp = t.I( 'wp-temp-form-div' ), 1794 respond = t.I( t.respondId ); 1795 1796 if ( ! temp || ! respond ) { 1797 return; 1798 } 1799 1800 parent.value = '0'; 1801 temp.parentNode.insertBefore( respond, temp ); 1802 temp.parentNode.removeChild( temp ); 1803 this.style.display = 'none'; 1804 this.onclick = null; 1805 return false; 1806 }; 1807 1808 /* 1809 * Set initial focus to the first form focusable element. 1810 * Try/catch used just to avoid errors in IE 7- which return visibility 1811 * 'inherit' when the visibility value is inherited from an ancestor. 1812 */ 1813 try { 1814 for ( var i = 0; i < commentForm.elements.length; i++ ) { 1815 element = commentForm.elements[i]; 1816 cssHidden = false; 1817 1818 // Modern browsers. 1819 if ( 'getComputedStyle' in window ) { 1820 style = window.getComputedStyle( element ); 1821 // IE 8. 1822 } else if ( document.documentElement.currentStyle ) { 1823 style = element.currentStyle; 1824 } 1825 1826 /* 1827 * For display none, do the same thing jQuery does. For visibility, 1828 * check the element computed style since browsers are already doing 1829 * the job for us. In fact, the visibility computed style is the actual 1830 * computed value and already takes into account the element ancestors. 1831 */ 1832 if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) { 1833 cssHidden = true; 1834 } 1835 1836 // Skip form elements that are hidden or disabled. 1837 if ( 'hidden' === element.type || element.disabled || cssHidden ) { 1838 continue; 1839 } 1840 1841 element.focus(); 1842 // Stop after the first focusable element. 1843 break; 1844 } 1845 1846 } catch( er ) {} 1847 1848 return false;
1849 }; 1850 1851 addComment.I = function( id ) { 1852 return document.getElementById( id ); 1853 }; 1854 }; 1855 1856 c.attachEventHandlers = function() { 1857 1858 var loadMoreComments = function($section) { 1859 var $that = $(selectors.moreCommentsLink, $section); 1860 var postId = $section.attr('data-snax-post-id'); 1861 var loadedPages = parseInt($section.attr('data-snax-loaded-pages'),10); 1862 var $list = $(selectors.list, $section); 1863 var requestData = { 1864 'action': 'snax_load_more_item_comments', 1865 'snax_item_id': postId, 1866 'loaded_pages': loadedPages 1867 }; 1868 1869 $spinner.appendTo($list); 1870 1871 var xhr = $.ajax({ 1872 'type': 'POST', 1873 'url': ctx.config.ajax_url, 1874 'dataType': 'json', 1875 'data': requestData 1876 }); 1877 1878 xhr.done(function (res) { 1879 if ('success' === res.status) { 1880 $spinner.remove(); 1881 if (res.args.html) { 1882 $list.append(res.args.html); 1883 removeDuplicateComments($list); 1884 maybeRemoveSeeMoreLink($list,$that); 1885 c.renderSeeMore(); 1886 $section.attr('data-snax-loaded-pages',loadedPages + 1); 1887 1888 addComment.init(); 1889 } 1890 } 1891 }); 1892 }; 1893 1894 var removeDuplicateComments = function($list){ 1895 var seen = {}; 1896 $(selectors.topLevelComment, $list).each(function( index ) { 1897 var id = $(this).attr('id'); 1898 if (seen[id]) { 1899 $(this).remove(); 1900 } else { 1901 seen[id] = true; 1902 } 1903 }); 1904 }; 1905 1906 var maybeRemoveSeeMoreLink = function($list,$link){ 1907 var topLevels = $('.depth-1', $list).length; 1908 var topLevelComments = parseInt( $list.attr('data-snax-top-level-comments'), 10 ); 1909 if (topLevels >= topLevelComments){ 1910 $link.remove(); 1911 } 1912 }; 1913 1914 $(selectors.moreCommentsLink).on('click', function (e) { 1915 e.preventDefault(); 1916 var $section = $(this).closest(selectors.section); 1917 loadMoreComments($section); 1918 }); 1919 1920 $(selectors.commentForm).on('submit', function(e) { 1921 e.preventDefault(); 1922 var $form = $(this); 1923 var url = $form.attr('action'); 1924 var commentParent = '0'; 1925 var $textarea = $('textarea', $form); 1926 var $section = $form.closest(selectors.section); 1927 var $list = $(selectors.list, $section); 1928 var $respondSection = $form.closest(selectors.respondSection); 1929 var $cancelButton = $(selectors.cancelReply, $respondSection); 1930 var $submitButton = $(selectors.submitButton, $form); 1931 var requestData = {}; 1932 1933 $.each($form.serializeArray(), function(i, field) { 1934 requestData[field.name] = field.value; 1935 }); 1936 1937 requestData['is_ajax_item_comment_form'] = true; 1938 commentParent = requestData['comment_parent']; 1939 1940 if (commentParent === '0') { 1941 $spinner.prependTo($list); 1942 } else { 1943 var $commentParent = $('#comment-' + commentParent); 1944 var $commentParentChildren = $('#comment-' + commentParent + '> .children'); 1945 if (! $commentParentChildren.length) { 1946 $commentParent.append('<ul class="children"></ul>'); 1947 $commentParentChildren = $('#comment-' + commentParent + '> .children'); 1948 } 1949 $spinner.prependTo($commentParentChildren); 1950 } 1951 1952 var xhr = $.ajax({ 1953 'type': 'POST', 1954 'url': url, 1955 'data': requestData 1956 }); 1957 1958 xhr.done(function (res) { 1959 $textarea.val(''); 1960 $cancelButton.trigger('click'); 1961 $submitButton.attr('disabled', true); 1962 if (res.indexOf('<body id="error-page">') != -1){ 1963 res = $(res).filter('p')[1]; 1964 } 1965 $spinner.replaceWith(res); 1966 if (commentParent !== '0') { 1967 var $parent = $commentParentChildren.closest(selectors.topLevelComment); 1968 $('.children', $parent).addClass('children-visible'); 1969 } 1970 }); 1971 1972 xhr.fail(function( jqXHR, textStatus ) { 1973 if(jqXHR.status===409){ 1974 alert(ctx.config.i18n.duplicate_comment); 1975 }else{ 1976 alert(ctx.config.i18n.comment_fail); 1977 } 1978 $spinner.remove(); 1979 }); 1980 }); 1981
1982 var validateForm = function( $form ) { 1983 var $submitButton = $(selectors.submitButton, $form); 1984 var $author = $(selectors.authorFields, $form); 1985 var $input = $('textarea', $form); 1986 var disable = false; 1987 if ( $author.length > 0 ){ 1988 var $authorName = $('#author', $author); 1989 var $authorMail = $('#email', $author); 1990 if ( $authorName.val() === '' || $authorMail.val() === '' ) { 1991 disable = true; 1992 } 1993 } 1994 if ($input.val() === '') { 1995 disable = true; 1996 } 1997 $submitButton.attr('disabled', disable); 1998 }; 1999 2000 $(selectors.textarea).keyup(function() { 2001 var $parent = $(this).closest(selectors.commentForm); 2002 var $author = $(selectors.authorFields, $parent); 2003 while($(this).outerHeight() < this.scrollHeight + parseFloat($(this).css('borderTopWidth')) + parseFloat($(this).css('borderBottomWidth'))) { 2004 $(this).height($(this).height()+1); 2005 } 2006 if ($(this).val()){ 2007 $author.show(); 2008 $parent.removeClass('snax-comment-form-collapsed').addClass('snax-comment-form-extended'); 2009 } else { 2010 $author.hide(); 2011 $parent.removeClass('snax-comment-form-extended').addClass('snax-comment-form-collapsed'); 2012 } 2013 validateForm($parent); 2014 }); 2015 2016 $(selectors.authorFields).keyup(function() { 2017 var $parent = $(this).closest(selectors.commentForm); 2018 validateForm($parent); 2019 }); 2020 2021 $(selectors.replyLogin).click(function(e){ 2022 e.preventDefault(); 2023 ctx.loginRequired(); 2024 }); 2025 $(selectors.respondLogin).click(function(e){ 2026 e.preventDefault(); 2027 ctx.loginRequired(); 2028 }); 2029 }; 2030 2031 // Fire. 2032 $(document).ready(function () { 2033 c.init(); 2034 c.overrideDefaultHandler(); 2035 }); 2036 2037})(jQuery, snax); 2038 2039/************************* 2040 * 2041 * Module: Delete Post 2042 * 2043 ************************/ 2044 2045(function ($, ctx) { 2046 2047 'use strict'; 2048 2049 /** CONFIG *******************************************/ 2050 2051 // Register new component. 2052 ctx.deletePostModule = {}; 2053 2054 // Component namespace shortcut. 2055 var c = ctx.deletePostModule; 2056 2057 // CSS selectors. 2058 var selectors = { 2059 'deleteLink': '.snax-delete-post' 2060 }; 2061 2062 // Allow accessing 2063 c.selectors = selectors; 2064 2065 /** INIT *******************************************/ 2066 2067 c.init = function () { 2068 c.attachEventHandlers(); 2069 }; 2070 2071 /** EVENTS *****************************************/ 2072 2073 c.attachEventHandlers = function() { 2074 2075 /* Delete item */ 2076 2077 $(selectors.deleteLink).on('click', function (e) { 2078 e.preventDefault(); 2079 var $link = $(this); 2080 2081 if (!confirm(ctx.config.i18n.are_you_sure)) { 2082 return; 2083 } 2084 2085 var $item = $link.parents('.g1-collection-item'); 2086 2087 if ( $item.length === 0 ) { 2088 var $item = $link.parents('article.post'); 2089 } 2090 2091 $item.addClass('snax-post-removing'); 2092 2093 ctx.deletePost($link, function(res) { 2094 if (res.status === 'success') { 2095 var redirectUrl = $link.attr('href'); 2096 2097 // Redirect and show status note on top of a target page. 2098 if (redirectUrl.length > 0) { 2099 var queryVarDelimiter = redirectUrl.indexOf('?') === -1 ? '?' : '&'; 2100 2101 window.location.href = redirectUrl + queryVarDelimiter + ctx.config.delete_status_var + '=success'; 2102 // Remove DOM element and show notification. 2103 } else { 2104 $item.addClass('snax-post-removed'); 2105 $item.slideToggle(750); 2106 2107 setTimeout(function() { 2108 snax.notifications.add(res.message); 2109 }, 1500); 2110 } 2111 2112 } else { 2113 ctx.notifications.add(res.message); 2114 } 2115 }); 2116 }); 2117 }; 2118 2119 // Fire. 2120 $(document).ready(function () { 2121 c.init(); 2122 }); 2123 2124})(jQuery, snax); 2125 2126/******************************* 2127 * 2128 * Module: Password Reset 2129 * 2130 ******************************/ 2131 2132(function ($, ctx) { 2133 2134 'use strict'; 2135 2136 /** CONFIG *******************************************/ 2137 2138 // Register new component. 2139 ctx.passwordReset = {}; 2140 2141 // Component namespace shortcut. 2142 var c = ctx.passwordReset; 2143 2144 // CSS selectors. 2145 var selectors = { 2146 'resetPasswordForm': '#snax_reset_password_form', 2147 'newPassword': '#snax_reset_password_form #new_password', 2148 'repeatPassword': '#snax_reset_password_form #repeat_password',
2149 'cookieName': '#snax_reset_password_form .rp-cookie-name', 2150 'cookieValue': '#snax_reset_password_form .rp-cookie_value', 2151 'resetErrorMessage': '#snax_reset_password_form .snax-reset-pass-error-message', 2152 'resetSuccessMessage': '#snax_reset_password_form .snax-reset-pass-success-message', 2153 }; 2154 2155 // Allow accessing 2156 c.selectors = selectors; 2157 2158 /** INIT *******************************************/ 2159 2160 c.init = function () { 2161 c.attachEventHandlers(); 2162 }; 2163 2164 /** EVENTS *****************************************/ 2165 2166 c.attachEventHandlers = function() { 2167 c.matchPasswords(); 2168 c.handleForm(); 2169 }; 2170 2171 c.matchPasswords = function() { 2172 var $newPassword = $(selectors.newPassword), 2173 $repeatPassword = $(selectors.repeatPassword); 2174 2175 $repeatPassword.on('change', function(e) { 2176 if ($newPassword.val() !== $repeatPassword.val()){ 2177 $repeatPassword.get(0).setCustomValidity( ctx.config.i18n.passwords_dont_match); 2178 } else { 2179 $repeatPassword.get(0).setCustomValidity(''); 2180 } 2181 }); 2182 } 2183 2184 c.handleForm = function() { 2185 var $form = $(selectors.resetPasswordForm), 2186 $cookieName = $(selectors.cookieName), 2187 $cookieValue = $(selectors.cookieValue), 2188 $newPassword = $(selectors.newPassword), 2189 $errorMessage = $(selectors.resetErrorMessage), 2190 $successMessage = $(selectors.resetSuccessMessage); 2191 2192 $form.on('submit', function(e) { 2193 2194 e.preventDefault(); 2195 e.stopPropagation(); 2196 e.stopImmediatePropagation(); 2197 2198 $errorMessage.text(''); 2199 $successMessage.text(''); 2200 2201 ctx.createCookie($cookieName.val(), $cookieValue.val(), 24); 2202 2203 var xhr = $.ajax({ 2204 'type': 'POST', 2205 'url': $form.attr('action') + '?action=resetpass', 2206 'data': $form.serialize(), 2207 }); 2208 2209 xhr.done(function (res) { 2210 if ($('.reset-pass', res).length){ 2211 $successMessage.html( '<p class="snax-validation-tip">' + ctx.config.i18n.password_set + '</p>' ); 2212 } 2213 if ($('#login_error', res).length){ 2214 $errorMessage.html( '<p class="snax-validation-tip">' + ctx.config.i18n.link_invalid + '</p>' ); 2215 } 2216 }); 2217 2218 return false; 2219 }); 2220 }; 2221 2222 // Fire. 2223 $(document).ready(function () { 2224 if ($(selectors.resetPasswordForm).length){ 2225 c.init(); 2226 } 2227 }); 2228 2229})(jQuery, snax); 2230 2231/******************************* 2232 * 2233 * Module: BuddyPress registration captcha 2234 * 2235 ******************************/ 2236 2237(function ($, ctx) { 2238 2239 'use strict'; 2240 2241 var useReCaptcha; 2242 var reCaptchaToken; 2243 2244 ctx.registerForm = function () { 2245 useReCaptcha = ctx.config.use_login_recaptcha; 2246 if (useReCaptcha && $('#buddypress #register-page').length >0) { 2247 loadReCaptcha(); 2248 $('#signup_submit').attr('disabled', true); 2249 } 2250 }; 2251 2252 var loadReCaptcha = function() { 2253 var apiUrl = ctx.config.recaptcha_api_url; 2254 var ver = ctx.config.recaptcha_version; 2255 var siteKey = ctx.config.recaptcha_site_key; 2256 2257 if (!siteKey || !apiUrl) { 2258 return; 2259 } 2260 2261 var renderLoginReCaptcha = function() { 2262 if ('30' === ver) { 2263 grecaptcha.execute( 2264 siteKey, 2265 { action: 'login' } 2266 ).then( function( token ) { 2267 $('#snax-register-recaptcha').html('<input type="hidden" name="g-recaptcha-response" value="'+ token +'">'); 2268 reCaptchaEnteredCorrectly(token); 2269 } ); 2270 } else { 2271 grecaptcha.render('snax-register-recaptcha', { 2272 'sitekey' : siteKey, 2273 'callback': reCaptchaEnteredCorrectly 2274 }); 2275 } 2276 }; 2277 2278 // Google reCaptcha API loaded. 2279 if (typeof grecaptcha !== 'undefined') { 2280 renderLoginReCaptcha(); 2281 } else { 2282 // API not loaded. Register callback and load script. 2283 window.snaxReCaptchaOnloadCallback = function() { 2284 renderLoginReCaptcha(); 2285 }; 2286 2287 if ('30' === ver) { 2288 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render='+siteKey+'" async defer>'); 2289 } else { 2290 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render=explicit" async defer>'); 2291 } 2292 } 2293 }; 2294 2295 var reCaptchaEnteredCorrectly = function(response) { 2296 reCaptchaToken = response; 2297 $('#signup_submit').attr('disabled', false); 2298 }; 2299 2300 // Fire. 2301 $(document).ready(function () { 2302 ctx.registerForm(); 2303 }); 2304 2305})(jQuery, snax); 2306 2307/******************************* 2308 * 2309 * Module: Notifications 2310 * 2311 ******************************/ 2312 2313(function ($, ctx) { 2314 2315 'use strict'; 2316 2317 var api = {}; // Public API. 2318 var timeout = 5000; 2319 var $notifications = false;
2320 var $notificationTpl = false; 2321 2322 var selectors = { 2323 'notifications': '.snax-notifications', 2324 'notification': '.snax-notification', 2325 'notificationText': '.snax-notification-text', 2326 'notificationClose':'.snax-notification-close' 2327 }; 2328 2329 var classes = { 2330 'on': 'snax-notifications-on', 2331 'off': 'snax-notifications-off', 2332 }; 2333 2334 api.add = function(html) { 2335 var $notification = $notificationTpl.clone(true); 2336 $notification.find(selectors.notificationText).html(html); 2337 2338 $notification.on('click', selectors.notificationClose, function() { 2339 api.remove($notification); 2340 }); 2341 2342 // Auto-remove. 2343 setTimeout(function() { 2344 api.remove($notification); 2345 }, timeout); 2346 2347 $notifications.prepend($notification); 2348 $notifications.removeClass(classes.off).addClass(classes.on); 2349 }; 2350 2351 api.remove = function($notification) { 2352 $notification.addClass('snax-notification-removed'); 2353 setTimeout(function() { 2354 $notification.remove(); 2355 2356 if (api.isQueueEmpty()) { 2357 $notifications.removeClass(classes.on).addClass(classe
2357s.off); 2358 } 2359 }, 5000); 2360 }; 2361 2362 api.isQueueEmpty = function() { 2363 return $notifications.find(selectors.notification).length === 0; 2364 }; 2365 2366 var initNotifications = function() { 2367 $notifications = $(selectors.notifications); 2368 2369 if ($notifications.length === 0) { 2370 return; 2371 } 2372 2373 // Store notification template and rmeove it from DOM. 2374 $notificationTpl = $notifications.find(selectors.notification).detach(); 2375 2376 // Enable API. 2377 ctx.notifications = api; 2378 }; 2379 2380 // Fire. 2381 $(document).ready(function () { 2382 initNotifications(); 2383 }); 2384 2385})(jQuery, snax); 2386 2387/******************************** 2388 * 2389 * Social Login GDPR integration 2390 * 2391 *******************************/ 2392 2393(function ($) { 2394 2395 'use strict'; 2396 2397 $(document).ready(function () { 2398 $('.snax-gdpr-consent-form').each(function() { 2399 var $form = $(this); 2400 var $links = $form.parent().find('.snax-social-login-links'); 2401 var $link; // A requested social login link. 2402 2403 // Toggle GDPR and Social Links visibility. 2404 var toggleVisiblity = function() { 2405 $form.toggleClass('snax-gdpr-consent-form-hidden snax-gdpr-consent-form-visible'); 2406 $links.toggleClass('snax-social-login-links-visible snax-social-login-links-hidden'); 2407 }; 2408 2409 // Social login link requested. 2410 $links.on('click', 'a.snax-social-login', function(e) { 2411 e.preventDefault(); 2412 2413 $link = $(this); 2414 2415 toggleVisiblity(); 2416 }); 2417 2418 // Accept GDPR. 2419 $form.on('click', '.snax-slog-gdpr-accept', function(e) { 2420 e.preventDefault(); 2421 2422 window.location.href = $link.attr('href'); 2423 }); 2424 2425 // Cancel GDPR. 2426 $form.on('click', '.snax-slog-gdpr-cancel', function(e) { 2427 e.preventDefault(); 2428 2429 toggleVisiblity(); 2430 }); 2431 }); 2432 }); 2433 2434})(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.