// ==UserScript==
// @name 4chan Image Viewer
// @namespace IdontKnowWhatToDoWithThis
// @description Opens current thread Images in 4chan into a popup viewer, tested in Tampermonkey
// @match *://*.4chan.org/*/res/*
// @match *://*.4chan.org/*/thread/*
// @match *://*.4channel.org/*/thread/*
// @version 8.4
// @copyright 2019+, Nicholas Perkins
// @source https://github.com/nicholas-s-perkins/4chanImageViewer
// @downloadURL https://update.greasyfork.icu/scripts/1275/4chan%20Image%20Viewer.user.js
// @updateURL https://update.greasyfork.icu/scripts/1275/4chan%20Image%20Viewer.meta.js
// ==/UserScript==
"use strict";
var Viewer;
(function (Viewer) {
/**
* Didn't want to use any external libraries. This is my handy library for dealing with the DOM
*/
var DomUtil = /** @class */ (function () {
function DomUtil(obj) {
this._elements = [];
this._listeners = [];
if (obj) {
if (obj instanceof NodeList) {
for (var i = 0; i < obj.length; ++i) {
this._elements.push(obj[i]);
}
}
else {
this._elements.push(obj);
}
}
}
Object.defineProperty(DomUtil.prototype, "elementList", {
get: function () {
return this._elements;
},
enumerable: true,
configurable: true
});
DomUtil.prototype.concat = function (collection) {
if (collection instanceof DomUtil) {
this._elements = this._elements.concat(collection._elements);
}
else {
this._elements = this._elements.concat(DomUtil.formatNodeList(collection));
}
return this;
};
/** Adds a click handler */
DomUtil.prototype.on = function (handler, func) {
var _this = this;
var handlers = handler.split(' ');
this.each(function (element) {
for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
var handler_1 = handlers_1[_i];
_this._listeners.push(new Listener(element, handler_1, func));
element.addEventListener(handler_1, func, false);
}
});
return this;
};
DomUtil.prototype.appendTo = function (obj) {
if (typeof obj === 'string') {
DomUtil.get(obj).append(this);
}
else if (obj instanceof DomUtil) {
obj.append(this);
}
else {
new DomUtil(obj).append(this);
}
return this;
};
DomUtil.prototype.off = function (handlerType) {
var remaining = [];
for (var _i = 0, _a = this._listeners; _i < _a.length; _i++) {
var listener = _a[_i];
if (handlerType == null || listener.type === handlerType) {
listener.element.removeEventListener(listener.type, listener.func);
}
else {
remaining.push(listener);
}
}
this._listeners = remaining;
return this;
};
DomUtil.prototype.remove = function () {
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
if (element.parentElement) {
element.parentElement.removeChild(element);
}
}
return this;
};
DomUtil.prototype.prepend = function (obj) {
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var thisElement = _a[_i];
for (var _b = 0, _c = obj._elements; _b < _c.length; _b++) {
var objElement = _c[_b];
if (thisElement.parentElement) {
thisElement.parentElement.insertBefore(objElement, thisElement);
}
}
}
return this;
};
DomUtil.prototype.append = function (obj) {
if (typeof obj === 'string') {
this.each(function (element) {
element.insertAdjacentHTML('beforeend', obj);
});
}
else if (obj instanceof DomUtil) {
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
for (var _b = 0, _c = obj._elements; _b < _c.length; _b++) {
var objEle = _c[_b];
element.appendChild(objEle);
}
}
}
else {
for (var _d = 0, _e = this._elements; _d < _e.length; _d++) {
var element = _e[_d];
element.appendChild(obj);
}
}
return this;
};
DomUtil.prototype.empty = function () {
this.each(function (element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
});
return this;
};
DomUtil.prototype.scrollToTop = function () {
if (this._elements.length > 0) {
this._elements[0].scrollTop = 0;
}
return this;
};
DomUtil.prototype.focus = function () {
if (this._elements.length > 0) {
this._elements[0].focus();
}
return this;
};
Object.defineProperty(DomUtil.prototype, "tabIndex", {
set: function (index) {
if (this._elements.length > 0) {
this._elements[0].tabIndex = index;
}
},
enumerable: true,
configurable: true
});
DomUtil.prototype.setAttr = function (attr, value) {
this.each(function (element) {
element[attr] = value;
});
return this;
};
DomUtil.prototype.setText = function (text) {
this.each(function (element) { return element.innerText = "" + text; });
return this;
};
DomUtil.prototype.setStyle = function (styleConfig) {
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
for (var propName in styleConfig) {
// @ts-ignore
element.style[propName] = styleConfig[propName];
}
}
return this;
};
DomUtil.prototype.setData = function (data) {
var _loop_1 = function (element) {
Object.keys(data).forEach(function (propName) {
element.dataset[propName] = data[propName];
});
};
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
_loop_1(element);
}
return this;
};
DomUtil.prototype.replaceWith = function (replacement) {
var replaceEle = replacement._elements;
this.each(function (element) {
if (element.parentElement) {
for (var i = replaceEle.length - 1; i >= 0; i--) {
element.parentElement.insertBefore(replaceEle[i], element);
}
element.parentElement.removeChild(element);
}
});
return this;
};
DomUtil.prototype.html = function (html) {
if (typeof html === 'string') {
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
element.innerHTML = html;
}
}
else {
this.each(function (element) {
DomUtil.get(element).remove();
});
this.append(html);
}
return this;
};
Object.defineProperty(DomUtil.prototype, "length", {
get: function () {
return this._elements.length;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "id", {
get: function () {
return this._elements.length > 0 ? this._elements[0].id : null;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "clientHeight", {
get: function () {
return this._elements.length > 0 ? this._elements[0].clientHeight : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "clientWidth", {
get: function () {
return this._elements.length > 0 ? this._elements[0].clientWidth : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "offsetHeight", {
get: function () {
return this._elements.length > 0 ? this._elements[0].offsetHeight : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "offsetWidth", {
get: function () {
return this._elements.length > 0 ? this._elements[0].offsetWidth : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(DomUtil.prototype, "tagName", {
get: function () {
return this._elements.length > 0 ? this._elements[0].tagName : null;
},
enumerable: true,
configurable: true
});
DomUtil.prototype.hasClass = function (className) {
return this._elements.length > 0 ? this._elements[0].classList.contains(className) : false;
};
DomUtil.prototype.getAttr = function (attr) {
if (this._elements.length > 0) {
var ele = this._elements[0];
return ele[attr];
}
else {
return null;
}
};
DomUtil.prototype.lightClone = function () {
var newCollection = new DomUtil();
this.each(function (element) {
var newEle = document.createElement(element.tagName);
newEle.className = element.className;
newEle.innerHTML = element.innerHTML;
newCollection._elements.push(newEle);
});
return newCollection;
};
DomUtil.prototype.addClass = function () {
var classNames = [];
for (var _i = 0; _i < arguments.length; _i++) {
classNames[_i] = arguments[_i];
}
this.each(function (element) {
element.classList.add.apply(element.classList, classNames);
});
return this;
};
DomUtil.prototype.removeClass = function () {
var classNames = [];
for (var _i = 0; _i < arguments.length; _i++) {
classNames[_i] = arguments[_i];
}
this.each(function (element) {
element.classList.remove.apply(element.classList, classNames);
});
return this;
};
DomUtil.prototype.each = function (func) {
for (var i = 0; i < this._elements.length; ++i) {
func(this._elements[i], i);
}
return this;
};
/** Finds all sub-elements matching the queryString */
DomUtil.prototype.find = function (queryString) {
var collection = new DomUtil();
for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
var element = _a[_i];
collection.concat(element.querySelectorAll(queryString));
}
return collection;
};
Object.defineProperty(DomUtil.prototype, "exists", {
get: function () {
return this._elements.length > 0;
},
enumerable: true,
configurable: true
});
/** because screw node lists */
DomUtil.formatNodeList = function (nodes) {
var arr = [];
for (var i = 0; i < nodes.length; ++i) {
arr.push(nodes[i]);
}
return arr;
};
DomUtil.get = function (query) {
if (typeof query === 'string') {
switch (query) {
case 'body':
return new DomUtil(document.body);
case 'head':
return new DomUtil(document.head);
default:
var nodes = document.querySelectorAll(query);
return new DomUtil(nodes);
}
}
else {
return new DomUtil(query);
}
};
DomUtil.getById = function (id) {
var ele = document.getElementById(id);
return new DomUtil(ele);
};
DomUtil.createElement = function (tagName, props) {
var newEle = document.createElement(tagName);
if (props) {
Object.keys(props).forEach(function (propName) {
if (propName == "style") {
newEle.style.cssText = props.style.cssText;
}
else {
newEle[propName] = props[propName];
}
});
}
return new DomUtil(newEle);
};
return DomUtil;
}());
Viewer.DomUtil = DomUtil;
var Listener = /** @class */ (function () {
function Listener(element, type, func) {
this.type = type;
this.func = func;
this.element = element;
}
return Listener;
}());
Viewer.Listener = Listener;
})(Viewer || (Viewer = {}));
var Viewer;
(function (Viewer) {
//IDs for important elements
Viewer.VIEW_ID = "mainView";
Viewer.IMG_ID = "mainImg";
Viewer.CENTER_BOX_ID = "imageBox";
Viewer.TOP_LAYER_ID = "viewerTopLayer";
Viewer.IMG_WRAPPER_ID = 'mainImgWrapper';
Viewer.TEXT_WRAPPER_ID = 'viewerTextWrapper';
Viewer.STYLE_ID = 'viewerStyle';
Viewer.MENU_ID = 'viewerBottomMenu';
Viewer.LEFT_ARROW = 'previousImageButton';
Viewer.RIGHT_ARROW = 'nextImageButton';
Viewer.TOP_MENU_ID = 'viewerMenuHeader';
Viewer.VIEWER_PAGE_DISPLAY = "viewerPageDisplay";
Viewer.VIEWER_TOTAL_DISPLAY = "viewerTotalDisplay";
Viewer.VIEWER_IMG_NAME_DISPLAY = "viewerNameDisplay";
Viewer.STYLE_TEXT = "\n div.reply.highlight,div.reply.highlight-anti{z-index:100 !important;position:fixed !important; top:1%;left:1%;}\n body{overflow:hidden !important;}\n #quote-preview{z-index:100;}\n a.quotelink, div.viewerBacklinks a.quotelink{color:#5c5cff !important;}\n a.quotelink:hover, div.viewerBacklinks a:hover{color:red !important;}\n #" + Viewer.IMG_ID + "{display:block !important; margin:auto;max-width:100%;height:auto;-webkit-user-select: none;cursor:pointer;}\n #" + Viewer.VIEW_ID + "{\n background-color:rgba(0,0,0,0.9);\n z-index:10;\n position:fixed;\n top:0;left:0;bottom:0;right:0;\n overflow:auto;\n text-align:center;\n -webkit-user-select: none;\n }\n #" + Viewer.CENTER_BOX_ID + " {display:flex;align-items:center;justify-content:center;flex-direction: column;min-height:100%;}\n #" + Viewer.IMG_WRAPPER_ID + " {width:100%;}\n #" + Viewer.TOP_LAYER_ID + "{position:fixed;top:0;bottom:0;left:0;right:0;z-index:20;opacity:0;visibility:hidden;transition:all .25s ease;}\n .viewerBlockQuote{color:white;}\n #" + Viewer.TEXT_WRAPPER_ID + "{max-width:60em;display:inline-block; color:gray;-webkit-user-select: all;}\n .bottomMenuShow{visibility:visible;}\n #" + Viewer.MENU_ID + "{box-shadow: -1px -1px 5px #888888;font-size:20px;padding:5px;background-color:white;position:fixed;bottom:0;right:0;z-index:200;}\n #" + Viewer.TOP_MENU_ID + "{font-size:20px;padding:5px;background-color:white;position:fixed;top:0;left:0;text-align:center;width:100%;color:black;z-index:200;}\n .hideCursor{cursor:none !important;}\n .hidden{visibility:hidden}\n .displayNone{display:none;}\n .pagingButtons{font-size:100px;color:white;text-shadow: 1px 1px 10px #27E3EB;z-index: 11;top: 50%;position: fixed;margin-top: -57px;width:100px;cursor:pointer;-webkit-user-select: none;}\n .pagingButtons:hover{color:#27E3EB;text-shadow: 1px 1px 10px #000}\n #" + Viewer.LEFT_ARROW + "{left:0;text-align:left;}\n #" + Viewer.RIGHT_ARROW + "{right:0;text-align:right;}\n @-webkit-keyframes flashAnimation{0%{ text-shadow: none;}100%{text-shadow: 0px 0px 5px blue;}}\n .flash{-webkit-animation: flashAnimation 1s alternate infinite linear;cursor:pointer;}\n .disableClick, .disableClick a{pointer-events: none;}\n ";
})(Viewer || (Viewer = {}));
var Viewer;
(function (Viewer) {
//cookieInfo
var INDEX_KEY = "imageBrowserIndexCookie";
var THREAD_KEY = "imageBrowserThreadCookie";
var WIDTH_KEY = "imageBrowserWidthCookie";
var HEIGHT_KEY = "imageBrowserHeightCookie";
//keycode object. Better than remembering what each code does.
var KEYS = { 38: 'up', 40: 'down', 37: 'left', 39: 'right', 27: 'esc', 86: 'v' };
var BODY = Viewer.DomUtil.get(document.body);
var WINDOW = Viewer.DomUtil.get(window);
var UNSAFE_WINDOW = Viewer.DomUtil.get(typeof unsafeWindow === 'undefined' ? window : unsafeWindow);
var MainView = /** @class */ (function () {
function MainView(imagePostIndex) {
var _this = this;
this.postData = [];
this.linkIndex = 0;
/** Determines if pre-loading can happen*/
this.canPreload = false;
/** determines if height of the image should be fit */
this.shouldFitHeight = false;
this.lastMousePos = { x: 0, y: 0 };
console.log("Building 4chan Image Viewer");
var currentThreadId = Viewer.DomUtil.get('.thread').id;
if (imagePostIndex != undefined) {
this.linkIndex = imagePostIndex;
MainView.setPersistentValue(INDEX_KEY, imagePostIndex);
}
//check if its the last thread opened, if so, remember where the index was.
else if (MainView.getPersistentValue(THREAD_KEY) === currentThreadId) {
var savedVal = MainView.getPersistentValue(INDEX_KEY);
if (savedVal != undefined) {
this.linkIndex = parseInt(savedVal);
}
else {
this.linkIndex = 0;
}
}
else {
this.linkIndex = 0;
MainView.setPersistentValue(INDEX_KEY, 0);
}
//set thread id
MainView.setPersistentValue(THREAD_KEY, currentThreadId);
//Create postData based on 4chan posts
this.postData = Viewer.PostData.getImagePosts(true);
if (this.linkIndex > (this.postData.length - 1)) {
alert('Last saved image index is too large, a thread may have been deleted. Index will be reset. ');
this.linkIndex = 0;
MainView.setPersistentValue(INDEX_KEY, 0);
}
//set shouldFit Height so image can know about it if it loads before menuInit()
var isHeight = MainView.getPersistentValue(HEIGHT_KEY);
this.shouldFitHeight = isHeight ? true : false;
var menuHtml = "\n \n |\n \n ";
var viewFrag = "\n \n
\n \n
\n
\n
![]()
\n
\n
\n
\n
〈
\n
〉
\n
\n
\n \n ";
BODY.append(viewFrag);
this.mainView = Viewer.DomUtil.getById(Viewer.VIEW_ID);
this.centerBox = Viewer.DomUtil.getById(Viewer.CENTER_BOX_ID);
this.mainImg = Viewer.DomUtil.getById(Viewer.IMG_ID);
this.textWrapper = Viewer.DomUtil.getById(Viewer.TEXT_WRAPPER_ID);
this.topLayer = Viewer.DomUtil.getById(Viewer.TOP_LAYER_ID);
this.customStyle = Viewer.DomUtil.getById(Viewer.STYLE_ID);
this.bottomMenu = Viewer.DomUtil.getById(Viewer.MENU_ID);
this.leftArrow = Viewer.DomUtil.getById(Viewer.LEFT_ARROW);
this.rightArrow = Viewer.DomUtil.getById(Viewer.RIGHT_ARROW);
this.topMenu = Viewer.DomUtil.getById(Viewer.TOP_MENU_ID);
this.pageDisplay = Viewer.DomUtil.getById(Viewer.VIEWER_PAGE_DISPLAY);
this.totalDisplay = Viewer.DomUtil.getById(Viewer.VIEWER_TOTAL_DISPLAY);
this.nameDisplay = Viewer.DomUtil.getById(Viewer.VIEWER_IMG_NAME_DISPLAY);
//add handlers
this.centerBox.on('click', function () {
_this.confirmExit();
});
this.textWrapper.on('click', function (event) {
_this.eventStopper(event);
});
this.bottomMenu.on('click', function () {
_this.menuClickHandler();
});
this.leftArrow.on('click', function (event) {
event.stopImmediatePropagation();
_this.previousImg();
});
this.rightArrow.on('click', function (event) {
event.stopImmediatePropagation();
_this.nextImg();
});
//build first image/video tag
this.changeData(0);
//initialize menu
this.menuInit();
//start preloading to next image index
this.canPreload = true;
window.setTimeout(function () {
_this.runImagePreloading(_this.linkIndex);
}, 100);
//some fixes for weird browser behaviors
this.centerBox.setStyle({ outline: '0' });
this.centerBox.tabIndex = 1;
this.centerBox.focus();
//add keybinding listener, unsafeWindow is used here instead because at least in Tampermonkey
//the safe window can fail to remove event listeners.
UNSAFE_WINDOW
.on('keydown', function (event) {
_this.arrowKeyListener(event);
})
.on('mousemove', function (event) {
_this.menuWatcher(event);
});
}
MainView.prototype.menuInit = function () {
var _this = this;
var menuControls = this.bottomMenu.find('input');
menuControls.each(function (input) {
var typedInput = input;
var cookieValue = MainView.getPersistentValue(input.id);
if (cookieValue === 'true') {
typedInput.checked = true;
}
else if (cookieValue === 'false') {
typedInput.checked = false;
}
typedInput.parentElement.classList.toggle('flash', typedInput.checked);
switch (typedInput.id) {
case WIDTH_KEY:
_this.setFitToScreenWidth(typedInput.checked);
break;
case HEIGHT_KEY:
_this.setFitToScreenHeight(typedInput.checked);
break;
}
});
};
MainView.prototype.menuClickHandler = function () {
var _this = this;
var menuControls = this.bottomMenu.find('input');
menuControls.each(function (ele) {
var input = ele;
switch (input.id) {
case WIDTH_KEY:
_this.setFitToScreenWidth(input.checked);
break;
case HEIGHT_KEY:
_this.setFitToScreenHeight(input.checked);
break;
}
input.parentElement.classList.toggle('flash', input.checked);
MainView.setPersistentValue(input.id, input.checked);
});
};
MainView.prototype.windowClick = function (event) {
if (!this) {
return;
}
event.preventDefault();
event.stopImmediatePropagation();
this.nextImg();
};
/* Event function for determining behavior of viewer keypresses */
MainView.prototype.arrowKeyListener = function (event) {
switch (KEYS[event.keyCode]) {
case 'right':
this.nextImg();
break;
case 'left':
this.previousImg();
break;
case 'esc':
this.destroy();
break;
}
};
/* preloads images starting with the index provided */
MainView.prototype.runImagePreloading = function (index) {
var _this = this;
if (this && index < this.postData.length) {
if (this.canPreload) {
//console.log('preloading: ' + index +' of '+(this.postData.length - 1) +' | '+ this.postData[index].imgSrc);
var loadFunc = function () {
_this.runImagePreloading(index + 1);
};
//have yet to figure out how to properly preload video, skip for now
if (this.postData[index].tagType === Viewer.TagType.VIDEO) {
window.setTimeout(loadFunc, 1);
}
else {
var newImage = document.createElement(this.postData[index].tagTypeName);
switch (this.postData[index].tagType) {
case Viewer.TagType.VIDEO:
newImage.oncanplaythrough = loadFunc;
break;
case Viewer.TagType.IMG:
newImage.onload = loadFunc;
break;
}
newImage.onerror = function () {
console.log("imageError");
_this.runImagePreloading(index + 1);
};
newImage.src = this.postData[index].imgSrc;
}
}
}
};
/* Sets the img and message to the next one in the list*/
MainView.prototype.nextImg = function () {
var _this = this;
if (this.linkIndex === this.postData.length - 1) {
this.topLayer.setStyle({
background: 'linear-gradient(to right,rgba(0,0,0,0) 90%,rgba(125,185,232,1) 100%)',
opacity: '.5',
visibility: 'visible'
});
window.setTimeout(function () {
_this.topLayer.setStyle({
opacity: '0',
visibility: 'hidden'
});
}, 500);
}
else {
this.changeData(1);
}
};
/* Sets the img and message to the previous one in the list*/
MainView.prototype.previousImg = function () {
var _this = this;
if (this.linkIndex === 0) {
this.topLayer.setStyle({
background: 'linear-gradient(to left,rgba(0,0,0,0) 90%,rgba(125,185,232,1) 100%)',
opacity: '.5',
visibility: 'visible'
});
window.setTimeout(function () {
_this.topLayer.setStyle({ opacity: '0' });
window.setTimeout(function () {
_this.topLayer.setStyle({ visibility: 'hidden' });
}, 200);
}, 500);
}
else {
this.changeData(-1);
}
};
MainView.prototype.changeData = function (delta) {
MainView.cleanLinks();
//ignore out of bounds
var newIndex = this.linkIndex + delta;
if (newIndex > this.postData.length - 1 || newIndex < 0) {
return;
}
if (this.postData[newIndex].tagTypeName !== this.mainImg.tagName || delta === 0) {
this.mainImg = this.replaceElement(this.mainImg, this.postData[newIndex].tagTypeName);
}
//console.log('Opening: "' + this.postData[this.linkIndex].imgSrc +'" at index ' + this.linkIndex);
this.mainImg.setAttr('src', this.postData[newIndex].imgSrc);
var nextLinks = this.postData[newIndex].linksContainer;
var nextQuote = this.postData[newIndex].quoteContainer;
this.textWrapper.empty();
this.textWrapper.append(nextLinks);
this.textWrapper.append(nextQuote);
this.linkIndex = newIndex;
this.mainView.scrollToTop();
MainView.setPersistentValue(INDEX_KEY, this.linkIndex);
//update menu info
this.pageDisplay.setText(this.linkIndex + 1);
this.totalDisplay.setText(this.postData.length);
this.nameDisplay.setText(this.postData[newIndex].imgSrc);
};
MainView.cleanLinks = function () {
var links = document.getElementsByClassName('quotelink');
for (var i = 0; i < links.length; ++i) {
links[i].dispatchEvent(new MouseEvent('mouseout'));
}
};
MainView.prototype.replaceElement = function (element, newTagType) {
var _this = this;
var rawElement = element.elementList[0];
var newElement = Viewer.DomUtil.createElement(newTagType, {
id: element.id,
className: rawElement.className,
style: rawElement.style,
autoplay: true,
controls: false,
loop: true
});
newElement
.on('click', function (event) {
event.stopPropagation();
_this.nextImg();
})
.on('load', function () {
_this.imageLoadHandler();
})
.on('progress', function (e) {
//console.log(e);
});
element.prepend(newElement);
element.remove();
return newElement;
};
MainView.prototype.eventStopper = function (event) {
event.stopPropagation();
if (event.target.nodeName === 'A') {
var confirmed = this.confirmExit('Exit Viewer to navigate to link?');
if (!confirmed) {
event.preventDefault();
}
}
};
MainView.prototype.confirmExit = function (message) {
var confirmed = window.confirm(message || 'Exit Viewer?');
if (confirmed) {
this.destroy();
}
return confirmed;
};
/* Removes the view and cleans up handlers*/
MainView.prototype.destroy = function () {
MainView.cleanLinks();
UNSAFE_WINDOW.off();
WINDOW.off();
BODY.off();
this.topLayer.remove();
this.mainView.remove();
this.customStyle.remove();
this.bottomMenu.remove();
BODY.setStyle({ overflow: 'auto' });
this.canPreload = false;
};
/*Mouse-move Handler that watches for when menus should appear and mouse behavior*/
MainView.prototype.menuWatcher = function (event) {
var _this = this;
var height_offset = window.innerHeight - this.bottomMenu.offsetHeight;
var width_offset = window.innerWidth - this.bottomMenu.offsetWidth;
var center = window.innerHeight / 2;
var halfArrow = this.leftArrow.offsetHeight / 2;
if (event.clientX >= width_offset && event.clientY >= height_offset) {
this.bottomMenu.removeClass('hidden').addClass('bottomMenuShow');
this.topMenu.removeClass('hidden').addClass('bottomMenuShow');
}
else if (this.bottomMenu.hasClass('bottomMenuShow')) {
this.bottomMenu.removeClass('bottomMenuShow').addClass('hidden');
this.topMenu.removeClass('bottomMenuShow').addClass('hidden');
}
if ((event.clientX <= (100) || event.clientX >= (window.innerWidth - 100)) &&
(event.clientY <= (center + halfArrow) && event.clientY >= (center - halfArrow))) {
this.rightArrow.removeClass('hidden');
this.leftArrow.removeClass('hidden');
}
else {
this.rightArrow.addClass('hidden');
this.leftArrow.addClass('hidden');
}
//avoids chrome treating mouseclicks as mousemoves
if (event.clientX !== this.lastMousePos.x && event.clientY !== this.lastMousePos.y) {
//mouse click moves to next image when invisible
this.mainImg.removeClass('hideCursor');
window.clearTimeout(this.mouseTimer);
BODY.off('click');
BODY.removeClass('hideCursor');
this.textWrapper.removeClass('disableClick');
this.mainImg.removeClass('disableClick');
this.centerBox.removeClass('disableClick');
if (event.target.id === this.mainImg.id) {
//hide cursor if it stops, show if it moves
this.mouseTimer = window.setTimeout(function () {
_this.mainImg.addClass('hideCursor');
_this.textWrapper.addClass('disableClick');
_this.mainImg.addClass('disableClick');
_this.centerBox.addClass('disableClick');
BODY.addClass('hideCursor')
.on('click', function (event) {
_this.windowClick(event);
});
}, 200);
}
}
this.lastMousePos.x = event.clientX;
this.lastMousePos.y = event.clientY;
};
/*Stores a key value pair as a cookie*/
MainView.setPersistentValue = function (key, value) {
document.cookie = key + '=' + value + ';expires=Thu, 01 Jan 3000 00:00:00 UTC;domain=.4chan.org;path=/';
};
/* Retrieves a cookie value via its key*/
MainView.getPersistentValue = function (key) {
var cookieMatch = document.cookie.match(new RegExp(key + '\\s*=\\s*([^;]+)'));
if (cookieMatch) {
return cookieMatch[1];
}
else {
return undefined;
}
};
MainView.prototype.setFitToScreenHeight = function (shouldFitImage) {
this.shouldFitHeight = shouldFitImage;
//ignore if image has no height as it is likely not loaded.
if (shouldFitImage && this.mainImg.getAttr('naturalHeight')) {
this.fitHeightToScreen();
}
else {
this.mainImg.setStyle({ maxHeight: '' });
}
};
;
MainView.prototype.setFitToScreenWidth = function (shouldFitImage) {
this.mainImg.setStyle({
maxWidth: shouldFitImage ? '100%' : 'none'
});
};
MainView.prototype.imageLoadHandler = function () {
if (this.shouldFitHeight) {
this.fitHeightToScreen();
}
};
/* Fits image to screen height*/
MainView.prototype.fitHeightToScreen = function () {
//sets the changeable properties to the image's real size
var height = this.mainImg.getAttr('naturalHeight');
this.mainImg.setStyle({ maxHeight: (height + 'px') });
//actually tests if it is too high including padding
var heightDiff = (this.mainImg.clientHeight > height) ?
this.mainImg.clientHeight - this.mainView.clientHeight :
height - this.mainView.clientHeight;
if (heightDiff > 0) {
this.mainImg.setStyle({ maxHeight: (height - heightDiff) + 'px' });
}
else {
this.mainImg.setStyle({ maxHeight: (height + 'px') });
}
};
return MainView;
}());
Viewer.MainView = MainView;
})(Viewer || (Viewer = {}));
var Viewer;
(function (Viewer) {
var TagType;
(function (TagType) {
TagType[TagType["IMG"] = 0] = "IMG";
TagType[TagType["VIDEO"] = 1] = "VIDEO";
})(TagType = Viewer.TagType || (Viewer.TagType = {}));
var PostData = /** @class */ (function () {
function PostData(imgSrc, quoteContainer, linksContainer, imageLink) {
this.imgSrc = imgSrc;
this.linksContainer = linksContainer;
this.quoteContainer = quoteContainer;
this.tagType = PostData.getElementType(imgSrc);
this.imageLink = imageLink;
}
Object.defineProperty(PostData.prototype, "tagTypeName", {
get: function () {
return TagType[this.tagType];
},
enumerable: true,
configurable: true
});
PostData.getElementType = function (src) {
if (src.match(/\.(?:(?:webm)|(?:ogg)|(?:mp4))$/)) {
return TagType.VIDEO;
}
else {
return TagType.IMG;
}
};
PostData.add4chanListenersToLinks = function (linkCollection) {
linkCollection.find('.quotelink')
.on('mouseover', Main.onThreadMouseOver)
.on('mouseout', Main.onThreadMouseOut);
};
PostData.getImagePosts = function (asCopy) {
var postData = [];
var postFiles = Viewer.DomUtil.get('#delform').find('.postContainer');
postFiles.each(function (post) {
var _post = Viewer.DomUtil.get(post);
var currentLinkTag = _post.find('.file .fileThumb');
var currentLink = currentLinkTag.getAttr('href');
if (!currentLink) {
return;
}
var currentPostBlock = _post.find('.postMessage');
var currentPostBacklinks = _post.find('.backlink');
var newPostBlock = currentPostBlock;
var newBackLinks = currentPostBacklinks;
if (asCopy) {
if (currentPostBlock.exists) {
newPostBlock = currentPostBlock.lightClone();
newPostBlock.addClass('viewerBlockQuote');
PostData.add4chanListenersToLinks(newPostBlock);
}
if (currentPostBacklinks.exists) {
newBackLinks = currentPostBacklinks.lightClone();
newBackLinks.addClass('viewerBacklinks');
PostData.add4chanListenersToLinks(newBackLinks);
}
}
postData.push(new PostData(currentLink, newPostBlock, newBackLinks, currentLinkTag));
});
return postData;
};
return PostData;
}());
Viewer.PostData = PostData;
})(Viewer || (Viewer = {}));
///
///
///
///
///
var Viewer;
(function (Viewer) {
function main() {
// ========= Build the main Button ========= //
Viewer.DomUtil.createElement('button')
.setStyle({ position: 'fixed', bottom: '0', right: '0', })
.html("Open Viewer")
.on('click', function () {
new Viewer.MainView();
})
.appendTo(document.body);
// ========= Build buttons for each image thumbnail ========= //
var posts = Viewer.PostData.getImagePosts(false);
var imagePostCount = 0;
for (var _i = 0, posts_1 = posts; _i < posts_1.length; _i++) {
var post = posts_1[_i];
Viewer.DomUtil.createElement('button')
.setStyle({
display: 'inline',
float: 'left',
clear: 'both',
fontSize: '11px',
cursor: 'pointer'
})
.setData({
postIndex: imagePostCount
})
.html('Open Viewer')
.on('click', function (e) {
e.preventDefault();
e.stopPropagation();
//make the viewer and put it on the window so we can clean it up later
new Viewer.MainView(parseInt(this.dataset.postIndex));
})
.appendTo(post.imageLink);
++imagePostCount;
}
}
Viewer.main = main;
})(Viewer || (Viewer = {}));
//run the module
Viewer.main();
//# sourceMappingURL=viewer.js.map