· 6 years ago · Feb 21, 2020, 05:18 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.65
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, 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KZ+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("===== JVCHAT ERROR =====");
1065 console.error(message)
1066 console.error(err);
1067 console.error("========================");
1068 try {
1069 addAlertbox("danger", message);
1070 } catch (e) {
1071 alert(message);
1072 }
1073 }
1074 }
1075 return wrapped;
1076}
1077
1078function toggleTextarea() {
1079 isReduced = !isReduced;
1080 configuration["default_reduced"] = isReduced;
1081 saveConfig();
1082
1083 let isDown = isScrollDown();
1084 document.getElementById("bloc-formulaire-forum").getElementsByClassName("jv-editor-toolbar")[0].classList.toggle("jvchat-hide");
1085 document.getElementById("jvchat-enlarge").classList.toggle("jvchat-hide");
1086 document.getElementById("jvchat-reduce").classList.toggle("jvchat-hide");
1087 document.getElementById("jvchat-post").classList.toggle("jvchat-hide");
1088 document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-reduced");
1089
1090 setTextareaHeight();
1091
1092 if (isDown) {
1093 setScrollDown();
1094 }
1095}
1096
1097function parseURL(url) {
1098 let regex = /^(.*?)(\/\d+-\d+-\d+-)(\d+)(-\d+-\d+-\d+-)(.*?)(\.htm)(.*)$/i;
1099 let [_, domain, ids, page, nums, title, htm, anchor] = url.match(regex);
1100 return { domain: domain, ids: ids, page: page, nums: nums, title: title, htm: htm, anchor: anchor };
1101}
1102
1103function buildURL(dict) {
1104 return `${dict.domain}${dict.ids}${dict.page}${dict.nums}${dict.title}${dict.htm}${dict.anchor}`;
1105}
1106
1107function getForum(document) {
1108 let ariane = document.getElementsByClassName("bloc-fil-ariane-crumb-forum")[0];
1109 let links = ariane.getElementsByTagName("a");
1110 let title = "";
1111 let forumLink = "";
1112
1113 for (let i = links.length - 1; i >= 0; i--) {
1114 forumLink = links[i];
1115 title = forumLink.innerHTML.trim();
1116 if (title.startsWith("Forum ")) {
1117 break;
1118 }
1119 }
1120
1121 return { href: forumLink.getAttribute("href"), title: title.replace("Forum ", "") };
1122}
1123
1124function getLastPage(document) {
1125 let blocPages = document.getElementsByClassName("bloc-liste-num-page")[0];
1126 let spans = blocPages.getElementsByTagName("span");
1127 let lastPage = 1;
1128 for (let span of spans) {
1129 let page = parseInt(span.textContent.trim());
1130 if (!isNaN(page) && page > lastPage) {
1131 lastPage = page;
1132 }
1133 }
1134 return lastPage;
1135}
1136
1137function parseMessage(elem) {
1138 let conteneurs = elem.getElementsByClassName("conteneur-message");
1139 let conteneur = conteneurs[conteneurs.length - 1];
1140 let blacklisted = conteneurs[0].classList.contains("conteneur-message-blacklist");
1141 let author = conteneur.getElementsByClassName("bloc-pseudo-msg")[0].textContent.trim();
1142 let avatar = conteneur.getElementsByClassName("user-avatar-msg")[0];
1143 if (avatar !== undefined) {
1144 avatar = avatar.getAttribute("data-srcset");
1145 }
1146 let date = conteneur.getElementsByClassName("bloc-date-msg")[0].textContent.trim();
1147 let content = conteneur.getElementsByClassName("text-enrichi-forum")[0];
1148 let id = parseInt(elem.getAttribute("data-id"));
1149 let edited = elem.getElementsByClassName("info-edition-msg")[0];
1150 if (edited !== undefined) {
1151 let msgEdited = edited.textContent.trim();
1152 edited = msgEdited.match(/Message édité le .*? à (.*?) par/i)[1];
1153 }
1154 return {
1155 author: author, dateString: date, date: parseDate(date), avatar: avatar, edited: edited,
1156 id: id, content: content, blacklisted: blacklisted
1157 };
1158}
1159
1160function parseUserInfo(elem) {
1161 let accountMp = elem.getElementsByClassName("jv-nav-account-mp")[0];
1162 if (accountMp === undefined) {
1163 return { author: undefined, avatar: undefined, mp: undefined, notif: undefined };
1164 }
1165 let numberMp = accountMp.getElementsByClassName("jv-account-number-mp")[0];
1166 let accountNotif = elem.getElementsByClassName("jv-nav-account-notif")[0];
1167 let numberNotif = accountNotif.getElementsByClassName("jv-account-number-notif")[0];
1168 let accountUser = elem.getElementsByClassName("jv-nav-account-user")[0];
1169 let avatarBox = accountUser.getElementsByClassName("account-avatar-box")[0];
1170 let authorBox = accountUser.getElementsByClassName("account-pseudo")[0];
1171 let mp = parseInt(numberMp.getAttribute("data-val"));
1172 let notif = parseInt(numberNotif.getAttribute("data-val"));
1173 let avatar = avatarBox.style["background-image"].slice(5, -2).replace("/avatar-md/", "/avatar/");
1174 let author = authorBox.textContent.trim();
1175 return { author: author, avatar: avatar, mp: mp, notif: notif };
1176}
1177
1178function getPage(elem) {
1179 let pageActive = elem.getElementsByClassName("page-active")[0];
1180 let page = 1;
1181 if (pageActive !== undefined) {
1182 page = parseInt(pageActive.textContent.trim());
1183 }
1184 return page;
1185}
1186
1187function parseTopicInfo(elem) {
1188 let title = elem.querySelector("#bloc-title-forum").textContent.trim();
1189 let connected = parseInt(elem.getElementsByClassName("nb-connect-fofo")[0].textContent.trim());
1190 let lastPage = getLastPage(elem);
1191 let page = getPage(elem);
1192 return { title: title, connected: connected, lastPage: lastPage, page: page };
1193}
1194
1195function fixMessage(elem) {
1196 let jvcares = Array.from(elem.getElementsByClassName("JvCare"));
1197 for (let jvcare of jvcares) {
1198 let a = document.createElement("a");
1199 a.setAttribute("target", "_blank");
1200 a.setAttribute("href", jvCake(jvcare.getAttribute("class")));
1201 a.innerHTML = jvcare.innerHTML;
1202 jvcare.outerHTML = a.outerHTML;
1203 }
1204 let togglableQuotes = Array.from(elem.querySelectorAll(".text-enrichi-forum > blockquote > blockquote"));
1205 for (let togglableQuote of togglableQuotes) {
1206 let toggleButton = document.createElement("div");
1207 toggleButton.classList.add("nested-quote-toggle-box");
1208 togglableQuote.insertBefore(toggleButton, togglableQuote.firstChild);
1209 // The click event is bound in the "dontScrollOnExpand()" function
1210 }
1211}
1212
1213function jvCake(cls) {
1214 let base16 = '0A12B34C56D78E9F', lien = '', s = cls.split(' ')[1];
1215 for (let i = 0; i < s.length; i += 2) {
1216 lien += String.fromCharCode(base16.indexOf(s.charAt(i)) * 16 + base16.indexOf(s.charAt(i + 1)));
1217 }
1218 return lien;
1219}
1220
1221function detectMosaic(elem) {
1222 let imagesShack = elem.getElementsByClassName("img-shack");
1223 if (imagesShack.length < 4) {
1224 return;
1225 }
1226 let mosaics = {};
1227 let regex = /^.+\/[0-9]+-[0-9]{1,2}-([a-z0-9]+)\.\w+$/i;
1228 for (let image of imagesShack) {
1229 let match = image.src.match(regex);
1230 if (!match) {
1231 continue;
1232 }
1233 [_, identifier] = match;
1234 if (mosaics.hasOwnProperty(identifier)) {
1235 mosaics[identifier].push(image);
1236 } else {
1237 mosaics[identifier] = [image]
1238 }
1239 }
1240 for (let identifier in mosaics) {
1241 let images = mosaics[identifier];
1242 if (images.length < 4) {
1243 continue;
1244 }
1245 images[0].parentNode.classList.add("jvchat-mosaic-root");
1246 images[0].classList.add("jvchat-mosaic");
1247 for (let image of images.slice(1)) {
1248 image.parentNode.classList.add("jvchat-mosaic");
1249 }
1250 }
1251}
1252
1253function improveImages(elem) {
1254 let imagesShack = elem.getElementsByClassName("img-shack");
1255 for (let image of imagesShack) {
1256 let src = image.src;
1257 let parent = image.parentNode;
1258 let extension = parent.href.split(".").pop();
1259 let direct = src.replace(/(.*?)\/minis\/(.*)\.\w+/i, "$1/fichiers/$2." + extension);
1260 image.setAttribute("data-src-mini", src);
1261 image.setAttribute("data-src-direct", direct);
1262 image.classList.add("jvchat-loadable-image");
1263 parent.href = direct;
1264 if (extension.toUpperCase() === "GIF") {
1265 image.src = direct;
1266 image.classList.remove("jvchat-loadable-image");
1267 } else if (configuration["load_images"]) {
1268 image.src = direct;
1269 }
1270 src = image.src;
1271 image.setAttribute("onerror", `this.onerror=null;this.src=this.getAttribute("data-src-direct");this.classList.remove("jvchat-loadable-image");`);
1272 }
1273}
1274
1275function clearPage(document) {
1276 let buttons = `
1277 <div id="jvchat-buttons-main" class='jvchat-buttons'>
1278 <button id='jvchat-post' tabindex="4" type="button" class='jvchat-hide jvchat-button-top icon-reply' title="Envoyer le message"></button>
1279 <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>
1280 <button id='jvchat-enlarge' tabindex="4" type="button" class='jvchat-button-solo icon-arrow-up-entypo' title="Agrandir la zone de texte"></button>
1281 <div>`;
1282
1283 document.head.insertAdjacentHTML("beforeend", CSS);
1284
1285 let previsu = document.getElementById("bloc-formulaire-forum").getElementsByClassName("previsu-editor")[0];
1286 if (previsu) {
1287 previsu.parentElement.removeChild(previsu);
1288 }
1289
1290 let messageTopic = document.getElementById("message_topic");
1291 if (messageTopic) {
1292 messageTopic.classList.add("jvchat-textarea");
1293 messageTopic.setAttribute("placeholder", "Hop hop hop, le message ne va pas s'écrire tout seul !");
1294 messageTopic.insertAdjacentHTML("afterend", buttons);
1295 messageTopic.addEventListener("keydown", tryCatch(postMessageIfEnter));
1296 document.getElementById("jvchat-post").addEventListener("click", tryCatch(postMessage));
1297 document.getElementById("jvchat-enlarge").addEventListener("click", tryCatch(toggleTextarea));
1298 document.getElementById("jvchat-reduce").addEventListener("click", tryCatch(toggleTextarea));
1299 }
1300 document.getElementsByClassName("conteneur-messages-pagi")[0].insertAdjacentHTML("afterbegin", "<div id='jvchat-main'><hr class='jvchat-ruler'></div>");
1301 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>");
1302
1303 document.getElementById("content-context").insertAdjacentHTML("afterbegin", PANEL);
1304 document.getElementById("content-context").insertAdjacentHTML("beforeend", "<div id='jvchat-right-padding'></div>");
1305
1306 document.getElementById("content").classList.add("jvchat-root");
1307 document.getElementById("bloc-formulaire-forum").classList.add("jvchat-reduced");
1308 document.getElementById("bloc-formulaire-forum").classList.add("jvchat-hide");
1309
1310 let toolbar = document.getElementById("bloc-formulaire-forum").getElementsByClassName("jv-editor-toolbar")[0];
1311 if (toolbar) {
1312 toolbar.classList.add("jvchat-hide");
1313 }
1314
1315 document.getElementById("jvchat-main").addEventListener("click", tryCatch(dontScrollOnExpand));
1316
1317 document.getElementById("jvchat-alerts").addEventListener("click", tryCatch(closeAlert));
1318
1319 document.getElementById("jvchat-leftbar-reduce").addEventListener("click", tryCatch(toggleSidebar));
1320 document.getElementById("jvchat-leftbar-extend").addEventListener("click", tryCatch(toggleSidebar));
1321 document.getElementById("jvchat-leftbar-config-open").addEventListener("click", tryCatch(toggleConfig));
1322 document.getElementById("jvchat-leftbar-config-close").addEventListener("click", tryCatch(toggleConfig));
1323
1324 document.getElementById("jvchat-turbo-checkbox").addEventListener("change", tryCatch(toggleTurbo));
1325
1326 document.getElementById("jvchat-play-sound-checkbox").checked = configuration["play_sound"];
1327 document.getElementById("jvchat-play-sound-checkbox").addEventListener("change", tryCatch(togglePlaySoundOption));
1328
1329 document.getElementById("jvchat-night-mode-checkbox").checked = configuration["night_mode"];
1330 document.getElementById("jvchat-night-mode-checkbox").addEventListener("change", tryCatch(toggleNightModeOption));
1331 if (configuration["night_mode"]) {
1332 document.getElementById("content").classList.add("jvchat-night-mode");
1333 }
1334
1335 document.getElementById("jvchat-hide-mosaic-checkbox").checked = configuration["hide_mosaic"];
1336 document.getElementById("jvchat-hide-mosaic-checkbox").addEventListener("change", tryCatch(toggleHideMosaicOption));
1337 if (configuration["hide_mosaic"]) {
1338 document.getElementById("jvchat-main").classList.add("jvchat-hide-mosaics");
1339 }
1340
1341 document.getElementById("jvchat-load-images-checkbox").checked = configuration["load_images"];
1342 document.getElementById("jvchat-load-images-checkbox").addEventListener("change", tryCatch(toggleLoadImagesOption));
1343
1344 document.getElementById("jvchat-turbo-delay-range").value = configuration["turbo_delay"];
1345 document.getElementById("jvchat-turbo-delay-span").innerHTML = `${configuration["turbo_delay"]} ms`;
1346 document.getElementById("jvchat-turbo-delay-range").addEventListener("input", tryCatch(changeTurboDelayOption));
1347
1348 document.getElementById("jvchat-max-width-range").value = configuration["max_width"];
1349 document.getElementById("jvchat-max-width-span").innerHTML = `${configuration["max_width"]} %`;
1350 document.getElementById("jvchat-max-width-range").addEventListener("input", tryCatch(changeMaxWidthOption));
1351 adjustMaxWidth(configuration["max_width"]);
1352
1353 let favs = Array.from(document.querySelectorAll("link[rel='icon'], link[rel='shortcut icon']"));
1354 for (let fav of favs) {
1355 fav.parentElement.removeChild(fav);
1356 }
1357 setFavicon("");
1358
1359 document.addEventListener("visibilitychange", function () {
1360 let hidden = document.hidden;
1361 if (hidden) {
1362 let newHr = document.getElementById("jvchat-ruler-new");
1363 if (newHr) {
1364 newHr.removeAttribute("id");
1365 }
1366 nbNewMessage = 0;
1367 } else if (!isError && !isLocked) {
1368 setFavicon("");
1369 }
1370 });
1371}
1372
1373function toggleSidebar(event) {
1374 let isDown = isScrollDown();
1375 document.getElementById("jvchat-leftbar-extend").classList.toggle("jvchat-hide");
1376 document.getElementById("jvchat-leftbar-reduce").classList.toggle("jvchat-hide");
1377 document.getElementById("jvchat-leftbar-config").classList.toggle("jvchat-hide");
1378 document.getElementById("jvchat-leftbar").classList.toggle("jvchat-leftbar-reduced");
1379 if (isDown) {
1380 setScrollDown();
1381 }
1382}
1383
1384function toggleConfig(event) {
1385 document.getElementById("jvchat-leftbar-config-open").classList.toggle("jvchat-hide");
1386 document.getElementById("jvchat-leftbar-config-close").classList.toggle("jvchat-hide");
1387 document.getElementById("jvchat-info").classList.toggle("jvchat-hide");
1388 document.getElementById("jvchat-config").classList.toggle("jvchat-hide");
1389}
1390
1391function forceUpdate() {
1392 // If waiting for next update, restart it immediately, otherwise just wait for the HTTP request to end
1393 if (!fetchingMessages) {
1394 clearTimeout(currentTimeoutId);
1395 updateMessages(currentFetchedPage, true);
1396 }
1397}
1398
1399function toggleTurbo(event) {
1400 let checked = document.getElementById("jvchat-turbo-checkbox").checked;
1401 updateIntervalIdx = 0;
1402 if (!checked) {
1403 turboActivated = false;
1404 } else {
1405 turboActivated = true;
1406 forceUpdate();
1407 }
1408}
1409
1410function togglePlaySoundOption(event) {
1411 let checked = document.getElementById("jvchat-play-sound-checkbox").checked;
1412 configuration["play_sound"] = checked;
1413 saveConfig();
1414}
1415
1416function toggleNightModeOption(event) {
1417 let checked = document.getElementById("jvchat-night-mode-checkbox").checked;
1418 configuration["night_mode"] = checked;
1419 saveConfig();
1420 document.getElementById("content").classList.toggle("jvchat-night-mode");
1421}
1422
1423function toggleHideMosaicOption(event) {
1424 let checked = document.getElementById("jvchat-hide-mosaic-checkbox").checked;
1425 configuration["hide_mosaic"] = checked;
1426 saveConfig();
1427 let isDown = isScrollDown();
1428 document.getElementById("jvchat-main").classList.toggle("jvchat-hide-mosaics");
1429 if (isDown) {
1430 setScrollDown();
1431 }
1432}
1433
1434function toggleLoadImagesOption(event) {
1435 let checked = document.getElementById("jvchat-load-images-checkbox").checked;
1436 configuration["load_images"] = checked;
1437 saveConfig();
1438 for (let image of document.getElementsByClassName("jvchat-loadable-image")) {
1439 image.src = image.getAttribute(checked ? "data-src-direct" : "data-src-mini");
1440 }
1441}
1442
1443function changeTurboDelayOption(event) {
1444 let ms = document.getElementById("jvchat-turbo-delay-range").value;
1445 document.getElementById("jvchat-turbo-delay-span").innerHTML = `${ms} ms`;
1446 configuration["turbo_delay"] = parseInt(ms);
1447 saveConfig();
1448}
1449
1450function changeMaxWidthOption(event) {
1451 let maxWidth = parseInt(document.getElementById("jvchat-max-width-range").value);
1452 document.getElementById("jvchat-max-width-span").innerHTML = `${maxWidth} %`;
1453 configuration["max_width"] = maxWidth;
1454 saveConfig();
1455 adjustMaxWidth(maxWidth);
1456}
1457
1458function adjustMaxWidth(maxWidth) {
1459 document.getElementById("page-messages-forum").style["flex-grow"] = maxWidth;
1460 document.getElementById("jvchat-right-padding").style["flex-grow"] = 100 - maxWidth;
1461}
1462
1463function closeAlert(event) {
1464 let target = event.target;
1465 if (!target) {
1466 return;
1467 }
1468 if (target.classList.contains("jvchat-alert-close")) {
1469 let parent = target.parentElement;
1470 parent.parentElement.removeChild(parent);
1471 }
1472}
1473
1474function postMessage() {
1475 if (freshForm === undefined) {
1476 addAlertbox("danger", "Impossible de poster le message, aucun formulaire trouvé");
1477 return;
1478 }
1479
1480 let textarea = document.getElementById("message_topic");
1481
1482 let formData = serializeForm(freshForm);
1483 formData["message_topic"] = textarea.value;
1484 let formulaire = document.getElementById("bloc-formulaire-forum");
1485
1486 formulaire.classList.add("jvchat-disabled-form");
1487 textarea.setAttribute("disabled", "true");
1488
1489 let timestamp = getTimestamp();
1490
1491 function onSuccess(res) {
1492 formulaire.classList.remove("jvchat-disabled-form");
1493 textarea.removeAttribute("disabled");
1494 let parsedPage = parsePage(res, timestamp);
1495 if (!parsedPage.alert) {
1496 textarea.value = "";
1497 }
1498 setTextareaHeight();
1499 setScrollDown();
1500 postingMessage = false;
1501 if (parsedPage.nbMessagesPage === 20) {
1502 // Bug si on poste un message générant une nouvelle page : la requête retourne la page
1503 // courrante, donc notre nouveau message n'apparaît pas, il faut forcer un refetch()
1504 // MAIS il faut attendre un peu, car JVC galère à créer la page...
1505 setTimeout(tryCatch(forceUpdate), 2000);
1506 }
1507 }
1508
1509 function onError(err, _) {
1510 addAlertbox("danger", err);
1511 formulaire.classList.remove("jvchat-disabled-form");
1512 textarea.removeAttribute("disabled");
1513 postingMessage = false;
1514 }
1515
1516 function onTimeout(err) {
1517 addAlertbox("warning", err);
1518 formulaire.classList.remove("jvchat-disabled-form");
1519 textarea.removeAttribute("disabled");
1520 postingMessage = false;
1521 }
1522
1523 let timeout = 20000;
1524 if (turboActivated) {
1525 timeout = 5000;
1526 }
1527
1528 postingMessage = true;
1529 request("POST", document.URL, onSuccess, onError, onTimeout, makeFormData(formData), false, timeout);
1530}
1531
1532function editMessage(bloc) {
1533 let textarea = bloc.getElementsByClassName("jvchat-edition-textarea")[0];
1534
1535 let blocEdition = bloc.getElementsByClassName("jvchat-edition")[0];
1536 let formData = JSON.parse(blocEdition.getAttribute("data-form"));
1537 formData["message_topic"] = textarea.value;
1538 formData["id_message"] = bloc.getAttribute("jvchat-id");
1539 formData["ajax_hash"] = freshHash;
1540 formData["action"] = "post";
1541 let edition = bloc.getElementsByClassName("jvchat-edition")[0];
1542
1543 edition.classList.add("jvchat-disabled-form");
1544 textarea.setAttribute("disabled", "true");
1545
1546 let timestamp = getTimestamp();
1547
1548 function onSuccess(res) {
1549 edition.classList.remove("jvchat-disabled-form");
1550 if (res['reset_form']) {
1551 let reset = document.createElement("html");
1552 reset.innerHTML = res["hidden_reset"];
1553 let resetData = serializeForm(reset);
1554 for (let key in resetData) {
1555 formData[key] = resetData[key];
1556 }
1557 blocEdition.setAttribute("data-form", JSON.stringify(formData));
1558 }
1559
1560 textarea.removeAttribute("disabled");
1561 if (res.erreur.length > 0) {
1562 for (let err of res.erreur) {
1563 addAlertbox("danger", err);
1564 }
1565 return;
1566 }
1567 let dom = document.createElement("html");
1568 dom.innerHTML = res["html"];
1569 let message = getMessages(dom)[0];
1570 addMessages([message], true, timestamp);
1571 }
1572
1573 function onError(err, _) {
1574 addAlertbox("danger", err);
1575 edition.classList.remove("jvchat-disabled-form");
1576 textarea.removeAttribute("disabled");
1577 }
1578
1579 function onTimeout(err) {
1580 addAlertbox("warning", err);
1581 edition.classList.remove("jvchat-disabled-form");
1582 textarea.removeAttribute("disabled");
1583 }
1584
1585 let url = "http://www.jeuxvideo.com/forums/ajax_edit_message.php";
1586
1587 request("POST", url, onSuccess, onError, onTimeout, makeFormData(formData), true, 20000);
1588}
1589
1590function requestEdit(bloc) {
1591 if (!bloc.getElementsByClassName("jvchat-edition")[0].classList.contains("jvchat-hide")) {
1592 return;
1593 }
1594
1595 let contentClasses = bloc.getElementsByClassName("jvchat-content")[0].classList;
1596 contentClasses.add("disabled-content");
1597
1598 function onSuccess(res) {
1599 contentClasses.remove("disabled-content");
1600 if (res.erreur.length > 0) {
1601 for (let err of res.erreur) {
1602 addAlertbox("danger", err);
1603 }
1604 return;
1605 }
1606 let dom = document.createElement("html");
1607 dom.innerHTML = res["html"];
1608 let textarea = dom.getElementsByTagName("textarea")[0]
1609 let txt = textarea.value;
1610 textarea.parentElement.removeChild(textarea);
1611 let form = dom.getElementsByTagName("form")[0];
1612 let formData = serializeForm(form);
1613 let editionBloc = bloc.getElementsByClassName("jvchat-edition")[0];
1614 editionBloc.setAttribute("data-form", JSON.stringify(formData));
1615 let height = computeHeight(countLines(txt));
1616 let isDown = isScrollDown();
1617 bloc.getElementsByClassName("jvchat-edition-textarea")[0].value = txt;
1618 bloc.getElementsByClassName("jvchat-edition-textarea")[0].style["height"] = `${height}rem`;
1619 bloc.getElementsByClassName("jvchat-content")[0].classList.add("jvchat-hide");
1620 editionBloc.classList.remove("jvchat-hide");
1621 if (isDown) {
1622 setScrollDown();
1623 }
1624 }
1625
1626 function onError(err, _) {
1627 addAlertbox("danger", err);
1628 contentClasses.remove("disabled-content");
1629 }
1630
1631 function onTimeout(err) {
1632 addAlertbox("warning", err);
1633 contentClasses.remove("disabled-content");
1634 }
1635
1636 let id = bloc.getAttribute("jvchat-id");
1637 let url = `http://www.jeuxvideo.com/forums/ajax_edit_message.php?id_message=${id}&ajax_hash=${freshHash}&action=get`;
1638 request("GET", url, onSuccess, onError, onTimeout, undefined, true, 5000);
1639}
1640
1641function requestDelete(bloc) {
1642 let contentClasses = bloc.getElementsByClassName("jvchat-content")[0].classList;
1643 contentClasses.add("disabled-content");
1644
1645 let id = parseInt(bloc.getAttribute("jvchat-id"));
1646
1647 function onSuccess(res) {
1648 contentClasses.remove("disabled-content");
1649 if (res.erreur.length > 0) {
1650 for (let err of res.erreur) {
1651 addAlertbox("danger", err);
1652 }
1653 return;
1654 }
1655
1656 if (lastEditionTime[id] === false) {
1657 return;
1658 }
1659
1660 let isDown = isScrollDown();
1661
1662 if (!bloc.getElementsByClassName("jvchat-edition")[0].classList.contains("jvchat-hide")) {
1663 bloc.getElementsByClassName("jvchat-content")[0].classList.remove("jvchat-hide");
1664 bloc.getElementsByClassName("jvchat-edition")[0].classList.add("jvchat-hide");
1665 }
1666
1667 bloc.closest(".jvchat-message").classList.add("jvchat-message-deleted");
1668 lastEditionTime[id] = false;
1669
1670 if (isDown) {
1671 setScrollDown();
1672 }
1673 }
1674
1675 function onError(err, _) {
1676 addAlertbox("danger", err);
1677 contentClasses.remove("disabled-content");
1678 }
1679
1680 function onTimeout(err) {
1681 addAlertbox("warning", err);
1682 contentClasses.remove("disabled-content");
1683 }
1684
1685
1686 let url = `http://www.jeuxvideo.com/forums/modal_del_message.php`;
1687 let deleteData = { "type": "delete", "ajax_hash": freshDeletionHash, "tab_message[]": id };
1688 request("POST", url, onSuccess, onError, onTimeout, makeFormData(deleteData), true, 5000);
1689}
1690
1691function countLines(text) {
1692 return text.split(/\r|\r\n|\n/).length;
1693}
1694
1695function computeHeight(lines) {
1696 return 1 * lines + 0.6;
1697}
1698
1699function setTextareaHeight(plusOne) {
1700 let textarea = document.getElementById("message_topic");
1701 if (!isReduced) {
1702 textarea.style["height"] = "";
1703 return;
1704 }
1705 plusOne = !!plusOne;
1706 let lines = countLines(textarea.value);
1707
1708 if (!plusOne && lines === 1) {
1709 textarea.style["height"] = "";
1710 return;
1711 }
1712
1713 if (plusOne) {
1714 lines += 1;
1715 }
1716
1717 let height = computeHeight(lines);
1718 textarea.style["height"] = `${height}rem`;
1719}
1720
1721function postMessageIfEnter(event) {
1722 if (isReduced && (event.which == 13 || event.keyCode == 13)) {
1723 if (event.shiftKey) {
1724 let isDown = isScrollDown();
1725 setTextareaHeight(true);
1726 if (isDown) {
1727 setScrollDown();
1728 }
1729 } else {
1730 event.preventDefault();
1731 postMessage();
1732 }
1733 }
1734}
1735
1736function serializeForm(form) {
1737 // Useless actually, just use new FormData(form)
1738 let dict = {};
1739
1740 for (let select of form.getElementsByTagName("select")) {
1741 dict[select.name] = select.querySelector("option[selected]").value;
1742 }
1743
1744 for (let input of form.getElementsByTagName("input")) {
1745 dict[input.name] = input.value;
1746 }
1747
1748 for (let textarea of form.getElementsByTagName("textarea")) {
1749 dict[textarea.name] = textarea.value;
1750 }
1751
1752 return dict;
1753}
1754
1755function makeFormData(dict) {
1756 var formData = new FormData();
1757 for (let key in dict) {
1758 formData.append(key, dict[key]);
1759 }
1760 return formData;
1761}
1762
1763function getMessages(document) {
1764 let blocMessages = document.getElementsByClassName("bloc-message-forum");
1765 let messages = [];
1766 for (let bloc of blocMessages) {
1767 messages.push(parseMessage(bloc));
1768 }
1769 return messages;
1770}
1771
1772function findDeletedMessages(res, requestTimestamp) {
1773 // On pourrait utiliser "getMessages()" mais pas besoin de parse chaque message (on a juste
1774 // besoin des id). Avantage : on peut utiliser cette fonction aussi dans "checkEdited()".
1775 let page = getPage(res);
1776 let blocMessages = res.getElementsByClassName("bloc-message-forum");
1777
1778 let newIds = []
1779 let newDates = [];
1780
1781 for (let bloc of blocMessages) {
1782 let id = parseInt(bloc.getAttribute("data-id"));
1783 let date = bloc.getElementsByClassName("bloc-date-msg")[0].textContent.trim();
1784 newIds.push(id);
1785 newDates.push(date);
1786 }
1787
1788 if (!messagesByPage.hasOwnProperty(page)) {
1789 messagesByPage[page] = [newIds, newDates];
1790 return;
1791 }
1792
1793 let [regIds, regDates] = messagesByPage[page];
1794
1795 let regLength = regIds.length;
1796 let newLength = newIds.length;
1797
1798 messagesByPage[page] = [newIds, newDates];
1799
1800 if (regLength === 0) {
1801 return;
1802 }
1803
1804 if (regLength <= newLength && regIds[0] === newIds[0] && regIds[regLength - 1] === newIds[regLength - 1]) {
1805 return;
1806 }
1807
1808 // Problème : on ne prend pas en compte les messages "ressuscités" (est-ce possible ?), mais
1809 // c'est compliqué, car si on part du principe que la suppression est réversible, il peut y
1810 // avoir des data race entre deux requêtes (message supprimé entre les deux, mais la 2ème
1811 // arrive avant la première => message ressuscité alors qu'il ne devrait pas).
1812
1813 let after = new Set(newIds);
1814 let isFirst = true;
1815
1816 for (let i = 0; i < regIds.length; i++) {
1817 let id = regIds[i];
1818 if (after.has(id)) {
1819 isFirst = false;
1820 continue;
1821 }
1822
1823 // Pas enregistré => blacklist, false => supprimé
1824 if (!lastEditionTime.hasOwnProperty(id) || lastEditionTime[id] === false) {
1825 continue;
1826 }
1827
1828 // Si message en début de page : vérifier qu'il n'est pas sur la page précédente
1829 // Vérifier aussi les début/fin de page en cas de PEMT, car 2 messages peuvent être "swap"
1830 if (isFirst || (regDates[i] === newDates[0]) || (newLength === 20 && regDates[i] === newDates[19])) {
1831 checkDeleted(id);
1832 continue;
1833 }
1834
1835 let [timestamp, _] = lastEditionTime[id];
1836
1837 if (timestamp >= requestTimestamp) {
1838 continue;
1839 }
1840
1841 let msg = document.querySelector(`.jvchat-message[jvchat-id="${id}"]`);
1842 if (msg) {
1843 msg.classList.add("jvchat-message-deleted");
1844 lastEditionTime[id] = false;
1845 }
1846 }
1847}
1848
1849function formatDate(date) {
1850 let now = new Date();
1851 try {
1852 now = new Date(now.toLocaleString('en-US', { timeZone: "Europe/Paris" }));
1853 } catch (e) { }
1854 let day = date.getDate();
1855 let month = date.getMonth();
1856 let year = date.getFullYear();
1857 if (now.getDate() === day && now.getMonth() === month && now.getFullYear() === year) {
1858 return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}`;
1859 } else {
1860 return `${day.toString().padStart(2, "0")}/${(month + 1).toString().padStart(2, "0")}/${year}`;
1861 }
1862}
1863
1864function makeMessage(message) {
1865 let content = message.content;
1866 fixMessage(content);
1867 detectMosaic(content);
1868 improveImages(content);
1869 let id = message.id;
1870 let avatar = message.avatar;
1871 let toQuoteDate = message.dateString;
1872 let titleDate = message.dateString;
1873 let textDate = formatDate(message.date);
1874 if (message.edited !== undefined) {
1875 textDate += "*";
1876 titleDate += ` (édité à ${message.edited})`;
1877 }
1878 let exists = avatar !== undefined;
1879 let author = exists ? message.author : `<i>${message.author}</i>`;
1880 let authorHref = exists ? `href="http://www.jeuxvideo.com/profil/${author.toLowerCase()}?mode=infos"` : "";
1881 let authorTitle = exists ? `title="Ouvrir le profil de ${author}"` : "";
1882 let authorAvatarHidden = exists ? "" : "class='jvchat-hide-visibility'";
1883 let editionSpan = '<span class="jvchat-edit jvchat-picto picto-msg-crayon" title="Modifier"></span>';
1884 let deletionSpan = '<span class="jvchat-delete jvchat-picto picto-msg-croix" title="Supprimer"></span>';
1885 let deletion = (currentUser.author === undefined) || (message.author.toLowerCase() !== currentUser.author.toLowerCase()) ? "" : deletionSpan;
1886 let edition = (currentUser.author === undefined) || (message.author.toLowerCase() !== currentUser.author.toLowerCase()) ? "" : editionSpan;
1887 let html =
1888 `<div class="jvchat-bloc-message">
1889 <div class="jvchat-message" jvchat-id=${id}>
1890 <div>
1891 <a ${authorAvatarHidden} ${authorHref} target="_blank" ${authorTitle}>
1892 <div class="jvchat-bloc-avatar jvchat-rounded" style="background-image: url(${avatar})"></div>
1893 </a>
1894 </div>
1895 <div class="jvchat-bloc-author-content">
1896 <div class="jvchat-toolbar">
1897 <h5 class="jvchat-author">${author}</h5>
1898 <div class="jvchat-tooltip">
1899 ${deletion}
1900 ${edition}
1901 <span class="jvchat-picto jvchat-quote picto-msg-quote" title="Citer"></span>
1902 <small class="jvchat-date" to-quote="${toQuoteDate}" title="${titleDate}">${textDate}</small>
1903 </div>
1904 </div>
1905 <div class="jvchat-content">${content.outerHTML}</div>
1906 <div class="jvchat-edition jvchat-hide">
1907 <textarea class="jvchat-edition-textarea jvchat-textarea"></textarea>
1908 <div class="jvchat-buttons">
1909 <button tabindex="0" type="button" class='jvchat-edition-check icon-check-jv jvchat-button-top' title="Valider la modification"></button>
1910 <button tabindex="0" type="button" class='jvchat-edition-cancel icon-cancel-circle jvchat-button-bottom' title="Annuler la modification"></button>
1911 </div>
1912 </div>
1913 </div>
1914 </div>
1915 <hr class="jvchat-ruler">
1916 </div>`;
1917 return html;
1918}
1919
1920function parseDate(string) {
1921 let [date, time] = string.toLowerCase().split("à");
1922 let [day, month, year] = date.trim().split(" ");
1923 let [hour, minute, second] = time.trim().split(":");
1924 let monthIndex = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"].indexOf(month.trim().toLowerCase());
1925 return new Date(parseInt(year), monthIndex, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
1926}
1927
1928function addMessages(messages, editing, requestTimestamp) {
1929 let main = document.getElementById("jvchat-main");
1930 let hasNewMessages = false;
1931 let init = true;
1932 let toInsert = "";
1933 let newMessagesIds = [];
1934 for (let message of messages) {
1935 let date = message.date;
1936 let id = message.id;
1937
1938 if (init === true && !editing) {
1939 init = false;
1940 let now = new Date();
1941 let delta = now - date;
1942 if (delta > 5 * 60 * 1000 + checkEditInterval) {
1943 shouldCheckEdited = false;
1944 } else {
1945 shouldCheckEdited = true;
1946 }
1947 }
1948
1949 if (message.blacklisted) {
1950 continue;
1951 }
1952
1953 if (firstMessageId === undefined) {
1954 firstMessageId = id;
1955 firstMessageDate = date;
1956 }
1957
1958 // 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
1959 if (id < firstMessageId && date < firstMessageDate) {
1960 continue;
1961 }
1962
1963 let referenced = lastEditionTime.hasOwnProperty(id);
1964 let edited = message.edited;
1965
1966 if (referenced) {
1967 if (lastEditionTime[id] === false) {
1968 continue;
1969 }
1970 let [timestamp, edition] = lastEditionTime[id];
1971 if (timestamp >= requestTimestamp || edition === edited) {
1972 continue;
1973 }
1974 }
1975
1976 let newBloc = makeMessage(message);
1977 lastEditionTime[id] = [requestTimestamp, edited];
1978
1979 if (referenced) {
1980 let selector = `.jvchat-message[jvchat-id="${id}"]`;
1981 let oldBloc = main.querySelector(selector).closest(".jvchat-bloc-message");
1982 let isDown = isScrollDown();
1983 oldBloc.outerHTML = newBloc;
1984 if (isDown) {
1985 setScrollDown();
1986 }
1987 continue;
1988 }
1989
1990 hasNewMessages = true;
1991 if (nbNewMessage === 0 && document.hidden) {
1992 let hrs = document.getElementsByClassName("jvchat-ruler");
1993 let lastHr = hrs[hrs.length - 1];
1994 lastHr.setAttribute("id", "jvchat-ruler-new");
1995 }
1996
1997 toInsert += newBloc;
1998 newMessagesIds.push(id);
1999 nbNewMessage++;
2000 }
2001
2002 if (toInsert !== "") {
2003 let isDown = isScrollDown();
2004 main.insertAdjacentHTML("beforeend", toInsert);
2005 if (isDown) {
2006 setScrollDown();
2007 }
2008 }
2009
2010 if (editing) {
2011 return;
2012 }
2013
2014 if (isScrollDown()) {
2015 let blocMessages = main.getElementsByClassName("jvchat-bloc-message");
2016 let nb = blocMessages.length;
2017 if (nb > 100) {
2018 for (let i = 0; i < nb - 100; i++) {
2019 main.removeChild(blocMessages[0]);
2020 }
2021 setScrollDown();
2022 }
2023 }
2024
2025 if (hasNewMessages) {
2026 if (!turboActivated) {
2027 decreaseUpdateInterval();
2028 }
2029 if (document.hidden) {
2030 setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
2031 if (configuration["play_sound"]) {
2032 ringBell.pause();
2033 ringBell.currentTime = 0;
2034 ringBell.play();
2035 }
2036 }
2037 for (let newMessageId of newMessagesIds) {
2038 let event = new CustomEvent('jvchat:newmessage', { 'detail': { id: newMessageId } });
2039 dispatchEvent(event);
2040 }
2041 } else {
2042 if (!turboActivated) {
2043 increaseUpdateInterval();
2044 }
2045 }
2046}
2047
2048function submitSondageAnswer(event) {
2049 let target = event.target;
2050 if (!target) {
2051 return;
2052 }
2053 if (target.classList.contains("click-sondage")) {
2054 let reponseNum = parseInt(target.getAttribute("sondage-reponse-num"));
2055 let sondageId = sondageChoices[reponseNum]["sondageId"];
2056 let reponseId = sondageChoices[reponseNum]["responseId"];
2057 let topicId = urlToFetch["ids"].split("-")[2];
2058 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}`;
2059
2060 function onSuccess(res) {
2061 if (res.erreur.length > 0) {
2062 for (let err of res.erreur) {
2063 addAlertbox("danger", err);
2064 }
2065 return;
2066 }
2067 let dom = document.createElement("html");
2068 dom.innerHTML = res["html"];
2069
2070 let sondage = parseSondage(dom);
2071 if (!sondage) {
2072 addAlertbox("warning", "Erreur lors de la récupération du sondage");
2073 return;
2074 }
2075
2076 setSondage(sondage);
2077 }
2078
2079 function onError(err, _) {
2080 addAlertbox("danger", err);
2081 }
2082
2083 function onTimeout(err) {
2084 addAlertbox("warning", err);
2085 }
2086
2087 request("POST", url, onSuccess, onError, onTimeout, undefined, true, 5000);
2088 }
2089}
2090
2091function setSondage(sondage) {
2092 let choix = document.getElementById("jvchat-sondage-choix");
2093
2094 if (sondage["answered"]) {
2095 choix.removeEventListener("click", submitSondageAnswer);
2096 choix.classList.remove("notanswered");
2097 } else {
2098 if (!sondageChoices) {
2099 sondageChoices = sondage["results"];
2100 }
2101 choix.addEventListener("click", submitSondageAnswer);
2102 choix.classList.add("notanswered");
2103 }
2104
2105 if (!choix.firstChild) {
2106 document.getElementById("jvchat-sondage-intitule").innerHTML = sondage["intitule"];
2107 let results = sondage["results"];
2108 for (let i = 0; i < results.length; i++) {
2109 let res = results[i];
2110 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>`;
2111 choix.insertAdjacentHTML("beforeend", tr);
2112 }
2113 } else {
2114 let trs = choix.getElementsByClassName("result-pourcent");
2115 for (let i = 0; i < trs.length; i++) {
2116 let res = sondage["results"][i];
2117 let tr = trs[i];
2118 tr.getElementsByClassName("pourcent")[0].innerHTML = `${res["pourcent"]} %`;
2119 tr.getElementsByTagName("span")[0].style["width"] = `${res["pourcent"]}%`;
2120 }
2121 }
2122
2123 document.getElementById("jvchat-sondage-votes").innerHTML = `(${sondage["votes"]} votes)`;
2124}
2125
2126function setUser(document, user) {
2127 let isConnected = (user.author !== undefined);
2128
2129 if (isConnected) {
2130 if (user.author !== currentUser.author) {
2131 let pseudo = document.getElementById("jvchat-user-pseudo");
2132 pseudo.innerHTML = user.author;
2133 let avatarLink = document.getElementById("jvchat-user-avatar-link");
2134 let notifLink = document.getElementById("jvchat-user-notif-link");
2135 avatarLink.setAttribute("href", `http://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=infos`);
2136 notifLink.setAttribute("href", `http://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=abonnements`);
2137 }
2138
2139 if (user.avatar !== currentUser.avatar) {
2140 let avatar = document.getElementById("jvchat-user-avatar");
2141 avatar.style["background-image"] = `url("${user.avatar}")`;
2142 }
2143
2144 if (user.mp !== currentUser.mp) {
2145 let mp = document.getElementById("jvchat-user-mp");
2146 mp.setAttribute("data-val", user.mp);
2147 if (user.mp > 0) {
2148 mp.classList.add("has-notif");
2149 } else {
2150 mp.classList.remove("has-notif");
2151 }
2152 }
2153
2154 if (user.notif !== currentUser.notif) {
2155 let notif = document.getElementById("jvchat-user-notif");
2156 notif.setAttribute("data-val", user.notif);
2157 if (user.notif > 0) {
2158 notif.classList.add("has-notif");
2159 } else {
2160 notif.classList.remove("has-notif");
2161 }
2162 }
2163 }
2164
2165 if ((userConnected === undefined && isConnected) || (userConnected !== undefined && isConnected !== userConnected)) {
2166 document.getElementById("jvchat-profil").classList.toggle("jvchat-hide");
2167 let isDown = isScrollDown();
2168 document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-hide");
2169 if (isDown) {
2170 setScrollDown();
2171 }
2172 }
2173
2174 if (userConnected !== undefined) {
2175 if (isConnected && !userConnected) {
2176 addAlertbox("success", "Vous êtes désormais connecté");
2177 } else if (!isConnected && userConnected) {
2178 addAlertbox("warning", "Vous avez été déconnecté");
2179 }
2180 }
2181
2182 userConnected = isConnected;
2183 currentUser = user;
2184}
2185
2186function setTopicTitle(document, topicTitle) {
2187 if (topicTitle !== currentTopicTitle) {
2188 currentTopicTitle = topicTitle;
2189 document.getElementById("jvchat-topic-title").innerHTML = topicTitle;
2190 }
2191}
2192
2193function setTopicNbConnected(document, nbConnected) {
2194 let txt = `${nbConnected} connectés`;
2195 if (!(nbConnected > 1)) {
2196 if (nbConnected === undefined) {
2197 txt = "? connectés";
2198 } else {
2199 txt = txt.slice(0, -1);
2200 }
2201 }
2202 document.getElementById("jvchat-topic-nb-connected").innerHTML = txt;
2203}
2204
2205function setTopicNbMessages(document, nbMessages) {
2206 let txt = `${nbMessages} messages`;
2207 if (!(nbMessages > 1)) {
2208 if (nbMessages === undefined) {
2209 txt = "? messages";
2210 } else {
2211 txt = txt.slice(0, -1);
2212 }
2213 }
2214 document.getElementById("jvchat-topic-nb-messages").innerHTML = txt;
2215}
2216
2217function triggerJVChat() {
2218 // TamperMonkey / Chrome bug: https://github.com/Tampermonkey/tampermonkey/issues/705#issuecomment-493895776
2219 if (window) {
2220 if (window.clearTimeout) {
2221 window.clearTimeout = window.clearTimeout.bind(window);
2222 }
2223 if (window.clearInterval) {
2224 window.clearInterval = window.clearInterval.bind(window);
2225 }
2226 if (window.setTimeout) {
2227 window.setTimeout = window.setTimeout.bind(window);
2228 }
2229 if (window.setInterval) {
2230 window.setInterval = window.setInterval.bind(window);
2231 }
2232 window.onbeforeunload = function (event) {
2233 leavingTopic = true;
2234 }
2235 }
2236
2237 let topicUrl = document.URL;
2238 let topic = parseTopicInfo(document);
2239 let user = parseUserInfo(document);
2240 let sondage = parseSondage(document);
2241
2242 urlToFetch = parseURL(topicUrl);
2243 urlToFetch.page = 1;
2244 urlToFetch.anchor = "";
2245
2246 urlToCheckEdit = parseURL(topicUrl);
2247 urlToCheckEdit.page = 1;
2248 urlToCheckEdit.anchor = "";
2249
2250 loadConfig();
2251 ringBell = new Audio(configuration["sound"]);
2252 clearPage(document);
2253
2254 setUser(document, user);
2255 setTopicTitle(document, topic.title);
2256 setTopicNbMessages(document, undefined);
2257 setTopicNbConnected(document, topic.connected);
2258
2259 if (sondage) {
2260 document.getElementById("jvchat-sondage").classList.remove("jvchat-hide")
2261 setSondage(sondage);
2262 }
2263
2264 document.getElementById("jvchat-topic-title").setAttribute("href", buildURL(urlToFetch));
2265
2266 let forum = getForum(document);
2267 let forumSide = document.getElementById("jvchat-forum-title");
2268 forumSide.setAttribute("href", forum.href);
2269 forumSide.innerHTML = forum.title;
2270
2271 let defaultReduced = configuration["default_reduced"];
2272 let messageTopic = document.getElementById("message_topic");
2273
2274 if (messageTopic && (defaultReduced === false || (messageTopic.value !== ""))) {
2275 toggleTextarea();
2276 }
2277
2278 let event = new CustomEvent('jvchat:activation');
2279 dispatchEvent(event);
2280
2281 let page = topic.lastPage > 1 ? topic.lastPage - 1 : topic.lastPage;
2282 updateMessages(page, true);
2283
2284 setInterval(checkEdited, checkEditInterval);
2285}
2286
2287
2288function updateMessages(page, goToLast) {
2289 if (postingMessage && turboActivated) {
2290 // Postpone message fetching, posting the message is priorized
2291 fetchingMessages = false;
2292 currentTimeoutId = setTimeout(tryCatch(function postponedUpdate() {
2293 updateMessages(page, goToLast);
2294 }), 100);
2295 return;
2296 }
2297
2298 let timestamp = getTimestamp();
2299
2300 function scheduleNextUpdate(interval, p, goLast) {
2301 fetchingMessages = false;
2302 currentTimeoutId = setTimeout(tryCatch(function scheduledUpdate() {
2303 updateMessages(p, goLast);
2304 }), interval);
2305 };
2306
2307 function onSuccess(res) {
2308 let parsed = parsePage(res, timestamp);
2309 let lastPage = parsed.lastPage;
2310 let currPage = parsed.page;
2311 let int = turboActivated ? configuration["turbo_delay"] : updateIntervals[updateIntervalIdx] * 1000;
2312
2313 if (page < lastPage && goToLast) {
2314 updateMessages(page + 1, true);
2315 } else if (currPage < page || parsed.nbMessagesPage === 0) { // Bug des messages supprimés
2316 scheduleNextUpdate(int, page - 1, false);
2317 } else if (page > lastPage) {
2318 updateMessages(lastPage, true);
2319 } else {
2320 scheduleNextUpdate(int, page, true);
2321 }
2322 }
2323
2324 function onError(err, _) {
2325 if (!isError) {
2326 isError = true;
2327 setFixedAlert("danger", err);
2328 }
2329 scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 60000, page, true);
2330 }
2331
2332 function onTimeout(err) {
2333 addAlertbox("warning", err);
2334 scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 10000, page, true);
2335 }
2336
2337 let timeout = 10000;
2338 if (turboActivated) {
2339 timeout = 5000;
2340 }
2341
2342 fetchingMessages = true;
2343 currentFetchedPage = page;
2344 urlToFetch.page = page;
2345 let urlLastPage = buildURL(urlToFetch);
2346 request("GET", urlLastPage, onSuccess, onError, onTimeout, undefined, false, timeout);
2347}
2348
2349function checkEdited() {
2350 if (!shouldCheckEdited || currentFetchedPage === 1 || isError) {
2351 return;
2352 }
2353
2354 urlToCheckEdit.page = currentFetchedPage - 1;
2355 let urlPrevLastPage = buildURL(urlToCheckEdit);
2356 let timestamp = getTimestamp();
2357
2358 function onSuccess(res) {
2359 let newMessages = [];
2360 let edited = res.getElementsByClassName("info-edition-msg");
2361 for (let msg of edited) {
2362 let bloc = msg.closest(".bloc-message-forum");
2363 newMessages.push(parseMessage(bloc));
2364 }
2365 addMessages(newMessages, true, timestamp);
2366 findDeletedMessages(res, timestamp);
2367 }
2368
2369 function onError(_, _) { }
2370
2371 function onTimeout(_) { }
2372
2373 request("GET", urlPrevLastPage, onSuccess, onError, onTimeout, undefined, false, 20000);
2374}
2375
2376function checkDeleted(id) {
2377 let url = `http://www.jeuxvideo.com/jvchat/forums/message/${id}`;
2378 let requestTimestamp = getTimestamp();
2379
2380 function onSuccess(_) {
2381 // Le message existe, il a disparu de la page car un message plus ancien a été supprimé
2382 }
2383
2384 function onError(_, status) {
2385 if (status === 410 && lastEditionTime[id] !== false) {
2386 let [timestamp, _] = lastEditionTime[id];
2387
2388 if (timestamp >= requestTimestamp) {
2389 return;
2390 }
2391
2392 let msg = document.querySelector(`.jvchat-message[jvchat-id="${id}"]`);
2393 if (msg) {
2394 msg.classList.add("jvchat-message-deleted");
2395 lastEditionTime[id] = false;
2396 }
2397 }
2398 }
2399
2400 function onTimeout(_) { }
2401
2402 request("GET", url, onSuccess, onError, onTimeout, undefined, false, 20000);
2403}
2404
2405function parseAlerts(res) {
2406 let alerts = [];
2407 let alertsDiv = res.getElementsByClassName("alert");
2408 for (let a of alertsDiv) {
2409 let type = "danger";
2410 if (a.classList.contains("alert-warning")) {
2411 type = "warning";
2412 } else if (a.classList.contains("alert-success")) {
2413 type = "success";
2414 }
2415 let message = a.getElementsByClassName("alert-row")[0].textContent.trim();
2416 alerts.push({ type: type, message: message });
2417 }
2418 return alerts;
2419}
2420
2421function increaseUpdateInterval() {
2422 if (updateIntervalIdx < updateIntervalMax) {
2423 updateIntervalIdx++;
2424 }
2425}
2426
2427function decreaseUpdateInterval() {
2428 updateIntervalIdx = transisitions[updateIntervalIdx];
2429}
2430
2431function parsePage(res, requestTimestamp) {
2432 let error = getTopicError(res);
2433 if (error !== undefined) {
2434 if (!isError) {
2435 updateIntervalIdx = updateIntervalMax;
2436 isError = true;
2437 setFixedAlert("danger", error);
2438 }
2439 return { lastPage: undefined, page: undefined, alert: true, nbMessagesPage: 0 }
2440 }
2441
2442 if (isError) {
2443 isError = false;
2444 updateIntervalIdx = 0;
2445 removeFixedAlert("Le topic ne retourne plus d'erreur");
2446 }
2447
2448 let form = getForm(res);
2449 if (form !== undefined) {
2450 freshForm = form;
2451 }
2452
2453 let hash = getHash(res);
2454 if (hash !== undefined) {
2455 freshHash = hash;
2456 }
2457
2458 let deletionHash = getDeletionHash(res);
2459 if (deletionHash !== undefined) {
2460 freshDeletionHash = deletionHash;
2461 }
2462
2463 let messages = getMessages(res);
2464 addMessages(messages, false, requestTimestamp);
2465 let user = parseUserInfo(res);
2466 setUser(document, user);
2467 let topic = parseTopicInfo(res);
2468 let nbMessages = (topic.lastPage - 1) * 20;
2469 if (topic.page == topic.lastPage) {
2470 nbMessages += messages.length;
2471 }
2472 findDeletedMessages(res, requestTimestamp);
2473 setTopicNbMessages(document, nbMessages);
2474 setTopicNbConnected(document, topic.connected);
2475 let alerts = parseAlerts(res);
2476 for (let alert of alerts) {
2477 addAlertbox(alert.type, alert.message);
2478 }
2479 let locked = getTopicLocked(res);
2480 let isLocked_ = (locked !== undefined);
2481
2482 if (isLocked_ && !isLocked) {
2483 updateInterval = updateIntervalMax;
2484 setFixedAlert("warning", locked);
2485 } else if (!isLocked_ && isLocked) {
2486 updateInterval = 0;
2487 removeFixedAlert("Le topic a été dévérouillé");
2488 }
2489 isLocked = isLocked_;
2490
2491 let sondage = parseSondage(res);
2492 if (sondage) {
2493 setSondage(sondage);
2494 }
2495
2496 return {
2497 page: topic.page, lastPage: topic.lastPage,
2498 nbMessagesPage: messages.length, alert: isLocked_ || (alerts.length > 0)
2499 };
2500}
2501
2502function addAlertbox(type, message) {
2503 // type: success / warning / danger
2504 let alert = `<div class="alert alert-${type}">
2505 <button class="close jvchat-alert-close" aria-hidden="true" data-dismiss="alert" type="button">×</button>
2506 <div class="alert-row">${message}</div>
2507 </div>`;
2508 document.getElementById("jvchat-fixed-alert").insertAdjacentHTML("afterend", alert);
2509}
2510
2511function setFixedAlert(type, message) {
2512 setFavicon("⨯");
2513 document.getElementById("jvchat-fixed-alert").getElementsByClassName("alert-row")[0].innerHTML = message;
2514 document.getElementById("jvchat-fixed-alert").setAttribute("class", `alert alert-${type}`);
2515}
2516
2517function removeFixedAlert(message) {
2518 document.getElementById("jvchat-fixed-alert").classList.add("jvchat-hide");
2519 if (message !== undefined) {
2520 addAlertbox("success", message);
2521 }
2522 if (document.hidden && nbNewMessage > 0) {
2523 setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
2524 } else {
2525 setFavicon("");
2526 }
2527}
2528
2529function makeJVChatButton() {
2530 let cls = 'btn-jvchat';
2531 let text = 'JVChat';
2532 let btn = `<button class="btn btn-actu-new-list-forum ${cls}">${text}</button>`;
2533 return btn;
2534}
2535
2536function addJVChatButton(document) {
2537 let css = `<style type="text/css">
2538 #forum-main-col .bloc-pre-pagi-forum {
2539 display: flex;
2540 }
2541
2542 #forum-main-col .bloc-pre-pagi-forum .bloc-pre-right {
2543 position: relative;
2544 right: unset;
2545 left: unset;
2546 top: unset;
2547 bottom: unset;
2548 overflow: hidden;
2549 display: flex;
2550 flex-wrap: wrap;
2551 justify-content: flex-end;
2552 margin-top: auto;
2553 flex: 1;
2554 }
2555
2556 #forum-main-col .bloc-pre-pagi-forum .bloc-pre-right button {
2557 float: right;
2558 min-width: 5.25rem;
2559 margin-left: 0.3125rem;
2560 }
2561 </style>`
2562 document.head.insertAdjacentHTML("beforeend", css);
2563 let blocPreRight = document.getElementsByClassName("bloc-pre-right");
2564 let jvchatButton = makeJVChatButton();
2565 for (let bloc of blocPreRight) {
2566 bloc.insertAdjacentHTML('afterbegin', jvchatButton);
2567 }
2568}
2569
2570function bindJVChatButton(document) {
2571 let buttons = document.getElementsByClassName('btn-jvchat');
2572 for (let btn of buttons) {
2573 btn.addEventListener('click', tryCatch(triggerJVChat));
2574 }
2575}
2576
2577function request(mode, url, callbackSuccess, callbackError, callbackTimeout, data, json, timeout) {
2578 json = !!json;
2579 let xhr = new XMLHttpRequest();
2580 xhr.timeout = timeout;
2581
2582 xhr.ontimeout = tryCatch(function xhrOnTimeout() {
2583 callbackTimeout(`La délai d'attente de la requête a expiré`);
2584 });
2585
2586 xhr.onerror = tryCatch(function xhrOnError() {
2587 callbackError(`La requête a échoué (${xhr.status}): ${xhr.statusText}`, xhr.status);
2588 });
2589
2590 xhr.onabort = tryCatch(function xhrOnAbort() {
2591 if (!leavingTopic) {
2592 callbackTimeout(`La requête a été interrompue pour une raison inconnue`);
2593 }
2594 });
2595
2596 xhr.onload = tryCatch(function xhrOnLoad() {
2597 if (xhr.status !== 200) {
2598 callbackError(`La requête a retourné une erreur (${xhr.status}): ${xhr.statusText}`, xhr.status);
2599 return;
2600 }
2601 callbackSuccess(xhr.response);
2602 });
2603
2604 if (data === undefined) {
2605 data = null;
2606 }
2607
2608 if (json) {
2609 xhr.responseType = "json";
2610 } else {
2611 xhr.responseType = "document";
2612 }
2613
2614 xhr.open(mode, url, true);
2615 xhr.setRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
2616 xhr.send(data);
2617};
2618
2619// On copie/colle le code de TopicLive et on se sent développeur :)
2620function makeFavicon() {
2621 let canvas = document.createElement("canvas");
2622 canvas.width = 16;
2623 canvas.height = 16;
2624 let context = canvas.getContext('2d');
2625 let image = new Image();
2626 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==';
2627 return { canvas: canvas, context: context, image: image };
2628};
2629
2630function clearFavicon() {
2631 favicon.context.clearRect(0, 0, favicon.canvas.width, favicon.canvas.height);
2632 favicon.context.drawImage(favicon.image, 0, 0);
2633};
2634
2635function fillFavicon(txt) {
2636 clearFavicon();
2637
2638 if (txt !== '') {
2639 let context = favicon.context;
2640 context.fillStyle = 'DodgerBlue';
2641 context.fillRect(0, 0, context.measureText(txt).width + 3, 11);
2642 context.fillStyle = 'white';
2643 context.font = 'bold 10px Verdana';
2644 context.textBaseline = 'bottom';
2645 context.fillText(txt, 1, 11);
2646 }
2647};
2648
2649function setFavicon(txt) {
2650 let fav = document.getElementById("jvchat-favicon");
2651 if (fav) {
2652 fav.parentElement.removeChild(fav);
2653 }
2654 fillFavicon(txt);
2655 let url = favicon.canvas.toDataURL('image/png');
2656 let icon = `<link id="jvchat-favicon" rel="shortcut icon" type="image/png" href="${url}">`;
2657 document.head.insertAdjacentHTML("beforeend", icon);
2658}
2659
2660function reverseMessage(node, isInit, isUl) {
2661 let quote = "";
2662 let prevIsP = false;
2663
2664 for (let child of node.childNodes) {
2665 let name = child.nodeName;
2666
2667 switch (name) {
2668 case "P": {
2669 quote += reverseMessage(child) + "\n\n";
2670 break;
2671 }
2672 case "STRONG": {
2673 quote += "'''" + reverseMessage(child) + "'''";
2674 break;
2675 }
2676 case "U": {
2677 quote += "<u>" + reverseMessage(child) + "</u>";
2678 break;
2679 }
2680 case "EM": {
2681 quote += "''" + reverseMessage(child) + "''";
2682 break;
2683 }
2684 case "BR": {
2685 break;
2686 }
2687 case "UL": {
2688 quote += reverseMessage(child, false, true) + "\n\n";
2689 break;
2690 }
2691 case "OL": {
2692 quote += reverseMessage(child, false, false) + "\n\n";
2693 break;
2694 }
2695 case "LI": {
2696 if (isUl === true) {
2697 quote += "* " + reverseMessage(child) + "\n";
2698 } else {
2699 quote += "# " + reverseMessage(child) + "\n";
2700 }
2701 break;
2702 }
2703 case "DIV": {
2704 let classList = child.classList;
2705 if (classList.contains("bloc-spoil-jv")) {
2706 quote += "<spoil>" + reverseMessage(child) + "</spoil>\n\n"
2707 } else if (classList.contains("contenu-spoil")) {
2708 quote += reverseMessage(child);
2709 }
2710 break;
2711 }
2712 case "SPAN": {
2713 let classList = child.classList;
2714 if (classList.contains("bloc-spoil-jv")) {
2715 quote += "<spoil>" + reverseMessage(child) + "</spoil>";
2716 } else if (classList.contains("contenu-spoil")) {
2717 quote += reverseMessage(child);
2718 }
2719 break;
2720 }
2721 case "LABEL": {
2722 break;
2723 }
2724 case "INPUT": {
2725 break;
2726 }
2727 case "IMG": {
2728 quote += child.alt;
2729 break;
2730 }
2731 case "A": {
2732 if (child.title) {
2733 quote += child.href;
2734 } else {
2735 quote += reverseMessage(child);
2736 }
2737 break;
2738 }
2739 case "PRE": {
2740 quote += reverseMessage(child) + "\n\n";
2741 break;
2742 }
2743 case "CODE": {
2744 quote += "<code>" + child.textContent + "</code>";
2745 break;
2746 }
2747 case "BLOCKQUOTE": {
2748 if (prevIsP) {
2749 quote = quote.trimEnd() + "\n" + reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
2750 } else {
2751 quote += reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
2752 }
2753
2754 break;
2755 }
2756 case "#text": {
2757 // The "isInit" check is to prevent the empty text surroudning message
2758 // However, it may happen that the root node contains valid text child, so it need to be added somehow
2759 // For some reason, an "new line" may be missing in this case, so just add it
2760 if (!isInit || child.textContent.trim() !== "") {
2761 quote += child.textContent;
2762 if (isInit && !quote.endsWith("\n")) {
2763 quote += "\n";
2764 }
2765 }
2766 break;
2767 }
2768 default: {
2769 break;
2770 }
2771 }
2772
2773 if (name == "P") {
2774 prevIsP = true;
2775 } else {
2776 prevIsP = false;
2777 }
2778 }
2779
2780 quote = quote.replace(/(\n){3,}/g, '\n\n').trim();
2781
2782 if (isInit) {
2783 quote = quote.replace(/^/gm, '> ');
2784 }
2785
2786 return quote;
2787}
2788
2789function reverseQuote(blocMessage) {
2790 let author = blocMessage.getElementsByClassName("jvchat-author")[0].textContent.trim();
2791 let date = blocMessage.getElementsByClassName("jvchat-date")[0].getAttribute("to-quote");
2792 let header = `> Le ${date} ${author} a écrit :\n`;
2793 let quoted = reverseMessage(blocMessage.getElementsByClassName("txt-msg")[0], true);
2794 return header + quoted + '\n\n';
2795}
2796
2797function insertAtCursor(input, textToInsert) {
2798 const value = input.value;
2799 const start = input.selectionStart;
2800 const end = input.selectionEnd;
2801 input.value = value.slice(0, start) + textToInsert + value.slice(end);
2802 input.selectionStart = input.selectionEnd = start + textToInsert.length;
2803}
2804
2805function dontScrollOnExpand(event) {
2806 let target = event.target;
2807 if (!target) {
2808 return;
2809 }
2810
2811 let classes = target.classList;
2812
2813 if (classes.contains("nested-quote-toggle-box")) {
2814 let isDown = isScrollDown();
2815 let blockQuote = target.closest(".blockquote-jv");
2816 let visible = blockQuote.getAttribute("data-visible");
2817 let value = visible === "1" ? "" : "1";
2818 blockQuote.setAttribute('data-visible', value);
2819 if (isDown) {
2820 setScrollDown();
2821 }
2822 } else if (classes.contains("txt-spoil") || classes.contains("aff-spoil") || classes.contains("masq-spoil")) {
2823 event.preventDefault();
2824 let check = target.closest(".bloc-spoil-jv").getElementsByClassName("open-spoil")[0];
2825 let isDown = isScrollDown();
2826 check.checked = !check.checked;
2827 if (isDown) {
2828 setScrollDown();
2829 }
2830 } else if (classes.contains("jvchat-quote")) {
2831 let bloc = target.closest(".jvchat-message");
2832 let quote = reverseQuote(bloc);
2833 let textarea = document.getElementById("message_topic");
2834 if (isReduced) {
2835 toggleTextarea();
2836 }
2837 insertAtCursor(textarea, quote);
2838 textarea.focus();
2839 } else if (classes.contains("jvchat-edit")) {
2840 let bloc = target.closest(".jvchat-message");
2841 requestEdit(bloc);
2842 } else if (classes.contains("jvchat-delete")) {
2843 let bloc = target.closest(".jvchat-message");
2844 event.stopPropagation();
2845 requestDelete(bloc);
2846 } else if (classes.contains("jvchat-edition-check")) {
2847 let bloc = target.closest(".jvchat-message");
2848 editMessage(bloc);
2849 } else if (classes.contains("jvchat-edition-cancel")) {
2850 let bloc = target.closest(".jvchat-message");
2851 let isDown = isScrollDown();
2852 bloc.getElementsByClassName("jvchat-content")[0].classList.remove("jvchat-hide");
2853 bloc.getElementsByClassName("jvchat-edition")[0].classList.add("jvchat-hide");
2854 if (isDown) {
2855 setScrollDown();
2856 }
2857 }
2858}
2859
2860function isScrollDown() {
2861 let element = document.getElementById("jvchat-main");
2862 return element.clientHeight + Math.floor(element.scrollTop) >= element.scrollHeight - 1;
2863}
2864
2865function setScrollDown() {
2866 let element = document.getElementById("jvchat-main");
2867 element.scrollTop = element.scrollHeight + 10000;
2868}
2869
2870function main() {
2871 addJVChatButton(document);
2872 bindJVChatButton(document);
2873}
2874
2875main();