· 6 years ago · Jan 06, 2020, 08:22 PM
1// ==UserScript==
2// @name JVChat Premium
3// @description Outil de discussion instantanée pour les forums de Jeuxvideo.com
4// @author Blaff
5// @namespace JVChatPremium
6// @version 0.1.62
7// @match http://*.jeuxvideo.com/forums/42-*
8// @match https://*.jeuxvideo.com/forums/42-*
9// @match http://*.jeuxvideo.com/forums/1-*
10// @match https://*.jeuxvideo.com/forums/1-*
11// @grant none
12// ==/UserScript==
13
14
15/*
16API : les développeurs peuvent créer des "plugins" pour JVChat à l'aide d'un système d'évènements.
17Cela permet par exemple à un deuxième userscript de modifier les messages au moment où ils sont
18ajoutés par JVChat, afin que chacun puisse personnaliser son affichage et ajouter de nouvelles
19fonctionnalités.
20
21Pour cela, il suffit d'écouter l'évènement "jvchat:newmessage". Celui-ci est émis chaque fois
22qu'un novueau message est ajouté, il contient l'identifiant dudit message à récupérer dans le DOM
23via le data-attribut "jvchat-id".
24
25Il existe aussi l'évènement "jvchat:activation" qui est émis une seule fois : à l'initialisation
26lorsque le topic pass en mode "JVChat" après appui sur le bouton.
27
28Exemple pour cacher les messages qui contiennent certains mot-clefs:
29
30 // my_plugin.user.js
31 let keywords = ["foobar", "barbaz"]
32
33 addEventListener("jvchat:newmessage", function(event) {
34 // L'id du message est stocké dans event.detail.id
35 let message = document.querySelector(`.jvchat-message[jvchat-id="${event.detail.id}"]`);
36 let text = message.querySelector(".txt-msg").textContent;
37 for (let keyword of keywords) {
38 if (text.includes(keyword)) {
39 message.style.display = "none";
40 return;
41 }
42 }
43 });
44
45*/
46
47
48/*
49TODO:
50- Smooth transition on append messages (slide-in plutôt que jump)
51- Détection captcha
52- Bouton actualiser les messages (+ afficher le delai courrant d'actualisation)
53- Notification avec @pseudo
54- Blacklist
55- Pouvoir voir les anciens messages
56- La leftbar ne se rétrécie pas si le titre du topic n'a pas d'espaces
57*/
58
59let CSS = `<style type="text/css" id="jvchat-css">
60#forum-right-col,
61#jv-footer,
62#middle,
63#zone-sponso,
64#header-top,
65#full-site,
66header.jv-header-menu,
67.titre-head-bloc,
68.bloc-pre-pagi-forum,
69.bloc-message-forum,
70.bloc-message-forum-anchor,
71.bloc-pagi-default,
72.bloc-outils-top,
73.bloc-outils-bottom,
74.option-previsu,
75.sondage-fofo,
76.nu-context-menu,
77.message-lock-topic,
78.form-post-message > .row > div:nth-child(2),
79.conteneur-messages-pagi > a:last-of-type,
80#bloc-meta-titre-jeu,
81#page-messages-forum > .container-content > .row:nth-child(1),
82.gameHeaderBanner,
83.gameHeaderSubNav,
84.bloc-sondage::before
85{
86 display: none!important;
87}
88
89html,
90body,
91#content,
92#content-context,
93#page-messages-forum,
94#page-messages-forum > .container-content,
95#jvchat-main,
96#content-forum,
97#forum-main-col,
98#forum-main-col > .conteneur-messages-pagi
99{
100 height: 100%;
101 width: 100%;
102}
103
104.jvchat-hide {
105 display: none!important;
106}
107
108.jvchat-hide-visibility {
109 visibility: hidden;
110}
111
112body {
113 overflow-y: unset;
114}
115
116.jvchat-disabled-form {
117 opacity: 0.5;
118 cursor: not-allowed;
119 pointer-events: none;
120}
121
122#bloc-formulaire-forum > .form-post-message > .row {
123 margin: 0;
124}
125
126.jv-editor > .conteneur-editor > .text-editor {
127 border: 0;
128 background: none;
129 display: flex!important;
130}
131
132#jvchat-alerts {
133 position: absolute;
134 z-index: 3;
135 right: 1rem;
136 left: 0;
137 overflow-y: hidden;
138}
139
140#jvchat-alerts .alert {
141 margin: 1rem 2rem;
142 border-radius: 0.5rem;
143}
144
145#content-context {
146 display: flex;
147}
148
149#jvchat-leftbar {
150 max-width: 15rem;
151 flex-grow: 100000;
152 flex-shrink: 100;
153 position: relative;
154}
155
156#jvchat-leftbar-button {
157 display: flex;
158 position: absolute;
159 right: 0;
160 top: 0;
161}
162
163#jvchat-leftbar-button span {
164 font-size: 1.2rem;
165 color: white;
166 padding: 0 1rem 0 0rem;
167 max-width: 100%;
168 cursor: pointer;
169 opacity: 0.3;
170}
171
172#jvchat-leftbar-button span:hover {
173 opacity: 1;
174}
175
176#page-messages-forum {
177 flex-basis: 35rem;
178 flex-grow: 100;
179 overflow-x: auto;
180 position: relative;
181 min-width: 13rem;
182}
183
184#jvchat-right-padding {
185 flex-shrink:1000;
186 flex-grow:0;
187}
188
189#forum-main-col > .conteneur-messages-pagi {
190 display: flex;
191 flex-direction: column;
192}
193
194#page-messages-forum > .container-content {
195 padding: 0;
196 max-width: unset;
197 min-width: unset;
198 min-height: unset;
199 max-height: unset;
200}
201
202.form-post-message {
203 margin: 0;
204}
205
206#message_topic {
207 resize: none;
208 min-width: unset;
209}
210
211.jvchat-edition-textarea {
212 resize: none;
213 width: 100%;
214 max-height: 6.5rem;
215 min-height: 3.5rem;
216}
217
218.jvchat-reduced #message_topic {
219 padding: 0.3rem;
220 height: 1.7rem;
221 max-height: 6.5rem;
222 overflow: auto;
223}
224
225.jvchat-reduced .jv-editor .conteneur-editor > * {
226 display: none;
227}
228
229#jvchat-buttons-main button {
230 padding: 0;
231 width: 2rem;
232}
233
234#jvchat-buttons-main button::before {
235 font-size: 1.4rem;
236}
237
238#jvchat-buttons-main button.icon-reply::before {
239 font-size: 1rem;
240}
241
242.jvchat-buttons {
243 display: flex;
244 flex-direction: column;
245}
246
247.jvchat-buttons button {
248 border: 0.0625rem solid #BEBECC;
249 border-left-width: 0;
250 height: 100%;
251 background: white;
252 color: gray;
253}
254
255.jvchat-buttons .jvchat-button-solo {
256 border-radius: 0 0.3rem 0.3rem 0;
257}
258
259.jvchat-buttons .jvchat-button-top {
260 border-radius: 0 0.3rem 0 0;
261}
262
263.jvchat-buttons .jvchat-button-bottom {
264 border-radius: 0 0 0.3rem 0;
265 border-top: 0!important;
266}
267
268.jvchat-textarea {
269 border-radius: 0.3rem 0 0 0.3rem!important;
270 border: 0.0625rem solid #BEBECC!important;
271 border-right-width: 0!important;
272}
273
274.jvchat-buttons button:hover {
275 background: lightgray;
276 color: black;
277}
278
279.jvchat-buttons button:focus {
280 border: dotted 1px!important;
281 color: black;
282 border: blue;
283}
284
285#content {
286 background-image: none;
287 margin-top: 0;
288}
289
290#content-forum {
291 margin: 0;
292}
293
294#forum-main-col {
295 display: block;
296 padding: 0;
297 max-width: unset;
298}
299
300#jvchat-leftbar > .panel {
301 margin: 0;
302 padding: 0 0.5rem;
303}
304
305#jvchat-leftbar #jvchat-profil .titre-info-fofo,
306#jvchat-leftbar #jvchat-configuration .titre-info-fofo {
307 margin-top: 0.5rem;
308}
309
310#jvchat-configuration-intro {
311 color: grey;
312 font-size: 0.84rem;
313}
314
315#jvchat-leftbar .titre-info-fofo {
316 margin-top: 1rem;
317}
318
319.jvchat-config-option {
320 margin-top: 2rem;
321}
322
323.jvchat-config-option p {
324 font-size:0.83rem;
325}
326
327.jvchat-config-option > label {
328 margin-bottom: 0.15rem;
329}
330
331.jvchat-range-option {
332 display: flex;
333
334}
335
336.jvchat-range-option > span {
337 white-space: nowrap;
338 margin: 0px 10px;
339}
340
341.jvchat-range-option > input {
342 width: 65%;
343}
344
345.jvchat-message {
346 display: flex;
347 margin-bottom: 0.35rem;
348}
349
350.jvchat-bloc-message {
351 animation-duration:0.5s;
352 animation-name: slidein;
353}
354
355.jvchat-message-deleted > div {
356 opacity: 0.2;
357 filter: grayscale(100%);
358}
359
360.jvchat-message-deleted {
361 position: relative;
362}
363
364.jvchat-message-deleted:after {
365 content: "Message supprimé";
366 position: absolute;
367 top: 40%;
368 left: 50%;
369 color: gray;
370 font-weight: bold;
371 opacity: 0.7;
372}
373
374.jvchat-message-deleted .jvchat-delete,
375.jvchat-message-deleted .jvchat-edit {
376 display: none;
377}
378
379@keyframes slidein {
380 from {
381 opacity: 0;
382 }
383
384 to {
385 opacity: 1;
386 }
387}
388
389.jvchat-toolbar {
390 margin: 0 0 .3rem 0;
391}
392
393.jvchat-author {
394 margin: 0;
395 display: inline-block;
396}
397
398.jvchat-tooltip {
399 display: flex;
400 float: right;
401 color: gray;
402}
403
404.jvchat-picto {
405 margin-right: 0.3rem;
406 visibility: hidden;
407}
408
409.jvchat-bloc-message:hover .jvchat-picto {
410 visibility: visible;
411 cursor: pointer;
412 opacity: 0.25;
413}
414
415.jvchat-picto:hover {
416 opacity: 1!important;
417}
418
419.jvchat-edition {
420 display: flex;
421}
422
423.jvchat-edition-check {
424 color: darkgreen!important;
425}
426
427.jvchat-edition-cancel {
428 color: darkred!important;
429}
430
431.jvchat-edition-textarea {
432 width: 100%;
433}
434
435hr.jvchat-ruler:first-of-type {
436 margin-top: auto;
437}
438
439.jvchat-ruler {
440 margin: 0rem 0rem .35rem 0rem;
441 border-style: solid;
442 border-width: 0.0625rem;
443 border-block-end-color: #ddd;
444}
445
446#jvchat-ruler-new {
447 border-bottom: 1px outset gray;
448}
449
450.jvchat-bloc-author-content {
451 overflow: hidden;
452 width: 100%;
453 margin-left: .875rem;
454}
455
456.jvchat-content > .txt-msg > p:last-of-type {
457 margin-bottom: 0;
458}
459
460.jvchat-content > .txt-msg p {
461 margin-bottom: 0.2rem;
462}
463
464.jvchat-content .text-enrichi-forum blockquote.blockquote-jv {
465 margin: 0.2rem 0;
466 padding: 0rem 0.3rem 0 0.3rem;
467}
468
469.jvchat-content .text-enrichi-forum .nested-quote-toggle-box {
470 position: relative!important;
471}
472
473.jvchat-rounded {
474 overflow: hidden;
475 border-radius: 50%;
476 background-size: cover;
477 background-repeat: no-repeat;
478 background-position: center center;
479}
480
481.jvchat-bloc-avatar {
482 min-width: 40px;
483 min-height: 40px;
484 width: 40px;
485 height: 40px;
486 box-shadow: -3px 3px 7px grey;
487}
488
489#jvchat-user-avatar-link {
490 width: 60%;
491 min-width: 3rem;
492 min-height: 3rem;
493 margin: auto;
494}
495
496.jvchat-user-avatar {
497 width: 100%;
498 padding-top: 100%;
499}
500
501.jvchat-content .img-shack {
502 height: 39px;
503 width: 52px;
504 display: inline-block;
505 vertical-align: bottom;
506 margin-bottom:0.27rem;
507 overflow: hidden;
508}
509
510.jvchat-content .img-stickers {
511 max-height: 39px;
512 min-height: 39px;
513 width: auto;
514 display: inline-block;
515 vertical-align: bottom;
516 margin-bottom:0.1rem;
517}
518
519#jvchat-main .bloc-spoil-jv .open-spoil {
520 position: unset;
521 display: none;
522}
523
524.new-stickers {
525 background-color: unset;
526}
527
528#jvchat-user-mp {
529 font-size: 1.3rem;
530 margin: auto;
531}
532
533#jvchat-user-notif {
534 font-size: 1.7rem;
535 margin: auto;
536}
537
538#jvchat-user-pseudo {
539 margin: 0.5rem;
540 text-align: center;
541 /* overflow-x: hidden; */
542}
543
544#jvchat-user-info {
545 display: flex;
546}
547
548#jvchat-user-info .jv-header-menu {
549 position: unset;
550 display: flex;
551 justify-content: center;
552 flex-direction: column;
553 margin: auto;
554}
555
556#jvchat-topic-link {
557 color: white;
558}
559
560#jvchat-topic-info {
561 display: flex;
562 flex-direction: column;
563}
564
565#jvchat-topic-nb-connected {
566 color: lightgray;
567}
568
569#jvchat-topic-nb-messages {
570 color: lightgray;
571}
572
573#bloc-formulaire-forum .jv-editor > .conteneur-editor {
574 margin: 0;
575 border: 0;
576 padding: 0.5rem;
577 line-height: normal;
578}
579
580#jvchat-main {
581 overflow-y: auto;
582 padding: 0.35rem 0.875rem;
583 display: flex;
584 flex-direction: column;
585}
586
587#jvchat-leftbar > .panel-jv-forum {
588 height: 100%;
589 overflow-y: auto;
590}
591
592#jvchat-leftbar.jvchat-leftbar-reduced {
593 flex-grow: 0;
594}
595
596#jvchat-leftbar.jvchat-leftbar-reduced > .panel > .panel-body {
597 display: none;
598}
599
600#jvchat-leftbar.jvchat-leftbar-reduced #jvchat-leftbar-button {
601 position: relative;
602}
603
604#jvchat-leftbar.jvchat-leftbar-reduced span {
605 padding: 0;
606}
607
608#jvchat-leftbar > .panel {
609 position: relative;
610}
611
612.disabled-content {
613 opacity: 0.3;
614 cursor: not-allowed;
615 pointer-events: none;
616}
617
618#jvchat-sondage-bloc {
619 background: unset;
620 padding: 0;
621}
622
623#jvchat-sondage-bloc .pourcent {
624 background: unset;
625 width: 5rem;
626}
627
628#jvchat-sondage-bloc .back-barre {
629 width: 5rem;
630}
631
632#jvchat-sondage-bloc .tab-choix {
633 margin-bottom: 1rem;
634}
635
636#jvchat-sondage-choix.notanswered .result-pourcent {
637 display: none;
638}
639
640#jvchat-sondage-choix.notanswered .reponse {
641 background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAN1wAADdcBQiibeAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAA5SURBVAiZbY7BDQAgDAKPTqaTOxp+rDaNvAhcAvLAACyLo0jDlF9YiCxk+0N+FJ1iWbrr5UH0AGADblEVRpZSAyQAAAAASUVORK5CYII=') no-repeat left 0.35rem;
642 padding-left: 0.7rem;
643}
644
645#jvchat-sondage-choix.notanswered .reponse > div {
646 cursor: pointer;
647}
648
649#jvchat-turbo {
650 display: inline-block;
651 cursor: pointer;
652 margin-top: 0.3rem;
653 -webkit-tap-highlight-color: transparent;
654 position: relative;
655}
656
657#jvchat-turbo i {
658 position: relative;
659 display: inline-block;
660 margin-right: .5rem;
661 width: 46px;
662 height: 26px;
663 background-color: #e6e6e6;
664 border-radius: 23px;
665 vertical-align: text-bottom;
666 transition: all 0.3s linear;
667}
668
669#jvchat-turbo i::before {
670 content: "";
671 position: absolute;
672 left: 0;
673 width: 42px;
674 height: 22px;
675 background-color: #fff;
676 border-radius: 11px;
677 transform: translate3d(2px, 2px, 0) scale3d(1, 1, 1);
678 transition: all 0.25s linear;
679}
680
681#jvchat-turbo i::after {
682 content: "";
683 position: absolute;
684 left: 0;
685 width: 22px;
686 height: 22px;
687 background-color: #fff;
688 border-radius: 11px;
689 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.24);
690 transform: translate3d(2px, 2px, 0);
691 transition: all 0.2s ease-in-out;
692}
693
694#jvchat-turbo:active i::after {
695 width: 28px;
696 transform: translate3d(2px, 2px, 0);
697}
698
699#jvchat-turbo:active input:checked + i::after {
700 transform: translate3d(16px, 2px, 0);
701}
702
703#jvchat-turbo input {
704 display: none;
705}
706
707#jvchat-turbo input:checked + i {
708 background-color: #4BD763;
709}
710
711#jvchat-turbo input:checked + i::before {
712 transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0);
713}
714
715#jvchat-turbo input:checked + i::after {
716 transform: translate3d(22px, 2px, 0);
717}
718
719#jvchat-turbo-span {
720 position: absolute;
721 margin-top: 4px;
722}
723
724#jvchat-main.jvchat-hide-mosaics .jvchat-mosaic-root::before {
725 content: "Mosaïque Cachée ¯\\\\_(ツ)_/¯";
726 color:black;
727 text-align:center;
728 font-size:10px;
729 display:block;
730 height:55px;
731 width:55px;
732 background: white;
733 border: solid #f00;
734}
735
736#jvchat-main.jvchat-hide-mosaics .jvchat-mosaic-root {
737 pointer-events: none;
738}
739
740
741#jvchat-main.jvchat-hide-mosaics .jvchat-mosaic {
742 display: none;
743}
744
745#jvchat-turbo-delay-range,
746#jvchat-max-width-range,
747#jvchat-hide-mosaic-checkbox,
748#jvchat-hide-mosaic-span,
749#jvchat-night-mode-checkbox,
750#jvchat-night-mode-span,
751#jvchat-load-images-checkbox,
752#jvchat-load-images-span,
753#jvchat-play-sound-checkbox,
754#jvchat-play-sound-span {
755 cursor: pointer;
756}
757
758#content.jvchat-night-mode #jvchat-leftbar > .panel {
759 background-color: #2F3136!important;
760}
761
762#content.jvchat-night-mode {
763 color: #dcddde!important;
764 scrollbar-color: #191A1C #2F3136!important;
765}
766
767#content.jvchat-night-mode .jvchat-ruler {
768 border-color: #2e3035!important;
769 border-top: 1px solid transparent !important;
770}
771
772#content.jvchat-night-mode .conteneur-editor,
773#content.jvchat-night-mode .bloc-editor-forum,
774#content.jvchat-night-mode .jvchat-bloc-message,
775#content.jvchat-night-mode .conteneur-messages-pagi {
776 background-color: #36393F!important;
777}
778
779#content.jvchat-night-mode #message_topic,
780#content.jvchat-night-mode .btn-group {
781 background: #484C52!important;
782}
783
784#content.jvchat-night-mode .jvchat-bloc-avatar {
785 box-shadow: -3px 3px 7px black!important;
786}
787
788#content.jvchat-night-mode #jvchat-leftbar > .panel {
789 background-color: #2F3136!important;
790}
791
792#content.jvchat-night-mode .jvchat-edition-textarea {
793 background-color: #2a2a2a!important;
794}
795
796#content.jvchat-night-mode .jvchat-buttons button {
797 background: #484c52!important;
798}
799
800#content.jvchat-night-mode .text-enrichi-forum blockquote.blockquote-jv {
801 border-left-color: #484C52!important;
802}
803
804#content.jvchat-night-mode .text-enrichi-forum .nested-quote-toggle-box::after {
805 background-color: #484c52!important;
806 border-color: #737373!important;
807 color: #737373!important;
808}
809
810#content.jvchat-night-mode .text-enrichi-forum .nested-quote-toggle-box:hover::before {
811 color: #cbcdce!important;
812}
813
814</style>`;
815
816let PANEL = `
817<div id='jvchat-leftbar'>
818 <div class='panel panel-jv-forum'>
819 <div id="jvchat-leftbar-button">
820 <div id="jvchat-leftbar-config">
821 <span id="jvchat-leftbar-config-open" class="nav-icon-config" title="Afficher les paramètres"></span>
822 <span id="jvchat-leftbar-config-close" class="nav-icon-close jvchat-hide" title="Fermer les paramètres"></span>
823 </div>
824 <div id="jvchar-leftbar-size">
825 <span id="jvchat-leftbar-reduce" class="icon-arrow-left" title="Masquer la sidebar"></span>
826 <span id="jvchat-leftbar-extend" class="icon-arrow-right jvchat-hide" title="Afficher la sidebar"></span>
827 </div>
828 </div>
829 <div class='panel-body'>
830 <div id='jvchat-info'>
831 <div id='jvchat-profil' class='jvchat-hide'>
832 <h4 class='titre-info-fofo'>Profil</h4>
833 <h4 id='jvchat-user-pseudo'></h4>
834 <div id='jvchat-user-info'>
835 <a title="Ouvrir le profil" id="jvchat-user-avatar-link" target="_blank"><div id='jvchat-user-avatar' class='jvchat-rounded jvchat-user-avatar'></div></a>
836 <div class='jv-header-menu'>
837 <a target="_blank" href="http://www.jeuxvideo.com/messages-prives/boite-reception.php" title="Ouvrir la boîte de réception" id="jvchat-user-mp-link"><span id="jvchat-user-mp" class="jv-account-number-mp" data-val="0"></span></a>
838 <a target="_blank" title="Ouvrir les notifications" href="http://www.jeuxvideo.com/messages-prives/boite-reception.php" id="jvchat-user-notif-link"><span id="jvchat-user-notif" class="jv-account-number-notif" data-val="0"></span></a>
839 </div>
840 </div>
841 </div>
842 <div id='jvchat-topic'>
843 <h4 class='titre-info-fofo'>Topic</h4>
844 <div id="jvchat-topic-info">
845 <div><strong><a title="Ouvrir le topic" id="jvchat-topic-title"></a></strong></div>
846 <span id="jvchat-topic-nb-connected"></span>
847 <span id="jvchat-topic-nb-messages"></span>
848 <label id="jvchat-turbo" title="Actualise la liste des messages plus rapidement">
849 <input id="jvchat-turbo-checkbox" type="checkbox">
850 <i></i>
851 <span id="jvchat-turbo-span">Mode Turbo</span>
852 </label>
853 </div>
854 </div>
855 <div id='jvchat-forum'>
856 <h4 class='titre-info-fofo'>Forum</h4>
857 <div id="jvchat-forum-info">
858 <div><strong><a title="Ouvrir le forum" id="jvchat-forum-title"></a></strong></div>
859 </div>
860 </div>
861 <div id='jvchat-sondage' class='jvchat-hide'>
862 <h4 class='titre-info-fofo'>Sondage</h4>
863 <div id="jvchat-sondage-bloc" class="bloc-sondage">
864 <label><span id="jvchat-sondage-intitule"></span></label>
865 <table class="tab-choix"><tbody id="jvchat-sondage-choix" class="notanswered"></tbody></table>
866 <span id="jvchat-sondage-votes"></span>
867 </div>
868 </div>
869 </div>
870 <div id='jvchat-config' class='jvchat-hide'>
871 <div id='jvchat-configuration'>
872 <h4 class='titre-info-fofo'>Configuration</h4>
873 <p id='jvchat-configuration-intro'><i>Les paramètres sont automatiquement sauvegardés et mis à jour lorsque vous les modifiez.</i></p>
874 <div class="jvchat-config-option" id="jvchat-play-sound">
875 <label>
876 <input id="jvchat-play-sound-checkbox" type="checkbox">
877 <span id="jvchat-play-sound-span">Alerte sonore</span>
878 </label>
879 <p>Joue un son de notification lorsqu'un nouveau message est posté et que vous êtes sur un onglet différent.</p>
880 </div>
881 <div class="jvchat-config-option" id="jvchat-night-mode">
882 <label>
883 <input id="jvchat-night-mode-checkbox" type="checkbox">
884 <span id="jvchat-night-mode-span">Thème sombre</span>
885 </label>
886 <p>Active un mode nuit pour protéger vos petits yeux fatigués le soir.</p>
887 </div>
888 <div class="jvchat-config-option" id="jvchat-load-imagesc">
889 <label>
890 <input id="jvchat-load-images-checkbox" type="checkbox">
891 <span id="jvchat-load-images-span">Charger les images</span>
892 </label>
893 <p>Remplace les miniatures NoelShack avec l'image source complète afin de laisser apparaître la transparence (cela sollicite davantage votre connexion Internet).</p>
894 </div>
895 <div class="jvchat-config-option" id="jvchat-hide-mosaic">
896 <label>
897 <input id="jvchat-hide-mosaic-checkbox" type="checkbox">
898 <span id="jvchat-hide-mosaic-span">Masquer les mosaïques</span>
899 </label>
900 <p>Cache automatiquement les mosaïques d'images NoelShack pour réduire le flooding.</p>
901 </div>
902 <div class="jvchat-config-option" id="jvchat-turbo-delay">
903 <label>
904 <span>Délai Turbo</span>
905 </label>
906 <div class='jvchat-range-option'>
907 <input id="jvchat-turbo-delay-range" type="range" min="0" max="1000" step="50">
908 <span id="jvchat-turbo-delay-span">? ms</span>
909 </div>
910 <p>Ajuste le délai d'actualisation des messages en mode turbo (celui-ci permet de mettre à jour les messages plus rapidement, mais est déconseillé si vous avez une connexion instable).</p>
911 </div>
912 <div class="jvchat-config-option" id="jvchat-max-width">
913 <label>
914 <span>Largeur des messages</span>
915 </label>
916 <div class='jvchat-range-option'>
917 <input id="jvchat-max-width-range" type="range" min="0" max="100" step="1">
918 <span id="jvchat-max-width-span">? %</span>
919 </div>
920 <p>Configure l'espace utilisé horizontalement par les messages (une valeur réduite facilite la lecture sur les écrans larges).</p>
921 </div>
922 </div>
923 </div>
924 </div>
925 </div>
926</div>
927`;
928
929function getForm(doc) {
930 return doc.getElementsByClassName('form-post-message')[0];
931}
932
933function getHash(doc) {
934 let hash = doc.querySelector("#ajax_hash_liste_messages")
935 if (!hash) {
936 return undefined;
937 }
938 return hash.getAttribute("value");
939}
940function getDeletionHash(doc) {
941 let hash = doc.querySelector("#ajax_hash_moderation_forum")
942 if (!hash) {
943 return undefined;
944 }
945 return hash.getAttribute("value");
946}
947
948
949let freshHash = getHash(document);
950let freshDeletionHash = getDeletionHash(document);
951let freshForm = getForm(document);
952let firstMessageId = undefined;
953let firstMessageDate = undefined;
954let lastEditionTime = {}; // id => [timestamp, edition] (Value should be set to 'false' for deleted messages)
955let messagesByPage = {};
956let userConnected = undefined;
957let updateIntervals = [2, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 30, 30, 30, 30, 30, 30, 30, 30, 60];
958let transisitions = [0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 19, 19, 19, 19, 19, 19, 19, 19, 31];
959let updateIntervalIdx = 0;
960let updateIntervalMax = updateIntervals.length - 1;
961let isLocked = false;
962let isError = false;
963let isReduced = true;
964let nbNewMessage = 0;
965let favicon = makeFavicon();
966let currentUser = { notif: undefined, mp: undefined, author: undefined, avatar: undefined };
967let currentTopicTitle = undefined;
968let turboActivated = false;
969let sondageChoices = undefined;
970let urlToFetch = undefined;
971let urlToCheckEdit = undefined;
972let currentFetchedPage = 1;
973let currentTimeoutId = -1;
974let shouldCheckEdited = false;
975let checkEditInterval = 30000;
976let postingMessage = false;
977let fetchingMessages = false;
978let leavingTopic = false;
979let storageKey = "jvchat-premium-configuration";
980let ringBell = undefined;
981let configuration = {
982 default_reduced: false,
983 turbo_delay: 500,
984 max_width: 100,
985 play_sound: false,
986 night_mode: false,
987 load_images: false,
988 hide_mosaic: false,
989 sound: "data:audio/mp3;base64, SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjI3LjEwMgAAAAAAAAAAAAAA//uQwAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAVAAAj6gAXFxcXIiIiIiIuLi4uLjo6Ojo6RUVFRVFRUVFRXV1dXV1oaGhoaHR0dHR/f39/f4uLi4uLl5eXl5eioqKirq6urq66urq6usXFxcXF0dHR0d3d3d3d6Ojo6Oj09PT09P////8AAAAATGF2YzU4LjUxAAAAAAAAAAAAAAAAJAYeAAAAAAAAI+ptpORkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//uQxAADk4YMnkEhl4L4QRnAkKWBAABvophhAsi/Z+qFd2/d6uJ0lh9XV7KPPMdJgaUaIgujxzFGPE2ktRjxMTdXafzC28Vcbwo49HSYm3ib//mKurj+JgaIQcgyDMRDNi9L0vfOvRuBajLZ43Tc3OpfNxlcpVn6zZnYGX17G2cbSHJsbni9huBKuNCwvyZzbROL6bBCmMSiP5MXwbM1+PvnK2dVn6zf7crHG5/f1on7+GLo95cEwwKGORk76jMgZR0gwgc35/kBJJRgjfGcBGBAjaY/LgmbxIEFRWTz65JBdui5ORgmSZOc/7Xd7nyMVituHKOKAYd/NcnrKFBiewUYXUFBinxRqR3qMKMdQUMYvs6tHvTBM3BG3y4Az8GFwTJ+u3/hBFv/qEme5IECCBACCIxepOI24UoSRC4/CEFDorbpAFAQJDfRpMeF6RrMo9pAupKGT678mgVJ/ygADLcECbamKEkCMA4G266kS6ogAAE6IOYiTB3hct+2Zn5mf37DhZ3bfsbXvn9nPxhyEQwPiPGsPKHktr41h5CYLHeO//uSxBiAWpYI6qMlhgsfQJ8UkydJz/FiwzXksn2WZt2wbiOT473mjZm+2vP7TBGSCwzGsMDBYrOCZ2OQvzy9/jh1eJZmeUHAmRTRu86vudv9swiWfsQn/sLDNf7FV8evkgwq/Pbe7b8z18Xxr7HB4sWcwYCOT7wLGV71HxzBujMIjM/M3KUWLFhgYGC/5bfOBIMHCoYcsocRMUJBgTDBxYsu/NFk9RxxhfHczMxDEc/Xzx2JZ+Zv0IIIRREywsymjLMljdU/60XWzDLlSC14oaEi6FAycKiUdQhsTk4sJl5Km0TcSUPFjtQSreVAGPChZAFsiLMOhR8RaqPIqT6SJEKcWCxAXJ1hpM4QJKtJE54NLEQy2nWLIi9U2uUtm5Mi8ZyGzxAQDsT725QkyqhRsng0rNGJ9w0gc+goygow8MQNwgSzklPWsIzKZRlNCRLE4VdFnEotKEiq7i0idygay2ER4WJ3NFSIujLui6pu3G0fal9afC2ZoUUKjIWYLoRzdUAAAdtpSljUoFR5tpa6VJceiiaHDKyryQtY1RaBIqHKCP/7ksQQgdjCDPikmTNDCcEfFpKQAbMWTEgjwookDhlpkMDi0ThgQQoUYMvTxCPIpgE8QtE+iKLJFCaiS1VcPj7oIJ1mKzmPH00MlFEbM0bI7PKWKxNGYJ56kmuwC4mXmpBsPHBE/TZKZgoda0PsIiRuyBhhD0MN9lhsgGl+F5oGnFyLHqRdYhkJh0hm1iCrUgZu7XP1B9HTSYJ8wII2TmTBoiHjR5VyN57mBimSWQ8YQCp74PyStZBpfQiPUwwo6J29FKqU5tm51Ml50i0jbJAqtNDvaWenZaLiyjVFVm4ymRLzQxQYhqDaqMqsVbiojXw5QqNIrg0JaYabfFVZCUQKnzkqiTMMGS1uJywzXkqjbe2Xb7/JFjEpL8rJAmhXQoQsnBah1z8ONl7KYomR0StnWab0jE1XhhRG66EiDQems0rU+WQkicIzbs1AUmyClUCGCbRAcWGpIiQqwzofM0sTsnjaSMrFlzTK6jiOKzxWxcbRpa5glRW0Zqmi6gBBDBEACALWUugs4tZMZIP8t+jGcU5EGdz1uIbn9Zh2zVgWEYb/+5LEEoAZei0VGbqAAy4iZKs7oAD4BmU5AbSaCkossWMiAGpiYCivAOCqbppug0DDoJAABgGEgEOTfZrLWJiFgwt7Aw2DQsrqv03d1EmTAjgPUFOBtRva5vtgMAACQDI4kBcZEzjaVfX06eHphaOLGZl4nBH5sXNuvUqv1LescZFzcnDE3LiEc8n+y6PrQTqq7VLqTpIkXJ9Avl83PZgThomlapdr6ft/9f7+tW//5mm7JsgjNkC4X3ZMzAAAAWAbmrqUSQAAMVRlNvVeMfhNPPjuMQluMzHVNFVoNDkZMSTSMBQXLhiQNGGQ3mU4lGAA0g4XDAcDTI4TjiCTXC29d8FGlNhGQIQoEFv2p2kTIoLYcFnAcTdqXkJYrJ3Nr/GhVFgqsW+AqwFXnOtaVONCuUD+khdnV6hYi5CYQyJOABicvrN3VRIiud/LF9W9VzDL+vqyafdL+fu1nj//j/+QB0cJXvmcipYZeFqOHN83j9LAvP7r//8ccsq3fv1b/2fgHBqPb//7qgAAAJBWQAAAADDJAdOccBowvjtzJ2QSML4X//uSxA0AGHTtFzntAAL5J+S3O1AAI5dhYzFfBjMEoF8KgBg4MMw6AxDAyA/MJQFkyHgazC0D4MRAKswIBCgMHUBFYYFLyp4qxxsKjELWfjSBcjM2GS26FprSHdvAiaPSLkqCxUWyTn3sxEBodWeOHGMsuujCLFNlrKZiu9/rOi5lrW7Pf3h39d/+7//7++c/3s1jvWu9uS3veb/LdYk+01yt1xNZEA0srFD4o/RSMTqzqn/+Q+Gb5HmneeUe/OgAAACwRggShMAAAAEAGNrBG1LlmNJYn755GQCQnXzmGC4dCoEmOYSo4mCQkm7SmBUGh4swwAwcXRi0XZouO4GCAYF9wFgARgGlD0BkwBGw8GJgPBfAw2DwMsDYAYSByAgCbOGRhzrhjELogYHBYIgGi6CMrnSBHzQDC4PAwOAQ48dYYjXfZE0WmziDwbXEDiUBKYzfr/5XIAQRgbHCWTr//8nEEDAgBUEEC1///8uFw0J8vn2Jsrmjf6/3wQghAAc1zBymsrBAAhYOnDowmajnJNAJXOSyc+coDERfPcFAmEp6gP/7ksQSgBnxJTT5zQADCJ3iw7/QAGgYfG+lOYsQQjHpksZBwhMUCExpA8DM8lcBGT6xmdlY4CBVYX2aVD0ASOTs5ZaUDB61JngKhoeHRKYEZSVOeKjDDsjCGQMVdd/Y3PSmB5VJtygVCAaKCp0lsPBLSEU61/WebEmzb/Fc0rTRMcLNENSJV7zdZNqf3zPXHGfrf3a0Aw9IgYFDDqsLWcL/43VsOzy5///vV3/pqbeXZJALQJVXluPP/+/Z5/yRYKgFZgMQFSYIsESmF7D1ZhTYWGYuyxYmKBhV5giwHOYFkA/goA0MB4AnDAXQDswOkA0AgEQYACA+mAXARZgRQASYBUBlmSA5n0hzmQIlmGQKmMAGmDwMqNICwSAAsBCUiGzZUvVyxi3X1HYym8YHg0VjOYdAWmbAUxWlkuf7v1cpbRdyrVfkFSMpGmEwHl2W9lt7f2P1g6Xb//+OOqtncGpWu9R/yrzv7z5//////vXcMqa1YoyQO6V/IPT+/5yr/7dyAEOGQxkBUzVMo9K5gxZkF2NAyJAjMCwUIDByBvkYmMD/+5LEEAAWsLUULv+CQ3qq6XW8vj7iYmQRn4ogkiiMZGTxga1Ixm+VH4i+HBQ1C3DaqGMehIxyNQEuGHUbT6URgmpbp5mZLZVOSR9l9A4ogABg4EtdcGXQdW13vf1e5y/T361NK11w67T9drzmFTv3LPKnf5vP+ct3aXIJXstY1DoXYoeTLtY8Ecag+evA7depy32yo0NisJLa5gsRUasPb/Fxf0NdBMjFMVQAKQJkUksbEuXnBRCMEAIZbbAoMQFjwOW7BwcYkEEwCWzUnDEMNbU0gld8LjEHOm18oDBIZhkmiiZYJomlAYEARpL/pwQ3L4hRV7copIMXRLWttff+N28IbcuAgIYbkhxKJAln0+1b1dtwXQ0lMNsCDj+piO6oHAy5HkaW67gJrr4TAaKXfZ0YBxsPG4gLJo2KWN3YnIog5BBBD0cK4SxJmW3MinRh/mWuDIXZc1coGp0p3ZIAUgxEWZbah7coIqvneRMZv9X3im6f3+M3pKxwjkdF/IWq0+q4TJK/tErVADlC2K3ggCkwexEjEKCdMrlb0yJgXzBD//uSxAwDF4FDKE9p6UsXKWKF/lE6B5MQQpA0qBFDrVwswNikEhZjBosQNDAAfY0R43a4iLAQWY4WQiAgsBAJI7McCMSlMcwOOUDBEVGYhRcjHPt6ynFGguLc9o+3CteK1k4Tr8kABUaI+mlfNNTqlucpWWErqMV3un1s1bVbAVzedMQBSAQi7xF5mnexdQmFli4jZr7Y3WuN1i41i+oBfkKfS7vXVvWtf/////////atcQlc5A0XJLtAAwAXEQBYAQIUwRENfMJ2BvzMkCgMxi0HLMGGBFjCvxag1hQCfAaVMzlsxAOwUHioAzH4KEJHMIoczWPU1Ep1xighBwNlCE8wknT2o0Gi8jSyZ1Xmlr4s1IQs/7hLzVNL3yyyLpwyTWkdIYOeBiUgyhZKTmCB0gIeqGbZmRMi8ZEqSKkkltZF3VompFRaQYCNVot1IPrZFmWyK2oVEOWtFrKky+rWvS0k6lKrV6nWjUyaJ1AqgKOI34wQdW8lev0QH6PyXkUh4ABQAELABpgCICKYDQNTmAJBBhiRqWAYaSDuGBlANxhMY//7ksQRgxgJAxJP7mnC+h3iCf29KBya04E2mwHhMSkSg5QIBTKhIqD51jSf2PKDqdO2IhNWlgSK5jiMa1MGCAaIXKCZ00aBhAFvJDLAR4Flkt1pJu6zhDgQ3BQJWYgI7TqJTCBAZpFM0M2OIjOjEWaKRc+dMWMkmqL6Q+AspMFKWnZ93tdFVq2sss+tHKW+jSuFzF6V2EjsKExCpZMNxz5PuAiZDNNiEKihvEECT1upMDrEGEQAABiEAkAICuYLsHkmFfgdJjNIqaYwsBAmBMgahgkJoGaSkGimoqhpQQY6aGBBIXFDDC8RDBFIGtmBMBPNExgNVIydIwxkAOcf0BjFndzSxG1CAfNLtuFpXSah9vzjeYdIb0BtR9JnzBrNwUUebNvrOF/Wc5zTyvYu3mswawCiFHuW+4WL/FPjecf0z//8YgDk0BxYFAY0VLLNNK0ix41GBUUSdm1A6sI3gUNJoSDw4a0JzrvvfY+/edrvtQQ5C237UsAQFxhfD8GOGFaYwjkZh8humBoDAYBThJpjDAntjAAeClRfFPomLIhGnAD/+5LEGIIR5V8ab2lJQimXI2ntrSjzimptsZ1R1UEIRuaFYnjUiQ4LJZCghwvyzt98qCIKZCIkG5A5MC8DSXLIrKYQliQs73NbPqTMa4F2Xtdb0/t9dD/kPXyb5h81qLRTmZbTX3c9G2yYbRSsXYGbCdCWgQDJEkUXeAABIBA8Kpahg7hLGWkWQZZIIBglAAGDAtSYD4b5wKMZMeo1IowMLECh4GKhINq2Lo6Ax5+JMvADAkp5dvkrx3XMiCWP9JrTMrQ4nANCAs4TiHmHDyCK0t22xjT/VXrz8VN/EnDOupZ7RMOkaFw4HNlRAbL2xrh31zqGXl8qV3i7f//+xREAIYC4FpgnA+GPicAZ2BExn/96GUkPwYXoU5jiTLm2KEeYh4FA8GgADAIAgEAMLTAHSdiNLkUljPSQBY1VfhX5kj+D0u9jZ26E0/u6OIuRBU9nxGbjqNIH1mOk3pyofR5LpZEXy3qInnZbt9KVJC1VV8dW6tSK/lKf6OWaktS9un1Omej9Mmn5eg7NzmIbnzXxyn5X9/b1qB8fFzokA1E/8j80//uSxFIDVSzjDA9pa4qhFqHN7bEpNu3lv/0gQgwDgDzApAaMFQGExczSTM8ENNT9lk1RAezDpAJMVBDk3OwBQ82MiFjJRYLAa7zOQAvMaABhBdDb0Q4hxkdmXtZBQtKpmN682IenWxUDayn8HdU4/PoozEkK5sxBbqnZ27tsn/3MvXstud2Ts8vhEc8JbZLEEO9Vfdb3hOted5oZ0Yv9H9dx398lx3b+bz4b+r6xIVKArPymZ/3J0v6Pd8I5/wN6tqfagAEAEjAFRgAhFmDYl6YIo1ZmSVYmQ6KuYFACRhiBdG3yEccYQSGGNKwq3hNBCsPpl4JqngksBtOp41KhZx2McK2HWcVodMxIeJhySqS8T0MA6xyDFHzIsLo0739X0MH3dVP8p2P5yGPtELNuz9ixz8V592fnk9scnZa+ti+rbezfkEce0380P8J/Wl+nAh/eL3Om8yJJf/96DfY1/ntiYCYAAOBBBQS5jEFnmYOFQaYzgBpmhgGEAD+YPiLRo9jGBZcyPTePRmIQDTVGQz1OKLVa2LuqgRe+28jSzBFmq//7ksRvg1Pcuw4vZQmKdxChgeyZOUvpr3H/sRr+9avTcxw/z/G7BaN4gcljCFoqjbj/WmNoFndqEZ1eUwfb/VPpO9kThpyDLLBUUd1yiRb1NwL7LUurG6E/7vanMq2lNjcfp+UIUZJ9r/+39Mf73fKITr1AAaAQYAAD5gHgxGHgWQZH4uRmezjGTYKQYNoPZgQKHGVsLcc6OYWAAAY0OVXNOaVvBUgvmEBX8oVFWd1Lr6GBBYZS6VwCWEJ4oE54eidU+raI7A16k5FhgkIX6kmOKv9Jf+2hbkdCXW8zxNLET8VdfNQtP3w3K2vHUT3w336cfqn19RtHMRU8Xr93z9rHxSU9tzP/U/0lfX58tXU1FM0X+/flWWaYPQMMoAwBQkAFUqG5gDAWGCea4YLgG5lmpumYMD2YJYDhgvlUGjwDIdpGYs1AbWmskTFEwFLRoEuKexJRKhUANcnhYjIIjYW0C5YjRMhCQVHVnNC0NHBADcwOQWiIMMciQa0r0nUbPcd60tUj1r8RF7f6d10+9fDfxrt2/UVzccV///9y8+3V/zf/+5LEl4IVOeUML2kJQpWs4intISiz8/TrWQPF0sINKowOZDRZ9KxaLudffCsg9ZacCbg2pipKlH4aAQMR8qAyHA7TMqkxMocTEwhggDD7ACNsAIswYgAAUDWkuW9SqBQEYKACGgVmSl+oFdtJVj0O5PSBgI2vQRLqVwYU04RhYPbMeIh4Sa5DCsYSS1ULjrt4dYrMdeGLeZlBftSL8qQM18g35z87f/IyQ9i8jOPefD4a8Y6+WUyMtpy825eJmr5QukXGqK0P3WUpFhFxk45l3oyYic9cfyztw0q2IqS+gpPtwEACJhMkHmLgDYZUSjxljg4GDMAoYQpN5m1BxkLhoYrfUCeNMpRU3wA4FQi9aYS40MXnQSmhuNV00ODy1dhYfBdnXwa17cGoNCMlb4pJkRG2G0NXc8Tz7/fVAspL6IjfehUzff3sNJemy/3Td/0p8NR4UuBz3UGjzXLa/Cel+k9FprVpavspVQvrB7FF+7/Xru0/zbc/iL9qVRAaCnMAANTAEQDMF4HozHXZjJQDSCApzACPxMC0QU/XDWKECy+Y//uSxLYCFOnNCg8gdMp3k+HZ7KUpbDMSwOHeNfQdYO0xu7Q568+YGNgl0XzmlCEcKzY0TjBxC00OsjMcSOtAnFjyWFSKfWLaFuadot6iZSMla/l6V4Sa+nv4pKr4xnm3afMNNxOu68dykmc/zdRVw7/dXW22lQkDNrh5+butfmEj9qqa+mpYT3rjn/T16W+u+L2h5y2EWZwQ2Bt/KszlJwGgOmFMK+YnYUpj0pPmRcD6GBJGCIUeZb4NYCBOSYLbQSp0WzeIiA5TWGgCIhLHhRLh9/nhEgCoenc0E0gNNt20hBkQl6LE6LzQqUSWHnqCkiVgigOtFi1gcjCyAmd672PWTSDXjSwjzs+sZmZB/yC+jZLCp6SEkSQkOczJORT+wvH/dlNYq6s5shYlAzkGNTyUREJaKUBGAox960rVLow2pVrQVetAulVwCYAIIARMEcDIxfD2jM/DVNKiU0zgRBwMGIYcBT5vTgVGCeBEYCYDJgIgLkwCJMBEBhhAkc5ZnGAJt23TZClnLIw7gCQzkT9UchtNd7Kam5hnLcpyOauYUf/7ksTZghVV9QovZQlKurMh5eSOmNHlW1c9+rFLS0TU+ZT9avfsSRNlG3xJVuop3dtdlske9SyUy9z3JGWci9THPKSkpVKaXrfTbzjNLdMqdQ1INDfDUGxuX2jla5T59nzZJmdmYoxBsJOPvFV4vWY6CkNZ4deRBymeS9VEKJMrJPfPsGsq8S1E1HP3zvOt/rtaTJgBgGGAABWCQgDA5SIMD8VIywnbjMzDwMDgHMwmh5jTxDyKGZqH4QBus4IpD1HJAByEaBMreJ20a7DssjLAGJv1S4T3u9hD2VeORON41cd6XusEwbmwmS1KQaVB/00sVdpl96Z0k57Vcu2EDkbbSOQad6eptBOIIeWzbKCb3O0/Kt4w1v2t8N/p0WdLIxWs31O8jMzX1o/JJGdeTvf60s265THrkpFpPRw/5M7kbpVmthF32oxqu2/qyryZQl/TvHVzzSLgG8AQB5gcAVGKiOgZOgchmctXGXUG4YN4M5hKGsGLqIwZxiYOAPOXnSlGhY4PLgOWwhtZQztQqpIswMWtvBu5rbJZVKBOouC4CyD/+5LE8wNa5gcCL2TNyw8/oIHtGTlDLoOzxrPZxATjDTBDGmj0GCIELDmMtnskY+9Yqi6kKp58W41Zva0lxl2YNiyaUmRxkshX4+8fB9XlwiajbeTZWtftGrNySddWiESR7rnQplcwNtc6HsfwmZUz2nM6jsQWlGFrjI7WKaDB5GKI9PfD+PGZy1cRJKsPlrRgBADmAaAsYEQKJhqlNmQUIGZUbTZljBtmBmAwYHpaBlcBbHUWmfOAoE1pDmSCkZQ7Sn29ctjzXEY5G72Y0RkmLblAmA0pzpZAEOfEHjY+nWdtagkujPqXulpt6am3ZPnaY2/cIopyS/xFCq6m8y2wuG6zLh6Pt5zkojEGJtF5NuWon9Kzx7EdFzDkDUTJqu6lwtuoupnq/PfIq3jHTf7m/vG6i/bHhr3Ma5d3q6b67lPzpD5JxScGut9iILwco0lqGEAGMgwBQwCQUzCIMzMJUPMzClfzKHC3MGwGAwdxBDOgAfOUIBytaKm0RBxURCEd4TF4XLIix5r9tu6AfOpC/osYBzs1ZPSQiHeyefEAJxKB//uSxOwDWL4DBC9pCYsBvyCB7RkpNjgWIhjiXPKNxE0FoMFggb7RQSfRIRwRPY95ma73bLaHkykULSov9FrxM9BOolmtpa0ln3klQ5tzCWRTordrjTms3mM6opJjJjbLdSDGfwfb/Hy3K3C/UQe7FalDTldCJzNef6lpbPtQ8zrP3bctnc0DqMZkzFUxwAgACQBoQB8YcAlZjhhNGPwxUZI4TBgsAEGEOLSZdQR5kIdPiXV2teFrszG5MERpmYVC3O07LoKJV37v34zK2tcub3EI3HLGGPJA1mOtfE3aHEV5A8XR1FpPNw7wje9924svlrhH5W53NTtb403b5JHcNK2X8NB27EFpXjZzD67xq3P3d3XeS0osxHJfRLsXZSBrlTuX/b5tDmxnrdQMcvdVuyWjkTnWvXKNaUX0Xlod0mu9jcyX9LgzO2PByKFJVQJACzwgANGQNjB4IwMT0LQy400jKACWME8AsRFdmD4HCJlGMomoEEP2gkGSVSMqa80pvnKS6vRdnQcbZdCHqahrP7Nbq1s5HNTurYY/wS6J6iIurP/7ksTvA1keBQRPaMnLB8AghewZOTHvZpaTHuiTP+mnmsROK2To2qrdLjWhrX5RvG009VWZBbdGV2VLY/a0ZNwpe6iPUhRrgZqR/swg1kKiHJS6NF5Ocy2aCxefCtnJZmaWOQsCbavJw5EK7F7SEl6UtD09D0FySMlSr8a0uUU5/j5hsmputye9BkbN1tWC0hcQMAwxtYgw3J00F6MDcCYYh6YmvCbgjSYjginkJANDsMIGuyPASKUXNRuKwdTHO2le3x7Q4tG6jNJBa9HbBhzz6vqlr0s2TxW+7xjdO4UumTNtQPTP9f7Rtf/Wc0zq18596/OfD1n5pjf+NRc4zjFcbiZ1m2Mw9TZxiF6XxL8XtvN4Ftazm9vf/frS1sUrjWK0+bY3Xfvv2xHpj7197zS2sbpee/1qNq2bZzjOa5z81zNuX2vquMfesbpe/trGqZpjH3uWAAQAUJMQAMBsWgwFqMzB9CpM4dgcwGhbTNCBgM7dY8z0QNTZHT7BUixtgoCmouAUYFAKRgFgkgAAAwDgEUwzANAcO7iEwCq2xRphqHb/+5LE8ANZagcCL2TJyx5A4Qq68AAeWrgECDBsRQRFlWmMhqPipymMrcKySaexxFHmJy6WQVYfh3HKj8Ds6dVgTNHXdeIySliMCNe+blbXqd/KVw3mi8y+dR96ZqUCzETltPekMMPC0WF00m5G8qsjp5dIKZ+4/J4hhPMbxk9uZlkvh6/jLtS6T9noZlleNzM1apGt+7CwkdfONPpLpNT0rivtF6d5JRejlihnmuTMIf3OWsl7KKKapZHQ3IBmnFlUchyHaOIzN+Syuo2SUVYPlNqCn+n4xG78SrR1lD/QTSyWlcKHJychjdSVSWMwDAM3EY7Rf/////4vzXlEEQJGHKsO7LsZ6U2J2PwPQf/////wNDUARKBH5g6tXeyKSKCcnRpY3DcYAAEAAAMEoLwyQhqjAOAaMLkNqt00SR7TGnEwMCcLkwGAN/0YLQF4kEeYBQBxgMgDf5zDxADC4wy5TXmbBm6JJimNECMEY0V/+ZUKbI8RFS75hwiwRgQhhQ3//mSGBgiSmIDAYQ8RaowYaYQy///4FAgIFCFcICDBAACE//uSxO0AK+Iu+xnsAAT7PuBXPaAAsrSBICGWxf///q8USLIIaMqLIIUF1n1hhHpwYQrd////69i6hehbyJiOCcatiVjSXVaU3Fpsebiy2x/////+jW3RSxAe2JliKcPPIim67osRh10V2u7ALDWcx1hv///////DiG7T48oOzeXsjTHhtmap3fdFMdYWZaysLddlhtaIrlpqFlMSp//////////3Hh1Qdf8HMPUvasytPttG5svXpBLO1LJlr//////7WYlRuzEotEYCfqZiTvX4k5UPRJynekxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqkxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/7ksQ5A8AAAaQcAAAgAAA0gAAABKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo="
990};
991
992function getTimestamp() {
993 return new Date().getTime();
994}
995
996function saveConfig() {
997 let config = JSON.stringify(configuration);
998 localStorage.setItem(storageKey, config);
999}
1000
1001function loadConfig() {
1002 let config = JSON.parse(localStorage.getItem(storageKey) || "{}");
1003 for (let key in config) {
1004 if (config.hasOwnProperty(key) && configuration.hasOwnProperty(key)) {
1005 configuration[key] = config[key];
1006 }
1007 }
1008}
1009
1010function getTopicLocked(elem) {
1011 let lock = elem.getElementsByClassName("message-lock-topic")[0];
1012 if (lock === undefined) {
1013 return lock;
1014 }
1015 let reason = lock.getElementsByTagName("span")[0].textContent.trim();
1016 return `Le topic a été vérouillé pour la raison suivante : "${reason}"`;
1017}
1018
1019function getTopicError(elem) {
1020 let error = elem.getElementsByClassName("img-erreur")[0];
1021 if (error === undefined) {
1022 return error;
1023 }
1024 return `Le topic présente une erreur: ${error.getAttribute("alt")}`;
1025}
1026
1027function parseSondage(elem) {
1028 let blocSondage = elem.getElementsByClassName("bloc-sondage")[0];
1029 if (!blocSondage) {
1030 return null;
1031 }
1032 let intitule = blocSondage.getElementsByClassName("intitule-sondage")[0].innerHTML;
1033 let answered = !!(blocSondage.getElementsByClassName("result-pourcent")[0]);
1034
1035 let choix = blocSondage.getElementsByClassName("tab-choix")[0].getElementsByTagName("tr");
1036 let results = [];
1037
1038 if (answered) {
1039 for (let ch of choix) {
1040 let pourcent = parseInt(ch.getElementsByClassName("pourcent")[0].innerHTML.trim().split(" ")[0]);
1041 let response = ch.getElementsByClassName("reponse")[0].innerHTML.trim();
1042 results.push({ response: response, pourcent: pourcent });
1043 }
1044 } else {
1045 for (let ch of choix) {
1046 let btnResponse = ch.getElementsByClassName("btn-sondage-reponse")[0];
1047 let response = btnResponse.innerHTML.trim();
1048 let sondageId = btnResponse.getAttribute("data-id-sondage");
1049 let responseId = btnResponse.getAttribute("data-id-reponse");
1050 results.push({ response: response, sondageId: sondageId, responseId: responseId });
1051 }
1052 }
1053
1054 let votes = parseInt(blocSondage.getElementsByClassName("pied-result")[0].innerHTML.trim().split(" ")[0]);
1055 return { answered: answered, intitule: intitule, results: results, votes: votes };
1056}
1057
1058function tryCatch(func) {
1059 function wrapped(optArg) {
1060 try {
1061 func(optArg);
1062 } catch (err) {
1063 let message = `Une erreur est survenue dans JVChat Premium: '${err.message}' (function '${func.name}', line ${err.lineNumber})`;
1064 console.error(message);
1065 try {
1066 addAlertbox("danger", message);
1067 } catch (e) {
1068 alert(message);
1069 }
1070 }
1071 }
1072 return wrapped;
1073}
1074
1075function toggleTextarea() {
1076 isReduced = !isReduced;
1077 configuration["default_reduced"] = isReduced;
1078 saveConfig();
1079
1080 let isDown = isScrollDown();
1081 document.getElementById("bloc-formulaire-forum").getElementsByClassName("jv-editor-toolbar")[0].classList.toggle("jvchat-hide");
1082 document.getElementById("jvchat-enlarge").classList.toggle("jvchat-hide");
1083 document.getElementById("jvchat-reduce").classList.toggle("jvchat-hide");
1084 document.getElementById("jvchat-post").classList.toggle("jvchat-hide");
1085 document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-reduced");
1086
1087 setTextareaHeight();
1088
1089 if (isDown) {
1090 setScrollDown();
1091 }
1092}
1093
1094function parseURL(url) {
1095 let regex = /^(.*?)(\/\d+-\d+-\d+-)(\d+)(-\d+-\d+-\d+-)(.*?)(\.htm)(.*)$/i;
1096 let [_, domain, ids, page, nums, title, htm, anchor] = url.match(regex);
1097 return { domain: domain, ids: ids, page: page, nums: nums, title: title, htm: htm, anchor: anchor };
1098}
1099
1100function buildURL(dict) {
1101 return `${dict.domain}${dict.ids}${dict.page}${dict.nums}${dict.title}${dict.htm}${dict.anchor}`;
1102}
1103
1104function getForum(document) {
1105 let ariane = document.getElementsByClassName("bloc-fil-ariane-crumb-forum")[0];
1106 let links = ariane.getElementsByTagName("a");
1107 let title = "";
1108 let forumLink = "";
1109
1110 for (let i = links.length - 1; i >= 0; i--) {
1111 forumLink = links[i];
1112 title = forumLink.innerHTML.trim();
1113 if (title.startsWith("Forum ")) {
1114 break;
1115 }
1116 }
1117
1118 return { href: forumLink.getAttribute("href"), title: title.replace("Forum ", "") };
1119}
1120
1121function getLastPage(document) {
1122 let blocPages = document.getElementsByClassName("bloc-liste-num-page")[0];
1123 let spans = blocPages.getElementsByTagName("span");
1124 let lastPage = 1;
1125 for (let span of spans) {
1126 let page = parseInt(span.textContent.trim());
1127 if (!isNaN(page) && page > lastPage) {
1128 lastPage = page;
1129 }
1130 }
1131 return lastPage;
1132}
1133
1134function parseMessage(elem) {
1135 let conteneurs = elem.getElementsByClassName("conteneur-message");
1136 let conteneur = conteneurs[conteneurs.length - 1];
1137 let blacklisted = conteneurs[0].classList.contains("conteneur-message-blacklist");
1138 let author = conteneur.getElementsByClassName("bloc-pseudo-msg")[0].textContent.trim();
1139 let avatar = conteneur.getElementsByClassName("user-avatar-msg")[0];
1140 let ddbHash = ""
1141 if (conteneur.getElementsByClassName("picto-msg-exclam")[0]) {
1142 ddbHash = "http://jeuxvideo.com" + conteneur.getElementsByClassName("picto-msg-exclam")[0].getAttribute("data-selector")
1143 }
1144 if (avatar !== undefined) {
1145 avatar = avatar.getAttribute("data-srcset");
1146 }
1147 let date = conteneur.getElementsByClassName("bloc-date-msg")[0].textContent.trim();
1148 let content = conteneur.getElementsByClassName("text-enrichi-forum")[0];
1149 let id = parseInt(elem.getAttribute("data-id"));
1150 let edited = elem.getElementsByClassName("info-edition-msg")[0];
1151 if (edited !== undefined) {
1152 let msgEdited = edited.textContent.trim();
1153 edited = msgEdited.match(/Message édité le .*? à (.*?) par/i)[1];
1154 }
1155 return {
1156 author: author, dateString: date, date: parseDate(date), avatar: avatar, edited: edited,
1157 id: id, content: content, blacklisted: blacklisted, ddbHash: ddbHash
1158 };
1159}
1160
1161function parseUserInfo(elem) {
1162 let accountMp = elem.getElementsByClassName("jv-nav-account-mp")[0];
1163 if (accountMp === undefined) {
1164 return { author: undefined, avatar: undefined, mp: undefined, notif: undefined };
1165 }
1166 let numberMp = accountMp.getElementsByClassName("jv-account-number-mp")[0];
1167 let accountNotif = elem.getElementsByClassName("jv-nav-account-notif")[0];
1168 let numberNotif = accountNotif.getElementsByClassName("jv-account-number-notif")[0];
1169 let accountUser = elem.getElementsByClassName("jv-nav-account-user")[0];
1170 let avatarBox = accountUser.getElementsByClassName("account-avatar-box")[0];
1171 let authorBox = accountUser.getElementsByClassName("account-pseudo")[0];
1172 let mp = parseInt(numberMp.getAttribute("data-val"));
1173 let notif = parseInt(numberNotif.getAttribute("data-val"));
1174 let avatar = avatarBox.style["background-image"].slice(5, -2).replace("/avatar-md/", "/avatar/");
1175 let author = authorBox.textContent.trim();
1176 return { author: author, avatar: avatar, mp: mp, notif: notif };
1177}
1178
1179function getPage(elem) {
1180 let pageActive = elem.getElementsByClassName("page-active")[0];
1181 let page = 1;
1182 if (pageActive !== undefined) {
1183 page = parseInt(pageActive.textContent.trim());
1184 }
1185 return page;
1186}
1187
1188function parseTopicInfo(elem) {
1189 let title = elem.querySelector("#bloc-title-forum").textContent.trim();
1190 let connected = parseInt(elem.getElementsByClassName("nb-connect-fofo")[0].textContent.trim());
1191 let lastPage = getLastPage(elem);
1192 let page = getPage(elem);
1193 return { title: title, connected: connected, lastPage: lastPage, page: page };
1194}
1195
1196function fixMessage(elem) {
1197 let jvcares = Array.from(elem.getElementsByClassName("JvCare"));
1198 for (let jvcare of jvcares) {
1199 let a = document.createElement("a");
1200 a.setAttribute("target", "_blank");
1201 a.setAttribute("href", jvCake(jvcare.getAttribute("class")));
1202 a.innerHTML = jvcare.innerHTML;
1203 jvcare.outerHTML = a.outerHTML;
1204 }
1205 let togglableQuotes = Array.from(elem.querySelectorAll(".text-enrichi-forum > blockquote > blockquote"));
1206 for (let togglableQuote of togglableQuotes) {
1207 let toggleButton = document.createElement("div");
1208 toggleButton.classList.add("nested-quote-toggle-box");
1209 togglableQuote.insertBefore(toggleButton, togglableQuote.firstChild);
1210 // The click event is bound in the "dontScrollOnExpand()" function
1211 }
1212}
1213
1214function jvCake(cls) {
1215 let base16 = '0A12B34C56D78E9F', lien = '', s = cls.split(' ')[1];
1216 for (let i = 0; i < s.length; i += 2) {
1217 lien += String.fromCharCode(base16.indexOf(s.charAt(i)) * 16 + base16.indexOf(s.charAt(i + 1)));
1218 }
1219 return lien;
1220}
1221
1222function detectMosaic(elem) {
1223 let imagesShack = elem.getElementsByClassName("img-shack");
1224 if (imagesShack.length < 4) {
1225 return;
1226 }
1227 let mosaics = {};
1228 let regex = /^.+\/[0-9]+-[0-9]{1,2}-([a-z0-9]+)\.\w+$/i;
1229 for (let image of imagesShack) {
1230 let match = image.src.match(regex);
1231 if (!match) {
1232 continue;
1233 }
1234 [_, identifier] = match;
1235 if (mosaics.hasOwnProperty(identifier)) {
1236 mosaics[identifier].push(image);
1237 } else {
1238 mosaics[identifier] = [image]
1239 }
1240 }
1241 for (let identifier in mosaics) {
1242 let images = mosaics[identifier];
1243 if (images.length < 4) {
1244 continue;
1245 }
1246 images[0].parentNode.classList.add("jvchat-mosaic-root");
1247 images[0].classList.add("jvchat-mosaic");
1248 for (let image of images.slice(1)) {
1249 image.parentNode.classList.add("jvchat-mosaic");
1250 }
1251 }
1252}
1253
1254function improveImages(elem) {
1255 let imagesShack = elem.getElementsByClassName("img-shack");
1256 for (let image of imagesShack) {
1257 let src = image.src;
1258 let parent = image.parentNode;
1259 let extension = parent.href.split(".").pop();
1260 let direct = src.replace(/(.*?)\/minis\/(.*)\.\w+/i, "$1/fichiers/$2." + extension);
1261 image.setAttribute("data-src-mini", src);
1262 image.setAttribute("data-src-direct", direct);
1263 image.classList.add("jvchat-loadable-image");
1264 parent.href = direct;
1265 if (extension.toUpperCase() === "GIF") {
1266 image.src = direct;
1267 image.classList.remove("jvchat-loadable-image");
1268 } else if (configuration["load_images"]) {
1269 image.src = direct;
1270 }
1271 src = image.src;
1272 image.setAttribute("onerror", `this.onerror=null;this.src=this.getAttribute("data-src-direct");this.classList.remove("jvchat-loadable-image");`);
1273 }
1274}
1275
1276function clearPage(document) {
1277 let buttons = `
1278 <div id="jvchat-buttons-main" class='jvchat-buttons'>
1279 <button id='jvchat-post' tabindex="4" type="button" class='jvchat-hide jvchat-button-top icon-reply' title="Envoyer le message"></button>
1280 <button id='jvchat-reduce' tabindex="5" type="button" class='jvchat-hide jvchat-button-bottom icon-arrow-down-entypo' title="Réduire la zone de texte"></button>
1281 <button id='jvchat-enlarge' tabindex="4" type="button" class='jvchat-button-solo icon-arrow-up-entypo' title="Agrandir la zone de texte"></button>
1282 <div>`;
1283
1284 document.head.insertAdjacentHTML("beforeend", CSS);
1285
1286 let previsu = document.getElementById("bloc-formulaire-forum").getElementsByClassName("previsu-editor")[0];
1287 if (previsu) {
1288 previsu.parentElement.removeChild(previsu);
1289 }
1290
1291 let messageTopic = document.getElementById("message_topic");
1292 if (messageTopic) {
1293 messageTopic.classList.add("jvchat-textarea");
1294 messageTopic.setAttribute("placeholder", "Hop hop hop, le message ne va pas s'écrire tout seul !");
1295 messageTopic.insertAdjacentHTML("afterend", buttons);
1296 messageTopic.addEventListener("keydown", tryCatch(postMessageIfEnter));
1297 document.getElementById("jvchat-post").addEventListener("click", tryCatch(postMessage));
1298 document.getElementById("jvchat-enlarge").addEventListener("click", tryCatch(toggleTextarea));
1299 document.getElementById("jvchat-reduce").addEventListener("click", tryCatch(toggleTextarea));
1300 }
1301 document.getElementsByClassName("conteneur-messages-pagi")[0].insertAdjacentHTML("afterbegin", "<div id='jvchat-main'><hr class='jvchat-ruler'></div>");
1302 document.getElementById("page-messages-forum").insertAdjacentHTML("afterbegin", "<div id='jvchat-alerts'><div id='jvchat-fixed-alert' class='jvchat-hide'><div class='alert-row'></div></div></div>");
1303
1304 document.getElementById("content-context").insertAdjacentHTML("afterbegin", PANEL);
1305 document.getElementById("content-context").insertAdjacentHTML("beforeend", "<div id='jvchat-right-padding'></div>");
1306
1307 document.getElementById("content").classList.add("jvchat-root");
1308 document.getElementById("bloc-formulaire-forum").classList.add("jvchat-reduced");
1309 document.getElementById("bloc-formulaire-forum").classList.add("jvchat-hide");
1310
1311 let toolbar = document.getElementById("bloc-formulaire-forum").getElementsByClassName("jv-editor-toolbar")[0];
1312 if (toolbar) {
1313 toolbar.classList.add("jvchat-hide");
1314 }
1315
1316 document.getElementById("jvchat-main").addEventListener("click", tryCatch(dontScrollOnExpand));
1317
1318 document.getElementById("jvchat-alerts").addEventListener("click", tryCatch(closeAlert));
1319
1320 document.getElementById("jvchat-leftbar-reduce").addEventListener("click", tryCatch(toggleSidebar));
1321 document.getElementById("jvchat-leftbar-extend").addEventListener("click", tryCatch(toggleSidebar));
1322 document.getElementById("jvchat-leftbar-config-open").addEventListener("click", tryCatch(toggleConfig));
1323 document.getElementById("jvchat-leftbar-config-close").addEventListener("click", tryCatch(toggleConfig));
1324
1325 document.getElementById("jvchat-turbo-checkbox").addEventListener("change", tryCatch(toggleTurbo));
1326
1327 document.getElementById("jvchat-play-sound-checkbox").checked = configuration["play_sound"];
1328 document.getElementById("jvchat-play-sound-checkbox").addEventListener("change", tryCatch(togglePlaySoundOption));
1329
1330 document.getElementById("jvchat-night-mode-checkbox").checked = configuration["night_mode"];
1331 document.getElementById("jvchat-night-mode-checkbox").addEventListener("change", tryCatch(toggleNightModeOption));
1332 if (configuration["night_mode"]) {
1333 document.getElementById("content").classList.add("jvchat-night-mode");
1334 }
1335
1336 document.getElementById("jvchat-hide-mosaic-checkbox").checked = configuration["hide_mosaic"];
1337 document.getElementById("jvchat-hide-mosaic-checkbox").addEventListener("change", tryCatch(toggleHideMosaicOption));
1338 if (configuration["hide_mosaic"]) {
1339 document.getElementById("jvchat-main").classList.add("jvchat-hide-mosaics");
1340 }
1341
1342 document.getElementById("jvchat-load-images-checkbox").checked = configuration["load_images"];
1343 document.getElementById("jvchat-load-images-checkbox").addEventListener("change", tryCatch(toggleLoadImagesOption));
1344
1345 document.getElementById("jvchat-turbo-delay-range").value = configuration["turbo_delay"];
1346 document.getElementById("jvchat-turbo-delay-span").innerHTML = `${configuration["turbo_delay"]} ms`;
1347 document.getElementById("jvchat-turbo-delay-range").addEventListener("input", tryCatch(changeTurboDelayOption));
1348
1349 document.getElementById("jvchat-max-width-range").value = configuration["max_width"];
1350 document.getElementById("jvchat-max-width-span").innerHTML = `${configuration["max_width"]} %`;
1351 document.getElementById("jvchat-max-width-range").addEventListener("input", tryCatch(changeMaxWidthOption));
1352 adjustMaxWidth(configuration["max_width"]);
1353
1354 let favs = Array.from(document.querySelectorAll("link[rel='icon'], link[rel='shortcut icon']"));
1355 for (let fav of favs) {
1356 fav.parentElement.removeChild(fav);
1357 }
1358 setFavicon("");
1359
1360 document.addEventListener("visibilitychange", function () {
1361 let hidden = document.hidden;
1362 if (hidden) {
1363 let newHr = document.getElementById("jvchat-ruler-new");
1364 if (newHr) {
1365 newHr.removeAttribute("id");
1366 }
1367 nbNewMessage = 0;
1368 } else if (!isError && !isLocked) {
1369 setFavicon("");
1370 }
1371 });
1372}
1373
1374function toggleSidebar(event) {
1375 let isDown = isScrollDown();
1376 document.getElementById("jvchat-leftbar-extend").classList.toggle("jvchat-hide");
1377 document.getElementById("jvchat-leftbar-reduce").classList.toggle("jvchat-hide");
1378 document.getElementById("jvchat-leftbar-config").classList.toggle("jvchat-hide");
1379 document.getElementById("jvchat-leftbar").classList.toggle("jvchat-leftbar-reduced");
1380 if (isDown) {
1381 setScrollDown();
1382 }
1383}
1384
1385function toggleConfig(event) {
1386 document.getElementById("jvchat-leftbar-config-open").classList.toggle("jvchat-hide");
1387 document.getElementById("jvchat-leftbar-config-close").classList.toggle("jvchat-hide");
1388 document.getElementById("jvchat-info").classList.toggle("jvchat-hide");
1389 document.getElementById("jvchat-config").classList.toggle("jvchat-hide");
1390}
1391
1392function forceUpdate() {
1393 // If waiting for next update, restart it immediately, otherwise just wait for the HTTP request to end
1394 if (!fetchingMessages) {
1395 clearTimeout(currentTimeoutId);
1396 updateMessages(currentFetchedPage, true);
1397 }
1398}
1399
1400function toggleTurbo(event) {
1401 let checked = document.getElementById("jvchat-turbo-checkbox").checked;
1402 updateIntervalIdx = 0;
1403 if (!checked) {
1404 turboActivated = false;
1405 } else {
1406 turboActivated = true;
1407 forceUpdate();
1408 }
1409}
1410
1411function togglePlaySoundOption(event) {
1412 let checked = document.getElementById("jvchat-play-sound-checkbox").checked;
1413 configuration["play_sound"] = checked;
1414 saveConfig();
1415}
1416
1417function toggleNightModeOption(event) {
1418 let checked = document.getElementById("jvchat-night-mode-checkbox").checked;
1419 configuration["night_mode"] = checked;
1420 saveConfig();
1421 document.getElementById("content").classList.toggle("jvchat-night-mode");
1422}
1423
1424function toggleHideMosaicOption(event) {
1425 let checked = document.getElementById("jvchat-hide-mosaic-checkbox").checked;
1426 configuration["hide_mosaic"] = checked;
1427 saveConfig();
1428 let isDown = isScrollDown();
1429 document.getElementById("jvchat-main").classList.toggle("jvchat-hide-mosaics");
1430 if (isDown) {
1431 setScrollDown();
1432 }
1433}
1434
1435function toggleLoadImagesOption(event) {
1436 let checked = document.getElementById("jvchat-load-images-checkbox").checked;
1437 configuration["load_images"] = checked;
1438 saveConfig();
1439 for (let image of document.getElementsByClassName("jvchat-loadable-image")) {
1440 image.src = image.getAttribute(checked ? "data-src-direct" : "data-src-mini");
1441 }
1442}
1443
1444function changeTurboDelayOption(event) {
1445 let ms = document.getElementById("jvchat-turbo-delay-range").value;
1446 document.getElementById("jvchat-turbo-delay-span").innerHTML = `${ms} ms`;
1447 configuration["turbo_delay"] = parseInt(ms);
1448 saveConfig();
1449}
1450
1451function changeMaxWidthOption(event) {
1452 let maxWidth = parseInt(document.getElementById("jvchat-max-width-range").value);
1453 document.getElementById("jvchat-max-width-span").innerHTML = `${maxWidth} %`;
1454 configuration["max_width"] = maxWidth;
1455 saveConfig();
1456 adjustMaxWidth(maxWidth);
1457}
1458
1459function adjustMaxWidth(maxWidth) {
1460 document.getElementById("page-messages-forum").style["flex-grow"] = maxWidth;
1461 document.getElementById("jvchat-right-padding").style["flex-grow"] = 100 - maxWidth;
1462}
1463
1464function closeAlert(event) {
1465 let target = event.target;
1466 if (!target) {
1467 return;
1468 }
1469 if (target.classList.contains("jvchat-alert-close")) {
1470 let parent = target.parentElement;
1471 parent.parentElement.removeChild(parent);
1472 }
1473}
1474
1475function postMessage() {
1476 if (freshForm === undefined) {
1477 addAlertbox("danger", "Impossible de poster le message, aucun formulaire trouvé");
1478 return;
1479 }
1480
1481 let textarea = document.getElementById("message_topic");
1482
1483 let formData = serializeForm(freshForm);
1484 formData["message_topic"] = textarea.value;
1485 let formulaire = document.getElementById("bloc-formulaire-forum");
1486
1487 formulaire.classList.add("jvchat-disabled-form");
1488 textarea.setAttribute("disabled", "true");
1489
1490 let timestamp = getTimestamp();
1491
1492 function onSuccess(res) {
1493 formulaire.classList.remove("jvchat-disabled-form");
1494 textarea.removeAttribute("disabled");
1495 let parsedPage = parsePage(res, timestamp);
1496 if (!parsedPage.alert) {
1497 textarea.value = "";
1498 }
1499 setTextareaHeight();
1500 setScrollDown();
1501 postingMessage = false;
1502 if (parsedPage.nbMessagesPage === 20) {
1503 // Bug si on poste un message générant une nouvelle page : la requête retourne la page
1504 // courrante, donc notre nouveau message n'apparaît pas, il faut forcer un refetch()
1505 // MAIS il faut attendre un peu, car JVC galère à créer la page...
1506 setTimeout(tryCatch(forceUpdate), 2000);
1507 }
1508 }
1509
1510 function onError(err, _) {
1511 addAlertbox("danger", err);
1512 formulaire.classList.remove("jvchat-disabled-form");
1513 textarea.removeAttribute("disabled");
1514 postingMessage = false;
1515 }
1516
1517 function onTimeout(err) {
1518 addAlertbox("warning", err);
1519 formulaire.classList.remove("jvchat-disabled-form");
1520 textarea.removeAttribute("disabled");
1521 postingMessage = false;
1522 }
1523
1524 let timeout = 20000;
1525 if (turboActivated) {
1526 timeout = 5000;
1527 }
1528
1529 postingMessage = true;
1530 request("POST", document.URL, onSuccess, onError, onTimeout, makeFormData(formData), false, timeout);
1531}
1532
1533function editMessage(bloc) {
1534 let textarea = bloc.getElementsByClassName("jvchat-edition-textarea")[0];
1535
1536 let blocEdition = bloc.getElementsByClassName("jvchat-edition")[0];
1537 let formData = JSON.parse(blocEdition.getAttribute("data-form"));
1538 formData["message_topic"] = textarea.value;
1539 formData["id_message"] = bloc.getAttribute("jvchat-id");
1540 formData["ajax_hash"] = freshHash;
1541 formData["action"] = "post";
1542 let edition = bloc.getElementsByClassName("jvchat-edition")[0];
1543
1544 edition.classList.add("jvchat-disabled-form");
1545 textarea.setAttribute("disabled", "true");
1546
1547 let timestamp = getTimestamp();
1548
1549 function onSuccess(res) {
1550 edition.classList.remove("jvchat-disabled-form");
1551 if (res['reset_form']) {
1552 let reset = document.createElement("html");
1553 reset.innerHTML = res["hidden_reset"];
1554 let resetData = serializeForm(reset);
1555 for (let key in resetData) {
1556 formData[key] = resetData[key];
1557 }
1558 blocEdition.setAttribute("data-form", JSON.stringify(formData));
1559 }
1560
1561 textarea.removeAttribute("disabled");
1562 if (res.erreur.length > 0) {
1563 for (let err of res.erreur) {
1564 addAlertbox("danger", err);
1565 }
1566 return;
1567 }
1568 let dom = document.createElement("html");
1569 dom.innerHTML = res["html"];
1570 let message = getMessages(dom)[0];
1571 addMessages([message], true, timestamp);
1572 }
1573
1574 function onError(err, _) {
1575 addAlertbox("danger", err);
1576 edition.classList.remove("jvchat-disabled-form");
1577 textarea.removeAttribute("disabled");
1578 }
1579
1580 function onTimeout(err) {
1581 addAlertbox("warning", err);
1582 edition.classList.remove("jvchat-disabled-form");
1583 textarea.removeAttribute("disabled");
1584 }
1585
1586 let url = "http://www.jeuxvideo.com/forums/ajax_edit_message.php";
1587
1588 request("POST", url, onSuccess, onError, onTimeout, makeFormData(formData), true, 20000);
1589}
1590
1591function requestEdit(bloc) {
1592 if (!bloc.getElementsByClassName("jvchat-edition")[0].classList.contains("jvchat-hide")) {
1593 return;
1594 }
1595
1596 let contentClasses = bloc.getElementsByClassName("jvchat-content")[0].classList;
1597 contentClasses.add("disabled-content");
1598
1599 function onSuccess(res) {
1600 contentClasses.remove("disabled-content");
1601 if (res.erreur.length > 0) {
1602 for (let err of res.erreur) {
1603 addAlertbox("danger", err);
1604 }
1605 return;
1606 }
1607 let dom = document.createElement("html");
1608 dom.innerHTML = res["html"];
1609 let textarea = dom.getElementsByTagName("textarea")[0]
1610 let txt = textarea.value;
1611 textarea.parentElement.removeChild(textarea);
1612 let form = dom.getElementsByTagName("form")[0];
1613 let formData = serializeForm(form);
1614 let editionBloc = bloc.getElementsByClassName("jvchat-edition")[0];
1615 editionBloc.setAttribute("data-form", JSON.stringify(formData));
1616 let height = computeHeight(countLines(txt));
1617 let isDown = isScrollDown();
1618 bloc.getElementsByClassName("jvchat-edition-textarea")[0].value = txt;
1619 bloc.getElementsByClassName("jvchat-edition-textarea")[0].style["height"] = `${height}rem`;
1620 bloc.getElementsByClassName("jvchat-content")[0].classList.add("jvchat-hide");
1621 editionBloc.classList.remove("jvchat-hide");
1622 if (isDown) {
1623 setScrollDown();
1624 }
1625 }
1626
1627 function onError(err, _) {
1628 addAlertbox("danger", err);
1629 contentClasses.remove("disabled-content");
1630 }
1631
1632 function onTimeout(err) {
1633 addAlertbox("warning", err);
1634 contentClasses.remove("disabled-content");
1635 }
1636
1637 let id = bloc.getAttribute("jvchat-id");
1638 let url = `http://www.jeuxvideo.com/forums/ajax_edit_message.php?id_message=${id}&ajax_hash=${freshHash}&action=get`;
1639 request("GET", url, onSuccess, onError, onTimeout, undefined, true, 5000);
1640}
1641
1642function requestDdb(bloc) {
1643 let contentClasses = bloc.getElementsByClassName("jvchat-content")[0].classList;
1644 contentClasses.add("disabled-content");
1645
1646 function onSuccess(res) {
1647 contentClasses.remove("disabled-content");
1648 if (res.erreur.length > 0) {
1649 for (let err of res.erreur) {
1650 addAlertbox("danger", err);
1651 }
1652 return;
1653 }
1654 let isDown = isScrollDown();
1655 if (isDown) {
1656 setScrollDown()
1657 }
1658 }
1659
1660 function onError(err, _) {
1661 addAlertbox("danger", err);
1662 contentClasses.remove("disabled-content");
1663 }
1664
1665 function onTimeout(err) {
1666 addAlertbox("warning", err);
1667 contentClasses.remove("disabled-content");
1668 }
1669
1670 let url = bloc.getElementsByClassName("jvchat-ddb jvchat-picto picto-msg-exclam")[0].getAttribute("data-selector");
1671 request("GET", url, onSuccess, onError, onTimeout, undefined, true, 5000);
1672}
1673
1674function requestDelete(bloc) {
1675 let contentClasses = bloc.getElementsByClassName("jvchat-content")[0].classList;
1676 contentClasses.add("disabled-content");
1677
1678 let id = parseInt(bloc.getAttribute("jvchat-id"));
1679
1680 function onSuccess(res) {
1681 contentClasses.remove("disabled-content");
1682 if (res.erreur.length > 0) {
1683 for (let err of res.erreur) {
1684 addAlertbox("danger", err);
1685 }
1686 return;
1687 }
1688
1689 if (lastEditionTime[id] === false) {
1690 return;
1691 }
1692
1693 let isDown = isScrollDown();
1694
1695 if (!bloc.getElementsByClassName("jvchat-edition")[0].classList.contains("jvchat-hide")) {
1696 bloc.getElementsByClassName("jvchat-content")[0].classList.remove("jvchat-hide");
1697 bloc.getElementsByClassName("jvchat-edition")[0].classList.add("jvchat-hide");
1698 }
1699
1700 bloc.closest(".jvchat-message").classList.add("jvchat-message-deleted");
1701 lastEditionTime[id] = false;
1702
1703 if (isDown) {
1704 setScrollDown();
1705 }
1706 }
1707
1708 function onError(err, _) {
1709 addAlertbox("danger", err);
1710 contentClasses.remove("disabled-content");
1711 }
1712
1713 function onTimeout(err) {
1714 addAlertbox("warning", err);
1715 contentClasses.remove("disabled-content");
1716 }
1717
1718
1719 let url = `http://www.jeuxvideo.com/forums/modal_del_message.php`;
1720 let deleteData = { "type": "delete", "ajax_hash": freshDeletionHash, "tab_message[]": id };
1721 request("POST", url, onSuccess, onError, onTimeout, makeFormData(deleteData), true, 5000);
1722}
1723
1724function countLines(text) {
1725 return text.split(/\r|\r\n|\n/).length;
1726}
1727
1728function computeHeight(lines) {
1729 return 1 * lines + 0.6;
1730}
1731
1732function setTextareaHeight(plusOne) {
1733 let textarea = document.getElementById("message_topic");
1734 if (!isReduced) {
1735 textarea.style["height"] = "";
1736 return;
1737 }
1738 plusOne = !!plusOne;
1739 let lines = countLines(textarea.value);
1740
1741 if (!plusOne && lines === 1) {
1742 textarea.style["height"] = "";
1743 return;
1744 }
1745
1746 if (plusOne) {
1747 lines += 1;
1748 }
1749
1750 let height = computeHeight(lines);
1751 textarea.style["height"] = `${height}rem`;
1752}
1753
1754function postMessageIfEnter(event) {
1755 if (isReduced && (event.which == 13 || event.keyCode == 13)) {
1756 if (event.shiftKey) {
1757 let isDown = isScrollDown();
1758 setTextareaHeight(true);
1759 if (isDown) {
1760 setScrollDown();
1761 }
1762 } else {
1763 event.preventDefault();
1764 postMessage();
1765 }
1766 }
1767}
1768
1769function serializeForm(form) {
1770 // Useless actually, just use new FormData(form)
1771 let dict = {};
1772
1773 for (let select of form.getElementsByTagName("select")) {
1774 dict[select.name] = select.querySelector("option[selected]").value;
1775 }
1776
1777 for (let input of form.getElementsByTagName("input")) {
1778 dict[input.name] = input.value;
1779 }
1780
1781 for (let textarea of form.getElementsByTagName("textarea")) {
1782 dict[textarea.name] = textarea.value;
1783 }
1784
1785 return dict;
1786}
1787
1788function makeFormData(dict) {
1789 var formData = new FormData();
1790 for (let key in dict) {
1791 formData.append(key, dict[key]);
1792 }
1793 return formData;
1794}
1795
1796function getMessages(document) {
1797 let blocMessages = document.getElementsByClassName("bloc-message-forum");
1798 let messages = [];
1799 for (let bloc of blocMessages) {
1800 messages.push(parseMessage(bloc));
1801 }
1802 return messages;
1803}
1804
1805function findDeletedMessages(res, requestTimestamp) {
1806 // On pourrait utiliser "getMessages()" mais pas besoin de parse chaque message (on a juste
1807 // besoin des id). Avantage : on peut utiliser cette fonction aussi dans "checkEdited()".
1808 let page = getPage(res);
1809 let blocMessages = res.getElementsByClassName("bloc-message-forum");
1810
1811 let newIds = []
1812 let newDates = [];
1813
1814 for (let bloc of blocMessages) {
1815 let id = parseInt(bloc.getAttribute("data-id"));
1816 let date = bloc.getElementsByClassName("bloc-date-msg")[0].textContent.trim();
1817 newIds.push(id);
1818 newDates.push(date);
1819 }
1820
1821 if (!messagesByPage.hasOwnProperty(page)) {
1822 messagesByPage[page] = [newIds, newDates];
1823 return;
1824 }
1825
1826 let [regIds, regDates] = messagesByPage[page];
1827
1828 let regLength = regIds.length;
1829 let newLength = newIds.length;
1830
1831 messagesByPage[page] = [newIds, newDates];
1832
1833 if (regLength === 0) {
1834 return;
1835 }
1836
1837 if (regLength <= newLength && regIds[0] === newIds[0] && regIds[regLength - 1] === newIds[regLength - 1]) {
1838 return;
1839 }
1840
1841 // Problème : on ne prend pas en compte les messages "ressuscités" (est-ce possible ?), mais
1842 // c'est compliqué, car si on part du principe que la suppression est réversible, il peut y
1843 // avoir des data race entre deux requêtes (message supprimé entre les deux, mais la 2ème
1844 // arrive avant la première => message ressuscité alors qu'il ne devrait pas).
1845
1846 let after = new Set(newIds);
1847 let isFirst = true;
1848
1849 for (let i = 0; i < regIds.length; i++) {
1850 let id = regIds[i];
1851 if (after.has(id)) {
1852 isFirst = false;
1853 continue;
1854 }
1855
1856 // Pas enregistré => blacklist, false => supprimé
1857 if (!lastEditionTime.hasOwnProperty(id) || lastEditionTime[id] === false) {
1858 continue;
1859 }
1860
1861 // Si message en début de page : vérifier qu'il n'est pas sur la page précédente
1862 // Vérifier aussi les début/fin de page en cas de PEMT, car 2 messages peuvent être "swap"
1863 if (isFirst || (regDates[i] === newDates[0]) || (newLength === 20 && regDates[i] === newDates[19])) {
1864 checkDeleted(id);
1865 continue;
1866 }
1867
1868 let [timestamp, _] = lastEditionTime[id];
1869
1870 if (timestamp >= requestTimestamp) {
1871 continue;
1872 }
1873
1874 let msg = document.querySelector(`.jvchat-message[jvchat-id="${id}"]`);
1875 if (msg) {
1876 msg.classList.add("jvchat-message-deleted");
1877 lastEditionTime[id] = false;
1878 }
1879 }
1880}
1881
1882function formatDate(date) {
1883 let now = new Date();
1884 let day = date.getDate();
1885 let month = date.getMonth();
1886 let year = date.getFullYear();
1887 if (now.getDate() === day && now.getMonth() === month && now.getFullYear() === year) {
1888 return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}`;
1889 } else {
1890 return `${day.toString().padStart(2, "0")}/${(month + 1).toString().padStart(2, "0")}/${year}`;
1891 }
1892}
1893
1894function makeMessage(message) {
1895 let content = message.content;
1896 fixMessage(content);
1897 detectMosaic(content);
1898 improveImages(content);
1899 let id = message.id;
1900 let avatar = message.avatar;
1901 let ddbSpan = message.ddbHash;
1902 let toQuoteDate = message.dateString;
1903 let titleDate = message.dateString;
1904 let textDate = formatDate(message.date);
1905 if (message.edited !== undefined) {
1906 textDate += "*";
1907 titleDate += ` (édité à ${message.edited})`;
1908 }
1909 let exists = avatar !== undefined;
1910 let author = exists ? message.author : `<i>${message.author}</i>`;
1911 let authorHref = exists ? `href="http://www.jeuxvideo.com/profil/${author.toLowerCase()}?mode=infos"` : "";
1912 let authorTitle = exists ? `title="Ouvrir le profil de ${author}"` : "";
1913 let authorAvatarHidden = exists ? "" : "class='jvchat-hide-visibility'";
1914 let editionSpan = '<span class="jvchat-edit jvchat-picto picto-msg-crayon" title="Modifier"></span>';
1915 let deletionSpan = '<span class="jvchat-delete jvchat-picto picto-msg-croix" title="Supprimer"></span>';
1916 let ddbionSpan = `<span class="jvchat-ddb jvchat-picto picto-msg-exclam" title="Signaler" data-selector="${ddbSpan}"></span>`
1917 let deletion = (currentUser.author === undefined) || (message.author.toLowerCase() !== currentUser.author.toLowerCase()) ? "" : deletionSpan;
1918 let edition = (currentUser.author === undefined) || (message.author.toLowerCase() !== currentUser.author.toLowerCase()) ? "" : editionSpan;
1919 let ddbion = (currentUser.author === undefined) || (message.author.toLowerCase() === currentUser.author.toLowerCase()) ? "" : ddbionSpan;
1920 let html =
1921 `<div class="jvchat-bloc-message">
1922 <div class="jvchat-message" jvchat-id=${id}>
1923 <div>
1924 <a ${authorAvatarHidden} ${authorHref} target="_blank" ${authorTitle}>
1925 <div class="jvchat-bloc-avatar jvchat-rounded" style="background-image: url(${avatar})"></div>
1926 </a>
1927 </div>
1928 <div class="jvchat-bloc-author-content">
1929 <div class="jvchat-toolbar">
1930 <h5 class="jvchat-author">${author}</h5>
1931 <div class="jvchat-tooltip">
1932 ${deletion}
1933 ${edition}
1934 <span class="jvchat-picto jvchat-quote picto-msg-quote" title="Citer"></span>
1935 ${ddbion}
1936 <small class="jvchat-date" to-quote="${toQuoteDate}" title="${titleDate}">${textDate}</small>
1937 </div>
1938 </div>
1939 <div class="jvchat-content">${content.outerHTML}</div>
1940 <div class="jvchat-edition jvchat-hide">
1941 <textarea class="jvchat-edition-textarea jvchat-textarea"></textarea>
1942 <div class="jvchat-buttons">
1943 <button tabindex="0" type="button" class='jvchat-edition-check icon-check-jv jvchat-button-top' title="Valider la modification"></button>
1944 <button tabindex="0" type="button" class='jvchat-edition-cancel icon-cancel-circle jvchat-button-bottom' title="Annuler la modification"></button>
1945 </div>
1946 </div>
1947 </div>
1948 </div>
1949 <hr class="jvchat-ruler">
1950 </div>`;
1951 return html;
1952}
1953
1954function parseDate(string) {
1955 let [date, time] = string.toLowerCase().split("à");
1956 let [day, month, year] = date.trim().split(" ");
1957 let [hour, minute, second] = time.trim().split(":");
1958 let monthIndex = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"].indexOf(month.trim().toLowerCase());
1959 return new Date(parseInt(year), monthIndex, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
1960}
1961
1962function addMessages(messages, editing, requestTimestamp) {
1963 let main = document.getElementById("jvchat-main");
1964 let hasNewMessages = false;
1965 let init = true;
1966 let toInsert = "";
1967 let newMessagesIds = [];
1968 for (let message of messages) {
1969 let date = message.date;
1970 let id = message.id;
1971
1972 if (init === true && !editing) {
1973 init = false;
1974 let now = new Date();
1975 let delta = now - date;
1976 if (delta > 5 * 60 * 1000 + checkEditInterval) {
1977 shouldCheckEdited = false;
1978 } else {
1979 shouldCheckEdited = true;
1980 }
1981 }
1982
1983 if (message.blacklisted) {
1984 continue;
1985 }
1986
1987 if (firstMessageId === undefined) {
1988 firstMessageId = id;
1989 firstMessageDate = date;
1990 }
1991
1992 // Attention à 2 choses: le changement d'heure et le fait qu'un message suivant un autre peut avoir un id inférieur au précédent
1993 if (id < firstMessageId && date < firstMessageDate) {
1994 continue;
1995 }
1996
1997 let referenced = lastEditionTime.hasOwnProperty(id);
1998 let edited = message.edited;
1999
2000 if (referenced) {
2001 if (lastEditionTime[id] === false) {
2002 continue;
2003 }
2004 let [timestamp, edition] = lastEditionTime[id];
2005 if (timestamp >= requestTimestamp || edition === edited) {
2006 continue;
2007 }
2008 }
2009
2010 let newBloc = makeMessage(message);
2011 lastEditionTime[id] = [requestTimestamp, edited];
2012
2013 if (referenced) {
2014 let selector = `.jvchat-message[jvchat-id="${id}"]`;
2015 let oldBloc = main.querySelector(selector).closest(".jvchat-bloc-message");
2016 let isDown = isScrollDown();
2017 oldBloc.outerHTML = newBloc;
2018 if (isDown) {
2019 setScrollDown();
2020 }
2021 continue;
2022 }
2023
2024 hasNewMessages = true;
2025 if (nbNewMessage === 0 && document.hidden) {
2026 let hrs = document.getElementsByClassName("jvchat-ruler");
2027 let lastHr = hrs[hrs.length - 1];
2028 lastHr.setAttribute("id", "jvchat-ruler-new");
2029 }
2030
2031 toInsert += newBloc;
2032 newMessagesIds.push(id);
2033 nbNewMessage++;
2034 }
2035
2036 if (toInsert !== "") {
2037 let isDown = isScrollDown();
2038 main.insertAdjacentHTML("beforeend", toInsert);
2039 if (isDown) {
2040 setScrollDown();
2041 }
2042 }
2043
2044 if (editing) {
2045 return;
2046 }
2047
2048 if (isScrollDown()) {
2049 let blocMessages = main.getElementsByClassName("jvchat-bloc-message");
2050 let nb = blocMessages.length;
2051 if (nb > 100) {
2052 for (let i = 0; i < nb - 100; i++) {
2053 main.removeChild(blocMessages[0]);
2054 }
2055 setScrollDown();
2056 }
2057 }
2058
2059 if (hasNewMessages) {
2060 if (!turboActivated) {
2061 decreaseUpdateInterval();
2062 }
2063 if (document.hidden) {
2064 setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
2065 if (configuration["play_sound"]) {
2066 ringBell.pause();
2067 ringBell.currentTime = 0;
2068 ringBell.play();
2069 }
2070 }
2071 for (let newMessageId of newMessagesIds) {
2072 let event = new CustomEvent('jvchat:newmessage', { 'detail': { id: newMessageId } });
2073 dispatchEvent(event);
2074 }
2075 } else {
2076 if (!turboActivated) {
2077 increaseUpdateInterval();
2078 }
2079 }
2080}
2081
2082function submitSondageAnswer(event) {
2083 let target = event.target;
2084 if (!target) {
2085 return;
2086 }
2087 if (target.classList.contains("click-sondage")) {
2088 let reponseNum = parseInt(target.getAttribute("sondage-reponse-num"));
2089 let sondageId = sondageChoices[reponseNum]["sondageId"];
2090 let reponseId = sondageChoices[reponseNum]["responseId"];
2091 let topicId = urlToFetch["ids"].split("-")[2];
2092 let url = `http://www.jeuxvideo.com/forums/ajax_topic_sondage_vote.php?id_topic=${topicId}&id_sondage_reponse=${reponseId}&id_sondage=${sondageId}&ajax_hash=${freshHash}`;
2093
2094 function onSuccess(res) {
2095 if (res.erreur.length > 0) {
2096 for (let err of res.erreur) {
2097 addAlertbox("danger", err);
2098 }
2099 return;
2100 }
2101 let dom = document.createElement("html");
2102 dom.innerHTML = res["html"];
2103
2104 let sondage = parseSondage(dom);
2105 if (!sondage) {
2106 addAlertbox("warning", "Erreur lors de la récupération du sondage");
2107 return;
2108 }
2109
2110 setSondage(sondage);
2111 }
2112
2113 function onError(err, _) {
2114 addAlertbox("danger", err);
2115 }
2116
2117 function onTimeout(err) {
2118 addAlertbox("warning", err);
2119 }
2120
2121 request("POST", url, onSuccess, onError, onTimeout, undefined, true, 5000);
2122 }
2123}
2124
2125function setSondage(sondage) {
2126 let choix = document.getElementById("jvchat-sondage-choix");
2127
2128 if (sondage["answered"]) {
2129 choix.removeEventListener("click", submitSondageAnswer);
2130 choix.classList.remove("notanswered");
2131 } else {
2132 if (!sondageChoices) {
2133 sondageChoices = sondage["results"];
2134 }
2135 choix.addEventListener("click", submitSondageAnswer);
2136 choix.classList.add("notanswered");
2137 }
2138
2139 if (!choix.firstChild) {
2140 document.getElementById("jvchat-sondage-intitule").innerHTML = sondage["intitule"];
2141 let results = sondage["results"];
2142 for (let i = 0; i < results.length; i++) {
2143 let res = results[i];
2144 let tr = `<tr><td class="result-pourcent"><div class="pourcent">${res["pourcent"]} %</div><div class="back-barre"><span style="width: ${res["pourcent"]}%;"></span></div></td><td class="reponse"><div class="click-sondage" sondage-reponse-num="${i}">${res["response"]}</div></td></tr>`;
2145 choix.insertAdjacentHTML("beforeend", tr);
2146 }
2147 } else {
2148 let trs = choix.getElementsByClassName("result-pourcent");
2149 for (let i = 0; i < trs.length; i++) {
2150 let res = sondage["results"][i];
2151 let tr = trs[i];
2152 tr.getElementsByClassName("pourcent")[0].innerHTML = `${res["pourcent"]} %`;
2153 tr.getElementsByTagName("span")[0].style["width"] = `${res["pourcent"]}%`;
2154 }
2155 }
2156
2157 document.getElementById("jvchat-sondage-votes").innerHTML = `(${sondage["votes"]} votes)`;
2158}
2159
2160function setUser(document, user) {
2161 let isConnected = (user.author !== undefined);
2162
2163 if (isConnected) {
2164 if (user.author !== currentUser.author) {
2165 let pseudo = document.getElementById("jvchat-user-pseudo");
2166 pseudo.innerHTML = user.author;
2167 let avatarLink = document.getElementById("jvchat-user-avatar-link");
2168 let notifLink = document.getElementById("jvchat-user-notif-link");
2169 avatarLink.setAttribute("href", `http://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=infos`);
2170 notifLink.setAttribute("href", `http://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=abonnements`);
2171 }
2172
2173 if (user.avatar !== currentUser.avatar) {
2174 let avatar = document.getElementById("jvchat-user-avatar");
2175 avatar.style["background-image"] = `url("${user.avatar}")`;
2176 }
2177
2178 if (user.mp !== currentUser.mp) {
2179 let mp = document.getElementById("jvchat-user-mp");
2180 mp.setAttribute("data-val", user.mp);
2181 if (user.mp > 0) {
2182 mp.classList.add("has-notif");
2183 } else {
2184 mp.classList.remove("has-notif");
2185 }
2186 }
2187
2188 if (user.notif !== currentUser.notif) {
2189 let notif = document.getElementById("jvchat-user-notif");
2190 notif.setAttribute("data-val", user.notif);
2191 if (user.notif > 0) {
2192 notif.classList.add("has-notif");
2193 } else {
2194 notif.classList.remove("has-notif");
2195 }
2196 }
2197 }
2198
2199 if ((userConnected === undefined && isConnected) || (userConnected !== undefined && isConnected !== userConnected)) {
2200 document.getElementById("jvchat-profil").classList.toggle("jvchat-hide");
2201 let isDown = isScrollDown();
2202 document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-hide");
2203 if (isDown) {
2204 setScrollDown();
2205 }
2206 }
2207
2208 if (userConnected !== undefined) {
2209 if (isConnected && !userConnected) {
2210 addAlertbox("success", "Vous êtes désormais connecté");
2211 } else if (!isConnected && userConnected) {
2212 addAlertbox("warning", "Vous avez été déconnecté");
2213 }
2214 }
2215
2216 userConnected = isConnected;
2217 currentUser = user;
2218}
2219
2220function setTopicTitle(document, topicTitle) {
2221 if (topicTitle !== currentTopicTitle) {
2222 currentTopicTitle = topicTitle;
2223 document.getElementById("jvchat-topic-title").innerHTML = topicTitle;
2224 }
2225}
2226
2227function setTopicNbConnected(document, nbConnected) {
2228 let txt = `${nbConnected} connectés`;
2229 if (!(nbConnected > 1)) {
2230 if (nbConnected === undefined) {
2231 txt = "? connectés";
2232 } else {
2233 txt = txt.slice(0, -1);
2234 }
2235 }
2236 document.getElementById("jvchat-topic-nb-connected").innerHTML = txt;
2237}
2238
2239function setTopicNbMessages(document, nbMessages) {
2240 let txt = `${nbMessages} messages`;
2241 if (!(nbMessages > 1)) {
2242 if (nbMessages === undefined) {
2243 txt = "? messages";
2244 } else {
2245 txt = txt.slice(0, -1);
2246 }
2247 }
2248 document.getElementById("jvchat-topic-nb-messages").innerHTML = txt;
2249}
2250
2251function triggerJVChat() {
2252 // TamperMonkey / Chrome bug: https://github.com/Tampermonkey/tampermonkey/issues/705#issuecomment-493895776
2253 if (window) {
2254 if (window.clearTimeout) {
2255 window.clearTimeout = window.clearTimeout.bind(window);
2256 }
2257 if (window.clearInterval) {
2258 window.clearInterval = window.clearInterval.bind(window);
2259 }
2260 if (window.setTimeout) {
2261 window.setTimeout = window.setTimeout.bind(window);
2262 }
2263 if (window.setInterval) {
2264 window.setInterval = window.setInterval.bind(window);
2265 }
2266 window.onbeforeunload = function (event) {
2267 leavingTopic = true;
2268 }
2269 }
2270
2271 let topicUrl = document.URL;
2272 let topic = parseTopicInfo(document);
2273 let user = parseUserInfo(document);
2274 let sondage = parseSondage(document);
2275
2276 urlToFetch = parseURL(topicUrl);
2277 urlToFetch.page = 1;
2278 urlToFetch.anchor = "";
2279
2280 urlToCheckEdit = parseURL(topicUrl);
2281 urlToCheckEdit.page = 1;
2282 urlToCheckEdit.anchor = "";
2283
2284 loadConfig();
2285 ringBell = new Audio(configuration["sound"]);
2286 clearPage(document);
2287
2288 setUser(document, user);
2289 setTopicTitle(document, topic.title);
2290 setTopicNbMessages(document, undefined);
2291 setTopicNbConnected(document, topic.connected);
2292
2293 if (sondage) {
2294 document.getElementById("jvchat-sondage").classList.remove("jvchat-hide")
2295 setSondage(sondage);
2296 }
2297
2298 document.getElementById("jvchat-topic-title").setAttribute("href", buildURL(urlToFetch));
2299
2300 let forum = getForum(document);
2301 let forumSide = document.getElementById("jvchat-forum-title");
2302 forumSide.setAttribute("href", forum.href);
2303 forumSide.innerHTML = forum.title;
2304
2305 let defaultReduced = configuration["default_reduced"];
2306 let messageTopic = document.getElementById("message_topic");
2307
2308 if (messageTopic && (defaultReduced === false || (messageTopic.value !== ""))) {
2309 toggleTextarea();
2310 }
2311
2312 let event = new CustomEvent('jvchat:activation');
2313 dispatchEvent(event);
2314
2315 let page = topic.lastPage > 1 ? topic.lastPage - 1 : topic.lastPage;
2316 updateMessages(page, true);
2317
2318 setInterval(checkEdited, checkEditInterval);
2319}
2320
2321
2322function updateMessages(page, goToLast) {
2323 if (postingMessage && turboActivated) {
2324 // Postpone message fetching, posting the message is priorized
2325 fetchingMessages = false;
2326 currentTimeoutId = setTimeout(tryCatch(function postponedUpdate() {
2327 updateMessages(page, goToLast);
2328 }), 100);
2329 return;
2330 }
2331
2332 let timestamp = getTimestamp();
2333
2334 function scheduleNextUpdate(interval, p, goLast) {
2335 fetchingMessages = false;
2336 currentTimeoutId = setTimeout(tryCatch(function scheduledUpdate() {
2337 updateMessages(p, goLast);
2338 }), interval);
2339 };
2340
2341 function onSuccess(res) {
2342 let parsed = parsePage(res, timestamp);
2343 let lastPage = parsed.lastPage;
2344 let currPage = parsed.page;
2345 let int = turboActivated ? configuration["turbo_delay"] : updateIntervals[updateIntervalIdx] * 1000;
2346
2347 if (page < lastPage && goToLast) {
2348 updateMessages(page + 1, true);
2349 } else if (currPage < page || parsed.nbMessagesPage === 0) { // Bug des messages supprimés
2350 scheduleNextUpdate(int, page - 1, false);
2351 } else if (page > lastPage) {
2352 updateMessages(lastPage, true);
2353 } else {
2354 scheduleNextUpdate(int, page, true);
2355 }
2356 }
2357
2358 function onError(err, _) {
2359 if (!isError) {
2360 isError = true;
2361 setFixedAlert("danger", err);
2362 }
2363 scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 60000, page, true);
2364 }
2365
2366 function onTimeout(err) {
2367 addAlertbox("warning", err);
2368 scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 10000, page, true);
2369 }
2370
2371 let timeout = 10000;
2372 if (turboActivated) {
2373 timeout = 5000;
2374 }
2375
2376 fetchingMessages = true;
2377 currentFetchedPage = page;
2378 urlToFetch.page = page;
2379 let urlLastPage = buildURL(urlToFetch);
2380 request("GET", urlLastPage, onSuccess, onError, onTimeout, undefined, false, timeout);
2381}
2382
2383function checkEdited() {
2384 if (!shouldCheckEdited || currentFetchedPage === 1 || isError) {
2385 return;
2386 }
2387
2388 urlToCheckEdit.page = currentFetchedPage - 1;
2389 let urlPrevLastPage = buildURL(urlToCheckEdit);
2390 let timestamp = getTimestamp();
2391
2392 function onSuccess(res) {
2393 let newMessages = [];
2394 let edited = res.getElementsByClassName("info-edition-msg");
2395 for (let msg of edited) {
2396 let bloc = msg.closest(".bloc-message-forum");
2397 newMessages.push(parseMessage(bloc));
2398 }
2399 addMessages(newMessages, true, timestamp);
2400 findDeletedMessages(res, timestamp);
2401 }
2402
2403 function onError(_, _) { }
2404
2405 function onTimeout(_) { }
2406
2407 request("GET", urlPrevLastPage, onSuccess, onError, onTimeout, undefined, false, 20000);
2408}
2409
2410function checkDeleted(id) {
2411 let url = `http://www.jeuxvideo.com/jvchat/forums/message/${id}`;
2412 let requestTimestamp = getTimestamp();
2413
2414 function onSuccess(_) {
2415 // Le message existe, il a disparu de la page car un message plus ancien a été supprimé
2416 }
2417
2418 function onError(_, status) {
2419 if (status === 410 && lastEditionTime[id] !== false) {
2420 let [timestamp, _] = lastEditionTime[id];
2421
2422 if (timestamp >= requestTimestamp) {
2423 return;
2424 }
2425
2426 let msg = document.querySelector(`.jvchat-message[jvchat-id="${id}"]`);
2427 if (msg) {
2428 msg.classList.add("jvchat-message-deleted");
2429 lastEditionTime[id] = false;
2430 }
2431 }
2432 }
2433
2434 function onTimeout(_) { }
2435
2436 request("GET", url, onSuccess, onError, onTimeout, undefined, false, 20000);
2437}
2438
2439function parseAlerts(res) {
2440 let alerts = [];
2441 let alertsDiv = res.getElementsByClassName("alert");
2442 for (let a of alertsDiv) {
2443 let type = "danger";
2444 if (a.classList.contains("alert-warning")) {
2445 type = "warning";
2446 } else if (a.classList.contains("alert-success")) {
2447 type = "success";
2448 }
2449 let message = a.getElementsByClassName("alert-row")[0].textContent.trim();
2450 alerts.push({ type: type, message: message });
2451 }
2452 return alerts;
2453}
2454
2455function increaseUpdateInterval() {
2456 if (updateIntervalIdx < updateIntervalMax) {
2457 updateIntervalIdx++;
2458 }
2459}
2460
2461function decreaseUpdateInterval() {
2462 updateIntervalIdx = transisitions[updateIntervalIdx];
2463}
2464
2465function parsePage(res, requestTimestamp) {
2466 let error = getTopicError(res);
2467 if (error !== undefined) {
2468 if (!isError) {
2469 updateIntervalIdx = updateIntervalMax;
2470 isError = true;
2471 setFixedAlert("danger", error);
2472 }
2473 return { lastPage: undefined, page: undefined, alert: true, nbMessagesPage: 0 }
2474 }
2475
2476 if (isError) {
2477 isError = false;
2478 updateIntervalIdx = 0;
2479 removeFixedAlert("Le topic ne retourne plus d'erreur");
2480 }
2481
2482 let form = getForm(res);
2483 if (form !== undefined) {
2484 freshForm = form;
2485 }
2486
2487 let hash = getHash(res);
2488 if (hash !== undefined) {
2489 freshHash = hash;
2490 }
2491
2492 let deletionHash = getDeletionHash(res);
2493 if (deletionHash !== undefined) {
2494 freshDeletionHash = deletionHash;
2495 }
2496
2497 let messages = getMessages(res);
2498 addMessages(messages, false, requestTimestamp);
2499 let user = parseUserInfo(res);
2500 setUser(document, user);
2501 let topic = parseTopicInfo(res);
2502 let nbMessages = (topic.lastPage - 1) * 20;
2503 if (topic.page == topic.lastPage) {
2504 nbMessages += messages.length;
2505 }
2506 findDeletedMessages(res, requestTimestamp);
2507 setTopicNbMessages(document, nbMessages);
2508 setTopicNbConnected(document, topic.connected);
2509 let alerts = parseAlerts(res);
2510 for (let alert of alerts) {
2511 addAlertbox(alert.type, alert.message);
2512 }
2513 let locked = getTopicLocked(res);
2514 let isLocked_ = (locked !== undefined);
2515
2516 if (isLocked_ && !isLocked) {
2517 updateInterval = updateIntervalMax;
2518 setFixedAlert("warning", locked);
2519 } else if (!isLocked_ && isLocked) {
2520 updateInterval = 0;
2521 removeFixedAlert("Le topic a été dévérouillé");
2522 }
2523 isLocked = isLocked_;
2524
2525 let sondage = parseSondage(res);
2526 if (sondage) {
2527 setSondage(sondage);
2528 }
2529
2530 return {
2531 page: topic.page, lastPage: topic.lastPage,
2532 nbMessagesPage: messages.length, alert: isLocked_ || (alerts.length > 0)
2533 };
2534}
2535
2536function addAlertbox(type, message) {
2537 // type: success / warning / danger
2538 let alert = `<div class="alert alert-${type}">
2539 <button class="close jvchat-alert-close" aria-hidden="true" data-dismiss="alert" type="button">×</button>
2540 <div class="alert-row">${message}</div>
2541 </div>`;
2542 document.getElementById("jvchat-fixed-alert").insertAdjacentHTML("afterend", alert);
2543}
2544
2545function setFixedAlert(type, message) {
2546 setFavicon("⨯");
2547 document.getElementById("jvchat-fixed-alert").getElementsByClassName("alert-row")[0].innerHTML = message;
2548 document.getElementById("jvchat-fixed-alert").setAttribute("class", `alert alert-${type}`);
2549}
2550
2551function removeFixedAlert(message) {
2552 document.getElementById("jvchat-fixed-alert").classList.add("jvchat-hide");
2553 if (message !== undefined) {
2554 addAlertbox("success", message);
2555 }
2556 if (document.hidden && nbNewMessage > 0) {
2557 setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
2558 } else {
2559 setFavicon("");
2560 }
2561}
2562
2563function makeJVChatButton() {
2564 let cls = 'btn-jvchat';
2565 let text = 'JVChat';
2566 let btn = `<button class="btn btn-actu-new-list-forum ${cls}">${text}</button>`;
2567 return btn;
2568}
2569
2570function addJVChatButton(document) {
2571 let css = `<style type="text/css">
2572 #forum-main-col .bloc-pre-pagi-forum {
2573 display: flex;
2574 }
2575
2576 #forum-main-col .bloc-pre-pagi-forum .bloc-pre-right {
2577 position: relative;
2578 right: unset;
2579 left: unset;
2580 top: unset;
2581 bottom: unset;
2582 overflow: hidden;
2583 display: flex;
2584 flex-wrap: wrap;
2585 justify-content: flex-end;
2586 margin-top: auto;
2587 flex: 1;
2588 }
2589
2590 #forum-main-col .bloc-pre-pagi-forum .bloc-pre-right button {
2591 float: right;
2592 min-width: 5.25rem;
2593 margin-left: 0.3125rem;
2594 }
2595 </style>`
2596 document.head.insertAdjacentHTML("beforeend", css);
2597 let blocPreRight = document.getElementsByClassName("bloc-pre-right");
2598 let jvchatButton = makeJVChatButton();
2599 for (let bloc of blocPreRight) {
2600 bloc.insertAdjacentHTML('afterbegin', jvchatButton);
2601 }
2602}
2603
2604function bindJVChatButton(document) {
2605 let buttons = document.getElementsByClassName('btn-jvchat');
2606 for (let btn of buttons) {
2607 btn.addEventListener('click', tryCatch(triggerJVChat));
2608 }
2609}
2610
2611function request(mode, url, callbackSuccess, callbackError, callbackTimeout, data, json, timeout) {
2612 json = !!json;
2613 let xhr = new XMLHttpRequest();
2614 xhr.timeout = timeout;
2615
2616 xhr.ontimeout = tryCatch(function xhrOnTimeout() {
2617 callbackTimeout(`La délai d'attente de la requête a expiré`);
2618 });
2619
2620 xhr.onerror = tryCatch(function xhrOnError() {
2621 callbackError(`La requête a échoué (${xhr.status}): ${xhr.statusText}`, xhr.status);
2622 });
2623
2624 xhr.onabort = tryCatch(function xhrOnAbort() {
2625 if (!leavingTopic) {
2626 callbackTimeout(`La requête a été interrompue pour une raison inconnue`);
2627 }
2628 });
2629
2630 xhr.onload = tryCatch(function xhrOnLoad() {
2631 if (xhr.status !== 200) {
2632 callbackError(`La requête a retourné une erreur (${xhr.status}): ${xhr.statusText}`, xhr.status);
2633 return;
2634 }
2635 callbackSuccess(xhr.response);
2636 });
2637
2638 if (data === undefined) {
2639 data = null;
2640 }
2641
2642 if (json) {
2643 xhr.responseType = "json";
2644 } else {
2645 xhr.responseType = "document";
2646 }
2647
2648 xhr.open(mode, url, true);
2649 xhr.setRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
2650 xhr.send(data);
2651};
2652
2653// On copie/colle le code de TopicLive et on se sent développeur :)
2654function makeFavicon() {
2655 let canvas = document.createElement("canvas");
2656 canvas.width = 16;
2657 canvas.height = 16;
2658 let context = canvas.getContext('2d');
2659 let image = new Image();
2660 image.src = 'data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABMLAAATCwAAAAAAAAAAAABkRgT/ZEYE/2RGBP9kRgT/ZEYE/2RGBf9lRgD/YEcV/zJW7v8tV///Llf+/y5X//8uV///Llf//y5X//8uV///ZEYE/2RGBP9kRgT/ZEYE/2RGBP9kRgX/ZUYA/2BHFf8yVu7/LVf//y5X/v8uV///Llf//y5X//8uV///Llf//2RGBP9kRgT/ZEYE/2VHBf9lRwX/ZEYF/2VGAP9gRxX/Mlbu/y1X//8uV/7/Llf//y5X//8uV///Llf//y5X//9kRgT/ZEcF/2ZIB/9iQwH/YkMB/2ZJCP9lRgD/YEcV/zJW7v8tV///L1f+/y1W//8tVv//L1j//y5X//8uV///ZUcE/2NFBP9ZOQD/bE8R/2xPEf9ZOQD/ZEQA/2FIFv8xVu7/Llj//yxV/v8uV///Llf//ytV//8vWP//Llf//2NFA/9kRgj/xLif/+7r5P/u6+T/w7if/2VGBP9fRhT/M1fv/yxW//8uV/7/1d3//9Xd//8uV///LVb//y9Y//9ZOQD/wLOZ/9/Yy/91WiD/dVog/97Yy//CtZj/VzsF/zFW8f8tV///L1f+/9Lb///S2///L1f//y5X//8rVf//bFAR/+nk2v91Wib/WzsA/1s7AP90Wif/7OfZ/2dQIv8wU+v/1d7//9Pb/v8tVv//LVb//9Pc///S2///Llf//2xQEf/p5Nr/dVom/1s7AP9bOwD/dFon/+zn2f9nUCL/MFPr/9Xe///T2/7/LVb//y1W///T3P//0tv//y5X//9ZOQD/wLOZ/9/Yy/91WiD/dVog/97Yy//CtZj/VzsF/zFW8f8tV///L1f+/9Lb///S2///L1f//y5X//8rVf//Y0UD/2RGCP/EuJ//7uvk/+7r5P/DuJ//ZUYE/19GFP8zV+//LFb//y5X/v/V3f//1d3//y5X//8tVv//L1j//2VHBP9jRQT/WTkA/2xPEf9sTxH/WTkA/2REAP9hSBb/MVbu/y5Y//8sVf7/Llf//y5X//8rVf//L1j//y5X//9kRgT/ZEcF/2ZIB/9iQwH/YkMB/2ZJCP9lRgD/YEcV/zJW7v8tV///L1f+/y1W//8tVv//L1j//y5X//8uV///ZEYE/2RGBP9kRgT/ZUcF/2VHBf9kRgX/ZUYA/2BHFf8yVu7/LVf//y5X/v8uV///Llf//y5X//8uV///Llf//2RGBP9kRgT/ZEYE/2RGBP9kRgT/ZEYF/2VGAP9gRxX/Mlbu/y1X//8uV/7/Llf//y5X//8uV///Llf//y5X//9kRgT/ZEYE/2RGBP9kRgT/ZEYE/2RGBf9lRgD/YEcV/zJW7v8tV///Llf+/y5X//8uV///Llf//y5X//8uV///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==';
2661 return { canvas: canvas, context: context, image: image };
2662};
2663
2664function clearFavicon() {
2665 favicon.context.clearRect(0, 0, favicon.canvas.width, favicon.canvas.height);
2666 favicon.context.drawImage(favicon.image, 0, 0);
2667};
2668
2669function fillFavicon(txt) {
2670 clearFavicon();
2671
2672 if (txt !== '') {
2673 let context = favicon.context;
2674 context.fillStyle = 'DodgerBlue';
2675 context.fillRect(0, 0, context.measureText(txt).width + 3, 11);
2676 context.fillStyle = 'white';
2677 context.font = 'bold 10px Verdana';
2678 context.textBaseline = 'bottom';
2679 context.fillText(txt, 1, 11);
2680 }
2681};
2682
2683function setFavicon(txt) {
2684 let fav = document.getElementById("jvchat-favicon");
2685 if (fav) {
2686 fav.parentElement.removeChild(fav);
2687 }
2688 fillFavicon(txt);
2689 let url = favicon.canvas.toDataURL('image/png');
2690 let icon = `<link id="jvchat-favicon" rel="shortcut icon" type="image/png" href="${url}">`;
2691 document.head.insertAdjacentHTML("beforeend", icon);
2692}
2693
2694function reverseMessage(node, isInit, isUl) {
2695 let quote = "";
2696 let prevIsP = false;
2697
2698 for (let child of node.childNodes) {
2699 let name = child.nodeName;
2700
2701 switch (name) {
2702 case "P": {
2703 quote += reverseMessage(child) + "\n\n";
2704 break;
2705 }
2706 case "STRONG": {
2707 quote += "'''" + reverseMessage(child) + "'''";
2708 break;
2709 }
2710 case "EM": {
2711 quote += "''" + reverseMessage(child) + "''";
2712 break;
2713 }
2714 case "BR": {
2715 break;
2716 }
2717 case "UL": {
2718 quote += reverseMessage(child, false, true) + "\n\n";
2719 break;
2720 }
2721 case "OL": {
2722 quote += reverseMessage(child, false, false) + "\n\n";
2723 break;
2724 }
2725 case "LI": {
2726 if (isUl === true) {
2727 quote += "* " + reverseMessage(child) + "\n";
2728 } else {
2729 quote += "# " + reverseMessage(child) + "\n";
2730 }
2731 break;
2732 }
2733 case "DIV": {
2734 let classList = child.classList;
2735 if (classList.contains("bloc-spoil-jv")) {
2736 quote += "<spoil>" + reverseMessage(child) + "</spoil>\n\n"
2737 } else if (classList.contains("contenu-spoil")) {
2738 quote += reverseMessage(child);
2739 }
2740 break;
2741 }
2742 case "SPAN": {
2743 let classList = child.classList;
2744 if (classList.contains("bloc-spoil-jv")) {
2745 quote += "<spoil>" + reverseMessage(child) + "</spoil>";
2746 } else if (classList.contains("contenu-spoil")) {
2747 quote += reverseMessage(child);
2748 }
2749 break;
2750 }
2751 case "LABEL": {
2752 break;
2753 }
2754 case "INPUT": {
2755 break;
2756 }
2757 case "IMG": {
2758 quote += child.alt;
2759 break;
2760 }
2761 case "A": {
2762 if (child.title) {
2763 quote += child.href;
2764 } else {
2765 quote += reverseMessage(child);
2766 }
2767 break;
2768 }
2769 case "PRE": {
2770 quote += reverseMessage(child) + "\n\n";
2771 break;
2772 }
2773 case "CODE": {
2774 quote += "<code>" + child.textContent + "</code>";
2775 break;
2776 }
2777 case "BLOCKQUOTE": {
2778 if (prevIsP) {
2779 quote = quote.trimEnd() + "\n" + reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
2780 } else {
2781 quote += reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
2782 }
2783
2784 break;
2785 }
2786 case "#text": {
2787 // The "isInit" check is to prevent the empty text surroudning message
2788 // However, it may happen that the root node contains valid text child, so it need to be added somehow
2789 // For some reason, an "new line" may be missing in this case, so just add it
2790 if (!isInit || child.textContent.trim() !== "") {
2791 quote += child.textContent;
2792 if (isInit && !quote.endsWith("\n")) {
2793 quote += "\n";
2794 }
2795 }
2796 break;
2797 }
2798 default: {
2799 break;
2800 }
2801 }
2802
2803 if (name == "P") {
2804 prevIsP = true;
2805 } else {
2806 prevIsP = false;
2807 }
2808 }
2809
2810 quote = quote.replace(/(\n){3,}/g, '\n\n').trim();
2811
2812 if (isInit) {
2813 quote = quote.replace(/^/gm, '> ');
2814 }
2815
2816 return quote;
2817}
2818
2819function reverseQuote(blocMessage) {
2820 let author = blocMessage.getElementsByClassName("jvchat-author")[0].textContent.trim();
2821 let date = blocMessage.getElementsByClassName("jvchat-date")[0].getAttribute("to-quote");
2822 let header = `> Le ${date} ${author} a écrit :\n`;
2823 let quoted = reverseMessage(blocMessage.getElementsByClassName("txt-msg")[0], true);
2824 return header + quoted + '\n\n';
2825}
2826
2827function insertAtCursor(input, textToInsert) {
2828 const value = input.value;
2829 const start = input.selectionStart;
2830 const end = input.selectionEnd;
2831 input.value = value.slice(0, start) + textToInsert + value.slice(end);
2832 input.selectionStart = input.selectionEnd = start + textToInsert.length;
2833}
2834
2835function dontScrollOnExpand(event) {
2836 let target = event.target;
2837 if (!target) {
2838 return;
2839 }
2840
2841 let classes = target.classList;
2842
2843 if (classes.contains("nested-quote-toggle-box")) {
2844 let isDown = isScrollDown();
2845 let blockQuote = target.closest(".blockquote-jv");
2846 let visible = blockQuote.getAttribute("data-visible");
2847 let value = visible === "1" ? "" : "1";
2848 blockQuote.setAttribute('data-visible', value);
2849 if (isDown) {
2850 setScrollDown();
2851 }
2852 } else if (classes.contains("txt-spoil") || classes.contains("aff-spoil") || classes.contains("masq-spoil")) {
2853 event.preventDefault();
2854 let check = target.closest(".bloc-spoil-jv").getElementsByClassName("open-spoil")[0];
2855 let isDown = isScrollDown();
2856 check.checked = !check.checked;
2857 if (isDown) {
2858 setScrollDown();
2859 }
2860 } else if (classes.contains("jvchat-quote")) {
2861 let bloc = target.closest(".jvchat-message");
2862 let quote = reverseQuote(bloc);
2863 let textarea = document.getElementById("message_topic");
2864 if (isReduced) {
2865 toggleTextarea();
2866 }
2867 insertAtCursor(textarea, quote);
2868 textarea.focus();
2869 } else if (classes.contains("jvchat-edit")) {
2870 let bloc = target.closest(".jvchat-message");
2871 requestEdit(bloc);
2872 } else if (classes.contains("jvchat-delete")) {
2873 let bloc = target.closest(".jvchat-message");
2874 event.stopPropagation();
2875 requestDelete(bloc);
2876 } else if (classes.contains("jvchat-ddb")) {
2877 event.stopPropagation();
2878 let bloc = target.closest(".jvchat-message");
2879 requestDdb(bloc);
2880 } else if (classes.contains("jvchat-edition-check")) {
2881 let bloc = target.closest(".jvchat-message");
2882 editMessage(bloc);
2883 } else if (classes.contains("jvchat-edition-cancel")) {
2884 let bloc = target.closest(".jvchat-message");
2885 let isDown = isScrollDown();
2886 bloc.getElementsByClassName("jvchat-content")[0].classList.remove("jvchat-hide");
2887 bloc.getElementsByClassName("jvchat-edition")[0].classList.add("jvchat-hide");
2888 if (isDown) {
2889 setScrollDown();
2890 }
2891 }
2892}
2893
2894function isScrollDown() {
2895 let element = document.getElementById("jvchat-main");
2896 return element.clientHeight + Math.floor(element.scrollTop) >= element.scrollHeight - 1;
2897}
2898
2899function setScrollDown() {
2900 let element = document.getElementById("jvchat-main");
2901 element.scrollTop = element.scrollHeight + 10000;
2902}
2903
2904function main() {
2905 addJVChatButton(document);
2906 bindJVChatButton(document);
2907}
2908
2909main();