Merge branch 'v3'
Conflicts: .gitignore CHANGELOG.md CONTRIBUTING.md LICENSE builds/appchan-x.user.js builds/crx/manifest.json builds/crx/script.js latest.js package.json src/General/Header.coffee src/General/Index.coffee src/General/Settings.coffee src/General/UI.coffee src/General/html/Settings/Settings.html src/Images/Gallery.coffee src/Images/ImageExpand.coffee src/Monitoring/ThreadWatcher.coffee
This commit is contained in:
commit
ea47aefd93
7
.gitignore
vendored
7
.gitignore
vendored
@ -3,6 +3,11 @@ node_modules/
|
||||
*.db
|
||||
tmp-crx/
|
||||
tmp-userscript/
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
testbuilds/
|
||||
builds/4chan-X.zip
|
||||
>>>>>>> v3
|
||||
Gruntfile.js
|
||||
builds/4chan-*
|
||||
Gruntfile.js
|
||||
Gruntfile.js
|
||||
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,3 +1,26 @@
|
||||
|
||||
**ccd0**
|
||||
- `Loop in New Tab` (enabled by default) causes videos opened in a separate tab to loop, and applies your settings for inline expanded videos to them.
|
||||
- Add WebM support to gallery (currently no controls).
|
||||
- Add PDF support to gallery, disabled by default. Enable with `PDF in Gallery`.
|
||||
- Fix behavior of .webm videos expanded within inline quotes.
|
||||
- Contract thumbnails in quoted previews to avoid crashes caused by videos in quoted previews on some systems.
|
||||
- Change interface when both `Autoplay` and `Show Controls` are unchecked. In this mode, videos are now activated by clicking on them. The first click expands the video, the second click plays the video, and the third click contracts it.
|
||||
- Add item `Expand videos` in `Image Expansion` menu, which enables expansion of videos by `Expand All Images`. Disabled by default. Previously videos were expanded.
|
||||
- Disable autoplay for videos expanded by `Expand All Images`.
|
||||
- Fix minimum width bug.
|
||||
|
||||
**fgts**
|
||||
- Update archive list.
|
||||
|
||||
**Nebukazar**
|
||||
- `Quote Threading` disabled by default
|
||||
- Added missing titles to Header icons
|
||||
|
||||
**Zixaphir**:
|
||||
- WebM Thumbnail Replacement. Use at your own risk.
|
||||
- Bugfixes.
|
||||
|
||||
### v2.9.15
|
||||
*2014-04-05*
|
||||
|
||||
@ -187,8 +210,8 @@
|
||||
**Spittie**
|
||||
- Check image dimension before uploading
|
||||
|
||||
**Vampiricwulf**
|
||||
- Flash embedding and other Flash features.
|
||||
## v1.7.0
|
||||
*2014-04-06*
|
||||
|
||||
**Zixaphir**
|
||||
- Update Custom Navigation legend to reflect index mode changes.
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
## Reporting bugs and suggestions
|
||||
|
||||
<<<<<<< HEAD
|
||||
Reporting bugs: (note that some of these links refer to Mayhem's 4chan X repo to avoid duplication of information resources. Bugs MUST be filed on [our issue tracker](https://github.com/zixaphir/appchan-x/issues), not Mayhem's)
|
||||
|
||||
1. Make sure both your **browser** and **Appchan X** are up to date.<br>
|
||||
@ -12,6 +13,19 @@ Reporting bugs: (note that some of these links refer to Mayhem's 4chan X repo to
|
||||
1. Precise steps to reproduce the problem, with the expected and actual results.
|
||||
2. [Console errors](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#console-errors), if any.
|
||||
3. Appchan X version, browser variant, browser version, and Greasemonkey version if you are using it.
|
||||
=======
|
||||
Reporting bugs:
|
||||
|
||||
1. Make sure both your **browser** and **4chan X** are up to date.<br>
|
||||
Only **Chrome**, **Firefox** and **Opera** are supported.<br>
|
||||
**SRWare Iron**, **Firefox ESR**, **Pale Moon**, **Waterfox**, and other derivatives are not supported, use them at your own risk.
|
||||
2. Look at the list of [known problems and solutions](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems).
|
||||
3. Disable your other extensions & scripts to identify conflicts.
|
||||
4. If your issue persists, open a [new issue](https://github.com/ccd0/4chan-x/issues) with the following information:
|
||||
1. Precise steps to reproduce the problem, with the expected and actual results.
|
||||
2. [Console errors](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#console-errors), if any.
|
||||
3. 4chan X version, browser variant, browser version, and Greasemonkey version if you are using it.
|
||||
>>>>>>> v3
|
||||
4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually.
|
||||
|
||||
Respect these guidelines:
|
||||
|
||||
@ -44,8 +44,7 @@ module.exports = (grunt) ->
|
||||
|
||||
meta:
|
||||
files:
|
||||
'LICENSE': 'src/General/meta/banner.js',
|
||||
'latest.js': 'src/General/meta/latest.js'
|
||||
'LICENSE': 'src/General/meta/banner.js'
|
||||
|
||||
crx:
|
||||
files:
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* appchan x - Version 2.9.15 - 2014-04-05
|
||||
* appchan x - Version 2.9.15 - 2014-04-11
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.5.1
|
||||
// @version 1.7.7
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace 4chan-X
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
builds/crx.crx
BIN
builds/crx.crx
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
||||
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
||||
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.5.1' />
|
||||
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.7' />
|
||||
</app>
|
||||
</gupdate>
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,62 +0,0 @@
|
||||
function GM_openInTab(_url) {
|
||||
self.port.emit("GM_openInTab", _url);
|
||||
return; // Should return the Window object
|
||||
};
|
||||
|
||||
function GM_setValue(_name, _value) {
|
||||
localStorage[_name] = _value;
|
||||
return;
|
||||
};
|
||||
|
||||
function GM_getValue(_name, _default) {
|
||||
if (localStorage[_name] === null && _default === null) return null;
|
||||
return (localStorage[_name] || _default);
|
||||
};
|
||||
|
||||
function GM_deleteValue(_name) {
|
||||
localStorage.removeItem(_name);
|
||||
return;
|
||||
};
|
||||
|
||||
function GM_listValues() {
|
||||
return Object.keys(localStorage);
|
||||
};
|
||||
|
||||
function GM_setClipboard(_text) {
|
||||
self.port.emit("GM_setClipboard", _text);
|
||||
};
|
||||
|
||||
//Deprecated
|
||||
function GM_log(_message) {
|
||||
console.log(_message);
|
||||
return;
|
||||
};
|
||||
|
||||
function GM_xmlhttpRequest(_details) {
|
||||
//Ugly hack? Race condition? Memory leak?
|
||||
_onload = _details.onload;
|
||||
_context = _details.context;
|
||||
self.port.emit("GM_xmlhttpRequest", _details);
|
||||
};
|
||||
|
||||
self.port.on("callback_GM_xmlhttpRequest", function(_response) {
|
||||
_response.context = _context;
|
||||
_onload(_response);
|
||||
});
|
||||
|
||||
function GM_addStyle(_css) {
|
||||
self.port.emit("GM_addStyle", _css);
|
||||
}
|
||||
|
||||
var GM_info = new Object();
|
||||
GM_info.version = '1.15';
|
||||
GM_info.scriptWillUpdate = true;
|
||||
|
||||
//To do
|
||||
function GM_registerMenuCommand(_caption, _commandFunc, _accessKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.port.on("load-userscript", function(_script) {
|
||||
eval(_script);
|
||||
});
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 185 B |
Binary file not shown.
|
Before Width: | Height: | Size: 232 B |
@ -1,82 +0,0 @@
|
||||
var data = require("sdk/self").data;
|
||||
var Request = require("sdk/request").Request;
|
||||
var tabs = require("sdk/tabs");
|
||||
var system = require("sdk/system");
|
||||
if (system.platform !== 'android') {
|
||||
// Clipboard is not supported on Android
|
||||
var clipboard = require("sdk/clipboard");
|
||||
}
|
||||
var pageMod = require("sdk/page-mod");
|
||||
pageMod.PageMod({
|
||||
include: ["*.4chan.org", "*.4cdn.org"],
|
||||
contentScriptFile: data.url("greaseshim.js"),
|
||||
contentScriptWhen: "start",
|
||||
onAttach: function(worker) {
|
||||
worker.port.emit("load-userscript", data.load("4chan-X.user.js"));
|
||||
|
||||
//GM_openInTab
|
||||
worker.port.on("GM_openInTab", function(url) {
|
||||
tabs.open(url);
|
||||
});
|
||||
|
||||
//GM_setClipboard
|
||||
worker.port.on("GM_setClipboard", function(text) {
|
||||
if (system.platform !== 'android') {
|
||||
// Clipboard is not supported on Android
|
||||
clipboard.set(text);
|
||||
}
|
||||
});
|
||||
|
||||
//GM_xmlhttpRequest
|
||||
worker.port.on("GM_xmlhttpRequest", function(details) {
|
||||
request = new Object();
|
||||
request.url = details.url;
|
||||
if (details.headers) {
|
||||
request.headers = details.headers;
|
||||
if (details.headers["Content-Type"]) {request.contentType = details.headers["Content-Type"]};
|
||||
};
|
||||
if (details.data) {request.content = encodeURIComponent(details.data)};
|
||||
if (details.overrideMimeType) {request.overrideMimeType = details.overrideMimeType};
|
||||
|
||||
request.onComplete = function(response) {
|
||||
response.finalUrl = details.url;
|
||||
response.responseText = response.text;
|
||||
for (var headerName in response.headers) {
|
||||
_string = headerName + ": " + response.headers[headerName] + " \n";
|
||||
response.responseHeaders += _string;
|
||||
}
|
||||
response.readyState = 4;
|
||||
worker.port.emit("callback_GM_xmlhttpRequest", response);
|
||||
}
|
||||
|
||||
xhr = Request(request);
|
||||
|
||||
switch(details.method){
|
||||
case "GET":
|
||||
xhr.get();
|
||||
break;
|
||||
case "POST":
|
||||
xhr.post();
|
||||
break;
|
||||
case "HEAD":
|
||||
xhr.head();
|
||||
break;
|
||||
case "PUT":
|
||||
xhr.put();
|
||||
break;
|
||||
default: xhr.get();
|
||||
}
|
||||
});
|
||||
|
||||
//GM_addStyle
|
||||
worker.port.on("GM_addStyle", function(css) {
|
||||
tabs.activeTab.attach({
|
||||
contentScript: "var style = document.createElement('style');" +
|
||||
"style.type = 'text/css';" +
|
||||
"style.innerHTML = '" + css + "';" +
|
||||
"document.head.appendChild(style);"
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "4chanx",
|
||||
"title": "4chan X",
|
||||
"id": "72DAF86E-9689-11E3-8BA3-F4B66188709B",
|
||||
"description": "Adds various features to 4chan.",
|
||||
"author": "Spittie",
|
||||
"license": "MIT",
|
||||
"version": "1.4.1"
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
var main = require("./main");
|
||||
|
||||
exports["test main"] = function(assert) {
|
||||
assert.pass("Unit test running!");
|
||||
};
|
||||
|
||||
exports["test main async"] = function(assert, done) {
|
||||
assert.pass("async Unit test running!");
|
||||
done();
|
||||
};
|
||||
|
||||
require("sdk/test").run(exports);
|
||||
1068
css/style.css
1068
css/style.css
File diff suppressed because it is too large
Load Diff
@ -1,128 +0,0 @@
|
||||
[{
|
||||
"uid": 0,
|
||||
"name": "Foolz",
|
||||
"domain": "archive.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["a", "biz", "co", "diy", "gd", "jp", "m", "sci", "sp", "tg", "tv", "v", "vg", "vp", "vr", "wsg"],
|
||||
"files": ["a", "biz", "gd", "diy", "jp", "m", "sci", "tg", "vg", "vp", "vr", "wsg"]
|
||||
}, {
|
||||
"uid": 1,
|
||||
"name": "NSFW Foolz",
|
||||
"domain": "nsfw.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["u"],
|
||||
"files": ["u"]
|
||||
}, {
|
||||
"uid": 2,
|
||||
"name": "The Dark Cave",
|
||||
"domain": "archive.thedarkcave.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["c", "int", "out", "po"],
|
||||
"files": ["c", "po"]
|
||||
}, {
|
||||
"uid": 3,
|
||||
"name": "4plebs Archive",
|
||||
"domain": "archive.4plebs.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"],
|
||||
"files": ["adv", "hr", "o", "pol", "s4s", "tg", "trv", "tv", "x"]
|
||||
}, {
|
||||
"uid": 18,
|
||||
"name": "4plebs Flash Archive",
|
||||
"domain": "flash.4plebs.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["f"],
|
||||
"files": ["f"]
|
||||
}, {
|
||||
"uid": 4,
|
||||
"name": "Nyafuu",
|
||||
"domain": "archive.nyafuu.org",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["c", "e", "w", "wg"],
|
||||
"files": ["c", "e", "w", "wg"]
|
||||
}, {
|
||||
"uid": 5,
|
||||
"name": "Love is Over",
|
||||
"domain": "loveisover.me",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["d", "i"],
|
||||
"files": ["d", "i"]
|
||||
}, {
|
||||
"uid": 8,
|
||||
"name": "Rebecca Black Tech",
|
||||
"domain": "archive.rebeccablacktech.com",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "fuuka",
|
||||
"boards": ["cgl", "g", "mu", "w"],
|
||||
"files": ["cgl", "g", "mu", "w"]
|
||||
}, {
|
||||
"uid": 9,
|
||||
"name": "Heinessen",
|
||||
"domain": "archive.heinessen.com",
|
||||
"http": true,
|
||||
"https": false,
|
||||
"software": "fuuka",
|
||||
"boards": ["an", "fit", "k", "mlp", "r9k", "toy"],
|
||||
"files": ["an", "fit", "k", "r9k", "toy"]
|
||||
}, {
|
||||
"uid": 10,
|
||||
"name": "warosu",
|
||||
"domain": "fuuka.warosu.org",
|
||||
"http": false,
|
||||
"https": true,
|
||||
"software": "fuuka",
|
||||
"boards": ["3", "biz", "cgl", "ck", "diy", "fa", "g", "ic", "jp", "lit", "sci", "tg", "vr"],
|
||||
"files": ["3", "biz", "cgl", "ck", "diy", "fa", "ic", "jp", "lit", "sci", "tg", "vr"]
|
||||
}, {
|
||||
"uid": 15,
|
||||
"name": "fgts",
|
||||
"domain": "fgts.eu",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["cm", "hm", "r", "soc", "y"],
|
||||
"files": ["cm", "hm", "r", "soc", "y"]
|
||||
}, {
|
||||
"uid": 16,
|
||||
"name": "maware",
|
||||
"domain": "archive.mawa.re",
|
||||
"http": true,
|
||||
"https": false,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["t"],
|
||||
"files": ["t"]
|
||||
}, {
|
||||
"uid": 17,
|
||||
"name": "installgentoo.com",
|
||||
"domain": "chan.installgentoo.com",
|
||||
"http": true,
|
||||
"https": false,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["g", "t"],
|
||||
"files": ["g", "t"]
|
||||
}, {
|
||||
"uid": 13,
|
||||
"name": "Foolz Beta",
|
||||
"domain": "beta.foolz.us",
|
||||
"http": true,
|
||||
"https": true,
|
||||
"withCredentials": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["a", "biz", "co", "d", "diy", "gd", "jp", "m", "s4s", "sci", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
|
||||
"files": ["a", "biz", "d", "diy", "gd", "jp", "m", "s4s", "sci", "tg", "u", "vg", "vp", "vr", "wsg"]
|
||||
}]
|
||||
@ -95,8 +95,8 @@
|
||||
"http": true,
|
||||
"https": true,
|
||||
"software": "foolfuuka",
|
||||
"boards": ["cm", "hm", "r", "soc", "y"],
|
||||
"files": ["cm", "hm", "r", "soc", "y"]
|
||||
"boards": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"],
|
||||
"files": ["cm", "h", "hc", "hm", "r", "s", "soc", "y"]
|
||||
}, {
|
||||
"uid": 16,
|
||||
"name": "maware",
|
||||
|
||||
@ -2,7 +2,7 @@ Config =
|
||||
main:
|
||||
'Miscellaneous':
|
||||
'JSON Navigation' : [
|
||||
true
|
||||
false
|
||||
'Use JSON for loading the Board Index and Threads. Also allows searching and sorting the board index and infinite scolling.'
|
||||
]
|
||||
'Catalog Links': [
|
||||
@ -69,6 +69,10 @@ Config =
|
||||
false
|
||||
'Indicate spoilers if Remove Spoilers is enabled, or make the text appear hovered if Remove Spoiler is disabled.'
|
||||
]
|
||||
'Show Support Message': [
|
||||
true
|
||||
'Warn if your browser is unsupported. 4chan X may not operate correctly on unsupported browser versions.'
|
||||
]
|
||||
|
||||
'Linkification':
|
||||
'Linkify': [
|
||||
@ -131,6 +135,10 @@ Config =
|
||||
true
|
||||
'Adds a simple and cute image gallery.'
|
||||
]
|
||||
'PDF in Gallery': [
|
||||
false
|
||||
'Show PDF files in gallery.'
|
||||
]
|
||||
'Sauce': [
|
||||
true
|
||||
'Add sauce links to images.'
|
||||
@ -141,15 +149,19 @@ Config =
|
||||
]
|
||||
'Replace GIF': [
|
||||
false
|
||||
'Replace thumbnail of gifs with its actual image.'
|
||||
]
|
||||
'Replace PNG': [
|
||||
false
|
||||
'Replace pngs.'
|
||||
'Replace gif thumbnails with the actual image.'
|
||||
]
|
||||
'Replace JPG': [
|
||||
false
|
||||
'Replace jpgs.'
|
||||
'Replace jpg thumbnails with the actual image.'
|
||||
]
|
||||
'Replace PNG': [
|
||||
false
|
||||
'Replace png thumbnails with the actual image.'
|
||||
]
|
||||
'Replace WEBM': [
|
||||
false
|
||||
'Replace webm thumbnails with the actual webm video. Probably will degrade browser performance ;)'
|
||||
]
|
||||
'Image Prefetching': [
|
||||
false
|
||||
@ -165,15 +177,19 @@ Config =
|
||||
]
|
||||
'Autoplay': [
|
||||
true
|
||||
'Videos begin playing immediately when opened inline.'
|
||||
'Videos begin playing immediately when opened.'
|
||||
]
|
||||
'Show Controls': [
|
||||
true
|
||||
'Show native seek and volume controls on videos. Contract videos when dragged to the left.'
|
||||
'Show controls on videos expanded inline. Turn this off if you want to contract videos by clicking on them.'
|
||||
]
|
||||
'Allow Sound': [
|
||||
true
|
||||
'Allow sound in inline videos.'
|
||||
'Allow sound in videos.'
|
||||
]
|
||||
'Loop in New Tab': [
|
||||
true
|
||||
'Loop videos opened in their own tabs, and apply settings for inline expanded videos to them.'
|
||||
]
|
||||
|
||||
'Menu':
|
||||
@ -366,6 +382,7 @@ Config =
|
||||
'Add \'(Cross-thread)\' to cross-threads quotes.'
|
||||
'Highlights own posts if Quote Markers are enabled.'
|
||||
]
|
||||
|
||||
imageExpansion:
|
||||
'Fit width': [
|
||||
false
|
||||
@ -379,6 +396,10 @@ Config =
|
||||
true
|
||||
'Expand all images along with spoilers.'
|
||||
]
|
||||
'Expand videos': [
|
||||
false
|
||||
'Expand all images also expands videos (no autoplay).'
|
||||
]
|
||||
'Expand from here': [
|
||||
false
|
||||
'Expand all images only from current position to thread end.'
|
||||
|
||||
@ -47,7 +47,7 @@ Get =
|
||||
# get all their backlinks.
|
||||
posts.forEach (qPost) ->
|
||||
if fullID in qPost.quotes
|
||||
handleQuotes qPost, 'quotelinks'
|
||||
handleQuotes qPost, 'quotelinks'
|
||||
|
||||
# Second:
|
||||
# If we have quote backlinks:
|
||||
@ -62,6 +62,7 @@ Get =
|
||||
quotelinks.filter (quotelink) ->
|
||||
{boardID, postID} = Get.postDataFromLink quotelink
|
||||
boardID is post.board.ID and postID is post.ID
|
||||
|
||||
postClone: (boardID, threadID, postID, root, context) ->
|
||||
if post = g.posts["#{boardID}.#{postID}"]
|
||||
Get.insert post, root, context
|
||||
@ -80,7 +81,7 @@ Get =
|
||||
insert: (post, root, context) ->
|
||||
# Stop here if the container has been removed while loading.
|
||||
return unless root.parentNode
|
||||
clone = post.addClone context
|
||||
clone = post.addClone context, ($.hasClass root, 'dialog')
|
||||
Clone.callbacks.execute [clone]
|
||||
|
||||
# Get rid of the side arrows/stubs.
|
||||
@ -104,6 +105,7 @@ Get =
|
||||
$.cache url,
|
||||
-> Get.archivedPost @, boardID, postID, root, context
|
||||
,
|
||||
responseType: 'json'
|
||||
withCredentials: url.archive.withCredentials
|
||||
else
|
||||
$.addClass root, 'warning'
|
||||
|
||||
@ -362,6 +362,10 @@ Header =
|
||||
bottom -= clientHeight - headRect.bottom + headRect.height - 10
|
||||
bottom
|
||||
|
||||
isNodeVisible: (node) ->
|
||||
{height} = node.getBoundingClientRect()
|
||||
Header.getTopOf(node) + height >= 0 and Header.getBottomOf(node) + height >= 0
|
||||
|
||||
isHidden: ->
|
||||
{top} = Header.bar.getBoundingClientRect()
|
||||
if Conf['Bottom header']
|
||||
|
||||
@ -171,9 +171,9 @@ Index =
|
||||
|
||||
scroll: ->
|
||||
return if Index.req or Conf['Index Mode'] isnt 'infinite' or (window.scrollY <= doc.scrollHeight - (300 + window.innerHeight)) or g.VIEW is 'thread'
|
||||
Index.pageNum = (Index.pageNum or Index.getCurrentPage()) + 1 # Avoid having to pushState to keep track of the current page
|
||||
Index.currentPage = (Index.currentPage or Index.getCurrentPage()) + 1 # Avoid having to pushState to keep track of the current page
|
||||
|
||||
return Index.endNotice() if Index.pageNum >= Index.pagesNum
|
||||
return Index.endNotice() if Index.currentPage >= Index.pagesNum
|
||||
|
||||
Index.buildIndex true
|
||||
|
||||
@ -419,8 +419,8 @@ Index =
|
||||
Header.scrollToIfNeeded Index.navLinks
|
||||
|
||||
getCurrentPage: ->
|
||||
if Conf['Index Mode'] is 'infinite' and Index.pageNum
|
||||
return Index.pageNum
|
||||
if Conf['Index Mode'] is 'infinite' and Index.currentPage
|
||||
return Index.currentPage
|
||||
+window.location.pathname.split('/')[2]
|
||||
|
||||
userPageNav: (pageNum) ->
|
||||
@ -469,8 +469,8 @@ Index =
|
||||
$.add pagesRoot, nodes
|
||||
Index.togglePagelist()
|
||||
|
||||
setPage: (pageNum) ->
|
||||
pageNum or= Index.getCurrentPage()
|
||||
setPage: (pageNum = Index.getCurrentPage()) ->
|
||||
Index.currentPage = pageNum
|
||||
maxPageNum = Index.getMaxPageNum()
|
||||
pagesRoot = $ '.pages', Index.pagelist
|
||||
# Previous/Next buttons
|
||||
@ -513,7 +513,7 @@ Index =
|
||||
return
|
||||
unless d.readyState is 'loading' or Index.root.parentElement
|
||||
$.replace $('.board'), Index.root
|
||||
delete Index.pageNum
|
||||
Index.currentPage = 0
|
||||
Index.req?.abort()
|
||||
Index.notice?.close()
|
||||
|
||||
@ -564,10 +564,14 @@ Index =
|
||||
Navigate.title()
|
||||
|
||||
try
|
||||
pageNum or= 0
|
||||
if req.status is 200
|
||||
Index.parse req.response, pageNum
|
||||
else if req.status is 304
|
||||
Index.pageNav pageNum or 0
|
||||
if Index.currentPage is pageNum
|
||||
Index.buildIndex()
|
||||
else
|
||||
Index.pageNav pageNum
|
||||
catch err
|
||||
c.error "Index failure: #{err.message}", err.stack
|
||||
# network error or non-JSON content for example.
|
||||
@ -588,7 +592,7 @@ Index =
|
||||
Index.parseThreadList pages
|
||||
Index.buildThreads()
|
||||
Index.sort()
|
||||
if pageNum?
|
||||
if pageNum? and Index.currentPage isnt pageNum
|
||||
Index.pageNav pageNum
|
||||
return
|
||||
Index.buildIndex()
|
||||
@ -752,16 +756,12 @@ Index =
|
||||
|
||||
buildIndex: (infinite) ->
|
||||
{sortedThreads} = Index
|
||||
nodes = []
|
||||
switch Conf['Index Mode']
|
||||
when 'paged', 'infinite'
|
||||
pageNum = Index.getCurrentPage()
|
||||
if pageNum > Index.getMaxPageNum()
|
||||
# Go to the last available page if we were past the limit.
|
||||
Index.pageNav Index.getMaxPageNum()
|
||||
return
|
||||
threadsPerPage = Index.getThreadsNumPerPage()
|
||||
|
||||
nodes = []
|
||||
threads = []
|
||||
i = threadsPerPage * pageNum
|
||||
max = i + threadsPerPage
|
||||
@ -777,8 +777,7 @@ Index =
|
||||
nodes = Index.buildCatalogViews()
|
||||
|
||||
else
|
||||
nodes = []
|
||||
i = 0
|
||||
i = 0
|
||||
while thread = sortedThreads[i++]
|
||||
nodes.push thread.OP.nodes.root.parentNode, $.el 'hr'
|
||||
Index.buildReplies thread
|
||||
@ -799,9 +798,11 @@ Index =
|
||||
unless Index.searchInput.dataset.searching
|
||||
Index.searchInput.dataset.searching = 1
|
||||
Index.pageBeforeSearch = Index.getCurrentPage()
|
||||
pageNum = 0
|
||||
Index.setPage pageNum = 0
|
||||
else
|
||||
pageNum = Index.getCurrentPage()
|
||||
unless Conf['Index Mode'] is 'infinite'
|
||||
pageNum = Index.getCurrentPage()
|
||||
|
||||
else
|
||||
return unless Index.searchInput.dataset.searching
|
||||
pageNum = Index.pageBeforeSearch
|
||||
@ -832,7 +833,6 @@ Index =
|
||||
filtered.push thread if Index.searchMatch thread, keywords
|
||||
Index.sortedThreads = filtered
|
||||
|
||||
|
||||
searchMatch: (thread, keywords) ->
|
||||
{info, file} = thread.OP
|
||||
text = []
|
||||
|
||||
@ -83,6 +83,11 @@ Main =
|
||||
Report.init()
|
||||
return
|
||||
when 'i.4cdn.org'
|
||||
if Conf['Loop in New Tab'] and video = $ 'video'
|
||||
Video.configure video
|
||||
$.on video, 'click', ->
|
||||
if !video.controls
|
||||
if video.paused then video.play() else video.pause()
|
||||
$.ready ->
|
||||
if Conf['404 Redirect'] and d.title in ['4chan - Temporarily Offline', '4chan - 404 Not Found']
|
||||
Redirect.init()
|
||||
@ -204,7 +209,7 @@ Main =
|
||||
<% if (type === 'userscript') { %>
|
||||
test = $.el 'span'
|
||||
test.classList.add 'a', 'b'
|
||||
if test.className isnt 'a b'
|
||||
if test.className isnt 'a b' and Conf['Show Support Message']
|
||||
new Notice 'warning', "Your version of Firefox is outdated (v<%= meta.min.firefox %> minimum) and <%= meta.name %> may not operate correctly.", 30
|
||||
|
||||
GMver = GM_info.version.split '.'
|
||||
|
||||
@ -115,11 +115,15 @@ Navigate =
|
||||
index: ->
|
||||
delete g.THREADID
|
||||
Index.cb.toggleCatalogMode() if Conf['Index Mode'] is 'catalog'
|
||||
QR.posts[0]?.thread = 'new'
|
||||
|
||||
thread: ->
|
||||
$.rmClass doc, 'catalog-mode' if Conf['Index Mode'] is 'catalog'
|
||||
QR.posts[0]?.thread = g.THREADID
|
||||
}[view]()
|
||||
|
||||
QR.status() # Re-enable the QR in the case of a 404'd thread or something.
|
||||
|
||||
g.VIEW = view
|
||||
|
||||
updateBoard: (boardID) ->
|
||||
|
||||
@ -3,6 +3,7 @@ Settings =
|
||||
# Appchan X settings link
|
||||
el = $.el 'a',
|
||||
className: 'settings-link'
|
||||
title: 'Appchan X Settings'
|
||||
href: 'javascript:;'
|
||||
textContent: 'Settings'
|
||||
$.on el, 'click', @open
|
||||
|
||||
@ -309,7 +309,7 @@ UI = do ->
|
||||
$.off d, 'mouseup', @up
|
||||
$.set "#{@id}.position", @style.cssText
|
||||
|
||||
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, offsetX, offsetY}) ->
|
||||
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, offsetX, offsetY, noRemove}) ->
|
||||
o = {
|
||||
root
|
||||
el
|
||||
@ -322,6 +322,7 @@ UI = do ->
|
||||
clientWidth: doc.clientWidth
|
||||
offsetX: offsetX or 45
|
||||
offsetY: offsetY or -120
|
||||
noRemove
|
||||
}
|
||||
o.hover = hover.bind o
|
||||
o.hoverend = hoverend.bind o
|
||||
@ -338,7 +339,7 @@ UI = do ->
|
||||
$.on root, 'mousemove', o.hover
|
||||
<% if (type === 'userscript') { %>
|
||||
# Workaround for https://github.com/MayhemYDG/4chan-x/issues/377
|
||||
o.workaround = (e) -> o.hoverend() unless root.contains e.target
|
||||
o.workaround = (e) -> o.hoverend(e) unless root.contains e.target
|
||||
$.on doc, 'mousemove', o.workaround
|
||||
<% } %>
|
||||
|
||||
|
||||
@ -223,7 +223,7 @@ div.center:not(.ad-cnt) {
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
}
|
||||
:root:not(.autohide) #scroll-marker {
|
||||
:root #header-bar:not(.autohide) #scroll-marker {
|
||||
pointer-events: none;
|
||||
}
|
||||
#header-bar #scroll-marker {
|
||||
@ -824,20 +824,26 @@ span.hide-announcement {
|
||||
/* File */
|
||||
.fileText:hover .fntrunc,
|
||||
.fileText:not(:hover) .fnfull,
|
||||
.expanded-image > .post > .file > .fileThumb > img[data-md5],
|
||||
:not(.expanded-image) > .post > .file .full-image {
|
||||
.expanded-image > .post > .file > .fileThumb > video[data-md5],
|
||||
.expanded-image > .post > .file > .fileThumb > img[data-md5] {
|
||||
display: none;
|
||||
}
|
||||
.full-image:not(#ihover) {
|
||||
display: none;
|
||||
}
|
||||
.expanded-image > .post > .file > .fileThumb > .full-image:not(#ihover) {
|
||||
display: inline;
|
||||
}
|
||||
.expanding {
|
||||
opacity: .5;
|
||||
}
|
||||
:root.fit-height .full-image {
|
||||
:root.fit-height .full-image:not(#ihover) {
|
||||
max-height: 100vh;
|
||||
}
|
||||
:root.fit-width .full-image {
|
||||
:root.fit-width .full-image:not(#ihover) {
|
||||
max-width: 100%;
|
||||
}
|
||||
:root.gecko.fit-width .full-image {
|
||||
:root.gecko.fit-width .full-image:not(#ihover) {
|
||||
width: 100%;
|
||||
}
|
||||
#ihover {
|
||||
@ -1459,34 +1465,44 @@ div.boardTitle {
|
||||
/* Flex > Non-Flex child max-width and overflow fix (Firefox only?) */
|
||||
width: 1%;
|
||||
}
|
||||
:root:not(.gal-fit-height) .gal-image {
|
||||
:root:not(.gal-fit-height):not(.gal-pdf) .gal-image {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
:root:not(.gal-fit-width) .gal-image {
|
||||
:root:not(.gal-fit-width):not(.gal-pdf) .gal-image {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
.gal-image a {
|
||||
margin: auto;
|
||||
line-height: 0;
|
||||
}
|
||||
:root.gal-pdf .gal-image a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.gal-fit-width .gal-image video,
|
||||
.gal-fit-width .gal-image img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.gal-fit-height .gal-image video,
|
||||
.gal-fit-height .gal-image img {
|
||||
/*
|
||||
Chrome doesn't support viewpoint units in calc()
|
||||
http://bugs.chromium.org/168840
|
||||
"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff."
|
||||
Well, fuck.
|
||||
*/
|
||||
max-height: 95vh;
|
||||
max-height: calc(100vh - 25px);
|
||||
}
|
||||
.gal-image iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.gal-buttons {
|
||||
font-size: 2em;
|
||||
margin-right: 10px;
|
||||
margin-right: 3px;
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
top: 5px;
|
||||
}
|
||||
.gal-pdf .gal-buttons {
|
||||
top: 40px;
|
||||
background: rgba(0,0,0,0.6) !important;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.gal-buttons i {
|
||||
vertical-align: baseline;
|
||||
border-top-width: .4em;
|
||||
@ -1506,12 +1522,12 @@ div.boardTitle {
|
||||
.gal-name,
|
||||
.gal-count {
|
||||
position: fixed;
|
||||
right: 178px;
|
||||
right: 195px;
|
||||
}
|
||||
.gal-hide-thumbnails .gal-buttons,
|
||||
.gal-hide-thumbnails .gal-count,
|
||||
.gal-hide-thumbnails .gal-name {
|
||||
right: 28px;
|
||||
right: 44px;
|
||||
}
|
||||
.gal-name {
|
||||
bottom: 6px;
|
||||
@ -1526,6 +1542,10 @@ div.boardTitle {
|
||||
.gal-buttons .menu-button:hover {
|
||||
color: rgb(95, 95, 101) !important;
|
||||
}
|
||||
:root.gal-pdf .gal-close:hover,
|
||||
:root.gal-pdf .gal-buttons .menu-button:hover {
|
||||
color: rgb(204, 204, 204) !important;
|
||||
}
|
||||
.gal-count {
|
||||
bottom: 27px;
|
||||
background: rgba(0,0,0,0.6) !important;
|
||||
@ -1533,21 +1553,21 @@ div.boardTitle {
|
||||
padding: 1px 5px 2px 5px;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
:root:not(.gal-fit-width) .gal-name {
|
||||
:root:not(.gal-fit-width):not(.gal-pdf) .gal-name {
|
||||
bottom: 23px !important;
|
||||
}
|
||||
:root:not(.gal-fit-width) .gal-count {
|
||||
:root:not(.gal-fit-width):not(.gal-pdf) .gal-count {
|
||||
bottom: 44px !important;
|
||||
}
|
||||
:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-buttons,
|
||||
:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-name,
|
||||
:root:not(.gal-fit-height):not(.gal-hide-thumbnails) .gal-count {
|
||||
right: 195px !important;
|
||||
:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-buttons,
|
||||
:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-name,
|
||||
:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-count {
|
||||
right: 178px !important;
|
||||
}
|
||||
:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-buttons,
|
||||
:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-name,
|
||||
:root.gal-hide-thumbnails:not(.gal-fit-height) .gal-count {
|
||||
right: 44px !important;
|
||||
:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-buttons,
|
||||
:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-name,
|
||||
:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-count {
|
||||
right: 28px !important;
|
||||
}
|
||||
@media screen and (resolution: 1dppx) {
|
||||
.fa-bars {
|
||||
|
||||
@ -13,4 +13,4 @@
|
||||
</div>
|
||||
</nav>
|
||||
<hr>
|
||||
<div class=section-container><section></section></div>
|
||||
<div class=section-container><section></section></div>
|
||||
|
||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
@ -1,11 +1,14 @@
|
||||
class Clone extends Post
|
||||
constructor: (@origin, @context) ->
|
||||
constructor: (@origin, @context, contractThumb) ->
|
||||
for key in ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']
|
||||
# Copy or point to the origin's key value.
|
||||
@[key] = origin[key]
|
||||
|
||||
{nodes} = origin
|
||||
root = nodes.root.cloneNode true
|
||||
root = if contractThumb
|
||||
@cloneWithoutVideo nodes.root
|
||||
else
|
||||
nodes.root.cloneNode true
|
||||
post = $ '.post', root
|
||||
info = $ '.postInfo', post
|
||||
@nodes =
|
||||
@ -56,6 +59,29 @@ class Clone extends Post
|
||||
@file.thumb = $ 'img[data-md5]', file
|
||||
@file.fullImage = $ '.full-image', file
|
||||
|
||||
# Contract thumbnails in quote preview
|
||||
if contractThumb
|
||||
$.rmClass root, 'expanded-image'
|
||||
$.rmClass @file.thumb, 'expanding'
|
||||
@file.isExpanded = $.hasClass root, 'expanded-image'
|
||||
|
||||
# Remove any #ihover ID
|
||||
@file.fullImage?.removeAttribute 'id'
|
||||
|
||||
# Remove video controls.
|
||||
($ '.video-controls', @file.text)?.remove()
|
||||
|
||||
@isDead = true if origin.isDead
|
||||
@isClone = true
|
||||
root.dataset.clone = origin.clones.push(@) - 1
|
||||
|
||||
cloneWithoutVideo: (node) ->
|
||||
if node.tagName is 'VIDEO'
|
||||
[]
|
||||
else if node.nodeType is Node.ELEMENT_NODE and $ 'video', node
|
||||
clone = node.cloneNode false
|
||||
$.add clone, @cloneWithoutVideo child for child in node.childNodes
|
||||
clone
|
||||
else
|
||||
node.cloneNode true
|
||||
|
||||
|
||||
@ -290,8 +290,8 @@ class Post
|
||||
@thread.posts.rm @
|
||||
@board.posts.rm @
|
||||
|
||||
addClone: (context) ->
|
||||
new Clone @, context
|
||||
addClone: (context, contractThumb) ->
|
||||
new Clone @, context, contractThumb
|
||||
|
||||
rmClone: (index) ->
|
||||
@clones.splice index, 1
|
||||
|
||||
@ -1 +0,0 @@
|
||||
postMessage({version:'<%= version %>'},'*')
|
||||
@ -1,34 +0,0 @@
|
||||
AutoGIF =
|
||||
init: ->
|
||||
return if !Conf['Auto-GIF'] or g.BOARD.ID in ['gif', 'wsg']
|
||||
|
||||
Post.callbacks.push
|
||||
name: 'Auto-GIF'
|
||||
cb: @node
|
||||
CatalogThread.callbacks.push
|
||||
name: 'Auto-GIF'
|
||||
cb: @catalogNode
|
||||
node: ->
|
||||
return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage
|
||||
{thumb, URL} = @file
|
||||
return unless /gif$/.test(URL) and !/spoiler/.test thumb.src
|
||||
if @file.isSpoiler
|
||||
# Revealed spoilers do not have height/width set, this fixes auto-gifs dimensions.
|
||||
{style} = thumb
|
||||
style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px'
|
||||
AutoGIF.replaceThumbnail thumb, URL
|
||||
catalogNode: ->
|
||||
{OP} = @thread
|
||||
return unless OP.file?.isImage
|
||||
{URL} = OP.file
|
||||
return unless /gif$/.test URL
|
||||
AutoGIF.replaceThumbnail @nodes.thumb, URL, true
|
||||
replaceThumbnail: (thumb, URL, isBackground) ->
|
||||
gif = $.el 'img'
|
||||
$.on gif, 'load', ->
|
||||
# Replace the thumbnail once the GIF has finished loading.
|
||||
if isBackground
|
||||
thumb.style.backgroundImage = "url(#{URL})"
|
||||
else
|
||||
thumb.src = URL
|
||||
gif.src = URL
|
||||
@ -15,10 +15,10 @@ Gallery =
|
||||
|
||||
Post.callbacks.push
|
||||
name: 'Gallery'
|
||||
cb: @node
|
||||
cb: @node
|
||||
|
||||
node: ->
|
||||
return unless @file?.isImage
|
||||
return unless @file
|
||||
if Gallery.nodes
|
||||
Gallery.generateThumb $ '.file', @nodes.root
|
||||
Gallery.nodes.total.textContent = Gallery.images.length
|
||||
@ -33,23 +33,23 @@ Gallery =
|
||||
nodes.el = dialog = $.el 'div',
|
||||
id: 'a-gallery'
|
||||
innerHTML: """
|
||||
<div class=gal-viewport>
|
||||
<span class=gal-buttons>
|
||||
<a class="menu-button" href="javascript:;"><i class="fa">\uf107</i></a>
|
||||
<a href=javascript:; class='gal-close fa'>\uf00d</a>
|
||||
</span>
|
||||
<a class=gal-name target="_blank"></a>
|
||||
<span class=gal-count>
|
||||
<span class='count'></span> / <span class='total'></span>
|
||||
</span>
|
||||
<div class=gal-prev></div>
|
||||
<div class=gal-image>
|
||||
<a href=javascript:;><img></a>
|
||||
</div>
|
||||
<div class=gal-next></div>
|
||||
</div>
|
||||
<div class=gal-thumbnails></div>
|
||||
"""
|
||||
<div class=gal-viewport>
|
||||
<span class=gal-buttons>
|
||||
<a class="menu-button" href="javascript:;"><i></i></a>
|
||||
<a href=javascript:; class=gal-close>×</a>
|
||||
</span>
|
||||
<a class=gal-name target="_blank"></a>
|
||||
<span class=gal-count>
|
||||
<span class='count'></span> / <span class='total'></span>
|
||||
</span>
|
||||
<div class=gal-prev></div>
|
||||
<div class=gal-image>
|
||||
<a href=javascript:;><img></a>
|
||||
</div>
|
||||
<div class=gal-next></div>
|
||||
</div>
|
||||
<div class=gal-thumbnails></div>
|
||||
"""
|
||||
|
||||
nodes[key] = $ value, dialog for key, value of {
|
||||
frame: '.gal-image'
|
||||
@ -65,12 +65,11 @@ Gallery =
|
||||
nodes.menu = new UI.Menu 'gallery'
|
||||
|
||||
{cb} = Gallery
|
||||
$.on nodes.frame, 'click', cb.blank
|
||||
$.on nodes.current, 'click', cb.download
|
||||
$.on nodes.next, 'click', cb.next
|
||||
$.on ($ '.gal-prev', dialog), 'click', cb.prev
|
||||
$.on ($ '.gal-next', dialog), 'click', cb.next
|
||||
$.on ($ '.gal-close', dialog), 'click', cb.close
|
||||
$.on nodes.frame, 'click', cb.blank
|
||||
$.on nodes.next, 'click', cb.advance
|
||||
$.on $('.gal-prev', dialog), 'click', cb.prev
|
||||
$.on $('.gal-next', dialog), 'click', cb.next
|
||||
$.on $('.gal-close', dialog), 'click', cb.close
|
||||
|
||||
$.on menuButton, 'click', (e) ->
|
||||
nodes.menu.toggle e, @, g
|
||||
@ -86,12 +85,7 @@ Gallery =
|
||||
|
||||
$.on d, 'keydown', cb.keybinds
|
||||
$.off d, 'keydown', Keybinds.keydown
|
||||
|
||||
i = 0
|
||||
files = $$ '.post .file'
|
||||
while file = files[i++]
|
||||
continue if $ '.fileDeletedRes, .fileDeleted', file
|
||||
Gallery.generateThumb file
|
||||
Gallery.generateThumb file for file, i in $$ '.post .file' when !$ '.fileDeletedRes, .fileDeleted', file
|
||||
$.add d.body, dialog
|
||||
|
||||
nodes.thumbs.scrollTop = 0
|
||||
@ -107,16 +101,22 @@ Gallery =
|
||||
|
||||
generateThumb: (file) ->
|
||||
post = Get.postFromNode file
|
||||
return unless post.file and (post.file.isImage or post.file.isVideo or Conf['PDF in Gallery'])
|
||||
title = ($ '.fileText a', file).textContent
|
||||
thumb = post.file.thumb.parentNode.cloneNode true
|
||||
if dupe = $ 'img + img', thumb
|
||||
$.rm dupe
|
||||
|
||||
thumb.className = 'gal-thumb'
|
||||
thumb.title = title
|
||||
thumb.dataset.id = Gallery.images.length
|
||||
thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href
|
||||
thumb.firstElementChild.style.cssText = ''
|
||||
thumb = $.el 'a',
|
||||
className: 'gal-thumb'
|
||||
href: post.file.URL
|
||||
target: '_blank'
|
||||
title: title
|
||||
|
||||
thumb.dataset.id = Gallery.images.length
|
||||
thumb.dataset.post = $('a[title="Highlight this post"]', post.nodes.info).href
|
||||
thumb.dataset.isVideo = true if post.file.isVideo
|
||||
|
||||
thumbImg = post.file.thumb.cloneNode false
|
||||
thumbImg.style.cssText = ''
|
||||
$.add thumb, thumbImg
|
||||
|
||||
$.on thumb, 'click', Gallery.cb.open
|
||||
|
||||
@ -130,8 +130,10 @@ Gallery =
|
||||
cb = switch key
|
||||
when 'Esc', Conf['Open Gallery']
|
||||
Gallery.cb.close
|
||||
when 'Right', 'Enter'
|
||||
when 'Right'
|
||||
Gallery.cb.next
|
||||
when 'Enter'
|
||||
Gallery.cb.advance
|
||||
when 'Left', ''
|
||||
Gallery.cb.prev
|
||||
|
||||
@ -150,15 +152,20 @@ Gallery =
|
||||
$.rmClass el, 'gal-highlight' if el = $ '.gal-highlight', nodes.thumbs
|
||||
$.addClass @, 'gal-highlight'
|
||||
|
||||
img = $.el 'img',
|
||||
elType = if @dataset.isVideo then 'video' else if /\.pdf$/.test(@href) then 'iframe' else 'img'
|
||||
$[if elType is 'iframe' then 'addClass' else 'rmClass'] nodes.el, 'gal-pdf'
|
||||
|
||||
file = $.el elType,
|
||||
src: name.href = @href
|
||||
title: name.download = name.textContent = @title
|
||||
|
||||
$.extend img.dataset, @dataset
|
||||
$.replace nodes.current, img
|
||||
$.extend file.dataset, @dataset
|
||||
nodes.current.pause?()
|
||||
$.replace nodes.current, file
|
||||
Video.configure file if @dataset.isVideo
|
||||
nodes.count.textContent = +@dataset.id + 1
|
||||
nodes.current = img
|
||||
nodes.frame.scrollTop = 0
|
||||
nodes.current = file
|
||||
nodes.frame.scrollTop = 0
|
||||
nodes.next.focus()
|
||||
|
||||
# Scroll
|
||||
@ -169,9 +176,9 @@ Gallery =
|
||||
return if top < 0
|
||||
|
||||
nodes.thumbs.scrollTop += top
|
||||
|
||||
$.on img, 'error', ->
|
||||
Gallery.cb.error img, thumb
|
||||
|
||||
$.on file, 'error', ->
|
||||
Gallery.cb.error file, thumb
|
||||
|
||||
image: (e) ->
|
||||
e.preventDefault()
|
||||
@ -183,24 +190,20 @@ Gallery =
|
||||
delete post.file.fullImage
|
||||
|
||||
src = @src.split '/'
|
||||
if src[2] is 'images.4chan.org'
|
||||
if src[2] is 'i.4cdn.org'
|
||||
URL = Redirect.to 'file',
|
||||
boardID: src[3]
|
||||
filename: src[5]
|
||||
if URL
|
||||
thumb.href = URL
|
||||
return unless Gallery.nodes.current is img
|
||||
revived = $.el 'img',
|
||||
src: URL
|
||||
title: img.title
|
||||
$.extend revived.dataset, img.dataset
|
||||
$.replace img, revived
|
||||
img.src = URL
|
||||
return
|
||||
if g.DEAD or post.isDead or post.file.isDead
|
||||
return
|
||||
|
||||
# XXX CORS for images.4chan.org WHEN?
|
||||
$.ajax "//api.4chan.org/#{post.board}/res/#{post.thread}.json", onload: ->
|
||||
$.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: ->
|
||||
return if @status isnt 200
|
||||
i = 0
|
||||
{posts} = @response
|
||||
@ -211,12 +214,22 @@ Gallery =
|
||||
if postObj.filedeleted
|
||||
post.kill true
|
||||
|
||||
prev: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id - 1]
|
||||
next: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id + 1]
|
||||
toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)()
|
||||
prev: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id - 1]
|
||||
next: -> Gallery.cb.open.call Gallery.images[+Gallery.nodes.current.dataset.id + 1]
|
||||
toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)()
|
||||
blank: (e) -> Gallery.cb.close() if e.target is @
|
||||
|
||||
advance: ->
|
||||
if Gallery.nodes.current.controls then return
|
||||
if Gallery.nodes.current.paused then return Gallery.nodes.current.play()
|
||||
Gallery.cb.next()
|
||||
|
||||
pause: ->
|
||||
{current} = Gallery.nodes
|
||||
current[if current.paused then 'play' else 'pause']() if current.nodeType is 'VIDEO'
|
||||
|
||||
close: ->
|
||||
Gallery.nodes.current.pause?()
|
||||
$.rm Gallery.nodes.el
|
||||
delete Gallery.nodes
|
||||
d.body.style.overflow = ''
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
ImageExpand =
|
||||
init: ->
|
||||
return if !Conf['Image Expansion']
|
||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||
|
||||
@EAI = $.el 'a',
|
||||
id: 'img-controls'
|
||||
@ -17,51 +17,66 @@ ImageExpand =
|
||||
cb: @node
|
||||
|
||||
node: ->
|
||||
return unless @file and (@file.isImage or @file.isVideo)
|
||||
return unless @file?.isImage or @file?.isVideo
|
||||
{thumb} = @file
|
||||
$.on thumb.parentNode, 'click', ImageExpand.cb.toggle
|
||||
if @isClone and $.hasClass thumb, 'expanding'
|
||||
# If we clone a post where the image is still loading,
|
||||
# make it loading in the clone too.
|
||||
ImageExpand.contract @
|
||||
ImageExpand.expand @
|
||||
if @isClone
|
||||
if $.hasClass thumb, 'expanding'
|
||||
# If we clone a post where the image is still loading,
|
||||
# make it loading in the clone too.
|
||||
ImageExpand.contract @
|
||||
ImageExpand.expand @
|
||||
|
||||
else if @file.isExpanded and @file.isVideo
|
||||
clone = @
|
||||
ImageExpand.setupVideoControls clone
|
||||
unless clone.origin.file.fullImage.paused
|
||||
$.queueTask -> Video.start clone.file.fullImage
|
||||
|
||||
return
|
||||
if ImageExpand.on and !@isHidden and (Conf['Expand spoilers'] or !@file.isSpoiler)
|
||||
ImageExpand.expand @
|
||||
|
||||
else if ImageExpand.on and !@isHidden and
|
||||
(Conf['Expand spoilers'] or !@file.isSpoiler) and
|
||||
(Conf['Expand videos'] or !@file.isVideo)
|
||||
ImageExpand.expand @, null, true
|
||||
cb:
|
||||
toggle: (e) ->
|
||||
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
|
||||
post = Get.postFromNode @
|
||||
return if post.file.isExpanded and post.file.fullImage?.controls
|
||||
e.preventDefault()
|
||||
ImageExpand.toggle Get.postFromNode @
|
||||
ImageExpand.toggle post
|
||||
|
||||
toggleAll: ->
|
||||
$.event 'CloseMenu'
|
||||
toggle = (post) ->
|
||||
{file} = post
|
||||
return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root
|
||||
if ImageExpand.on and
|
||||
(!Conf['Expand spoilers'] and file.isSpoiler or
|
||||
!Conf['Expand videos'] and file.isVideo or
|
||||
Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0)
|
||||
return
|
||||
$.queueTask func, post
|
||||
|
||||
if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut'
|
||||
ImageExpand.EAI.className = 'contract-all-shortcut a-icon'
|
||||
ImageExpand.EAI.title = 'Contract All Images'
|
||||
func = ImageExpand.expand
|
||||
func = (post) -> ImageExpand.expand post, null, true
|
||||
else
|
||||
ImageExpand.EAI.className = 'expand-all-shortcut a-icon'
|
||||
ImageExpand.EAI.title = 'Expand All Images'
|
||||
func = ImageExpand.contract
|
||||
|
||||
g.posts.forEach (post) ->
|
||||
for post in [post].concat post.clones
|
||||
{file} = post
|
||||
return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root
|
||||
if ImageExpand.on and !post.isHidden and
|
||||
(!Conf['Expand spoilers'] and file.isSpoiler or
|
||||
Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0)
|
||||
return
|
||||
$.queueTask func, post
|
||||
toggle post for post in [post, post.clones...]
|
||||
return
|
||||
|
||||
setFitness: ->
|
||||
(if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-'
|
||||
|
||||
toggle: (post) ->
|
||||
{thumb} = post.file
|
||||
unless post.file.isExpanded or $.hasClass thumb, 'expanding'
|
||||
unless post.file.isExpanded or $.hasClass post.file.thumb, 'expanding'
|
||||
ImageExpand.expand post
|
||||
return
|
||||
|
||||
@ -90,87 +105,94 @@ ImageExpand =
|
||||
ImageExpand.contract post
|
||||
|
||||
contract: (post) ->
|
||||
{thumb} = post.file
|
||||
if post.file.isVideo and video = post.file.fullImage
|
||||
video.pause()
|
||||
TrashQueue.add video, post
|
||||
thumb.parentNode.href = video.src
|
||||
thumb.parentNode.target = '_blank'
|
||||
for eventName, cb of ImageExpand.videoCB
|
||||
$.off video, eventName, cb
|
||||
$.rm post.file.videoControls
|
||||
delete post.file.videoControls
|
||||
$.rmClass post.nodes.root, 'expanded-image'
|
||||
$.rmClass post.file.thumb, 'expanding'
|
||||
$.rmClass thumb, 'expanding'
|
||||
post.file.isExpanded = false
|
||||
post.file.fullImage.pause() if post.file.isVideo
|
||||
post.file.videoControls?.map($.rm)
|
||||
delete post.file.videoControls
|
||||
|
||||
expand: (post, src) ->
|
||||
expand: (post, src, disableAutoplay) ->
|
||||
# Do not expand images of hidden/filtered replies, or already expanded pictures.
|
||||
{thumb, isVideo} = post.file
|
||||
return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding'
|
||||
$.addClass thumb, 'expanding'
|
||||
if post.file.fullImage
|
||||
if el = post.file.fullImage
|
||||
# Expand already-loaded/ing picture.
|
||||
$.asap (-> (file = post.file.fullImage) and (file.videoHeight or file.naturalHeight)), ->
|
||||
ImageExpand.completeExpand post
|
||||
return
|
||||
file =
|
||||
$.el (if post.file.isImage then 'img' else 'video'),
|
||||
TrashQueue.remove el
|
||||
else
|
||||
el = post.file.fullImage = $.el (if isVideo then 'video' else 'img'),
|
||||
className: 'full-image'
|
||||
src: src or post.file.URL
|
||||
post.file.fullImage = file
|
||||
if isVideo
|
||||
file.loop = true
|
||||
file.controls = Conf['Show Controls']
|
||||
$.on file, 'error', ImageExpand.error
|
||||
$.asap (-> (file = post.file.fullImage) and (file.videoHeight or file.naturalHeight)), ->
|
||||
$.on el, 'error', ImageExpand.error
|
||||
el.src = src or post.file.URL
|
||||
if isVideo
|
||||
# XXX Firefox doesn't seem to size videos correctly?
|
||||
file.style.maxHeight = file.videoHeight + "px"
|
||||
file.style.maxWidth = file.videoWidth + "px"
|
||||
ImageExpand.completeExpand post
|
||||
$.after (if file.controls then thumb.parentNode else thumb), file
|
||||
el.loop = true
|
||||
$.after thumb, el unless el is thumb.nextSibling
|
||||
$.asap (-> el.videoHeight or el.naturalHeight), ->
|
||||
ImageExpand.completeExpand post, disableAutoplay
|
||||
|
||||
completeExpand: (post) ->
|
||||
{thumb} = post.file
|
||||
return unless $.hasClass thumb, 'expanding' # contracted before the image loaded
|
||||
post.file.isExpanded = true
|
||||
ImageExpand.setupVideo post if post.file.isVideo
|
||||
$.addClass post.nodes.root, 'expanded-image'
|
||||
$.rmClass post.file.thumb, 'expanding'
|
||||
completeExpand: (post, disableAutoplay) ->
|
||||
return unless $.hasClass post.file.thumb, 'expanding' # contracted before the image loaded
|
||||
unless post.nodes.root.parentNode
|
||||
# Image might start/finish loading before the post is inserted.
|
||||
# Don't scroll when it's expanded in a QP for example.
|
||||
ImageExpand.completeExpand2 post
|
||||
return
|
||||
{bottom} = post.nodes.root.getBoundingClientRect()
|
||||
$.queueTask ->
|
||||
ImageExpand.completeExpand2 post, disableAutoplay
|
||||
return unless bottom <= 0
|
||||
window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom
|
||||
|
||||
setupVideo: (post) ->
|
||||
{file} = post
|
||||
video = file.fullImage
|
||||
file.videoControls = []
|
||||
video.muted = not Conf['Allow Sound']
|
||||
if video.controls
|
||||
# contract link in file info
|
||||
completeExpand2: (post, disableAutoplay) ->
|
||||
$.addClass post.nodes.root, 'expanded-image'
|
||||
$.rmClass post.file.thumb, 'expanding'
|
||||
post.file.isExpanded = true
|
||||
if post.file.isVideo
|
||||
ImageExpand.setupVideoControls post
|
||||
Video.configure post.file.fullImage, disableAutoplay
|
||||
|
||||
videoCB: do ->
|
||||
# dragging to the left contracts the video
|
||||
mousedown = false
|
||||
mouseover: -> mousedown = false
|
||||
mousedown: (e) -> mousedown = true if e.button is 0
|
||||
mouseup: (e) -> mousedown = false if e.button is 0
|
||||
mouseout: (e) -> ImageExpand.contract(Get.postFromNode @) if mousedown and e.clientX <= @getBoundingClientRect().left
|
||||
click: (e) ->
|
||||
if @paused and not @controls
|
||||
@play()
|
||||
e.preventDefault()
|
||||
|
||||
setupVideoControls: (post) ->
|
||||
{file} = post
|
||||
{thumb} = file
|
||||
video = file.fullImage
|
||||
|
||||
# disable link to file so native controls can work
|
||||
file.thumb.parentNode.removeAttribute 'href'
|
||||
file.thumb.parentNode.removeAttribute 'target'
|
||||
|
||||
# setup callbacks on video element
|
||||
$.on video, eventName, cb for eventName, cb of ImageExpand.videoCB
|
||||
|
||||
# setup controls in file info
|
||||
file.videoControls = $.el 'span',
|
||||
className: 'video-controls'
|
||||
if Conf['Show Controls']
|
||||
contract = $.el 'a',
|
||||
textContent: 'contract'
|
||||
href: 'javascript:;'
|
||||
title: 'You can also contract the video by dragging it to the left.'
|
||||
$.on contract, 'click', (e) -> ImageExpand.contract post
|
||||
file.videoControls.push $.tn('\u00A0'), contract
|
||||
# drag left to contract
|
||||
file.mousedown = false
|
||||
$.on video, 'mousedown', (e) -> file.mousedown = true if e.button is 0
|
||||
$.on video, 'mouseup', (e) -> file.mousedown = false if e.button is 0
|
||||
$.on video, 'mouseover', (e) -> file.mousedown = false
|
||||
$.on video, 'mouseout', (e) ->
|
||||
if file.mousedown and e.clientX <= video.getBoundingClientRect().left
|
||||
ImageExpand.contract post
|
||||
if Conf['Autoplay']
|
||||
video.play()
|
||||
else unless video.controls
|
||||
play = $.el 'a',
|
||||
textContent: 'play'
|
||||
href: 'javascript:;'
|
||||
$.on play, 'click', (e) ->
|
||||
video[@textContent]()
|
||||
@textContent = if @textContent is 'play' then 'pause' else 'play'
|
||||
file.videoControls.push $.tn('\u00A0'), play
|
||||
$.add file.videoControls, [$.tn('\u00A0'), contract]
|
||||
$.add file.text, file.videoControls
|
||||
|
||||
error: ->
|
||||
@ -198,7 +220,7 @@ ImageExpand =
|
||||
|
||||
timeoutID = setTimeout ImageExpand.expand, 10000, post
|
||||
<% if (type === 'crx') { %>
|
||||
$.ajax post.file.URL,
|
||||
$.ajax @src,
|
||||
onloadend: ->
|
||||
return if @status isnt 404
|
||||
clearTimeout timeoutID
|
||||
@ -221,7 +243,7 @@ ImageExpand =
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
return if !Conf['Image Expansion']
|
||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||
|
||||
el = $.el 'span',
|
||||
textContent: 'Image Expansion'
|
||||
|
||||
@ -9,36 +9,50 @@ ImageHover =
|
||||
name: 'Image Hover'
|
||||
cb: @catalogNode
|
||||
node: ->
|
||||
return unless @file and (@file.isImage or @file.isVideo)
|
||||
return unless @file?.isImage or @file?.isVideo
|
||||
$.on @file.thumb, 'mouseover', ImageHover.mouseover
|
||||
catalogNode: ->
|
||||
return unless @thread.OP.file and (@thread.OP.file.isImage or @thread.OP.file.isVideo)
|
||||
return unless (file = @thread.OP.file) and (file.isImage or file.isVideo)
|
||||
$.on @nodes.thumb, 'mouseover', ImageHover.mouseover
|
||||
mouseover: (e) ->
|
||||
post = if $.hasClass @, 'thumb'
|
||||
g.posts[@parentNode.dataset.fullID]
|
||||
else
|
||||
Get.postFromNode @
|
||||
el = if post.file.isImage
|
||||
$.el 'img',
|
||||
id: 'ihover'
|
||||
src: post.file.URL
|
||||
{isVideo} = post.file
|
||||
if post.file.fullImage
|
||||
el = post.file.fullImage
|
||||
TrashQueue.remove el
|
||||
else
|
||||
$.el 'video',
|
||||
controls: false
|
||||
id: 'ihover'
|
||||
el = $.el (if isVideo then 'video' else 'img'),
|
||||
className: 'full-image'
|
||||
src: post.file.URL
|
||||
autoplay: Conf['Autoplay']
|
||||
muted: !Conf['Allow Sound']
|
||||
loop: true
|
||||
$.add Header.hover, el
|
||||
post.file.fullImage = el
|
||||
{thumb} = post.file
|
||||
|
||||
if d.body.contains thumb
|
||||
$.after thumb, el unless el is thumb.nextSibling
|
||||
else
|
||||
$.add Header.hover, el if el.parentNode isnt Header.hover
|
||||
el.id = 'ihover'
|
||||
el.dataset.fullID = post.fullID
|
||||
if isVideo
|
||||
el.loop = true
|
||||
el.controls = false
|
||||
el.muted = not Conf['Allow Sound']
|
||||
el.play() if Conf['Autoplay']
|
||||
UI.hover
|
||||
root: @
|
||||
el: el
|
||||
latestEvent: e
|
||||
endEvents: 'mouseout click'
|
||||
asapTest: -> post.file.isVideo or el.naturalHeight
|
||||
asapTest: -> (el.videoHeight or el.naturalHeight)
|
||||
noRemove: true
|
||||
cb: ->
|
||||
if isVideo
|
||||
el.pause()
|
||||
TrashQueue.add el, post
|
||||
el.removeAttribute 'id'
|
||||
$.on el, 'error', ImageHover.error
|
||||
error: ->
|
||||
return unless doc.contains @
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
ImageLoader =
|
||||
init: ->
|
||||
return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"]
|
||||
return unless Conf["Image Prefetching"] or Conf["Replace JPG"] or Conf["Replace PNG"] or Conf["Replace GIF"] or Conf["Replace WEBM"]
|
||||
|
||||
Post.callbacks.push
|
||||
name: 'Image Replace'
|
||||
cb: @node
|
||||
|
||||
|
||||
Thread.callbacks.push
|
||||
name: 'Image Replace'
|
||||
cb: @thread
|
||||
|
||||
return unless Conf['Image Prefetching'] and g.VIEW is 'thread'
|
||||
|
||||
|
||||
prefetch = $.el 'label',
|
||||
innerHTML: '<input type=checkbox name="prefetch"> Prefetch Images'
|
||||
|
||||
@ -22,27 +20,49 @@ ImageLoader =
|
||||
type: 'header'
|
||||
el: prefetch
|
||||
order: 104
|
||||
|
||||
|
||||
thread: ->
|
||||
ImageLoader.thread = @
|
||||
|
||||
node: ->
|
||||
return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage
|
||||
return unless @file
|
||||
{isImage, isVideo} = @file
|
||||
return if @isClone or @isHidden or @thread.isHidden or !(isImage or isVideo)
|
||||
{thumb, URL} = @file
|
||||
return unless (Conf[string = "Replace #{if (type = (URL.match /\w{3}$/)[0].toUpperCase()) is 'PEG' then 'JPG' else type}"] and !/spoiler/.test thumb.src) or Conf['prefetch']
|
||||
{style} = thumb
|
||||
type = if (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) is 'JPEG' then 'JPG' else match
|
||||
replace = "Replace #{type}"
|
||||
return unless (Conf[replace] and !/spoiler/.test thumb.src) or (Conf['prefetch'] and g.VIEW is 'thread')
|
||||
if @file.isSpoiler
|
||||
# Revealed spoilers do not have height/width set, this fixes the image's dimensions.
|
||||
{style} = thumb
|
||||
style.maxHeight = style.maxWidth = if @isReply then '125px' else '250px'
|
||||
img = $.el 'img'
|
||||
if Conf[string]
|
||||
$.on img, 'load', ->
|
||||
# Replace the thumbnail once the GIF has finished loading.
|
||||
file = $.el if isImage then 'img' else 'video'
|
||||
if Conf[replace]
|
||||
if isVideo
|
||||
|
||||
file.alt = thumb.alt
|
||||
file.dataset.md5 = thumb.dataset.md5
|
||||
file.style.height = style.height
|
||||
file.style.width = style.width
|
||||
file.style.maxHeight = style.maxHeight
|
||||
file.style.maxWidth = style.maxWidth
|
||||
file.loop = true
|
||||
file.autoplay = Conf['Autoplay']
|
||||
if Conf['Image Hover']
|
||||
$.on file, 'mouseover', ImageHover.mouseover
|
||||
cb = =>
|
||||
$.off file, 'load loadedmetadata', cb
|
||||
# Replace the thumbnail once the file has finished loading.
|
||||
if isVideo
|
||||
$.replace thumb, file
|
||||
@file.thumb = file # XXX expanding requires the post.file.thumb node.
|
||||
return
|
||||
thumb.src = URL
|
||||
img.src = URL
|
||||
$.on file, 'load loadedmetadata', cb
|
||||
file.src = URL
|
||||
|
||||
toggle: ->
|
||||
enabled = Conf['prefetch'] = @checked
|
||||
if enabled
|
||||
ImageLoader.thread.posts.forEach ImageLoader.node.call
|
||||
g.BOARD.posts.forEach ImageLoader.node.call
|
||||
return
|
||||
14
src/Images/TrashQueue.coffee
Normal file
14
src/Images/TrashQueue.coffee
Normal file
@ -0,0 +1,14 @@
|
||||
TrashQueue =
|
||||
init: -> return
|
||||
|
||||
add: (video, post) ->
|
||||
if @killNext and video isnt @killNext
|
||||
delete @killNextPost?.file?.fullImage
|
||||
$.rm @killNext
|
||||
@killNext = video
|
||||
@killNextPost = post
|
||||
|
||||
remove: (video) ->
|
||||
if video is @killNext
|
||||
delete @killNext
|
||||
|
||||
21
src/Images/Video.coffee
Normal file
21
src/Images/Video.coffee
Normal file
@ -0,0 +1,21 @@
|
||||
Video =
|
||||
configure: (video, disableAutoplay) ->
|
||||
video.loop = true
|
||||
video.muted = !Conf['Allow Sound']
|
||||
video.controls = Conf['Show Controls']
|
||||
video.autoplay = false
|
||||
if Conf['Autoplay'] and not disableAutoplay
|
||||
Video.start video
|
||||
else
|
||||
video.pause()
|
||||
|
||||
start: (video) ->
|
||||
{controls} = video
|
||||
video.controls = false
|
||||
video.play()
|
||||
# Hacky workaround for Firefox forever-loading bug for very short videos
|
||||
if controls
|
||||
$.asap (-> (video.readyState >= 3 and video.currentTime <= Math.max 0.1, (video.duration - 0.5)) or !d.contains video), ->
|
||||
video.controls = true
|
||||
, 500
|
||||
|
||||
@ -8,7 +8,7 @@ ExpandThread =
|
||||
return unless summary = $.x 'following-sibling::*[contains(@class,"summary")][1]', thread.OP.nodes.root
|
||||
a = $.el 'a',
|
||||
textContent: ExpandThread.text '+', summary.textContent.match(/\d+/g)...
|
||||
href: "#{thread.board.ID}/res/#{thread.ID}"
|
||||
href: "res/#{thread.ID}"
|
||||
className: 'summary'
|
||||
$.on a, 'click', ExpandThread.cbToggle
|
||||
$.replace summary, a
|
||||
|
||||
@ -299,13 +299,11 @@ QR =
|
||||
blob.name = url.substr(url.lastIndexOf('/')+1, url.length)
|
||||
name_start = header.indexOf('name="') + 6
|
||||
if (name_start - 6 != -1)
|
||||
name_end = header.substr(name_start, header.length).indexOf('"')
|
||||
name_end = header.substr(name_start, header.length).indexOf('"')
|
||||
blob.name = header.substr(name_start, name_end)
|
||||
|
||||
return if blob.type is null
|
||||
QR.error "Unsupported file type."
|
||||
return unless blob.type in QR.mimeTypes
|
||||
QR.error "Unsupported file type."
|
||||
QR.handleFiles([blob])
|
||||
|
||||
handleUrl: ->
|
||||
@ -778,6 +776,7 @@ QR =
|
||||
QR.cooldown.auto = false
|
||||
QR.status()
|
||||
QR.error err
|
||||
QR.captcha.setup() if QR.captcha.isEnabled
|
||||
return
|
||||
|
||||
h1 = $ 'h1', resDoc
|
||||
@ -812,10 +811,13 @@ QR =
|
||||
# Enable auto-posting if we have stuff left to post, disable it otherwise.
|
||||
postsCount = QR.posts.length - 1
|
||||
QR.cooldown.auto = postsCount and isReply
|
||||
QR.captcha.setup() if QR.captcha.isEnabled and QR.cooldown.auto
|
||||
|
||||
unless Conf['Persistent QR'] or QR.cooldown.auto
|
||||
QR.close()
|
||||
else
|
||||
if QR.posts.length > 1
|
||||
QR.captcha.setup()
|
||||
post.rm()
|
||||
|
||||
QR.cooldown.set {req, post, isReply, threadID}
|
||||
|
||||
@ -80,8 +80,6 @@ QR.post = class
|
||||
$.rmClass QR.nodes.el, 'dump'
|
||||
else if @ is QR.selected
|
||||
(QR.posts[index-1] or QR.posts[index+1]).select()
|
||||
if QR.captcha.isEnabled
|
||||
QR.captcha.setup()
|
||||
QR.posts.splice index, 1
|
||||
QR.status()
|
||||
delete: ->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user