PageSourceSearch

https://linen-spider-803378.hostingersite.com/wp-content/plugins/snax/assets/js/front.js?ver=1.76

js linen-spider-803378.hostingersite.com collected 2026-10-03 04:38:16 UTC 74,058 bytes, 2,448 lines download raw bytes

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.