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 ctx.openPopup = function ($content, args, action) { 1224 if (!$.fn.magnificPopup) { 1225 return; 1226 } 1227 1228 args = args || {}; 1229 1230 // Set translatable label. 1231 if (!args.tClose) { 1232 args.tClose = ctx.config.i18n.popup_close_label; 1233 } 1234 1235 args.items = { 1236 src: $content, 1237 type: 'inline' 1238 }; 1239 1240 args.callbacks = { 1241 'open': function () { 1242 $('body').trigger('snaxPopupOpened', [ action ]); 1243 } 1244 }; 1245 1246 $.magnificPopup.open(args); 1247 1248 // Override the Close action. 1249 if (typeof args.onClose === 'function') { 1250 $.magnificPopup.instance.close = args.onClose; 1251 } 1252 }; 1253 1254 ctx.closePopup = function () { 1255 if (!$.fn.magnificPopup) { 1256 return; 1257 } 1258 1259 $.magnificPopup.close(); 1260 }; 1261 1262})(jQuery, snax); 1263 1264/*********************** 1265 * 1266 * Module: Login Popup 1267 * 1268 **********************/ 1269 1270(function ($, ctx) { 1271 1272 'use strict'; 1273 1274 var selectors = { 1275 'popupContent': '#snax-popup-content', 1276 'usernameField': '#user_login' 1277 }; 1278 1279 ctx.openLoginPopup = function (onClose) { 1280 var $content = $(selectors.popupContent); 1281 1282 var args = {}; 1283 1284 if (typeof onClose === 'function') { 1285 args.onClose = onClose; 1286 } 1287 1288 ctx.openPopup($content, args, 'login'); 1289 1290 // Delay till popup opens. 1291 setTimeout(function() { 1292 $content.find(selectors.usernameField).focus(); 1293 }, 100); 1294 }; 1295 1296 ctx.redirectToLoginPage = function () { 1297 window.location.href = ctx.config.login_url; 1298 }; 1299 1300 // fire 1301 $(document).ready(function () { 1302 $('body').on('snaxLoginRequired', function(e, blocked) { 1303 var onClose = false;
1304 1305 if (blocked) { 1306 onClose = function() { 1307 window.location.href = ctx.config.site_url; 1308 }; 1309 } 1310 1311 // Snax Login Popup is ON. 1312 if (ctx.config.enable_login_popup) { 1313 ctx.openLoginPopup(onClose); 1314 1315 // Custom handler is defined. 1316 } else if ( typeof snaxLoginRequiredHandler === 'function' ) { 1317 snaxLoginRequiredHandler(onClose); 1318 1319 // Redirect as a fallback. 1320 } else { 1321 ctx.redirectToLoginPage(); 1322 } 1323 }); 1324 }); 1325 1326})(jQuery, snax); 1327 1328/******************************** 1329 * 1330 * Module: Login Required Action 1331 * 1332 *******************************/ 1333 1334(function ($, ctx) { 1335 1336 'use strict'; 1337 1338 $(document).ready(function () { 1339 // Login requested by click. 1340 $('.snax-login-required').click(function (e) { 1341 e.preventDefault(); 1342 1343 ctx.loginRequired(); 1344 }); 1345 1346 // Login requested by URL. 1347 if (ctx.getUrlParameter(ctx.config.login_popup_url_var) && !ctx.config.logged_in){ 1348 ctx.loginRequired(); 1349 } 1350 1351 // Login requested by setting var. 1352 if (ctx.forceLoginPopup) { 1353 ctx.loginRequired(ctx.forceLoginPopup.blocked); 1354 } 1355 }); 1356 1357})(jQuery, snax); 1358 1359/************************* 1360 * 1361 * Module: Actions Menu 1362 * 1363 ************************/ 1364 1365(function ($, ctx) { 1366 1367 'use strict'; 1368 1369 var selectors = { 1370 'actions' : '.snax-actions', 1371 'actionsToggle': '.snax-actions-toggle', 1372 'actionsExpanded': '.snax-actions-expanded' 1373 }; 1374 1375 var classes = { 1376 'expanded': 'snax-actions-expanded' 1377 }; 1378 1379 ctx.actionsMenuSelectors = selectors; 1380 ctx.actionsMenuClasses = classes; 1381 1382 ctx.actionsMenu = function () { 1383 var $body = $('body'); 1384 1385 $('body').on('click', selectors.actionsToggle, function(e) { 1386 e.preventDefault(); 1387 1388 var $toggle = $(e.target); 1389 1390 $toggle.parents(selectors.actions).toggleClass(classes.expanded); 1391 }); 1392 1393 // Hide on focus out. 1394 $body.on('click touchstart', function (e) { 1395 var $activeMenu = $(e.target).parents(selectors.actions); 1396 1397 // Collapse all expanded menus except active one. 1398 $(selectors.actionsExpanded).not($activeMenu).removeClass(classes.expanded); 1399 }); 1400 }; 1401 1402 // Fire. 1403 $(document).ready(function () { 1404 ctx.actionsMenu(); 1405 }); 1406 1407})(jQuery, snax); 1408 1409 1410/************************* 1411 * 1412 * Module: Item Share 1413 * 1414 ************************/ 1415 1416(function ($, ctx) { 1417 1418 'use strict'; 1419 1420 var selectors = { 1421 'wrapper' : '.snax-item-share', 1422 'toggle': '.snax-item-share-toggle', 1423 'expandedState': '.snax-item-share-expanded' 1424 }; 1425 1426 var classes = { 1427 'expanded': 'snax-item-share-expanded' 1428 }; 1429 1430 ctx.itemShareSelectors = selectors; 1431 ctx.itemShareClasses = classes; 1432 1433 ctx.itemShare = function () { 1434 // On none touchable devices, shares visibility is handled via css :hover. 1435 // On touch devices there is no "hover", so we emulate hover via CSS class toggle on click. 1436 $(selectors.toggle).on('click touchstart', function (e) { 1437 e.preventDefault(); 1438
1439 $(this).parents(selectors.wrapper).addClass(classes.expanded); 1440 }); 1441 1442 // Hide shares on focus out. 1443 $('body').on('click touchstart', function (e) { 1444 var $activeElem = $(e.target).parents(selectors.expandedState); 1445 1446 // Collapse all expanded micro shares except active one. 1447 $(selectors.expandedState).not($activeElem).removeClass(classes.expanded); 1448 }); 1449 }; 1450 1451 // Fire. 1452 $(document).ready(function () { 1453 if (ctx.isTouchDevice()) { 1454 $('body').removeClass('snax-hoverable'); 1455 1456 } 1457 1458 ctx.itemShare(); 1459 }); 1460 1461})(jQuery, snax); 1462 1463 1464/************************* 1465 * 1466 * Module: Delete Item 1467 * 1468 ************************/ 1469 1470(function ($, ctx) { 1471 1472 'use strict'; 1473 1474 /** CONFIG *******************************************/ 1475 1476 // Register new component. 1477 ctx.deleteItemModule = {}; 1478 1479 // Component namespace shortcut. 1480 var c = ctx.deleteItemModule; 1481 1482 // CSS selectors. 1483 var selectors = { 1484 'deleteLink': '.snax-delete-item' 1485 }; 1486 1487 var i18n = { 1488 'confirm': 'Are you sure?' 1489 }; 1490 1491 // Allow accessing 1492 c.selectors = selectors; 1493 c.i18n = i18n; 1494 1495 /** INIT *******************************************/ 1496 1497 c.init = function () { 1498 c.attachEventHandlers(); 1499 }; 1500 1501 /** EVENTS *****************************************/ 1502 1503 c.attachEventHandlers = function() { 1504 1505 /* Delete item */ 1506 1507 $(selectors.deleteLink).on('click', function (e) { 1508 e.preventDefault(); 1509 1510 if (!confirm(i18n.confirm)) { 1511 return; 1512 } 1513 1514 ctx.deleteItem($(this), function(res) { 1515 if (res.status === 'success') { 1516 location.href = res.args.redirect_url; 1517 } else { 1518 alert(res.message); 1519 } 1520 }); 1521 }); 1522 }; 1523 1524 // Fire. 1525 $(document).ready(function () { 1526 c.init(); 1527 }); 1528 1529})(jQuery, snax); 1530 1531 1532/******************************* 1533 * 1534 * Module: Set Item as Featured 1535 * 1536 ******************************/ 1537 1538(function ($, ctx) { 1539 1540 'use strict'; 1541 1542 /** CONFIG *******************************************/ 1543 1544 // Register new component. 1545 ctx.setItemAsFeaturedModule = {}; 1546 1547 // Component namespace shortcut. 1548 var c = ctx.setItemAsFeaturedModule; 1549 1550 // CSS selectors. 1551 var selectors = { 1552 'link': '.snax-set-item-as-featured' 1553 }; 1554 1555 // Allow accessing 1556 c.selectors = selectors; 1557 1558 /** INIT *******************************************/ 1559 1560 c.init = function () { 1561 c.attachEventHandlers(); 1562 }; 1563 1564 /** EVENTS *****************************************/ 1565 1566 c.attachEventHandlers = function() { 1567 1568 /* Delete item */ 1569 1570 $(selectors.link).on('click', function (e) { 1571 e.preventDefault(); 1572 1573 ctx.setItemAsFeatured($(this), function(res) { 1574 if (res.status === 'success') { 1575 location.reload(); 1576 } else { 1577 alert(res.message); 1578 } 1579 }); 1580 }); 1581 }; 1582 1583 // Fire. 1584 $(document).ready(function () { 1585 c.init(); 1586 }); 1587 1588})(jQuery, snax); 1589 1590/******************************* 1591 * 1592 * Module: Froala for items 1593 * 1594 ******************************/ 1595 1596(function ($, ctx) { 1597 1598 'use strict'; 1599 1600 /** CONFIG *******************************************/ 1601 1602 // Register new component. 1603 ctx.froalaSimple = {}; 1604 1605 // Component namespace shortcut. 1606 var c = ctx.froalaSimple; 1607 1608 // CSS selectors. 1609 var selectors = { 1610 'froalaEditor': '.froala-editor-simple' 1611 }; 1612 1613 // Allow accessing 1614 c.selectors = selectors; 1615 1616 /** INIT *******************************************/ 1617 1618 c.init = function () { 1619 c.attachEventHandlers(); 1620 c.applyFroala(); 1621 }; 1622 1623 /** EVENTS *****************************************/ 1624 1625 c.attachEventHandlers = function() { 1626 $( 'body' ).on( 'snaxNewCardAdded', function($card) { 1627 c.applyFroala(); 1628 }); 1629 }; 1630 1631 c.renderFroala = function() { 1632 var $textarea = $(this); 1633 var config = { 1634 'key': 'CMFIZJNKLDXIREJI==', 1635 'language': c.getFroalaEditorConfig('language'), 1636 'heightMin': 200, 1637 // Sticky toolbar. 1638 'toolbarSticky': true, 1639 'toolbarStickyOffset': $('body').hasClass('admin-bar') ? 32 : 0, 1640 // Toolbar buttons on large devices (≥ 1200px). 1641 'toolbarButtons': ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1642 // On medium devices (≥ 992px). 1643 toolbarButtonsMD: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1644 // On small devices (≥ 768px). 1645 toolbarButtonsSM: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1646 // On extra small devices (< 768px). 1647 toolbarButtonsXS: ['bold', 'italic', 'insertLink', '|', 'undo', 'redo'], 1648 charCounterMax: c.getFroalaEditorMaxCharacters($textarea) 1649 }; 1650 // Override Froala's config using this filter function. 1651 if (typeof c.froalaEditorConfig === 'function') { 1652 config = c.froalaEditorConfig(config); 1653 } 1654 if (snax.inDebugMode()) { 1655 snax.log(config); 1656 } 1657 // Init. 1658 $textarea.froalaEditor(config); 1659 }; 1660 1661 c.getFroalaEditorConfig = function(id) { 1662 var config = c.config.froala; 1663 1664 if (typeof config[id] !== 'undefined') { 1665 return config[id]; 1666 } 1667 1668 return null; 1669 }; 1670 1671 c.getFroalaEditorMaxCharacters = function($editor) { 1672 var maxCharacters = parseInt($editor.attr('maxlength'), 10); 1673 1674 return maxCharacters > 0 ? maxCharacters : -1; 1675 }; 1676 1677 c.applyFroala = function () { 1678 1679 // Check if Froals script is loaded. 1680 if (!$.fn.froalaEditor) { 1681 return; 1682 } 1683 $(selectors.froalaEditor).each(c.renderFroala); 1684 }; 1685 1686 // Fire. 1687 $(document).ready(function () { 1688 if (snax.frontendSubmission) { 1689 c.config = snax.frontendSubmission.config; 1690 } 1691 if (snax.post) { 1692 c.config = snax.post.config; 1693 } 1694 if (typeof c.config === 'undefined') { 1695 return; 1696 } 1697 c.init(); 1698 }); 1699 1700})(jQuery, snax); 1701 1702/******************************* 1703 * 1704 * Module: Single item comments 1705 * 1706 ******************************/ 1707 1708(function ($, ctx) { 1709 1710 'use strict'; 1711 1712 /** CONFIG *******************************************/ 1713 1714 // Register new component. 1715 ctx.singleItemComments = {}; 1716 1717 // Component namespace shortcut. 1718 var c = ctx.singleItemComments; 1719 1720 var seeMoreHtml = '<a class="snax-see-all-replies">' + ctx.config.i18n.see_all_replies + '</a>'; 1721 1722 // CSS selectors. 1723 var selectors = { 1724 'section': '.snax-item-comments', 1725 'topLevelComment': '.snax-item-comments .depth-1', 1726 'moreCommentsLink': '.snax-item-comments-more-link', 1727 'allRepliesLink': '.snax-see-all-replies', 1728 'list': '.comment-list', 1729 'commentForm': '.snax-item-comments .comment-form', 1730 'textarea': '.snax-item-comments .comment-form textarea', 1731 'submitButton': '.submit', 1732 'cancelReply': '#cancel-comment-reply-link', 1733 'respondSection': '#respond', 1734 'replyLogin': '.comment-reply-login', 1735 'respondLogin': '.snax-comment-login', 1736 'authorFields': '.snax-item-comment-autor', 1737 }; 1738
1739 var $spinner = $('<div class="snax-upload-icon"></div>'); 1740 1741 // Allow accessing 1742 c.selectors = selectors; 1743 1744 /** INIT *******************************************/ 1745 1746 c.init = function () { 1747 $(selectors.submitButton, selectors.commentForm).attr('disabled', true); 1748 c.renderSeeMore(); 1749 c.attachEventHandlers(); 1750 }; 1751 1752 /** EVENTS *****************************************/ 1753 1754 c.renderSeeMore = function() { 1755 $(selectors.topLevelComment).each(function( index ) { 1756 if($('.children li', this).length>1 && $(this).attr('appendedSeeMoreLink')!== 'true'){ 1757 $(this).append(seeMoreHtml); 1758 $(this).attr('appendedSeeMoreLink','true'); 1759 } 1760 }); 1761 $(selectors.allRepliesLink).on('click', function (e) { 1762 var $parent = $(this).closest(selectors.topLevelComment); 1763 $('.children', $parent).addClass('children-visible'); 1764 $(this).remove(); 1765 }); 1766 }; 1767 1768 //overrides core comment-reply.js to handle multiple forms 1769 c.overrideDefaultHandler = function() { 1770 if (typeof addComment === 'undefined') { 1771 return; 1772 } 1773 1774 addComment.moveForm = function( commId, parentId, respondId, postId ) { 1775 var div, element, style, cssHidden, 1776 t = this, 1777 comm = t.I( commId ), 1778 respond = t.I( respondId ), 1779 cancel = respond.querySelector( '#cancel-comment-reply-link' ), 1780 parent = respond.querySelector( '#comment_parent' ), 1781 post = respond.querySelector( '#comment_post_ID' ), 1782 commentForm = respond.getElementsByTagName( 'form' )[0]; 1783 1784 if ( ! comm || ! respond || ! cancel || ! parent || ! commentForm ) { 1785 return; 1786 } 1787 1788 t.respondId = respondId; 1789 postId = postId || false; 1790 1791 if ( ! t.I( 'wp-temp-form-div' ) ) { 1792 div = document.createElement( 'div' ); 1793 div.id = 'wp-temp-form-div'; 1794 div.style.display = 'none'; 1795 respond.parentNode.insertBefore( div, respond ); 1796 } 1797 1798 comm.parentNode.insertBefore( respond, comm.nextSibling ); 1799 if ( post && postId ) { 1800 post.value = postId; 1801 } 1802 parent.value = parentId; 1803 cancel.style.display = ''; 1804 1805 cancel.onclick = function() { 1806 var t = addComment, 1807 temp = t.I( 'wp-temp-form-div' ), 1808 respond = t.I( t.respondId ); 1809 1810 if ( ! temp || ! respond ) { 1811 return; 1812 } 1813 1814 parent.value = '0'; 1815 temp.parentNode.insertBefore( respond, temp ); 1816 temp.parentNode.removeChild( temp ); 1817 this.style.display = 'none'; 1818 this.onclick = null; 1819 return false; 1820 }; 1821 1822 /* 1823 * Set initial focus to the first form focusable element. 1824 * Try/catch used just to avoid errors in IE 7- which return visibility 1825 * 'inherit' when the visibility value is inherited from an ancestor. 1826 */ 1827 try { 1828 for ( var i = 0; i < commentForm.elements.length; i++ ) { 1829 element = commentForm.elements[i]; 1830 cssHidden = false; 1831 1832 // Modern browsers. 1833 if ( 'getComputedStyle' in window ) { 1834 style = window.getComputedStyle( element ); 1835 // IE 8. 1836 } else if ( document.documentElement.currentStyle ) { 1837 style = element.currentStyle; 1838 } 1839 1840 /* 1841 * For display none, do the same thing jQuery does. For visibility, 1842 * check the element computed style since browsers are already doing 1843 * the job for us. In fact, the visibility computed style is the actual 1844 * computed value and already takes into account the element ancestors. 1845 */ 1846 if ( ( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) || style.visibility === 'hidden' ) { 1847 cssHidden = true; 1848 } 1849 1850 // Skip form elements that are hidden or disabled. 1851 if ( 'hidden' === element.type || element.disabled || cssHidden ) { 1852 continue; 1853 } 1854 1855 element.focus(); 1856 // Stop after the first focusable element. 1857 break; 1858 } 1859 1860 } catch( er ) {} 1861 1862 return false;
1863 }; 1864 1865 addComment.I = function( id ) { 1866 return document.getElementById( id ); 1867 }; 1868 }; 1869 1870 c.attachEventHandlers = function() { 1871 1872 var loadMoreComments = function($section) { 1873 var $that = $(selectors.moreCommentsLink, $section); 1874 var postId = $section.attr('data-snax-post-id'); 1875 var loadedPages = parseInt($section.attr('data-snax-loaded-pages'),10); 1876 var $list = $(selectors.list, $section); 1877 var requestData = { 1878 'action': 'snax_load_more_item_comments', 1879 'snax_item_id': postId, 1880 'loaded_pages': loadedPages 1881 }; 1882 1883 $spinner.appendTo($list); 1884 1885 var xhr = $.ajax({ 1886 'type': 'POST', 1887 'url': ctx.config.ajax_url, 1888 'dataType': 'json', 1889 'data': requestData 1890 }); 1891 1892 xhr.done(function (res) { 1893 if ('success' === res.status) { 1894 $spinner.remove(); 1895 if (res.args.html) { 1896 $list.append(res.args.html); 1897 removeDuplicateComments($list); 1898 maybeRemoveSeeMoreLink($list,$that); 1899 c.renderSeeMore(); 1900 $section.attr('data-snax-loaded-pages',loadedPages + 1); 1901 1902 addComment.init(); 1903 } 1904 } 1905 }); 1906 }; 1907 1908 var removeDuplicateComments = function($list){ 1909 var seen = {}; 1910 $(selectors.topLevelComment, $list).each(function( index ) { 1911 var id = $(this).attr('id'); 1912 if (seen[id]) { 1913 $(this).remove(); 1914 } else { 1915 seen[id] = true; 1916 } 1917 }); 1918 }; 1919 1920 var maybeRemoveSeeMoreLink = function($list,$link){ 1921 var topLevels = $('.depth-1', $list).length; 1922 var topLevelComments = parseInt( $list.attr('data-snax-top-level-comments'), 10 ); 1923 if (topLevels >= topLevelComments){ 1924 $link.remove(); 1925 } 1926 }; 1927 1928 $(selectors.moreCommentsLink).on('click', function (e) { 1929 e.preventDefault(); 1930 var $section = $(this).closest(selectors.section); 1931 loadMoreComments($section); 1932 }); 1933 1934 $(selectors.commentForm).on('submit', function(e) { 1935 e.preventDefault(); 1936 var $form = $(this); 1937 var url = $form.attr('action'); 1938 var commentParent = '0'; 1939 var $textarea = $('textarea', $form); 1940 var $section = $form.closest(selectors.section); 1941 var $list = $(selectors.list, $section); 1942 var $respondSection = $form.closest(selectors.respondSection); 1943 var $cancelButton = $(selectors.cancelReply, $respondSection); 1944 var $submitButton = $(selectors.submitButton, $form); 1945 var requestData = {}; 1946 1947 $.each($form.serializeArray(), function(i, field) { 1948 requestData[field.name] = field.value; 1949 }); 1950 1951 requestData['is_ajax_item_comment_form'] = true; 1952 commentParent = requestData['comment_parent']; 1953 1954 if (commentParent === '0') { 1955 $spinner.prependTo($list); 1956 } else { 1957 var $commentParent = $('#comment-' + commentParent); 1958 var $commentParentChildren = $('#comment-' + commentParent + '> .children'); 1959 if (! $commentParentChildren.length) { 1960 $commentParent.append('<ul class="children"></ul>'); 1961 $commentParentChildren = $('#comment-' + commentParent + '> .children'); 1962 } 1963 $spinner.prependTo($commentParentChildren); 1964 } 1965 1966 var xhr = $.ajax({ 1967 'type': 'POST', 1968 'url': url, 1969 'data': requestData 1970 }); 1971 1972 xhr.done(function (res) { 1973 $textarea.val(''); 1974 $cancelButton.trigger('click'); 1975 $submitButton.attr('disabled', true); 1976 if (res.indexOf('<body id="error-page">') != -1){ 1977 res = $(res).filter('p')[1]; 1978 } 1979 $spinner.replaceWith(res); 1980 if (commentParent !== '0') { 1981 var $parent = $commentParentChildren.closest(selectors.topLevelComment); 1982 $('.children', $parent).addClass('children-visible'); 1983 } 1984 }); 1985 1986 xhr.fail(function( jqXHR, textStatus ) { 1987 if(jqXHR.status===409){ 1988 alert(ctx.config.i18n.duplicate_comment); 1989 }else{ 1990 alert(ctx.config.i18n.comment_fail); 1991 } 1992 $spinner.remove(); 1993 }); 1994 }); 1995
1996 var validateForm = function( $form ) { 1997 var $submitButton = $(selectors.submitButton, $form); 1998 var $author = $(selectors.authorFields, $form); 1999 var $input = $('textarea', $form); 2000 var disable = false; 2001 if ( $author.length > 0 ){ 2002 var $authorName = $('#author', $author); 2003 var $authorMail = $('#email', $author); 2004 if ( $authorName.val() === '' || $authorMail.val() === '' ) { 2005 disable = true; 2006 } 2007 } 2008 if ($input.val() === '') { 2009 disable = true; 2010 } 2011 $submitButton.attr('disabled', disable); 2012 }; 2013 2014 $(selectors.textarea).keyup(function() { 2015 var $parent = $(this).closest(selectors.commentForm); 2016 var $author = $(selectors.authorFields, $parent); 2017 while($(this).outerHeight() < this.scrollHeight + parseFloat($(this).css('borderTopWidth')) + parseFloat($(this).css('borderBottomWidth'))) { 2018 $(this).height($(this).height()+1); 2019 } 2020 if ($(this).val()){ 2021 $author.show(); 2022 $parent.removeClass('snax-comment-form-collapsed').addClass('snax-comment-form-extended'); 2023 } else { 2024 $author.hide(); 2025 $parent.removeClass('snax-comment-form-extended').addClass('snax-comment-form-collapsed'); 2026 } 2027 validateForm($parent); 2028 }); 2029 2030 $(selectors.authorFields).keyup(function() { 2031 var $parent = $(this).closest(selectors.commentForm); 2032 validateForm($parent); 2033 }); 2034 2035 $(selectors.replyLogin).click(function(e){ 2036 e.preventDefault(); 2037 ctx.loginRequired(); 2038 }); 2039 $(selectors.respondLogin).click(function(e){ 2040 e.preventDefault(); 2041 ctx.loginRequired(); 2042 }); 2043 }; 2044 2045 // Fire. 2046 $(document).ready(function () { 2047 c.init(); 2048 c.overrideDefaultHandler(); 2049 }); 2050 2051})(jQuery, snax); 2052 2053/************************* 2054 * 2055 * Module: Delete Post 2056 * 2057 ************************/ 2058 2059(function ($, ctx) { 2060 2061 'use strict'; 2062 2063 /** CONFIG *******************************************/ 2064 2065 // Register new component. 2066 ctx.deletePostModule = {}; 2067 2068 // Component namespace shortcut. 2069 var c = ctx.deletePostModule; 2070 2071 // CSS selectors. 2072 var selectors = { 2073 'deleteLink': '.snax-delete-post' 2074 }; 2075 2076 // Allow accessing 2077 c.selectors = selectors; 2078 2079 /** INIT *******************************************/ 2080 2081 c.init = function () { 2082 c.attachEventHandlers(); 2083 }; 2084 2085 /** EVENTS *****************************************/ 2086 2087 c.attachEventHandlers = function() { 2088 2089 /* Delete item */ 2090 2091 $(selectors.deleteLink).on('click', function (e) { 2092 e.preventDefault(); 2093 var $link = $(this); 2094 2095 if (!confirm(ctx.config.i18n.are_you_sure)) { 2096 return; 2097 } 2098 2099 var $item = $link.parents('.g1-collection-item'); 2100 2101 if ( $item.length === 0 ) { 2102 var $item = $link.parents('article.post'); 2103 } 2104 2105 $item.addClass('snax-post-removing'); 2106 2107 ctx.deletePost($link, function(res) { 2108 if (res.status === 'success') { 2109 var redirectUrl = $link.attr('href'); 2110 2111 // Redirect and show status note on top of a target page. 2112 if (redirectUrl.length > 0) { 2113 var queryVarDelimiter = redirectUrl.indexOf('?') === -1 ? '?' : '&'; 2114 2115 window.location.href = redirectUrl + queryVarDelimiter + ctx.config.delete_status_var + '=success'; 2116 // Remove DOM element and show notification. 2117 } else { 2118 $item.addClass('snax-post-removed'); 2119 $item.slideToggle(750); 2120 2121 setTimeout(function() { 2122 snax.notifications.add(res.message); 2123 }, 1500); 2124 } 2125 2126 } else { 2127 ctx.notifications.add(res.message); 2128 } 2129 }); 2130 }); 2131 }; 2132 2133 // Fire. 2134 $(document).ready(function () { 2135 c.init(); 2136 }); 2137 2138})(jQuery, snax); 2139 2140/******************************* 2141 * 2142 * Module: Password Reset 2143 * 2144 ******************************/ 2145 2146(function ($, ctx) { 2147 2148 'use strict'; 2149 2150 /** CONFIG *******************************************/ 2151 2152 // Register new component. 2153 ctx.passwordReset = {}; 2154 2155 // Component namespace shortcut. 2156 var c = ctx.passwordReset; 2157 2158 // CSS selectors. 2159 var selectors = { 2160 'resetPasswordForm': '#snax_reset_password_form', 2161 'newPassword': '#snax_reset_password_form #new_password', 2162 'repeatPassword': '#snax_reset_password_form #repeat_password',
2163 'cookieName': '#snax_reset_password_form .rp-cookie-name', 2164 'cookieValue': '#snax_reset_password_form .rp-cookie_value', 2165 'resetErrorMessage': '#snax_reset_password_form .snax-reset-pass-error-message', 2166 'resetSuccessMessage': '#snax_reset_password_form .snax-reset-pass-success-message', 2167 }; 2168 2169 // Allow accessing 2170 c.selectors = selectors; 2171 2172 /** INIT *******************************************/ 2173 2174 c.init = function () { 2175 c.attachEventHandlers(); 2176 }; 2177 2178 /** EVENTS *****************************************/ 2179 2180 c.attachEventHandlers = function() { 2181 c.matchPasswords(); 2182 c.handleForm(); 2183 }; 2184 2185 c.matchPasswords = function() { 2186 var $newPassword = $(selectors.newPassword), 2187 $repeatPassword = $(selectors.repeatPassword); 2188 2189 $repeatPassword.on('change', function(e) { 2190 if ($newPassword.val() !== $repeatPassword.val()){ 2191 $repeatPassword.get(0).setCustomValidity( ctx.config.i18n.passwords_dont_match); 2192 } else { 2193 $repeatPassword.get(0).setCustomValidity(''); 2194 } 2195 }); 2196 } 2197 2198 c.handleForm = function() { 2199 var $form = $(selectors.resetPasswordForm), 2200 $cookieName = $(selectors.cookieName), 2201 $cookieValue = $(selectors.cookieValue), 2202 $newPassword = $(selectors.newPassword), 2203 $errorMessage = $(selectors.resetErrorMessage), 2204 $successMessage = $(selectors.resetSuccessMessage); 2205 2206 $form.on('submit', function(e) { 2207 2208 e.preventDefault(); 2209 e.stopPropagation(); 2210 e.stopImmediatePropagation(); 2211 2212 $errorMessage.text(''); 2213 $successMessage.text(''); 2214 2215 ctx.createCookie($cookieName.val(), $cookieValue.val(), 24); 2216 2217 var xhr = $.ajax({ 2218 'type': 'POST', 2219 'url': $form.attr('action') + '?action=resetpass', 2220 'data': $form.serialize(), 2221 }); 2222 2223 xhr.done(function (res) { 2224 if ($('.reset-pass', res).length){ 2225 $successMessage.html( '<p class="snax-validation-tip">' + ctx.config.i18n.password_set + '</p>' ); 2226 } 2227 if ($('#login_error', res).length){ 2228 $errorMessage.html( '<p class="snax-validation-tip">' + ctx.config.i18n.link_invalid + '</p>' ); 2229 } 2230 }); 2231 2232 return false; 2233 }); 2234 }; 2235 2236 // Fire. 2237 $(document).ready(function () { 2238 if ($(selectors.resetPasswordForm).length){ 2239 c.init(); 2240 } 2241 }); 2242 2243})(jQuery, snax); 2244 2245/******************************* 2246 * 2247 * Module: BuddyPress registration captcha 2248 * 2249 ******************************/ 2250 2251(function ($, ctx) { 2252 2253 'use strict'; 2254 2255 var useReCaptcha; 2256 var reCaptchaToken; 2257 2258 ctx.registerForm = function () { 2259 useReCaptcha = ctx.config.use_login_recaptcha; 2260 if (useReCaptcha && $('#buddypress #register-page').length >0) { 2261 loadReCaptcha(); 2262 $('#signup_submit').attr('disabled', true); 2263 } 2264 }; 2265 2266 var loadReCaptcha = function() { 2267 var apiUrl = ctx.config.recaptcha_api_url; 2268 var ver = ctx.config.recaptcha_version; 2269 var siteKey = ctx.config.recaptcha_site_key; 2270 2271 if (!siteKey || !apiUrl) { 2272 return; 2273 } 2274 2275 var renderLoginReCaptcha = function() { 2276 if ('30' === ver) { 2277 grecaptcha.execute( 2278 siteKey, 2279 { action: 'login' } 2280 ).then( function( token ) { 2281 $('#snax-register-recaptcha').html('<input type="hidden" name="g-recaptcha-response" value="'+ token +'">'); 2282 reCaptchaEnteredCorrectly(token); 2283 } ); 2284 } else { 2285 grecaptcha.render('snax-register-recaptcha', { 2286 'sitekey' : siteKey, 2287 'callback': reCaptchaEnteredCorrectly 2288 }); 2289 } 2290 }; 2291 2292 // Google reCaptcha API loaded. 2293 if (typeof grecaptcha !== 'undefined') { 2294 renderLoginReCaptcha(); 2295 } else { 2296 // API not loaded. Register callback and load script. 2297 window.snaxReCaptchaOnloadCallback = function() { 2298 renderLoginReCaptcha(); 2299 }; 2300 2301 if ('30' === ver) { 2302 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render='+siteKey+'" async defer>'); 2303 } else { 2304 $('head').append('<script src="' + apiUrl + '?onload=snaxReCaptchaOnloadCallback&render=explicit" async defer>'); 2305 } 2306 } 2307 }; 2308 2309 var reCaptchaEnteredCorrectly = function(response) { 2310 reCaptchaToken = response; 2311 $('#signup_submit').attr('disabled', false); 2312 }; 2313 2314 // Fire. 2315 $(document).ready(function () { 2316 ctx.registerForm(); 2317 }); 2318 2319})(jQuery, snax); 2320 2321/******************************* 2322 * 2323 * Module: Notifications 2324 * 2325 ******************************/ 2326 2327(function ($, ctx) { 2328 2329 'use strict'; 2330 2331 var api = {}; // Public API. 2332 var timeout = 5000; 2333 var $notifications = false;
2334 var $notificationTpl = false; 2335 2336 var selectors = { 2337 'notifications': '.snax-notifications', 2338 'notification': '.snax-notification', 2339 'notificationText': '.snax-notification-text', 2340 'notificationClose':'.snax-notification-close' 2341 }; 2342 2343 var classes = { 2344 'on': 'snax-notifications-on', 2345 'off': 'snax-notifications-off', 2346 }; 2347 2348 api.add = function(html) { 2349 var $notification = $notificationTpl.clone(true); 2350 $notification.find(selectors.notificationText).html(html); 2351 2352 $notification.on('click', selectors.notificationClose, function() { 2353 api.remove($notification); 2354 }); 2355 2356 // Auto-remove. 2357 setTimeout(function() { 2358 api.remove($notification); 2359 }, timeout); 2360 2361 $notifications.prepend($notification); 2362 $notifications.removeClass(classes.off).addClass(classes.on); 2363 }; 2364 2365 api.remove = function($notification) { 2366 $notification.addClass('snax-notification-removed'); 2367 setTimeout(function() { 2368 $notification.remove(); 2369 2370 if (api.isQueueEmpty()) { 2371 $notifications.removeClass(classes.on).addClass(classe
2371s.off); 2372 } 2373 }, 5000); 2374 }; 2375 2376 api.isQueueEmpty = function() { 2377 return $notifications.find(selectors.notification).length === 0; 2378 }; 2379 2380 var initNotifications = function() { 2381 $notifications = $(selectors.notifications); 2382 2383 if ($notifications.length === 0) { 2384 return; 2385 } 2386 2387 // Store notification template and rmeove it from DOM. 2388 $notificationTpl = $notifications.find(selectors.notification).detach(); 2389 2390 // Enable API. 2391 ctx.notifications = api; 2392 }; 2393 2394 // Fire. 2395 $(document).ready(function () { 2396 initNotifications(); 2397 }); 2398 2399})(jQuery, snax); 2400 2401/******************************** 2402 * 2403 * Social Login GDPR integration 2404 * 2405 *******************************/ 2406 2407(function ($) { 2408 2409 'use strict'; 2410 2411 $(document).ready(function () { 2412 $('.snax-gdpr-consent-form').each(function() { 2413 var $form = $(this); 2414 var $links = $form.parent().find('.snax-social-login-links'); 2415 var $link; // A requested social login link. 2416 2417 // Toggle GDPR and Social Links visibility. 2418 var toggleVisiblity = function() { 2419 $form.toggleClass('snax-gdpr-consent-form-hidden snax-gdpr-consent-form-visible'); 2420 $links.toggleClass('snax-social-login-links-visible snax-social-login-links-hidden'); 2421 }; 2422 2423 // Social login link requested. 2424 $links.on('click', 'a.snax-social-login', function(e) { 2425 e.preventDefault(); 2426 2427 $link = $(this); 2428 2429 toggleVisiblity(); 2430 }); 2431 2432 // Accept GDPR. 2433 $form.on('click', '.snax-slog-gdpr-accept', function(e) { 2434 e.preventDefault(); 2435 2436 window.location.href = $link.attr('href'); 2437 }); 2438 2439 // Cancel GDPR. 2440 $form.on('click', '.snax-slog-gdpr-cancel', function(e) { 2441 e.preventDefault(); 2442 2443 toggleVisiblity(); 2444 }); 2445 }); 2446 }); 2447 2448})(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.