Library for creating custom image elements on Furaffinity
สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.greasyfork.org/scripts/492931/1549454/Furaffinity-Submission-Image-Viewer.js
// ==UserScript== // @name Furaffinity-Submission-Image-Viewer // @namespace Violentmonkey Scripts // @grant GM_info // @version 1.1.1 // @author Midori Dragon // @description Library for creating custom image elements on Furaffinity // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png // @license MIT // @homepageURL https://greasyfork.org/scripts/492931-furaffinity-submission-image-viewer // @supportURL https://greasyfork.org/scripts/492931-furaffinity-submission-image-viewer/feedback // ==/UserScript== // jshint esversion: 8 (() => { "use strict"; var __webpack_modules__ = { 656: (module, __webpack_exports__, __webpack_require__) => { __webpack_require__.d(__webpack_exports__, { A: () => __WEBPACK_DEFAULT_EXPORT__ }); var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(601), _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__), _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314), ___CSS_LOADER_EXPORT___ = __webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__)()(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()); ___CSS_LOADER_EXPORT___.push([ module.id, ".siv-image-main {\n object-fit: cover;\n}\n\n.siv-image-preview {\n object-fit: cover;\n image-rendering: pixelated;\n}\n\n.siv-image-container {\n width: 0px;\n height: 0px;\n overflow: hidden;\n}\n\n.siv-parent-container {\n overflow: hidden;\n}\n\n.zoomable-image {\n transition: transform 0.3s;\n transform-origin: center;\n}\n", "" ]); const __WEBPACK_DEFAULT_EXPORT__ = ___CSS_LOADER_EXPORT___; }, 314: module => { module.exports = function(cssWithMappingToString) { var list = []; list.toString = function toString() { return this.map((function(item) { var content = "", needLayer = void 0 !== item[5]; if (item[4]) content += "@supports (".concat(item[4], ") {"); if (item[2]) content += "@media ".concat(item[2], " {"); if (needLayer) content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {"); content += cssWithMappingToString(item); if (needLayer) content += "}"; if (item[2]) content += "}"; if (item[4]) content += "}"; return content; })).join(""); }; list.i = function i(modules, media, dedupe, supports, layer) { if ("string" == typeof modules) modules = [ [ null, modules, void 0 ] ]; var alreadyImportedModules = {}; if (dedupe) for (var k = 0; k < this.length; k++) { var id = this[k][0]; if (null != id) alreadyImportedModules[id] = true; } for (var _k = 0; _k < modules.length; _k++) { var item = [].concat(modules[_k]); if (!dedupe || !alreadyImportedModules[item[0]]) { if (void 0 !== layer) if (void 0 === item[5]) item[5] = layer; else { item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}"); item[5] = layer; } if (media) if (!item[2]) item[2] = media; else { item[1] = "@media ".concat(item[2], " {").concat(item[1], "}"); item[2] = media; } if (supports) if (!item[4]) item[4] = "".concat(supports); else { item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}"); item[4] = supports; } list.push(item); } } }; return list; }; }, 601: module => { module.exports = function(i) { return i[1]; }; }, 72: module => { var stylesInDOM = []; function getIndexByIdentifier(identifier) { for (var r###lt = -1, i = 0; i < stylesInDOM.length; i++) if (stylesInDOM[i].identifier === identifier) { r###lt = i; break; } return r###lt; } function modulesToDom(list, options) { for (var idCountMap = {}, identifiers = [], i = 0; i < list.length; i++) { var item = list[i], id = options.base ? item[0] + options.base : item[0], count = idCountMap[id] || 0, identifier = "".concat(id, " ").concat(count); idCountMap[id] = count + 1; var indexByIdentifier = getIndexByIdentifier(identifier), obj = { css: item[1], media: item[2], sourceMap: item[3], supports: item[4], layer: item[5] }; if (-1 !== indexByIdentifier) { stylesInDOM[indexByIdentifier].references++; stylesInDOM[indexByIdentifier].updater(obj); } else { var updater = addElementStyle(obj, options); options.byIndex = i; stylesInDOM.splice(i, 0, { identifier, updater, references: 1 }); } identifiers.push(identifier); } return identifiers; } function addElementStyle(obj, options) { var api = options.domAPI(options); api.update(obj); return function updater(newObj) { if (newObj) { if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) return; api.update(obj = newObj); } else api.remove(); }; } module.exports = function(list, options) { var lastIdentifiers = modulesToDom(list = list || [], options = options || {}); return function update(newList) { newList = newList || []; for (var i = 0; i < lastIdentifiers.length; i++) { var index = getIndexByIdentifier(lastIdentifiers[i]); stylesInDOM[index].references--; } for (var newLastIdentifiers = modulesToDom(newList, options), _i = 0; _i < lastIdentifiers.length; _i++) { var _index = getIndexByIdentifier(lastIdentifiers[_i]); if (0 === stylesInDOM[_index].references) { stylesInDOM[_index].updater(); stylesInDOM.splice(_index, 1); } } lastIdentifiers = newLastIdentifiers; }; }; }, 659: module => { var memo = {}; module.exports = function insertBySelector(insert, style) { var target = function getTarget(target) { if (void 0 === memo[target]) { var styleTarget = document.querySelector(target); if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) try { styleTarget = styleTarget.contentDocument.head; } catch (e) { styleTarget = null; } memo[target] = styleTarget; } return memo[target]; }(insert); if (!target) throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); target.appendChild(style); }; }, 540: module => { module.exports = function insertStyleElement(options) { var element = document.createElement("style"); options.setAttributes(element, options.attributes); options.insert(element, options.options); return element; }; }, 56: (module, __unused_webpack_exports, __webpack_require__) => { module.exports = function setAttributesWithoutAttributes(styleElement) { var nonce = true ? __webpack_require__.nc : 0; if (nonce) styleElement.setAttribute("nonce", nonce); }; }, 825: module => { module.exports = function domAPI(options) { if ("undefined" == typeof document) return { update: function update() {}, remove: function remove() {} }; var styleElement = options.insertStyleElement(options); return { update: function update(obj) { !function apply(styleElement, options, obj) { var css = ""; if (obj.supports) css += "@supports (".concat(obj.supports, ") {"); if (obj.media) css += "@media ".concat(obj.media, " {"); var needLayer = void 0 !== obj.layer; if (needLayer) css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {"); css += obj.css; if (needLayer) css += "}"; if (obj.media) css += "}"; if (obj.supports) css += "}"; var sourceMap = obj.sourceMap; if (sourceMap && "undefined" != typeof btoa) css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */"); options.styleTagTransform(css, styleElement, options.options); }(styleElement, options, obj); }, remove: function remove() { !function removeStyleElement(styleElement) { if (null === styleElement.parentNode) return false; styleElement.parentNode.removeChild(styleElement); }(styleElement); } }; }; }, 113: module => { module.exports = function styleTagTransform(css, styleElement) { if (styleElement.styleSheet) styleElement.styleSheet.cssText = css; else { for (;styleElement.firstChild; ) styleElement.removeChild(styleElement.firstChild); styleElement.appendChild(document.createTextNode(css)); } }; } }, __webpack_module_cache__ = {}; function __webpack_require__(moduleId) { var cachedModule = __webpack_module_cache__[moduleId]; if (void 0 !== cachedModule) return cachedModule.exports; var module = __webpack_module_cache__[moduleId] = { id: moduleId, exports: {} }; __webpack_modules__[moduleId](module, module.exports, __webpack_require__); return module.exports; } __webpack_require__.n = module => { var getter = module && module.__esModule ? () => module.default : () => module; __webpack_require__.d(getter, { a: getter }); return getter; }; __webpack_require__.d = (exports, definition) => { for (var key in definition) if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); }; __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); __webpack_require__.nc = void 0; class ZoomableImage { constructor(imgElem) { this._speed = .1; this._size = { w: 0, h: 0 }; this._position = { x: 0, y: 0 }; this._target = { x: 0, y: 0 }; this._pointer = { x: 0, y: 0 }; this._scale = 1; this._doInitPosition = true; this.imgElem = imgElem; } get zoomEnabled() { return "true" === this.imgElem.getAttribute("zoom-enabled"); } set zoomEnabled(value) { if (value) { this.imgElem.classList.add("zoomable-image"); this.imgElem.setAttribute("zoom-enabled", "true"); this.imgElem.addEventListener("wheel", this.onWheel.bind(this)); } else { this.imgElem.classList.remove("zoomable-image"); this.imgElem.setAttribute("zoom-enabled", "false"); this.imgElem.removeEventListener("wheel", this.onWheel.bind(this)); } } init() { this._container = this.imgElem.parentElement; this._size.w = this.imgElem.offsetWidth; this._size.h = this.imgElem.offsetHeight; this._position.x = (this._container.offsetWidth - this._size.w) / 2; this._position.y = (this._container.offsetHeight - this._size.h) / 2; this._target.x = 0; this._target.y = 0; this._pointer.x = 0; this._pointer.y = 0; this._scale = 1; } onWheel(event) { event.preventDefault(); if (this._doInitPosition) { this._doInitPosition = false; this.init(); } if (null == this._container) return; const rect = this.imgElem.getBoundingClientRect(), imageCenterX = rect.left + rect.width / 2, imageCenterY = rect.top + rect.height / 2; this._pointer.x = event.clientX - imageCenterX; this._pointer.y = event.clientY - imageCenterY; this._target.x = this._pointer.x / this._scale; this._target.y = this._pointer.y / this._scale; const prevScale = this._scale; this._scale += -1 * Math.max(-1, Math.min(1, event.deltaY)) * this._speed * this._scale; this._scale = Math.max(1, Math.min(4, this._scale)); const scaleFactor = this._scale / prevScale; this._position.x -= this._target.x * (scaleFactor - 1); this._position.y -= this._target.y * (scaleFactor - 1); const containerRect = this._container.getBoundingClientRect(), maxX = (containerRect.width - rect.width * this._scale) / 2, maxY = (containerRect.height - rect.height * this._scale) / 2; this._position.x = Math.min(Math.max(this._position.x, maxX), -maxX); this._position.y = Math.min(Math.max(this._position.y, maxY), -maxY); this.imgElem.style.transform = `translate(${this._position.x}px,${this._position.y}px) scale(${this._scale})`; } disconnectedCallback() { this.imgElem.removeEventListener("wheel", this.onWheel.bind(this)); } } class PanableImage extends ZoomableImage { constructor(imgElem) { super(imgElem); this._isDragging = false; this._hasMoved = false; this._startX = 0; this._startY = 0; this._lastTranslateX = 0; this._lastTranslateY = 0; this._prevTransition = ""; this.imgElem.draggable = false; this.imgElem.addEventListener("mousedown", this.onMouseDown.bind(this)); this.imgElem.addEventListener("mousemove", this.onMouseMove.bind(this)); this.imgElem.addEventListener("mouseup", this.onMouseUp.bind(this)); this.imgElem.addEventListener("mouseleave", this.onMouseUp.bind(this)); this.imgElem.addEventListener("click", this.onClick.bind(this)); this.imgElem.addEventListener("contextmenu", this.onClick.bind(this)); } get panEnabled() { return "true" === this.imgElem.getAttribute("pan-enabled"); } set panEnabled(value) { if (value) { this.imgElem.classList.add("panable-image"); this.imgElem.setAttribute("pan-enabled", "true"); } else { this.imgElem.classList.remove("panable-image"); this.imgElem.setAttribute("pan-enabled", "false"); } } get isDragging() { return this._isDragging; } set isDragging(value) { if (value !== this._isDragging) { this._isDragging = value; if (value) { this._prevTransition = this.imgElem.style.transition; this.imgElem.style.transition = "none"; this.imgElem.style.cursor = "grabbing"; this.zoomEnabled = false; } else { this.imgElem.style.transition = this._prevTransition; this.imgElem.style.cursor = "grab"; this.zoomEnabled = true; } } } onMouseDown(event) { if (this.panEnabled) { this.isDragging = true; this._hasMoved = false; this._startX = event.clientX - this._lastTranslateX; this._startY = event.clientY - this._lastTranslateY; } } onMouseMove(event) { if (!this.isDragging || !this.panEnabled) return; event.preventDefault(); const x = event.clientX - this._startX, y = event.clientY - this._startY; if (Math.abs(x - this._lastTranslateX) > 5 || Math.abs(y - this._lastTranslateY) > 5) this._hasMoved = true; this._lastTranslateX = x; this._lastTranslateY = y; const currentTransform = window.getComputedStyle(this.imgElem).transform, scale = new DOMMatrix(currentTransform).a; this.imgElem.style.transform = `translate(${x}px, ${y}px) scale(${scale})`; } onMouseUp() { this.isDragging = false; } onClick(event) { if (this._hasMoved) { event.preventDefault(); event.stopPropagation(); } } disconnectedCallback() { this.imgElem.removeEventListener("mousedown", this.onMouseDown.bind(this)); this.imgElem.removeEventListener("mousemove", this.onMouseMove.bind(this)); this.imgElem.removeEventListener("mouseup", this.onMouseUp.bind(this)); this.imgElem.removeEventListener("mouseleave", this.onMouseUp.bind(this)); this.imgElem.removeEventListener("click", this.onClick.bind(this)); this.imgElem.removeEventListener("contextmenu", this.onClick.bind(this)); } } class FAImage extends PanableImage { constructor(zoomEnabled = true, panEnabled = true) { super(document.createElement("img")); this.imgElem.classList.add("siv-fa-image"); this.imgElem.classList.add("blocked-content"); this.imgElem.draggable = false; this.zoomEnabled = zoomEnabled; this.panEnabled = panEnabled; } get dataFullviewSrc() { var _a; return null !== (_a = this.imgElem.getAttribute("data-fullview-src")) && void 0 !== _a ? _a : ""; } set dataFullviewSrc(value) { this.imgElem.setAttribute("data-fullview-src", value); } get dataPreviewSrc() { var _a; return null !== (_a = this.imgElem.getAttribute("data-preview-src")) && void 0 !== _a ? _a : ""; } set dataPreviewSrc(value) { if (null != value) this.imgElem.setAttribute("data-preview-src", value); } set src(value) { this.imgElem.src = value; this.dataFullviewSrc = value; } } function waitForCondition(condition) { return new Promise((resolve => { const check = () => { if (condition()) resolve(); else requestAnimationFrame(check); }; check(); })); } var injectStylesIntoStyleTag = __webpack_require__(72), injectStylesIntoStyleTag_default = __webpack_require__.n(injectStylesIntoStyleTag), styleDomAPI = __webpack_require__(825), styleDomAPI_default = __webpack_require__.n(styleDomAPI), insertBySelector = __webpack_require__(659), insertBySelector_default = __webpack_require__.n(insertBySelector), setAttributesWithoutAttributes = __webpack_require__(56), setAttributesWithoutAttributes_default = __webpack_require__.n(setAttributesWithoutAttributes), insertStyleElement = __webpack_require__(540), insertStyleElement_default = __webpack_require__.n(insertStyleElement), styleTagTransform = __webpack_require__(113), styleTagTransform_default = __webpack_require__.n(styleTagTransform), Style = __webpack_require__(656), options = {}; options.styleTagTransform = styleTagTransform_default(); options.setAttributes = setAttributesWithoutAttributes_default(); options.insert = insertBySelector_default().bind(null, "head"); options.domAPI = styleDomAPI_default(); options.insertStyleElement = insertStyleElement_default(); injectStylesIntoStyleTag_default()(Style.A, options); Style.A && Style.A.locals && Style.A.locals; function checkTags(element) { var _a; if (!("1" === document.body.getAttribute("data-user-logged-in"))) { setBlockedState(element, false); return; } const tagsHideMissingTags = "1" === document.body.getAttribute("data-tag-blocklist-hide-tagless"), tags = null === (_a = element.getAttribute("data-tags")) || void 0 === _a ? void 0 : _a.trim().split(/\s+/); let blockReason = ""; if (null != tags && tags.length > 0 && "" !== tags[0]) { const blockedTags = function getBannedTags(tags) { var _a; const tagsBlocklist = null !== (_a = document.body.getAttribute("data-tag-blocklist")) && void 0 !== _a ? _a : []; let bTags = []; if (null == tags || 0 === tags.length) return []; for (const tag of tags) for (const blockedTag of tagsBlocklist) if (tag === blockedTag) bTags.push(blockedTag); return [ ...new Set(bTags) ]; }(tags); if (blockedTags.length <= 0) setBlockedState(element, false); else { setBlockedState(element, true); blockReason = "Blocked tags:\n"; for (const tag of blockedTags) blockReason += "• " + tag + "\n"; } } else { setBlockedState(element, tagsHideMissingTags); if (tagsHideMissingTags) blockReason = "Content is missing tags."; } if ("" !== blockReason && "submissionImg" !== element.id) element.setAttribute("title", blockReason); } function setBlockedState(element, isBlocked) { element.classList[isBlocked ? "add" : "remove"]("blocked-content"); } var __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(r###lt) { r###lt.done ? resolve(r###lt.value) : function adopt(value) { return value instanceof P ? value : new P((function(resolve) { resolve(value); })); }(r###lt.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); })); }; class CustomImageViewer extends EventTarget { get onImageLoad() { return this._onImageLoad; } set onImageLoad(handler) { this._onImageLoad = handler; } get onImageLoadStart() { return this._onImageLoadStart; } set onImageLoadStart(handler) { this._onImageLoadStart = handler; } get onPreviewImageLoad() { return this._onPreviewImageLoad; } set onPreviewImageLoad(handler) { this._onPreviewImageLoad = handler; } constructor(parentContainer, imageUrl, previewUrl) { super(); Object.setPrototypeOf(this, CustomImageViewer.prototype); this.imageUrl = imageUrl; this.previewUrl = previewUrl; this.parentContainer = parentContainer; this.parentContainer.classList.add("siv-parent-container"); this.faImage = new FAImage; this.faImage.imgElem.classList.add("siv-image-main"); this.faImage.imgElem.addEventListener("load", this.faImageLoaded.bind(this)); this.faImagePreview = new FAImage; this.faImagePreview.imgElem.classList.add("siv-image-preview"); this._invisibleContainer = document.createElement("div"); this._invisibleContainer.classList.add("siv-image-container"); this._imageLoaded = false; this.reset(); } get imageLoaded() { return this._imageLoaded; } set imageLoaded(value) { if (this._imageLoaded !== value) { this._imageLoaded = value; if (value) this.invokeImageLoad(); } } reset() { var _a, _b; this.imageLoaded = false; null === (_a = this.faImage.imgElem.parentNode) || void 0 === _a || _a.removeChild(this.faImage.imgElem); null === (_b = this.faImagePreview.imgElem.parentNode) || void 0 === _b || _b.removeChild(this.faImagePreview.imgElem); this.faImage.src = this.imageUrl; this.faImage.dataPreviewSrc = this.previewUrl; if (null == this.previewUrl) this.faImagePreview.src = ""; else { this.faImagePreview.src = this.previewUrl; this.faImagePreview.imgElem.addEventListener("load", this.invokePreviewImageLoad.bind(this)); } } load() { return __awaiter(this, void 0, void 0, (function*() { this.reset(); checkTags(this.faImage.imgElem); this._invisibleContainer.appendChild(this.faImage.imgElem); document.body.appendChild(this._invisibleContainer); if (null != this.previewUrl && !this.imageLoaded) { checkTags(this.faImagePreview.imgElem); yield this.checkImageLoadStart(); } })); } checkImageLoadStart() { return __awaiter(this, void 0, void 0, (function*() { yield waitForCondition((() => 0 !== this.faImage.imgElem.offsetWidth)); this.faImagePreview.imgElem.style.width = this.faImage.imgElem.offsetWidth + "px"; this.faImagePreview.imgElem.style.height = this.faImage.imgElem.offsetHeight + "px"; if (!this.imageLoaded) { this.parentContainer.appendChild(this.faImagePreview.imgElem); const previewCondition = () => 0 !== this.faImagePreview.imgElem.offsetWidth; yield waitForCondition(previewCondition); this.invokeImageLoadStart(); } })); } faImageLoaded() { var _a, _b; null === (_a = this.faImagePreview.imgElem.parentNode) || void 0 === _a || _a.removeChild(this.faImagePreview.imgElem); this.parentContainer.appendChild(this.faImage.imgElem); null === (_b = this._invisibleContainer.parentNode) || void 0 === _b || _b.removeChild(this._invisibleContainer); this.imageLoaded = true; } invokeImageLoad() { var _a; null === (_a = this._onImageLoad) || void 0 === _a || _a.call(this); this.dispatchEvent(new Event("image-load")); } invokeImageLoadStart() { var _a; null === (_a = this._onImageLoadStart) || void 0 === _a || _a.call(this); this.dispatchEvent(new Event("image-load-start")); } invokePreviewImageLoad() { var _a; null === (_a = this._onPreviewImageLoad) || void 0 === _a || _a.call(this); this.dispatchEvent(new Event("preview-image-load")); } } Object.defineProperties(window, { FAImageViewer: { get: () => CustomImageViewer } }); })();