NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Whatsapp Wall // @namespace http://whatsappweb.com/whatsapp-wall // @version 0.2.2 // @description Whatsapp web media slide show! // @author Tom Van Rossom // @match https://web.whatsapp.com/ // @grant none // @supportURL https://github.com/tomvanrossom/Whatsapp-Wall // @require https://code.jquery.com/jquery-2.1.4.min.js // ==/UserScript== ( function( $ ) { const DEFAULT_INTERVAL = 5000; function addGlobalStyle( css ) { let head; let style; head = document.getElementsByTagName( 'head' )[ 0 ]; if ( !head ) { return; } style = document.createElement( 'style' ); style.type = 'text/css'; style.innerHTML = css; head.appendChild( style ); } //Init addGlobalStyle( '.media-viewer-thumbs-container { display: none; }' ); addGlobalStyle( '.menu.menu-horizontal.media-panel-tools { display: none; }' ); addGlobalStyle( 'div.chat.media-chat { background-color: transparent; color: white; }' ); addGlobalStyle( 'div.chat-body { background-color: rgba(255,255,255,0.8); flex-grow: 0; padding: 8px; border-radius: 4px; }' ); addGlobalStyle( 'div.media-panel-header { z-index: 999; background-color: transparent; }' ); addGlobalStyle('span.media-caption { z-index: 999; background-color: white; padding: 15px; border-radius: 5px; }'); addGlobalStyle('div.media-content { background-color: black; position: absolute; width: 100%; height: 100%; padding: 0; }'); addGlobalStyle( '.btn-round { z-index: -1; }' ); addGlobalStyle( 'div.media > div.object-fit > div { position: absolute; padding: 0; }' ); addGlobalStyle( '.media-viewer .avatar { height: 120px !important; width: 120px !important; margin-top: 110px;}' ); addGlobalStyle( '.media-viewer div.chat-body { max-height: 40px; margin-top: 110px; }' ); addGlobalStyle( '.fadeIn {animation: fadeInOut 10s; transition: transform 5s linear; transform: scale(1.1);}' ); addGlobalStyle( '@keyframes fadeInOut { 0% { opacity: 0; } 5% { opacity: 1; } 45% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 0; }}' ); addGlobalStyle( '.media-viewer .avatar {border: 1px solid white; }' ); //Init on global context $( document ).ready( function() { var uniqueImages = new Set(); var currentImage; var imagePointer; var newImages=false; function getMediaParent() { let divMedia = $( 'div.media > div.object-fit > div' ); if ( divMedia.length > 0 ) { return divMedia; } else { if ( $( 'div.media > audio' ).length > 0 ) { return $( 'div.media' ); } } } var imageObserver; function observeImages() { if ( imageObserver ) { imageObserver.disconnect(); } // select the target node var target = $( '#app > div > span:nth-child(2)' )[ 0 ]; // create an observer instance imageObserver = new MutationObserver( function( mutations ) { //console.log('observeImages'); //console.log(mutations); var divParent = getMediaParent(); if(divParent){ var h = divParent.height(); var w = divParent.width(); var mediaObj = $( divParent.children()[ 0 ] ); if ( mediaObj[ 0 ] ) { if ( mediaObj.is( 'img' ) ) { mediaObj.load( function( e ) { startTimeOutNext(); } ); } mediaObj[ 0 ].addEventListener( 'loadeddata', function( e ) { startTimeOutNext( e.target.duration * 1000 ); }, false ); if (( mediaObj.is('img') ) || ( mediaObj.is('video') )) { var src = mediaObj.attr('src'); currentImage = src; //console.log('currentImage: ' + currentImage); if(src){ uniqueImages.add(src); } //console.log('totalImages: ' + uniqueImages.size); //console.log(uniqueImages); if ( mediaObj.is('img') ){ mediaObj.addClass('fadeIn'); } if ( w / h > 1.78 ) { mediaObj.css( 'width', '100%' ).css( 'height', 'auto' ); divParent.css( 'width', '100%' ).css( 'height', 'auto' ); } else { mediaObj.css( 'height', '100%' ).css( 'width', 'auto' ); divParent.css( 'height', '100%' ).css( 'width', 'auto' ); } } }else{ console.log('not image or video!? '); startTimeOutNext(); } } // observer.disconnect(); } ); // configuration of the observer: var config = { childList: true, subtree: true }; // pass in the target node, as well as the observer options imageObserver.observe( target, config ); } var messagesObserver; function observeMessages() { if ( messagesObserver ) { messagesObserver.disconnect(); } var target = $( '#main > div.pane-body.pane-chat-tile-container > div > div > div.message-list' )[ 0 ]; messagesObserver = new MutationObserver( function( mutations ) { console.log('observeMessages'); //console.log(mutations); let newImageMessages = searchNewImageMessages(mutations); if (newImageMessages.length > 0) { //console.log(newImageMessages); console.log('New images/videos have arrived'); newImages = true; nextMedia(); } } ); // configuration of the observer: var config = { childList: true }; // pass in the target node, as well as the observer options messagesObserver.observe( target, config ); } function searchNewImageMessages (mutations) { return mutations.map(function (mutation) { console.log('map'); return mutation.addedNodes; }).reduce(function (allMutations, mutationNodes) { console.log('reduce'); allMutations.push(mutationNodes[0]); return allMutations; /* This is important! */ }, []).filter(function (node) { console.log('filter'); return node && node.className && node.className.indexOf('msg') > -1; }).map(function (node) { console.log('map 2'); return node.children[1]; }).filter(function (node) { console.log('filter 2'); return node && node.className && (node.className.indexOf('message-image') > -1 || node.className.indexOf('message-video') > -1); }); } var timeOutNext; function startTimeOutNext( transitionInterval ) { transitionInterval = transitionInterval || DEFAULT_INTERVAL; if ( timeOutNext ) { clearTimeout( timeOutNext ); } timeOutNext = setTimeout( function() { timeOutNext = undefined; nextMedia(); }, transitionInterval ); } function nextMedia() { if ( !timeOutNext ) { console.log('nextMedia'); fadeOut(); var currImg = currentImageShown(); if (newImages && !imagePointer) { console.log('new images have arrived: store pointer'); imagePointer = currImg; } var src = goToNext();//normal if(!uniqueImages.has(src)){ console.log('image never shown before: do nothing: '+uniqueImages.size); }else{ if(newImages){ console.log('new images have arrived: go to first new image'); let prevSrc = null; do { prevSrc = src; src = goToNext(); console.log('scroll forward to new'); } while (uniqueImages.has(src) && prevSrc !== src); newImages = false; } if (src === currImg) { if(isItReallyTheEnd(src)){ console.log('The end: go back'); let prevSrc; let index = uniqueImages.lenght * 2; index = index < 100? 100:index; while (prevSrc !== src || 0 > index--) { console.log('scroll backwards to: '+imagePointer); prevSrc = src; src = goToPrevious(); if (imagePointer && imagePointer.localeCompare(src) === 0) { console.log('Previous location found: ' + imagePointer); imagePointer = undefined; goToNext(); break; } if(prevSrc === src){ console.log('The beginning?: maybe the same image is used multiple times'); src = goToPrevious(); } } if (src === currImg) { console.log('It is still stuck :('); src = goToPrevious(); src = goToPrevious(); } } } } uniqueImages.add(src); } } function isItReallyTheEnd(src) { console.log('The end?: maybe the same image is used multiple times'); let prevSrc = src; let index = 5; while (prevSrc === src && 0 < index--) { prevSrc = src; console.log('check next'); src = goToNext(); } return prevSrc === src; } function goToNext() { // Send KeyDown Event let event = new Event('keydown'); event.keyCode = 39; // keyright window.dispatchEvent(event); return currentImageShown(); } function goToPrevious() { // Send KeyDown Event let event = new Event('keydown'); event.keyCode = 37; // keyleft window.dispatchEvent(event); return currentImageShown(); } function currentImageShown() { var divParent = getMediaParent(); if (divParent && divParent.children) { var mediaObj = $(divParent.children()[0]); if (( mediaObj.is('img') ) || ( mediaObj.is('video') )) { return mediaObj.attr('src'); } } console.log('Something is not right'); startTimeOutNext(); return 'not found'; } function fadeOut() { var divParent = getMediaParent(); if (divParent && divParent.children) { var mediaObj = $(divParent.children()[0]); if (( mediaObj.is('img') ) || ( mediaObj.is('video') )) { mediaObj.removeClass('fadeIn'); } } } function startObservers() { console.log('startObservers'); observeImages(); observeMessages(); } $( 'body' ).on( 'click', '#pane-side > div > div > div > div', startObservers ); } ); } )( jQuery );