- // ==UserScript==
- // @name Neochan autoplay
- // @namespace http://tampermonkey.net/
- // @version 0.1
- // @description Adds media controls for neochan
- // @author You
- // @match https://*.neochan.ru/*
- // @match https://*.neochan.net/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=neochan.net
- // @grant GM_addStyle
- // ==/UserScript==
-
- (function() {
- 'use strict';
- GM_addStyle(
- ".ap-arrow a {display: block;}" +
- ".ap-arrow {position: fixed; top: 50%; font-size: x-large; cursor: pointer; background-color: rgba(255, 255, 255, 0.2); width: 50px;padding-top: 5px;padding-bottom: 5px;text-align: center; left:0;} " +
- ".ap-arrow-right {right: 0; left: unset;}"+
- ".ap-media-button { display: block; margin-left: 14px;margin-top: 10px; margin-bottom: 5px; border: 0; background: transparent; box-sizing: border-box;width: 0; height: 35px; border-color: transparent transparent transparent #202020; transition: 100ms all ease; "+
- "cursor: pointer; border-style: solid; border-width: 16px 0 16px 23px;}" +
- " .ap-media-button.paused { border-style: double; border-width: 0px 0 0px 23px; } .ap-media-button:hover { border-color: transparent transparent transparent #404040; }"
- )
-
- const PlayVidLinks = true,
- ReplaceGifs = true,
- AddCustomSmiles = true,
- Smiles = {
- "enid_wink": {
- 'smileHtml': '<ul class="smile-tab smile-tab-enid_wink" style="list-style: none; text-align: left; display: none;"><div class="smile" style="display: inline"><i class="s42 s42-enid_wink smiles-icon" data-func="AddSmile" data-arg1="enid_wink"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_amazed smiles-icon" data-func="AddSmile" data-arg1="emma_amazed"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_angry smiles-icon" data-func="AddSmile" data-arg1="emma_angry"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_beauty smiles-icon" data-func="AddSmile" data-arg1="emma_beauty"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_braces smiles-icon" data-func="AddSmile" data-arg1="emma_braces"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_cool smiles-icon" data-func="AddSmile" data-arg1="emma_cool"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_crying smiles-icon" data-func="AddSmile" data-arg1="emma_crying"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_duck smiles-icon" data-func="AddSmile" data-arg1="emma_duck"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_duck2 smiles-icon" data-func="AddSmile" data-arg1="emma_duck2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_scared smiles-icon" data-func="AddSmile" data-arg1="emma_scared"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_serious smiles-icon" data-func="AddSmile" data-arg1="emma_serious"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_serious2 smiles-icon" data-func="AddSmile" data-arg1="emma_serious2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_smile smiles-icon" data-func="AddSmile" data-arg1="emma_smile"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_smile2 smiles-icon" data-func="AddSmile" data-arg1="emma_smile2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_swag smiles-icon" data-func="AddSmile" data-arg1="emma_swag"></i></div><div class="smile" style="display: inline"><i class="s42 s42-emma_tongue smiles-icon" data-func="AddSmile" data-arg1="emma_tongue"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_cat smiles-icon" data-func="AddSmile" data-arg1="enid_cat"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_cute smiles-icon" data-func="AddSmile" data-arg1="enid_cute"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_disgust smiles-icon" data-func="AddSmile" data-arg1="enid_disgust"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_eating smiles-icon" data-func="AddSmile" data-arg1="enid_eating"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_rage smiles-icon" data-func="AddSmile" data-arg1="enid_rage"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_sad smiles-icon" data-func="AddSmile" data-arg1="enid_sad"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_scared smiles-icon" data-func="AddSmile" data-arg1="enid_scared"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_scared2 smiles-icon" data-func="AddSmile" data-arg1="enid_scared2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_scared3 smiles-icon" data-func="AddSmile" data-arg1="enid_scared3"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_smile smiles-icon" data-func="AddSmile" data-arg1="enid_smile"></i></div><div class="smile" style="display: inline"><i class="s42 s42-enid_smile2 smiles-icon" data-func="AddSmile" data-arg1="enid_smile2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_beaten smiles-icon" data-func="AddSmile" data-arg1="jenna_beaten"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_cringe smiles-icon" data-func="AddSmile" data-arg1="jenna_cringe"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_facepalm smiles-icon" data-func="AddSmile" data-arg1="jenna_facepalm"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_kiss smiles-icon" data-func="AddSmile" data-arg1="jenna_kiss"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_laugh smiles-icon" data-func="AddSmile" data-arg1="jenna_laugh"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_nose smiles-icon" data-func="AddSmile" data-arg1="jenna_nose"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_sick smiles-icon" data-func="AddSmile" data-arg1="jenna_sick"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_smile smiles-icon" data-func="AddSmile" data-arg1="jenna_smile"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_smoke smiles-icon" data-func="AddSmile" data-arg1="jenna_smoke"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_think smiles-icon" data-func="AddSmile" data-arg1="jenna_think"></i></div><div class="smile" style="display: inline"><i class="s42 s42-jenna_what_ smiles-icon" data-func="AddSmile" data-arg1="jenna_what_"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_cold smiles-icon" data-func="AddSmile" data-arg1="wedn_cold"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_crying smiles-icon" data-func="AddSmile" data-arg1="wedn_crying"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_eyes smiles-icon" data-func="AddSmile" data-arg1="wedn_eyes"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_growl smiles-icon" data-func="AddSmile" data-arg1="wedn_growl"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_mad smiles-icon" data-func="AddSmile" data-arg1="wedn_mad"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_sad smiles-icon" data-func="AddSmile" data-arg1="wedn_sad"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_smile smiles-icon" data-func="AddSmile" data-arg1="wedn_smile"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_smile2 smiles-icon" data-func="AddSmile" data-arg1="wedn_smile2"></i></div><div class="smile" style="display: inline"><i class="s42 s42-wedn_tired smiles-icon" data-func="AddSmile" data-arg1="wedn_tired"></i></div><div class="smile" style="display: inline"><i class="s42 s42-puppy smiles-icon" data-func="AddSmile" data-arg1="puppy"></i></div></ul>',
- 'smileIcHtml': '<div class="sc-item" data-func="changeSmileTab" data-arg1="enid_wink"><i class="smile-category-image s42-enid_wink"></i></div>'
- }
- }
- let mediaElems = [],
- curIndex =0, maxIndex = 0,
- plrPause = false, musToVid = false;
-
- function htmlToElements(html) {
- var template = document.createElement('template');
- template.innerHTML = html;
- return template.content;
- }
-
- function createArrow(rightSide) {
- const arrow = htmlToElements(`<div class="ap-nav ap-arrow ${rightSide ? 'ap-arrow-right' : ''}"><a ><i class="fa fa-chevron-${rightSide ? 'right' : 'left'}"></i></a></div>`);
- arrow.querySelector("a").addEventListener('click', (e) => {
- if (curIndex == 0 && !rightSide) return;
- if (curIndex == maxIndex-1 && rightSide) curIndex = -1;
- let elem = "";
- if (plrPause){
- const fM = rightSide ?
- mediaElems.find(e => curIndex < e.getAttribute('ap-index') && e.classList.contains('ap-media')) :
- mediaElems.filter(e => curIndex > e.getAttribute('ap-index') && e.classList.contains('ap-media'))?.slice(-1)?.[0];
- if (fM){
- elem = fM;
- curIndex = +fM.getAttribute('ap-index');}
- else{
- // elem = mediaElems.filter(e => maxIndex > e.getAttribute('ap-index') && e.classList.contains('ap-media'))?.slice(-1)?.[0];
- elem = rightSide ? mediaElems.find(e => 0 <= e.getAttribute('ap-index') && e.classList.contains('ap-media')) :
- mediaElems.filter(e => maxIndex > e.getAttribute('ap-index') && e.classList.contains('ap-media'))?.slice(-1)?.[0];
- }
- }else{
- const video = document.querySelector('video');
- if (video) video.loop = true;
- curIndex = curIndex + (rightSide ? 1 : -1);
- elem = mediaElems.find(e => curIndex == e.getAttribute('ap-index'));
- }
- if (!elem) return;
- document.querySelector("#close-plyr__audio")?.click();
- elem.click();
- elem.scrollIntoView({block: "center", inline: "nearest"});
- switchPlBut();
- });
- return arrow;
- }
- function switchPlBut(val){
- const mB = document.querySelector('.ap-media-button')
- if (!mB) return;
- if (val !== undefined){
- mB.style.display = val ? 'block' : 'none';
- return;
- }
- if (document.querySelector('#player-container img')?.tagName == 'IMG') {
- mB.style.display = 'none';
- } else {
- mB.style.display = 'block';
- }
- }
- function render(){
- if(window.location.href.match(/usermod\.php\?|mod\.php\?/)){
- const art = document.querySelectorAll('article.post'),
- controls = document.querySelector('.view-inline-pc');
- for (let i = 0; i < art.length; i++) {
- const a = art[i];
- if(a.querySelector('.view-inline-pc')) continue;
- const header = a.querySelector('header'),
- postNum = header.querySelector('.post_anchor').name,
- contrDup = controls.cloneNode(true),
- links = contrDup.querySelectorAll('a');
- for (let j = 0; j < links.length; j++) {
- const l = links[j];
- l.href = l.href.replace(/\/\d+\//, `/${postNum}/`);
- }
- header.insertBefore(contrDup, header.querySelector('time'));
- }
- }
- const mE = document.querySelectorAll(`.audio-url, img.webm-file, .img.preview${PlayVidLinks ? ', .y-link' : ""}`);
- if (mE.length == mediaElems.length) return;
- let index = 0;
- mediaElems = [...mE];
- mE.forEach(e => { //, vimeo-link
- const ind = index++, p = e.parentNode;
- if(e.hasAttribute('ap-parsed')) return;
- e.setAttribute("ap-index", ind);
- const classNames = ['webm-file', 'audio-url'];
- if (PlayVidLinks) classNames.push('y-link'); //, 'vimeo-link'
- if(classNames.some(cN => e.classList.contains(cN))) {
- e.classList.add('ap-media');
- }
- if(e.classList.contains("img")){
- if(p.name == "ClipboardImage.png"){
- const ext = p.href?.match(/\.\w+$/)?.[0];
- if (ext && ext != '.png') p.setAttribute("name", p.name.replace(/\.png$/, ext));
- }
- if(ReplaceGifs && p.href.endsWith('.gif')){
- p.querySelector('.post-gif-marker').remove();
- e.src = e.src.replace(/\/thumb\//, '/src/').replace(/\.\w+$/, '.gif');
- }
- }
- e.addEventListener('click', e => {
- const t = e.currentTarget;
- curIndex = +t.getAttribute('ap-index');
- const plClose = document.querySelector("#close-plyr__audio");
- if (plClose){
- musToVid = true;
- plClose.click();
- }
- if (e.currentTarget.classList.contains('ap-media')) switchPlBut(true);
- else switchPlBut(false);
- });
- e.toggleAttribute('ap-parsed');
- })
- maxIndex = index;
- }
- function obsBody(){
- const navElems = document.querySelectorAll('.ap-nav'),
- player = document.querySelector('video, #player-container img, .plyr'),
- smilePan = document.querySelector(".smile-picker"),
- navDisplayed = ['none', undefined].includes(navElems[0]?.style?.display)? false : true;
- if (AddCustomSmiles && smilePan && !document.querySelector(".user-smiles-added")){
- const smList = smilePan.querySelector('.smile-picker-box'),
- smCat = smilePan.querySelector('.smile-category'),
- smLength = Object.keys(Smiles).length;
- for(let key in Smiles){
- if(smCat.querySelector(`[data-arg1="${key}"]`)) continue;
- smList.insertBefore(htmlToElements(Smiles[key].smileHtml), smList.firstChild);
- smCat.insertBefore(htmlToElements(Smiles[key].smileIcHtml), smCat.firstChild);
- }
- smList.querySelectorAll('.button').forEach((b)=>{
- const n = +b.getAttribute('data-arg2') + smLength;
- b.id = `btn-more-${n}`;
- b.setAttribute('data-arg2', n);
- });
- smCat.classList.add('user-smiles-added');
- }
- if (navElems.length && (!player && navDisplayed || player && !navDisplayed)){
- if (navDisplayed) { render(); }
- navElems.forEach(e => {e.style.display = e.style.display == 'none' ? '' : 'none';});
- if (musToVid && !navDisplayed) {
- musToVid = false;
- } else if (!musToVid){
- document.querySelector('.ap-media-button')?.classList.remove("paused");
- plrPause = false;
- }
- } else {
- if(player && navElems.length && plrPause) setNextEvent();
- if (navDisplayed || !player) return;
- render();
- createNav();
- }
- switchPlBut();
- }
- function createNav(){
- const rightArrow = createArrow(true),
- leftArrow = createArrow(),
- navElem = document.querySelector(".page-nav"),
- l = navElem.appendChild(leftArrow),
- btn = htmlToElements("<button class='ap-media-button ap-nav'></button>");
- navElem.appendChild(rightArrow);
- document.querySelector('.ap-arrow-right').appendChild(btn);
- document.querySelector('.ap-media-button').addEventListener('click', e => {
- plrPause = e.currentTarget.classList.toggle("paused");
- setNextEvent();
- return false;
- });
- }
-
- function crMediaObserver(element, checkFunc){
- const plInp = document.querySelector('.plyr__progress > input');
- if (element && !element.hasAttribute('ap-parsed')) {
- const observer = new MutationObserver(() => {
- if(!plrPause) {
- element.toggleAttribute('ap-parsed');
- observer.disconnect();
- return;
- }
- if (checkFunc()){
- document.querySelector('.ap-arrow-right a')?.click();
- observer.disconnect();
- }
- });
- observer.observe(element, {attributes: true});
- element.toggleAttribute('ap-parsed');
- };
- }
-
- function setNextEvent(){
- const video = document.querySelector('video');
- if (video){
- video?.toggleAttribute('loop');
- if(video.hasAttribute('ap-parsed')) return;
- video?.addEventListener("ended", (event) => {
- document.querySelector('.ap-arrow-right a')?.click();
- });
- video.toggleAttribute('ap-parsed');
- return;
- }
- const plInp = document.querySelector('.plyr__progress > input');
- crMediaObserver(plInp, () => plInp.style.cssText == '--value: 100%;');
- if (PlayVidLinks){
- const ytPlr = document.querySelector('#youtubevideo');
- crMediaObserver(ytPlr, () => ytPlr.classList.contains('plyr--stopped') && ytPlr.classList.contains('plyr__poster-enabled'));
- }
- }
- function waitForElm(selector) {
- return new Promise(resolve => {
- if (document.querySelector(selector)) {
- return resolve(document.querySelector(selector));
- }
- const observer = new MutationObserver(mutations => {
- if (document.querySelector(selector)) {
- observer.disconnect();
- resolve(document.querySelector(selector));
- }
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- });
- }
-
- render();
- const threadObserver = new MutationObserver(render),
- bodyObserver = new MutationObserver(obsBody),
- article = document.querySelector("article.thread");
- if (!article) return;
- threadObserver.observe(article, {childList: true});
- bodyObserver.observe(document.body, {childList: true});
- waitForElm('#player-container').then(() => {
- bodyObserver.observe(document.querySelector('#player-container'), {attributes: true})
- })
- })();