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