// ==UserScript==
// @name Iwara Custom Sort
// @name:ja Iwara Custom ソート
// @version 0.204
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.deleteValue
// @run-at document-end
// @match https://ecchi.iwara.tv/*
// @match https://www.iwara.tv/*
// @match http://ecchi.iwara.tv/*
// @match http://www.iwara.tv/*
// @description Automatically sort teaser images on /videos, /images, /subscriptions, /users, /playlist, and sidebars using customizable sort function. Can load and sort multiple pages at once.
// @description:ja /videos、/images、/subscriptions、/playlist、/usersとサイドバーのサムネイルを自動的にソートします。ソート方法はカスタマイズすることができます、一度に複数のページを読み込んでソートすることができます。
// @license AGPL-3.0-or-later
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/sweetalert2.all.min.js#sha256-4/EaXPJ/6N3TkeW1FnAqmfV7JNVmnIFQ3bllkklPJ9U=
// @require https://unpkg.com/[email protected]/dist/loglevel.min.js#sha384-Op9lLc4V1M516+nNY8VWsadxPqqnzIpcU8UqrxIqJeVa+jbqbsAjsttJPJyACagp
// @require https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js#sha256-ofji4RA7ZKBd+T/Ij8gVhRYyfvqark3dhZ/wD+/wkPg=
// @namespace https://greasyfork.org/users/245195
// ==/UserScript==
/* jshint esversion: 6 */
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 4);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = log;
/***/ }),
/* 1 */
/***/ (function(module, exports) {
module.exports = rxjs;
/***/ }),
/* 2 */
/***/ (function(module, exports) {
module.exports = Swal;
/***/ }),
/* 3 */
/***/ (function(module, exports) {
module.exports = rxjs.operators;
/***/ }),
/* 4 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: external "log"
var external_log_ = __webpack_require__(0);
// EXTERNAL MODULE: external "Swal"
var external_Swal_ = __webpack_require__(2);
var external_Swal_default = /*#__PURE__*/__webpack_require__.n(external_Swal_);
// EXTERNAL MODULE: external "rxjs"
var external_rxjs_ = __webpack_require__(1);
// EXTERNAL MODULE: external "rxjs.operators"
var external_rxjs_operators_ = __webpack_require__(3);
// CONCATENATED MODULE: ./src/sort_component/index.ts
const createNumberInput = (value, min, max, step, width, ...className) => {
const input = document.createElement('input');
input.type = 'number';
input.value = value.toString();
input.min = min.toString();
input.max = max.toString();
input.step = step.toString();
input.setAttribute('required', '');
input.style.width = width;
input.classList.add(...className);
return input;
};
const createLabel = (text, ...className) => {
const label = document.createElement('label');
label.innerHTML = text;
label.classList.add(...className);
return label;
};
const createTextInput = (text, maxLength, size, ...className) => {
const input = document.createElement('input');
input.type = 'text';
input.value = text;
input.maxLength = maxLength;
input.size = size;
input.classList.add(...className);
return input;
};
const createButton = (text, ...className) => {
const button = document.createElement('button');
button.innerHTML = text;
button.classList.add(...className);
return button;
};
/* harmony default export */ var sort_component = (class {
constructor(initialSortValue, defaultSortValue, pageCount) {
this.label1 = createLabel('', 'text-primary');
this.loadedPageCount = 0;
this.sortValueInput = createTextInput(initialSortValue, 120, 65, 'form-control', 'input-sm');
this.sortButton = createButton('Sort', 'btn', 'btn-sm', 'btn-primary');
this.sort$ = Object(external_rxjs_["fromEvent"])(this.sortButton, 'click').pipe(Object(external_rxjs_operators_["map"])(() => this.sortValueInput.value));
this.sortValueInput.addEventListener('keyup', (event) => {
if (event.key === 'Enter') {
this.sortButton.click();
}
});
const resetDefaultButton = createButton('Default', 'btn', 'btn-sm', 'btn-info');
Object(external_rxjs_["fromEvent"])(resetDefaultButton, 'click').subscribe(() => {
this.sortValueInput.value = defaultSortValue;
GM.setValue('sortValue', defaultSortValue);
});
const label3 = createLabel('', 'text-primary');
const pageCountInput = createNumberInput(pageCount, 1, 300, 1, '7rem', 'form-control', 'input-sm');
pageCountInput.addEventListener('change', (event) => {
GM.setValue('pageCount', Number.parseInt(event.target.value, 10));
label3.innerHTML = 'Refresh to apply the change.';
});
const label2 = createLabel('pages loaded', 'text-primary');
this.UI = document.createElement('div');
this.UI.style.display = 'inline-block';
this.UI.classList.add('form-inline', 'container');
this.UI.append(this.sortValueInput, resetDefaultButton, this.sortButton, this.label1, pageCountInput, label2, label3);
this.UI.childNodes.forEach((node) => {
node.style.margin = '5px 2px';
});
this.loadedPageCount = 0;
this.addLoadedPageCount();
}
sort() {
this.sortButton.click();
}
addLoadedPageCount() {
this.loadedPageCount += 1;
this.label1.innerHTML = `${this.loadedPageCount} of `;
}
});
// CONCATENATED MODULE: ./src/teaser_element_selector/index.ts
/* harmony default export */ var teaser_element_selector = ('.node-teaser, .node-sidebar_teaser, .node-wide_teaser');
// CONCATENATED MODULE: ./src/sort_teasers/index.ts
// eslint-disable-next-line no-new-func
const getTeaserValue = (item, expression) => new Function('views', 'likes', 'ratio', 'image', 'gallery', 'private', `return (${expression})`)(item.viewCount, item.likeCount, Math.min(item.likeCount / Math.max(1, item.viewCount), 1), item.imageFactor, item.galleryFactor, item.privateFactor);
const sortContainer = (container, valueExpression) => {
const viewsIconSelector = '.glyphicon-eye-open';
const likesIconSelector = '.glyphicon-heart';
const imageFieldSelector = '.field-type-image';
const galleryIconSelector = '.glyphicon-th-large';
const privateDivSelector = '.private-video';
const teaserDivs = Array.from(container.querySelectorAll(teaser_element_selector));
let sortedTeaserCount = 0;
if (container.hasAttribute('data-sorted-teaser-count')) {
sortedTeaserCount = parseInt(container.getAttribute('data-sorted-teaser-count'), 10);
}
teaserDivs.forEach((div) => {
if (!div.hasAttribute('data-original-order')) {
div.setAttribute('data-original-order', sortedTeaserCount.toString());
sortedTeaserCount += 1;
}
});
container.setAttribute('data-sorted-teaser-count', sortedTeaserCount.toString());
const getNearbyNumber = (element) => {
const parsePrefixed = (str) => Number.parseFloat(str) * (str.includes('k') ? 1000 : 1);
return element ? parsePrefixed(element.nextSibling.wholeText.replace(/,/g, '')) : 0;
};
const teasers = teaserDivs.map((div) => ({
domElement: div,
viewCount: getNearbyNumber(div.querySelector(viewsIconSelector)),
likeCount: getNearbyNumber(div.querySelector(likesIconSelector)),
imageFactor: div.querySelector(imageFieldSelector) ? 1 : 0,
galleryFactor: div.querySelector(galleryIconSelector) ? 1 : 0,
privateFactor: div.querySelector(privateDivSelector) ? 1 : 0,
}));
const teaserValuePairs = teasers.map((teaser) => [
teaser.domElement,
getTeaserValue(teaser, valueExpression),
]);
teaserValuePairs.sort((itemA, itemB) => itemB[1] - itemA[1]);
teaserDivs.map((div) => {
const anchor = document.createElement('span');
div.before(anchor);
return anchor;
}).forEach((div, index) => div.replaceWith(teaserValuePairs[index][0]));
};
/* harmony default export */ var sort_teasers = ((containers, valueExpression) => {
GM.setValue('sortValue', valueExpression);
let sortedCount = 0;
containers.forEach((grid) => {
sortContainer(grid, valueExpression);
sortedCount += 1;
});
external_log_["info"](`${sortedCount} grids sorted`);
});
// CONCATENATED MODULE: ./src/get_teaser_containers/index.ts
/* harmony default export */ var get_teaser_containers = ((node) => {
const containerSelector = '.views-responsive-grid, .node-playlist .field-name-field-videos';
return Array.from(node.querySelectorAll(containerSelector))
.filter((grid) => Boolean(grid.querySelector(teaser_element_selector)));
});
// CONCATENATED MODULE: ./src/get_page_param/index.ts
const getNumberParam = (URL_, name) => {
const param = URL_.searchParams.get(name);
return param ? Number.parseInt(param, 10) : 0;
};
/* harmony default export */ var get_page_param = ((URL_) => getNumberParam(URL_, 'page'));
// CONCATENATED MODULE: ./src/init_parent/index.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const changePageParam = (anchor, value) => {
const anchorURL = new URL(anchor.href, window.location.href);
anchorURL.searchParams.set('page', value.toString());
anchor.href = anchorURL.href;
};
const groupCurrentPageItems = (currentPageItems) => {
const parentItem = document.createElement('li');
currentPageItems[0].before(parentItem);
currentPageItems[0].style.marginLeft = '0';
const groupList = document.createElement('ul');
groupList.style.display = 'inline';
groupList.style.backgroundColor = 'hsla(0, 0%, 75%, 50%)';
currentPageItems.forEach((item) => {
item.classList.replace('pager-item', 'pager-current');
groupList.append(item);
});
parentItem.append(groupList);
};
const adjustPageAnchors = (container, pageCount) => {
const currentPage = get_page_param(new URL(window.location.href));
if (currentPage > 0) {
const previousPageAnchor = container.querySelector('.pager-previous a');
changePageParam(previousPageAnchor, Math.max(0, currentPage - pageCount));
}
const nextPage = currentPage + pageCount;
{
const lastPageAnchor = container.querySelector('.pager-last a');
const nextPageAnchor = container.querySelector('.pager-next a');
if (nextPageAnchor) {
changePageParam(nextPageAnchor, nextPage);
}
if (lastPageAnchor
&& get_page_param(new URL(lastPageAnchor.href, window.location.href)) < nextPage) {
nextPageAnchor.remove();
lastPageAnchor.remove();
}
}
const currentPageAnchors = Array.from(container.querySelectorAll('.pager-item a'))
.filter((anchor) => {
const page = get_page_param(new URL(anchor.href, window.location.href));
return page >= currentPage && page < nextPage;
});
if (currentPageAnchors.length > 0) {
const currentPageItems = [
container.querySelector('.pager-current'),
...currentPageAnchors.map((anchor) => anchor.parentElement),
];
groupCurrentPageItems(currentPageItems);
}
};
const fixImages = () => {
const brokenImages = get_teaser_containers(document).flatMap((container) => Array.from(container.querySelectorAll('img'))).filter((img) => img.complete && img.naturalWidth === 0);
brokenImages.forEach((img) => {
img.src = img.src;
external_log_["info"]('Reload a broken image');
});
};
/* harmony default export */ var init_parent = ((teasersAddedMeesage) => __awaiter(undefined, void 0, void 0, function* () {
const defaultSortValue = '(ratio / (private * 2.0 + 1) + Math.log(likes) / 250) / (image + 8.0)';
const initialSortValue = yield GM.getValue('sortValue', defaultSortValue);
const pageCount = yield GM.getValue('pageCount', 1);
const sortComponent = new sort_component(initialSortValue, defaultSortValue, pageCount);
document.querySelector('#user-links').after(sortComponent.UI);
if (get_teaser_containers(document).length === 0) {
return;
}
let pages = [];
sortComponent.sort$.subscribe((sortValue) => {
try {
sort_teasers(get_teaser_containers(document), sortValue);
}
catch (error) {
external_Swal_default.a.fire('Sorting Failed', `An error accured while sorting: ${error.toString()}`);
pages.forEach((page) => {
page.src = '';
page.remove();
});
pages = [];
}
});
let imageToFix$ = Object(external_rxjs_["merge"])(Object(external_rxjs_["of"])(0), sortComponent.sort$.pipe(Object(external_rxjs_operators_["mapTo"])(0)));
if (document.querySelector('.pager') && !document.querySelector('#comments')) {
{
const pageURL = new URL(window.location.href);
const params = pageURL.searchParams;
let page = get_page_param(pageURL);
for (let pageLeft = pageCount - 1; pageLeft > 0; pageLeft -= 1) {
page += 1;
params.set('page', page.toString());
const nextPage = (() => (document.createElement(navigator.userAgent.indexOf('Firefox') > -1 ? 'embed' : 'iframe')))();
nextPage.src = pageURL.toString();
nextPage.style.display = 'none';
pages.push(nextPage);
external_log_["info"]('Add page:', nextPage.src);
}
}
document.body.append(...pages);
external_log_["debug"](pages);
document.querySelectorAll('.pager').forEach((list) => adjustPageAnchors(list, pageCount));
const addTeasers$ = Object(external_rxjs_["fromEvent"])(window, 'message').pipe(Object(external_rxjs_operators_["filter"])((event) => (new URL(event.origin).hostname === window.location.hostname
&& event.data === teasersAddedMeesage
&& pages.length > 0)));
addTeasers$.subscribe((event) => {
sortComponent.sort();
sortComponent.addLoadedPageCount();
const loadedPage = event.source
.frameElement;
loadedPage.src = '';
loadedPage.remove();
});
imageToFix$ = Object(external_rxjs_["merge"])(imageToFix$, addTeasers$.pipe(Object(external_rxjs_operators_["mapTo"])(0)));
}
sortComponent.sort();
imageToFix$.pipe(Object(external_rxjs_operators_["mergeMap"])(() => Object(external_rxjs_["timer"])(0, 8000).pipe(Object(external_rxjs_operators_["take"])(2))), Object(external_rxjs_operators_["auditTime"])(6000)).subscribe(() => fixImages());
}));
// CONCATENATED MODULE: ./src/timeout/index.ts
/* harmony default export */ var timeout = ((delay) => new Promise((resolve) => {
setTimeout(resolve, delay);
}));
// CONCATENATED MODULE: ./src/init_child/index.ts
var init_child_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/* harmony default export */ var init_child = ((teasersAddedMeesage) => init_child_awaiter(undefined, void 0, void 0, function* () {
const teaserGrids = get_teaser_containers(document);
if (teaserGrids.length === 0) {
return;
}
yield timeout(500);
const parentGrids = get_teaser_containers(window.parent.document);
for (let i = 0, j = 0; i < parentGrids.length; i += 1) {
if (teaserGrids[j].className === parentGrids[i].className) {
teaserGrids[j].className = '';
parentGrids[i].prepend(teaserGrids[j]);
j += 1;
}
}
window.parent.postMessage(teasersAddedMeesage, window.location.origin);
}));
// CONCATENATED MODULE: ./src/index.ts
var src_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const initialize = () => src_awaiter(undefined, void 0, void 0, function* () {
const isParent = (window === window.parent);
external_log_["debug"](`isParent: ${isParent}.`, window.location);
const teasersAddedMeesage = 'iwara custom sort: teasersAdded';
if (isParent) {
yield init_parent(teasersAddedMeesage);
}
else {
yield init_child(teasersAddedMeesage);
}
});
(() => src_awaiter(undefined, void 0, void 0, function* () {
try {
external_log_["setLevel"]('trace');
external_log_["debug"](`Parsed: ${document.readyState}`);
yield initialize();
}
catch (error) {
external_log_["trace"](error);
}
}))();
/***/ })
/******/ ]);