xref: /plugin/mikioplugin/script.js (revision beae10569609c073e726df9eef35ee6d8306dbd5)
1
2
3jQuery().ready(function () {
4    var stripHtml = function(htmlString) {
5        var tempDiv = document.createElement("div");
6        tempDiv.innerHTML = htmlString;
7        return tempDiv.textContent || tempDiv.innerText || "";
8    };
9
10    var urlMatches = function(url, start = 'start') {
11        const windowURL = new URL(window.location);
12        const urlObject = new URL(url.startsWith('http') ? url : window.origin + url);
13        let match = true;
14
15        // remove trailing slashes
16        let windowPath = windowURL.pathname.replace(/\/$/, '');;
17        let urlPath = urlObject.pathname.replace(/\/$/, '');;
18
19        // add in start page if missing
20        if(windowURL.searchParams.has('id') || urlObject.searchParams.has('id')) {
21            // using search params
22            if(windowURL.searchParams.has('id') === false) {
23                windowURL.searchParams.append('id', start);
24            }
25
26            if(urlObject.searchParams.has('id') === false) {
27                urlObject.searchParams.append('id', start);
28            }
29        } else {
30            // dont seem to be using search params
31            if (windowPath.endsWith('/doku.php')) {
32                windowPath = windowPath + '/' + start;
33            }
34            if (urlPath.endsWith('/doku.php')) {
35                urlPath = urlPath + '/' + start;
36            }
37        }
38
39        if(windowURL.origin !== urlObject.origin || windowPath !== urlPath) {
40            return false;
41        }
42
43        urlObject.searchParams.forEach((val, key) => {
44            if(!windowURL.searchParams.has(key) || windowURL.searchParams.get(key) !== val) {
45                match = false;
46            }
47        });
48
49        return match;
50    };
51
52    jQuery('.mikiop-button').on('click', function (event) {
53        if (jQuery(this).hasClass('mikiop-disabled')) {
54            event.preventDefault();
55        }
56
57        if (jQuery(this).attr('data-toggle') === 'collapse') {
58            event.preventDefault();
59
60            const btn = jQuery(this);
61            const target = jQuery(btn.attr('data-target'));
62
63            const newText = btn.attr('data-toggle-text');
64            if (newText !== '') {
65                btn.attr('data-toggle-text', btn.text());
66                btn.text(newText);
67            }
68
69            target.slideToggle();
70        }
71    });
72
73    jQuery('.mikiop-accordian-title').on('click', function (event) {
74        event.preventDefault();
75        let accordianBody = jQuery(this).siblings('.mikiop-accordian-body');
76        if (!accordianBody.is(':visible')) {
77            let accordian = jQuery(this).closest('.mikiop-accordian');
78            if (accordian.hasClass('mikiop-autoclose')) {
79                accordian.find('.mikiop-accordian-body:visible').slideUp();
80            }
81        }
82
83        jQuery(this).siblings('.mikiop-accordian-body').slideToggle();
84    });
85
86    jQuery('.mikiop-alert-close').on('click', function (event) {
87        event.preventDefault();
88        jQuery(this).closest('.mikiop-alert').hide();
89    });
90
91    jQuery('.mikiop-carousel').each(function () {
92        var items = jQuery(this).find('.mikiop-carousel-item');
93        var indicators = '';
94        var active = false;
95
96        for (var i = 0; i < items.length; i++) {
97            if (jQuery(items[i]).hasClass('mikiop-active')) {
98                active = true;
99                indicators += '<li class="mikiop mikiop-carousel-indicator mikiop-active"></li>';
100            } else {
101                indicators += '<li class="mikiop mikiop-carousel-indicator"></li>';
102            }
103        };
104
105        jQuery(this).find('.mikiop-carousel-indicators').html(indicators);
106
107        if (!active) {
108            jQuery(this).find('.mikiop-carousel-item').first().addClass('mikiop-active');
109            jQuery(this).find('.mikiop-carousel-indicator').first().addClass('mikiop-active');
110        }
111
112        if (jQuery(this).attr('data-auto-start') == 'true') {
113            var carousel = jQuery(this);
114            timeout = carousel.find('.mikiop-carousel-item.mikiop-active').attr('data-interval');
115
116            if (timeout == 0) {
117                timeout = 3;
118            }
119
120            var nextSlide = function () {
121                var timeout = carouselNext(carousel);
122
123                if (timeout == 0) {
124                    timeout = 3;
125                }
126
127                window.setTimeout(nextSlide, (timeout * 1000) + 500);
128            };
129
130            window.setTimeout(nextSlide, (timeout * 1000) + 500);
131        }
132    });
133
134    jQuery('.mikiop-carousel-control-prev').on('click', function (event) {
135        event.preventDefault();
136
137        var parent = jQuery(this).parent();
138        carouselPrev(parent);
139    });
140
141    function carouselPrev(parent) {
142
143        var slides = parent.find('.mikiop-carousel-item');
144
145        for (var i = 0; i < slides.length; i++) {
146            if (jQuery(slides[i]).hasClass('mikiop-active')) {
147                var target = null;
148                var next = 0;
149
150                if (i == 0) {
151                    next = slides.length - 1;
152                } else {
153                    next = i - 1;
154                }
155                target = jQuery(slides[next]);
156
157                if (jQuery(parent).hasClass('mikiop-transition-fade')) {
158                    target.css('z-index', 0).addClass('mikiop-active');
159                    jQuery(slides[i]).fadeOut(function () {
160                        jQuery(this).removeClass('mikiop-active').css('display', '');
161                        target.css('z-index', '');
162                    });
163                } else if (jQuery(parent).hasClass('mikiop-transition-slide')) {
164                    target.css('left', '-100%').addClass('mikiop-active');
165                    target.animate({ left: '0' }, 500);
166                    jQuery(slides[i]).animate({ left: '100%' }, 500, function () {
167                        jQuery(this).removeClass('mikiop-active').css('left', '');
168                        target.css('left', '');
169                    })
170                } else {
171                    target.addClass('mikiop-active');
172                    jQuery(slides[i]).removeClass('mikiop-active');
173                }
174
175                parent.find('.mikiop-carousel-indicator').removeClass('mikiop-active');
176                parent.find('.mikiop-carousel-indicator:nth-child(' + (next + 1) + ')').addClass('mikiop-active');
177
178                break;
179            }
180        }
181    };
182
183    jQuery('.mikiop-carousel-control-next').on('click', function (event) {
184        event.preventDefault();
185        var parent = jQuery(this).parent();
186
187        carouselNext(parent);
188    });
189
190    function carouselNext(parent) {
191        var slides = parent.find('.mikiop-carousel-item');
192        var delay = 0;
193
194        for (var i = 0; i < slides.length; i++) {
195
196
197            if (jQuery(slides[i]).hasClass('mikiop-active')) {
198                var target = null;
199                var next = 0;
200
201
202                if (i == slides.length - 1) {
203                    next = 0;
204                } else {
205                    next = i + 1;
206                }
207                target = jQuery(slides[next]);
208
209                delay = target.attr('data-interval');
210                if (typeof delay == 'undefined') {
211                    delay = 0;
212                }
213
214                if (jQuery(parent).hasClass('mikiop-transition-fade')) {
215                    target.css('z-index', 0).addClass('mikiop-active');
216                    jQuery(slides[i]).fadeOut(function () {
217                        jQuery(this).removeClass('mikiop-active').css('display', '');
218                        target.css('z-index', '');
219                    });
220                } else if (jQuery(parent).hasClass('mikiop-transition-slide')) {
221                    target.css('left', '100%').addClass('mikiop-active');
222                    target.animate({ left: '0' }, 500);
223                    jQuery(slides[i]).animate({ left: '-100%' }, 500, function () {
224                        jQuery(this).removeClass('mikiop-active').css('left', '');
225                        target.css('left', '');
226                    })
227                } else {
228                    target.addClass('mikiop-active');
229                    jQuery(slides[i]).removeClass('mikiop-active');
230                }
231
232                parent.find('.mikiop-carousel-indicator').removeClass('mikiop-active');
233                parent.find('.mikiop-carousel-indicator:nth-child(' + (next + 1) + ')').addClass('mikiop-active');
234
235                break;
236            }
237        }
238
239        return (delay);
240    };
241
242    jQuery('.mikiop-carousel-indicator').on('click', function (event) {
243        event.preventDefault();
244
245        var parent = jQuery(this).closest('.mikiop-carousel-indicators');
246        if (parent) {
247            var group = jQuery(this).closest('.mikiop-carousel');
248            if (group) {
249                var items = jQuery(group).find('.mikiop-carousel-indicator');
250
251                var item = -1;
252                var active = 0;
253                for (var i = 0; i < items.length; i++) {
254                    if (jQuery(items[i]).hasClass('mikiop-active')) {
255                        active = i;
256                    }
257
258                    if (items[i] == jQuery(this)[0]) {
259                        item = i;
260                    }
261                }
262
263                if (item != active) {
264                    if (jQuery(group).hasClass('mikiop-transition-fade')) {
265                        var target = jQuery(group).find('.mikiop-carousel-item:nth-child(' + (item + 1) + ')');
266
267                        target.css('z-index', 0).addClass('mikiop-active');
268                        jQuery(group).find('.mikiop-carousel-item:nth-child(' + (active + 1) + ')').fadeOut(function () {
269                            jQuery(this).removeClass('mikiop-active').css('display', '');
270                            target.css('z-index', '');
271                        });
272
273                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (item + 1) + ')').addClass('mikiop-active');
274                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (active + 1) + ')').removeClass('mikiop-active');
275                    } else if (jQuery(group).hasClass('mikiop-transition-slide')) {
276                        var target = jQuery(group).find('.mikiop-carousel-item:nth-child(' + (item + 1) + ')');
277
278                        if (item < active) {
279                            target.css('left', '-100%').addClass('mikiop-active');
280                            target.animate({ left: '0' }, 500);
281                            jQuery(group).find('.mikiop-carousel-item:nth-child(' + (active + 1) + ')').animate({ left: '100%' }, 500, function () {
282                                jQuery(this).removeClass('mikiop-active').css('left', '');
283                                target.css('left', '');
284                            });
285                        } else {
286                            target.css('left', '100%').addClass('mikiop-active');
287                            target.animate({ left: '0' }, 500);
288                            jQuery(group).find('.mikiop-carousel-item:nth-child(' + (active + 1) + ')').animate({ left: '-100%' }, 500, function () {
289                                jQuery(this).removeClass('mikiop-active').css('left', '');
290                                target.css('left', '');
291                            });
292                        }
293
294                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (item + 1) + ')').addClass('mikiop-active');
295                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (active + 1) + ')').removeClass('mikiop-active');
296                    } else {
297                        jQuery(group).find('.mikiop-carousel-item:nth-child(' + (item + 1) + ')').addClass('mikiop-active');
298                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (item + 1) + ')').addClass('mikiop-active');
299                        jQuery(group).find('.mikiop-carousel-item:nth-child(' + (active + 1) + ')').removeClass('mikiop-active');
300                        jQuery(group).find('.mikiop-carousel-indicator:nth-child(' + (active + 1) + ')').removeClass('mikiop-active');
301                    }
302                }
303            }
304        }
305    });
306
307    jQuery('.mikiop-tab-item a').on('click', function (event) {
308        event.preventDefault();
309
310        var parent = jQuery(this).closest('.mikiop-tab-item');
311        if (parent) {
312            var group = jQuery(parent).closest('.mikiop-tab-group');
313            if (group) {
314                var items = jQuery(group).find('.mikiop-tab-item');
315
316                var item = -1;
317                for (var i = 0; i < items.length; i++) {
318                    if (items[i] == parent[0]) {
319                        item = i;
320                        break;
321                    }
322                }
323
324                if (item != -1) {
325                    var panes = jQuery(group).siblings('.mikiop-tab-content').find('.mikiop-tab-pane');
326
327                    if (panes.length > item) {
328                        if (!jQuery(panes[item]).hasClass('mikiop-show')) {
329                            jQuery(panes).removeClass('mikiop-show');
330                            jQuery(panes[item]).addClass('mikiop-show');
331
332                            jQuery(items).find('a').removeClass('mikiop-active');
333                            jQuery(items[item]).find('a').addClass('mikiop-active');
334                        }
335                    }
336                }
337            }
338        }
339    });
340
341    // Quiz
342    var quizReset = function(quizRef) {
343        quizRef.find('.mikiop-quiz-button-prev').attr('disabled', true);
344        quizRef.find('.mikiop-quiz-result').hide();
345        quizRef.find('.mikiop-quiz-button-submit').show().attr('disabled', false);
346        quizRef.find('.mikiop-quiz-button-reset').hide();
347
348        var status = quizRef.attr('data-status');
349        status = status.replace('$1', '1');
350        status = status.replace('$2', quizRef.children('.mikiop-quiz-item').length);
351        quizRef.find('.mikiop-quiz-status-text').html(status);
352
353        if (quizRef.children('.mikiop-quiz-item').length == 1) {
354            quizRef.find('.mikiop-quiz-button-next').attr('disabled', true);
355        } else {
356            quizRef.find('.mikiop-quiz-button-next').attr('disabled', false);
357        }
358
359        quizRef.children('.mikiop-quiz-item').find('input[type="radio"], input[type="checkbox"]').prop('checked', false);
360
361        var full = quizRef.attr('data-full');
362        if(!full) {
363            quizRef.children('.mikiop-quiz-item').not(':first-child').hide();
364            quizRef.children('.mikiop-quiz-item:first-child').show();
365        } else {
366            quizRef.children('.mikiop-quiz-item').show();
367        }
368    };
369
370    jQuery('.mikiop-quiz').each(function () {
371        quizReset(jQuery(this));
372    });
373
374    jQuery('.mikiop-quiz-button-prev').on('click', function (event) {
375        var parent = jQuery(this).closest('.mikiop-quiz');
376        var questions = parent.children('.mikiop-quiz-item');
377        parent.find('.mikiop-quiz-button-next').attr('disabled', false);
378
379        for (var i = 0; i < questions.length; i++) {
380            if (jQuery(questions[i]).is(':visible')) {
381                i--;
382
383                if (i <= 0) {
384                    jQuery(this).attr('disabled', true);
385                }
386
387                jQuery(questions[i + 1]).hide();
388                jQuery(questions[i]).show();
389                parent.find('.mikiop-quiz-status-number').html(i + 1);
390
391                var status = parent.attr('data-status');
392                status = status.replace('$1', i + 1);
393                status = status.replace('$2', parent.children('.mikiop-quiz-item').length);
394                parent.find('.mikiop-quiz-status-text').html(status);
395
396                break;
397            }
398        }
399    });
400
401    jQuery('.mikiop-quiz-button-next').on('click', function (event) {
402        var parent = jQuery(this).closest('.mikiop-quiz');
403        var questions = parent.children('.mikiop-quiz-item');
404        parent.find('.mikiop-quiz-button-prev').attr('disabled', false);
405
406        for (var i = 0; i < questions.length; i++) {
407            if (jQuery(questions[i]).is(':visible')) {
408                i++;
409
410                if (i >= questions.length - 1) {
411                    jQuery(this).attr('disabled', true);
412                }
413
414                jQuery(questions[i - 1]).hide();
415                jQuery(questions[i]).show();
416
417                var status = parent.attr('data-status');
418                status = status.replace('$1', i + 1);
419                status = status.replace('$2', parent.children('.mikiop-quiz-item').length);
420                parent.find('.mikiop-quiz-status-text').html(status);
421
422                break;
423            }
424        }
425    });
426
427    jQuery('.mikiop-quiz-button-submit').on('click', function (event) {
428        var parent = jQuery(this).closest('.mikiop-quiz');
429        var questions = parent.children('.mikiop-quiz-item');
430        var correct = 0;
431        var totalScore = 0;
432        var result = '<div class="mikiop-quiz-question">Result</div>';
433
434        var usingScoring = false;
435        var usingCorrect = false;
436        var questionCount = 0;
437
438        parent.find('.mikiop-quiz-button-prev').attr('disabled', true);
439        parent.find('.mikiop-quiz-button-next').attr('disabled', true);
440        parent.find('.mikiop-quiz-button-submit').attr('disabled', true);
441        parent.find('.mikiop-quiz-status-text').html('');
442
443        var resetButton = parent.find('.mikiop-quiz-button-reset');
444        if(resetButton.length > 0) {
445            parent.find('.mikiop-quiz-button-submit').hide();
446            resetButton.show();
447        }
448
449
450        for (var i = 0; i < questions.length; i++) {
451            var showNewLine = true;
452            var question = stripHtml(jQuery(questions[i]).attr('data-question'));
453            var regex = /^((\w+ ?)*[):])/;
454
455            if (regex.test(question)) {
456                question = question.match(regex)[1];
457                showNewLine = false;
458            }
459
460            result += '<p class="mikiop-quiz-result-question"><strong>' + question + '</strong>' + (showNewLine ? '<br>' : ' ');
461
462            var checked = jQuery(questions[i]).find("input:checked");
463            var answer = jQuery(questions[i]).attr('data-answer');
464            var value = checked.val();
465
466            if(answer != undefined) {
467                usingCorrect = true;
468                questionCount++;
469            }
470
471            if (typeof value == 'undefined') {
472                result += 'Not answered';
473            } else {
474                // check that input radio groups with the same name have at least 1 answer
475                let radioPass = true;
476                let radioGroups = {};
477                const radios = jQuery(questions[i]).find('input[type="radio"]');
478
479                radios.each(function () {
480                    const groupName = jQuery(this).attr('name');
481
482                    if (!radioGroups[groupName]) {
483                        radioGroups[groupName] = [];
484                    }
485
486                    radioGroups[groupName].push(jQuery(this));
487                });
488
489                for (const key in radioGroups) {
490                    if (radioGroups.hasOwnProperty(key)) {
491                        const group = radioGroups[key];
492                        const anySelected = group.some(function (radio) {
493                            return radio.prop('checked');
494                        });
495
496                        if (!anySelected) {
497                            result += 'An option was not answered';
498                            radioPass = false;
499                            break;
500                        }
501                    }
502                }
503
504                if(radioPass) {
505                    var totalItemScore = 0;
506                    var selectedItems = [];
507                    var itemIsScored = false;
508
509                    checked.each(function() {
510                        var item = jQuery(this);
511
512                        var score = item.attr('data-score');
513
514                        if(score != undefined && score.length > 0) {
515                            usingScoring = true;
516                            itemIsScored = true;
517                            totalItemScore += parseInt(score, 10);
518                        } else if(answer != undefined) {
519                            usingCorrect = true;
520                            selectedItems.push(item.val());
521                        }
522                    });
523
524                    if(itemIsScored) {
525                        var scorePlaceholder = parent.attr('data-result-score');
526                        result += scorePlaceholder.replace('$1', totalItemScore);
527                        totalScore += totalItemScore;
528                    } else {
529                        var correctText = parent.attr('data-correct');
530                        var incorrectText = parent.attr('data-incorrect');
531
532                        result += selectedItems.join(", ") + ' - ';
533
534                        if(answer == undefined) {
535                            result += "No answer set for question";
536                        } else if(answer.indexOf('|') !== -1) {
537                            var answerArray = answer.split('|');
538                            if(answerArray.length == selectedItems.length) {
539                                var totalMatch = true;
540                                answerArray.forEach(function(answerItem) {
541                                    var matching = selectedItems.some(function(selectedItem) {
542                                        return answerItem.localeCompare(selectedItem) === 0;
543                                    });
544
545                                    if(!matching) {
546                                        totalMatch = false;
547                                    }
548                                });
549
550                                if(totalMatch) {
551                                    correct++;
552                                    result += correctText;
553                                } else {
554                                    result += incorrectText;
555                                }
556                            } else {
557                                result += incorrectText;
558                            }
559                        } else {
560                            if (selectedItems.length > 0 && answer.localeCompare(selectedItems[0]) == 0) {
561                                correct++;
562                                result += correctText;
563                            } else {
564                                result += incorrectText;
565                            }
566                        }
567                    }
568                }
569            }
570
571            result += '</p>';
572
573            jQuery(questions[i]).hide();
574        }
575
576        var status = [];
577
578        if(usingScoring) {
579            status.push(parent.attr('data-result-score-total').replace('$1', totalScore));
580        }
581
582        if(usingCorrect) {
583            status.push(parent.attr('data-result-correct').replace('$1', correct).replace('$2', questionCount));
584        }
585
586        result += '<p class="mikiop-quiz-result-total">' + status.join('<br>') + '</p>';
587
588        parent.find('.mikiop-quiz-result').html(result).show();
589    });
590
591    jQuery('.mikiop-quiz-button-reset').on('click', function (event) {
592        quizReset(jQuery(this).closest('.mikiop-quiz'));
593    });
594
595    // `Pagination
596    jQuery('.mikiop-pagination').each(function() {
597        var pagination = jQuery(this);
598        var startId = pagination.attr('data-start') || 'start';
599        var pages = pagination.find('li:not(.mikiop-pagination-prev,.mikiop-pagination-next)');
600        if (pages.length > 0) {
601            var active = -1;
602            var found = -1;
603
604            for (i = 0; i < pages.length; i++) {
605                if (jQuery(pages[i]).hasClass('mikiop-active')) {
606                    if (active != -1) {
607                        jQuery(pages[i]).removeClass('mikiop-active')
608                    } else {
609                        active = i;
610                    }
611                }
612
613                var link = jQuery(pages[i]).find('a').attr('href');
614                if(urlMatches(link, startId)) {
615                    found = i;
616                }
617            }
618
619            if (active == -1 && found != -1) {
620                active = found;
621                jQuery(pages[found]).addClass('mikiop-active');
622            }
623
624            if (active != -1) {
625                if (active == 0) {
626                    jQuery('.mikiop-pagination').find('.mikiop-pagination-prev').addClass('mikiop-disabled');
627                } else {
628                    jQuery('.mikiop-pagination').find('.mikiop-pagination-prev').find('a').attr('href', jQuery(pages[active - 1]).find('a').attr('href'));
629                }
630
631                if (active == pages.length - 1) {
632                    jQuery('.mikiop-pagination').find('.mikiop-pagination-next').addClass('mikiop-disabled');
633                } else {
634                    jQuery('.mikiop-pagination').find('.mikiop-pagination-next').find('a').attr('href', jQuery(pages[active + 1]).find('a').attr('href'));
635                }
636            } else {
637                jQuery('.mikiop-pagination').find('.mikiop-pagination-prev').addClass('mikiop-disabled');
638                jQuery('.mikiop-pagination').find('.mikiop-pagination-next').addClass('mikiop-disabled');
639            }
640        } else {
641            jQuery('.mikiop-pagination').find('.mikiop-pagination-prev').addClass('mikiop-disabled');
642            jQuery('.mikiop-pagination').find('.mikiop-pagination-next').addClass('mikiop-disabled');
643        }
644    });
645
646    // Reveal
647    jQuery('.mikiop-box').on('mouseenter', function () {
648        jQuery(this).children('.mikiop-reveal').fadeOut();
649    });
650
651    jQuery('.mikiop-box').on('mouseleave', function () {
652        jQuery(this).children('.mikiop-reveal').fadeIn();
653    });
654
655    // Tooltip
656    jQuery('.mikiop-tooltip').hover(function (event) {
657        jQuery('<div class="mikiop-tooltip-banner">' + jQuery(this).attr('data-tooltip') + '</div>').appendTo('body');
658    }, function () {
659        jQuery('.mikiop-tooltip-banner').remove();
660    });
661
662    jQuery('.mikiop-tooltip').on('mousemove', function (event) {
663        var moveLeft = 20;
664        var moveDown = 10;
665        jQuery('.mikiop-tooltip-banner').css('top', event.pageY + moveDown).css('left', event.pageX + moveLeft);
666    });
667
668    // Nav
669    jQuery('.mikiop-nav').on('click', function (event) {
670        jQuery('.mikiop-nav').not(this).removeClass('mikiop-nav-open');
671        jQuery(this).toggleClass('mikiop-nav-open');
672    });
673    jQuery(document).on('click', function (event) {
674        if (!jQuery(event.target).closest('.mikiop-nav').length) {
675            // Hide the dropdown if clicked outside
676            jQuery('.mikiop-nav').removeClass('mikiop-nav-open');
677        }
678    });
679
680    jQuery('.mikiop-collapse').hide();
681});
682