diff --git a/LICENSE b/LICENSE index d6ce97d24..bf2f9d5ae 100755 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* 4chan X - Version 1.2.36 - 2013-08-26 +* 4chan X - Version 1.2.36 - 2013-08-30 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE diff --git a/builds/4chan-X.user.js b/builds/4chan-X.user.js index 3f7468b7f..c3ac33243 100644 --- a/builds/4chan-X.user.js +++ b/builds/4chan-X.user.js @@ -22,7 +22,7 @@ // ==/UserScript== /* -* 4chan X - Version 1.2.36 - 2013-08-26 +* 4chan X - Version 1.2.36 - 2013-08-30 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -2252,22 +2252,22 @@ if (post.no === postID) { break; } - if (post.no > postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - withCredentials: url.archive.withCredentials - }); - } else { - $.addClass(root, 'warning'); - root.textContent = "Post No." + postID + " was not found."; - } - return; + } + if (post.no !== postID) { + if (url = Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + $.cache(url, function() { + return Get.archivedPost(this, boardID, postID, root, context); + }, { + withCredentials: url.archive.withCredentials + }); + } else { + $.addClass(root, 'warning'); + root.textContent = "Post No." + postID + " was not found."; } + return; } board = g.boards[boardID] || new Board(boardID); thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); @@ -2528,7 +2528,7 @@ entries = __slice.call(entry.parentNode.children); entries.sort(function(first, second) { - return +(first.style.order || first.style.webkitOrder) - +(second.style.order || second.style.webkitOrder); + return first.style.order - second.style.order; }); return entries[entries.indexOf(entry) + direction]; }; @@ -2619,7 +2619,7 @@ }; Menu.prototype.parseEntry = function(entry) { - var el, style, subEntries, subEntry, _i, _len; + var el, subEntries, subEntry, _i, _len; el = entry.el, subEntries = entry.subEntries; $.addClass(el, 'entry'); @@ -2627,8 +2627,7 @@ e.stopPropagation(); return this.focus(el); }).bind(this)); - style = el.style; - style.webkitOrder = style.order = entry.order || 100; + el.style.order = entry.order || 100; if (!subEntries) { return; } @@ -5797,7 +5796,9 @@ this.nodes.label.hidden = false; } URL.revokeObjectURL(this.URL); - this.showFileData(); + if (this === QR.selected) { + this.showFileData(); + } if (!/^image/.test(file.type)) { this.nodes.el.style.backgroundImage = null; return; @@ -6434,9 +6435,11 @@ QR.captcha.nodes.input.focus(); return window.focus(); }; - setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); + notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); @@ -6914,7 +6917,6 @@ ImageExpand.expand(post); return; } - ImageExpand.contract(post); root = post.nodes.root; rect = (Conf['Advance on contract'] ? (function() { var next; @@ -6939,8 +6941,9 @@ x = -window.scrollX; } if (x || y) { - return window.scrollBy(x, y); + window.scrollBy(x, y); } + return ImageExpand.contract(post); }, contract: function(post) { $.rmClass(post.nodes.root, 'expanded-image'); @@ -7935,7 +7938,7 @@ online: function() { if (ThreadUpdater.online = navigator.onLine) { ThreadUpdater.outdateCount = 0; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); ThreadUpdater.update(); ThreadUpdater.set('status', null, null); } else { @@ -7976,7 +7979,7 @@ } ThreadUpdater.outdateCount = 0; if (ThreadUpdater.seconds > ThreadUpdater.interval) { - return ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + return ThreadUpdater.setInterval(); } }, scrollBG: function() { @@ -7988,7 +7991,7 @@ }, autoUpdate: function() { if (ThreadUpdater.online) { - return ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); + return ThreadUpdater.timeout(); } else { return clearTimeout(ThreadUpdater.timeoutID); } @@ -8003,21 +8006,29 @@ ThreadUpdater.interval = this.value = val; return $.cb.value.call(this); }, - load: function() { + load: function(e) { var klass, req, text, _ref; req = ThreadUpdater.req; + if (e.type !== 'loadend') { + req.onloadend = null; + delete ThreadUpdater.req; + if (e.type === 'timeout') { + ThreadUpdater.set('status', 'Retrying', null); + ThreadUpdater.update(); + } + return; + } switch (req.status) { case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); break; case 404: g.DEAD = true; ThreadUpdater.set('timer', null); ThreadUpdater.set('status', '404', 'warning'); - clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.thread.kill(); $.event('ThreadUpdate', { 404: true, @@ -8026,7 +8037,7 @@ break; default: ThreadUpdater.outdateCount++; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -8036,15 +8047,18 @@ return delete ThreadUpdater.req; } }, - getInterval: function() { - var i, j; + setInterval: function() { + var cur, i, j; i = ThreadUpdater.interval; - j = Math.min(ThreadUpdater.outdateCount, 10); + j = (cur = ThreadUpdater.outdateCount < 10) ? cur : 10; if (!d.hidden) { - j = Math.min(j, 7); + j = j < 7 ? j : 7; } - return ThreadUpdater.seconds = Conf['Optional Increase'] ? Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]) : i; + ThreadUpdater.seconds = Conf['Optional Increase'] ? (cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i) ? cur : i : i; + ThreadUpdater.set('timer', ThreadUpdater.seconds); + clearTimeout(ThreadUpdater.timeoutID); + return ThreadUpdater.timeout(); }, intervalShortcut: function() { var settings; @@ -8067,16 +8081,10 @@ } }, timeout: function() { - var n; - ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - if (!(n = --ThreadUpdater.seconds)) { + ThreadUpdater.set('timer', --ThreadUpdater.seconds); + if (ThreadUpdater.seconds <= 0) { return ThreadUpdater.update(); - } else if (n <= -60) { - ThreadUpdater.set('status', 'Retrying', null); - return ThreadUpdater.update(); - } else if (n > 0) { - return ThreadUpdater.set('timer', n); } }, update: function() { @@ -8085,19 +8093,21 @@ if (!ThreadUpdater.online) { return; } - ThreadUpdater.seconds = 0; + clearTimeout(ThreadUpdater.timeoutID); if (Conf['Auto Update']) { ThreadUpdater.set('timer', '...'); } else { ThreadUpdater.set('timer', 'Update'); } if (ThreadUpdater.req) { - ThreadUpdater.req.onloadend = null; ThreadUpdater.req.abort(); } url = "//api.4chan.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { - onloadend: ThreadUpdater.cb.load + onabort: ThreadUpdater.cb.load, + onloadend: ThreadUpdater.cb.load, + ontimeout: ThreadUpdater.cb.load, + timeout: $.MINUTE }, { whenModified: true }); @@ -8900,9 +8910,11 @@ Header.scrollToPost(post.nodes.root); return window.focus(); }; - return setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); + return notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; }, onUpdate: function(e) { if (e.detail[404]) { diff --git a/builds/crx/manifest.json b/builds/crx/manifest.json index 107f69c78..5c3c2d3b3 100755 --- a/builds/crx/manifest.json +++ b/builds/crx/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "http://seaweedchan.github.io/4chan-x/", - "minimum_chrome_version": "27", + "minimum_chrome_version": "29", "permissions": [ "storage" ] diff --git a/builds/crx/script.js b/builds/crx/script.js index 97b6c40e3..5a2e11ec3 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* 4chan X - Version 1.2.36 - 2013-08-26 +* 4chan X - Version 1.2.36 - 2013-08-30 * * Licensed under the MIT license. * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE @@ -2266,22 +2266,22 @@ if (post.no === postID) { break; } - if (post.no > postID) { - if (url = Redirect.to('post', { - boardID: boardID, - postID: postID - })) { - $.cache(url, function() { - return Get.archivedPost(this, boardID, postID, root, context); - }, { - withCredentials: url.archive.withCredentials - }); - } else { - $.addClass(root, 'warning'); - root.textContent = "Post No." + postID + " was not found."; - } - return; + } + if (post.no !== postID) { + if (url = Redirect.to('post', { + boardID: boardID, + postID: postID + })) { + $.cache(url, function() { + return Get.archivedPost(this, boardID, postID, root, context); + }, { + withCredentials: url.archive.withCredentials + }); + } else { + $.addClass(root, 'warning'); + root.textContent = "Post No." + postID + " was not found."; } + return; } board = g.boards[boardID] || new Board(boardID); thread = g.threads["" + boardID + "." + threadID] || new Thread(threadID, board); @@ -2542,7 +2542,7 @@ entries = __slice.call(entry.parentNode.children); entries.sort(function(first, second) { - return +(first.style.order || first.style.webkitOrder) - +(second.style.order || second.style.webkitOrder); + return first.style.order - second.style.order; }); return entries[entries.indexOf(entry) + direction]; }; @@ -2633,7 +2633,7 @@ }; Menu.prototype.parseEntry = function(entry) { - var el, style, subEntries, subEntry, _i, _len; + var el, subEntries, subEntry, _i, _len; el = entry.el, subEntries = entry.subEntries; $.addClass(el, 'entry'); @@ -2641,8 +2641,7 @@ e.stopPropagation(); return this.focus(el); }).bind(this)); - style = el.style; - style.webkitOrder = style.order = entry.order || 100; + el.style.order = entry.order || 100; if (!subEntries) { return; } @@ -5186,10 +5185,12 @@ notif.onclose = function() { return notice.close(); }; - return setTimeout(function() { - notif.onclose = null; - return notif.close(); - }, 7 * $.SECOND); + return notif.onshow = function() { + return setTimeout(function() { + notif.onclose = null; + return notif.close(); + }, 7 * $.SECOND); + }; }, notifications: [], cleanNotifications: function() { @@ -5801,7 +5802,9 @@ this.nodes.label.hidden = false; } URL.revokeObjectURL(this.URL); - this.showFileData(); + if (this === QR.selected) { + this.showFileData(); + } if (!/^image/.test(file.type)) { this.nodes.el.style.backgroundImage = null; return; @@ -6419,9 +6422,11 @@ QR.captcha.nodes.input.focus(); return window.focus(); }; - setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); + notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; } if (!(Conf['Persistent QR'] || QR.cooldown.auto)) { QR.close(); @@ -6899,7 +6904,6 @@ ImageExpand.expand(post); return; } - ImageExpand.contract(post); root = post.nodes.root; rect = (Conf['Advance on contract'] ? (function() { var next; @@ -6924,8 +6928,9 @@ x = -window.scrollX; } if (x || y) { - return window.scrollBy(x, y); + window.scrollBy(x, y); } + return ImageExpand.contract(post); }, contract: function(post) { $.rmClass(post.nodes.root, 'expanded-image'); @@ -7920,7 +7925,7 @@ online: function() { if (ThreadUpdater.online = navigator.onLine) { ThreadUpdater.outdateCount = 0; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); ThreadUpdater.update(); ThreadUpdater.set('status', null, null); } else { @@ -7961,7 +7966,7 @@ } ThreadUpdater.outdateCount = 0; if (ThreadUpdater.seconds > ThreadUpdater.interval) { - return ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + return ThreadUpdater.setInterval(); } }, scrollBG: function() { @@ -7973,7 +7978,7 @@ }, autoUpdate: function() { if (ThreadUpdater.online) { - return ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); + return ThreadUpdater.timeout(); } else { return clearTimeout(ThreadUpdater.timeoutID); } @@ -7988,21 +7993,29 @@ ThreadUpdater.interval = this.value = val; return $.cb.value.call(this); }, - load: function() { + load: function(e) { var klass, req, text, _ref; req = ThreadUpdater.req; + if (e.type !== 'loadend') { + req.onloadend = null; + delete ThreadUpdater.req; + if (e.type === 'timeout') { + ThreadUpdater.set('status', 'Retrying', null); + ThreadUpdater.update(); + } + return; + } switch (req.status) { case 200: g.DEAD = false; ThreadUpdater.parse(JSON.parse(req.response).posts); - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); break; case 404: g.DEAD = true; ThreadUpdater.set('timer', null); ThreadUpdater.set('status', '404', 'warning'); - clearTimeout(ThreadUpdater.timeoutID); ThreadUpdater.thread.kill(); $.event('ThreadUpdate', { 404: true, @@ -8011,7 +8024,7 @@ break; default: ThreadUpdater.outdateCount++; - ThreadUpdater.set('timer', ThreadUpdater.getInterval()); + ThreadUpdater.setInterval(); _ref = req.status === 304 ? [null, null] : ["" + req.statusText + " (" + req.status + ")", 'warning'], text = _ref[0], klass = _ref[1]; ThreadUpdater.set('status', text, klass); } @@ -8021,15 +8034,18 @@ return delete ThreadUpdater.req; } }, - getInterval: function() { - var i, j; + setInterval: function() { + var cur, i, j; i = ThreadUpdater.interval; - j = Math.min(ThreadUpdater.outdateCount, 10); + j = (cur = ThreadUpdater.outdateCount < 10) ? cur : 10; if (!d.hidden) { - j = Math.min(j, 7); + j = j < 7 ? j : 7; } - return ThreadUpdater.seconds = Conf['Optional Increase'] ? Math.max(i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j]) : i; + ThreadUpdater.seconds = Conf['Optional Increase'] ? (cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i) ? cur : i : i; + ThreadUpdater.set('timer', ThreadUpdater.seconds); + clearTimeout(ThreadUpdater.timeoutID); + return ThreadUpdater.timeout(); }, intervalShortcut: function() { var settings; @@ -8052,16 +8068,10 @@ } }, timeout: function() { - var n; - ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); - if (!(n = --ThreadUpdater.seconds)) { + ThreadUpdater.set('timer', --ThreadUpdater.seconds); + if (ThreadUpdater.seconds <= 0) { return ThreadUpdater.update(); - } else if (n <= -60) { - ThreadUpdater.set('status', 'Retrying', null); - return ThreadUpdater.update(); - } else if (n > 0) { - return ThreadUpdater.set('timer', n); } }, update: function() { @@ -8070,19 +8080,21 @@ if (!ThreadUpdater.online) { return; } - ThreadUpdater.seconds = 0; + clearTimeout(ThreadUpdater.timeoutID); if (Conf['Auto Update']) { ThreadUpdater.set('timer', '...'); } else { ThreadUpdater.set('timer', 'Update'); } if (ThreadUpdater.req) { - ThreadUpdater.req.onloadend = null; ThreadUpdater.req.abort(); } url = "//api.4chan.org/" + ThreadUpdater.thread.board + "/res/" + ThreadUpdater.thread + ".json"; return ThreadUpdater.req = $.ajax(url, { - onloadend: ThreadUpdater.cb.load + onabort: ThreadUpdater.cb.load, + onloadend: ThreadUpdater.cb.load, + ontimeout: ThreadUpdater.cb.load, + timeout: $.MINUTE }, { whenModified: true }); @@ -8885,9 +8897,11 @@ Header.scrollToPost(post.nodes.root); return window.focus(); }; - return setTimeout(function() { - return notif.close(); - }, 7 * $.SECOND); + return notif.onshow = function() { + return setTimeout(function() { + return notif.close(); + }, 7 * $.SECOND); + }; }, onUpdate: function(e) { if (e.detail[404]) { @@ -11392,15 +11406,6 @@ Conf['CachedTitles'] = []; $.get(Conf, function(items) { $.extend(Conf, items); - if (!items) { - new Notice('error', $.el('span', { - innerHTML: "It seems like your 4chan X settings became corrupted due to a Chrome bug.
\nUnfortunately, you'll have to fix it yourself." - })); - Main.logError({ - message: 'Chrome Storage API bug', - error: new Error('~') - }); - } return Main.initFeatures(); }); $.on(d, '4chanMainInit', Main.initStyle); diff --git a/css/style.css b/css/style.css new file mode 100644 index 000000000..c2646837c --- /dev/null +++ b/css/style.css @@ -0,0 +1,939 @@ +/* General */ +.dialog { + box-shadow: 0 1px 2px rgba(0, 0, 0, .15); + border: 1px solid; + display: block; + padding: 0; +} +.field { + background-color: #FFF; + border: 1px solid #CCC; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #333; + font-family: inherit; + font-size: 13px; + margin: 0; + padding: 2px 4px 3px; + outline: none; + transition: color .25s, border-color .25s, flex .25s; +} +.field::-moz-placeholder, +.field:hover::-moz-placeholder { + color: #AAA !important; +} +.field:hover { + border-color: #999; +} +.field:hover, .field:focus { + color: #000; +} +.field[disabled] { + background-color: #F2F2F2; + color: #888; +} +.move { + cursor: move; +} +label, .watcher-toggler { + cursor: pointer; +} +a[href="javascript:;"] { + text-decoration: none; +} +.warning { + color: red; +} + +/* 4chan style fixes */ +.opContainer, .op { + display: block !important; +} +.post { + overflow: visible !important; +} +[hidden] { + display: none !important; +} + +/* fixed, z-index */ +#overlay, +#qp, #ihover, +#updater, #thread-stats, +#navlinks, #header, +#qr { + position: fixed; +} +#overlay { + z-index: 999; +} +#notifications { + z-index: 70; +} +#qp, #ihover { + z-index: 60; +} +#menu { + z-index: 50; +} +#navlinks, #updater, #thread-stats { + z-index: 40; +} +#qr { + z-index: 30; +} +#thread-watcher:hover { + z-index: 20; +} +#header { + z-index: 10; +} +#thread-watcher { + z-index: 5; +} + +/* Header */ +:root.top-header body { + margin-top: 2em; +} +:root.bottom-header body { + margin-bottom: 2em; +} +:root.fourchan-x #navtopright, +:root.fourchan-x #navbotright, +:root.fourchan-x:not(.show-original-top-board-list) #boardNavDesktop, +:root.fourchan-x:not(.show-original-bot-board-list) #boardNavDesktopFoot { + display: none !important; +} +#header { + right: 0; + left: 0; +} +#header.top { + top: 0; +} +#header.bottom { + bottom: 0; +} +#header-bar { + border-width: 0; + display: flex; + padding: 3px; + position: relative; + transition: all .1s .05s ease-in-out; +} +#header.top #header-bar { + border-bottom-width: 1px; +} +#header.bottom #header-bar { + box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); + border-top-width: 1px; +} +#board-list { + flex: 1; + align-self: center; + text-align: center; +} +#header-bar.autohide:not(:hover) { + box-shadow: none; + transition: all .8s .6s cubic-bezier(.55, .055, .675, .19); +} +#header.top #header-bar.autohide:not(:hover) { + margin-bottom: -1em; + -webkit-transform: translateY(-100%); + transform: translateY(-100%); +} +#header.bottom #header-bar.autohide:not(:hover) { + -webkit-transform: translateY(100%); + transform: translateY(100%); +} +#toggle-header-bar { + left: 0; + right: 0; + height: 10px; + position: absolute; +} +#header.top #toggle-header-bar { + cursor: n-resize; + bottom: -10px; +} +#header.bottom #toggle-header-bar { + cursor: s-resize; + top: -10px; +} +#header.top #header-bar.autohide #toggle-header-bar { + cursor: s-resize; +} +#header.bottom #header-bar.autohide #toggle-header-bar { + cursor: n-resize; +} +#header-bar a:not(.entry) { + text-decoration: none; + padding: 1px; +} +.shortcut:not(:last-child)::after { + content: " / "; +} +.brackets-wrap::before { + content: " [ "; +} +.brackets-wrap::after { + content: " ] "; +} + +/* Notifications */ +#notifications { + height: 0; + text-align: center; +} +#header.bottom #notifications { + position: fixed; + top: 0; + left: 0; + width: 100%; +} +.notification { + color: #FFF; + font-weight: 700; + text-shadow: 0 1px 2px rgba(0, 0, 0, .5); + box-shadow: 0 1px 2px rgba(0, 0, 0, .15); + border-radius: 2px; + margin: 1px auto; + width: 500px; + max-width: 100%; + position: relative; + transition: all .25s ease-in-out; +} +.notification.error { + background-color: hsla(0, 100%, 38%, .9); +} +.notification.warning { + background-color: hsla(36, 100%, 38%, .9); +} +.notification.info { + background-color: hsla(200, 100%, 38%, .9); +} +.notification.success { + background-color: hsla(104, 100%, 38%, .9); +} +.notification a { + color: white; +} +.notification > .close { + padding: 6px; + top: 0; + right: 0; + position: absolute; +} +.message { + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 6px 20px; + max-height: 200px; + width: 100%; + overflow: auto; +} + +/* Settings */ +:root.fourchan-x body { + -moz-box-sizing: border-box; + box-sizing: border-box; +} +#overlay { + background-color: rgba(0, 0, 0, .5); + display: flex; + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; +} +#fourchanx-settings { + -moz-box-sizing: border-box; + box-sizing: border-box; + box-shadow: 0 0 15px rgba(0, 0, 0, .15); + height: 600px; + max-height: 100%; + width: 900px; + max-width: 100%; + margin: auto; + padding: 3px; + display: flex; + flex-direction: column; +} +#fourchanx-settings > nav { + display: flex; + padding: 2px 2px 0; +} +#fourchanx-settings > nav a { + text-decoration: underline; +} +#fourchanx-settings > nav a.close { + text-decoration: none; + padding: 2px; +} +.sections-list { + flex: 1; +} +.tab-selected { + font-weight: 700; +} +.section-container { + flex: 1; + position: relative; +} +.section-container > section { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow: auto; +} +.section-sauce ul, +.section-rice ul { + list-style: none; + margin: 0; + padding: 8px; +} +.section-sauce li, +.section-rice li { + padding-left: 4px; +} +.section-main label { + text-decoration: underline; +} +.section-filter ul, +.section-qr ul { + padding: 0; +} +.section-filter li, +.section-qr li { + margin: 10px 40px; +} +.section-filter textarea { + height: 500px; +} +.section-qr textarea { + height: 200px; +} +.section-sauce textarea { + height: 350px; +} +.section-rice .field[name="boardnav"] { + width: 100%; +} +.section-rice textarea { + height: 150px; +} +.section-archives table { + width: 100%; +} +.section-archives th:not(:first-child) { + width: 30%; +} +.section-archives td { + text-align: center; +} +.section-archives select { + width: 90%; +} +.section-keybinds .field { + font-family: monospace; +} +#fourchanx-settings fieldset { + border: 1px solid; + border-radius: 3px; +} +#fourchanx-settings legend { + font-weight: 700; +} +#fourchanx-settings textarea { + font-family: monospace; + min-width: 100%; + max-width: 100%; +} +#fourchanx-settings code { + color: #000; + background-color: #FFF; + padding: 0 2px; +} +.unscroll { + overflow: hidden; +} + +/* Announcement Hiding */ +:root.hide-announcement #globalMessage, +:root.hide-announcement-enabled #toggleMsgBtn { + display: none; +} +a.hide-announcement { + float: left; +} + +/* Unread */ +#unread-line { + margin: 0; +} + +/* Thread Updater */ +#updater:not(:hover) { + background: none; + border: none; + box-shadow: none; +} +#updater > .move { + padding: 0 3px; +} +#updater > div:last-child { + text-align: center; +} +#updater input[type=number] { + width: 4em; +} +#updater:not(:hover) > div:not(.move) { + display: none; +} +#updater input[type="button"] { + width: 100%; +} +.new { + color: limegreen; +} + +/* Thread Watcher */ +#thread-watcher { + max-width: 200px; + min-width: 150px; + padding: 3px; + position: absolute; +} +#thread-watcher > div:first-child { + display: flex; + align-items: center; +} +#thread-watcher .move { + flex: 1; +} +#watcher-status:not(:empty)::before { + content: "("; +} +#watcher-status:not(:empty)::after { + content: ")"; +} +#watched-threads:not(:hover) { + max-height: 150px; + overflow: hidden; +} +#watched-threads div { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +#watched-threads .current { + font-weight: 700; +} +#watched-threads a { + text-decoration: none; +} +#watched-threads .dead-thread a[title] { + text-decoration: line-through; +} + +/* Thread Stats */ +#thread-stats { + background: none; + border: none; + box-shadow: none; +} + +/* Quote */ +.deadlink { + text-decoration: none !important; +} +.backlink.deadlink:not(.forwardlink), +.quotelink.deadlink:not(.forwardlink) { + text-decoration: underline !important; +} +.inlined { + opacity: .5; +} +#qp input, .forwarded { + display: none; +} +.quotelink.forwardlink, +.backlink.forwardlink { + text-decoration: none; + border-bottom: 1px dashed; +} +.filtered { + text-decoration: underline line-through; +} +.inline { + border: 1px solid; + display: table; + margin: 2px 0; +} +.inline .post { + border: 0 !important; + background-color: transparent !important; + display: table !important; + margin: 0 !important; + padding: 1px 2px !important; +} +#qp > .opContainer::after { + content: ''; + clear: both; + display: table; +} +#qp .post { + border: none; + margin: 0; + padding: 2px 2px 5px; +} +#qp img { + max-height: 80vh; + max-width: 50vw; +} +.qphl { + outline: 2px solid rgba(216, 94, 49, .7); +} + +/* File */ +.fileText:hover .fntrunc, +.fileText:not(:hover) .fnfull, +.expanded-image > .post > .file > .fileThumb > img[data-md5], +:not(.expanded-image) > .post > .file > .fileThumb > .full-image { + display: none; +} +.expanding { + opacity: .5; +} +.expanded-image { + clear: both; +} +.expanded-image > .op > .file::after { + content: ''; + clear: both; + display: table; +} +:root.fit-height .full-image { + max-height: 100vh; +} +:root.fit-width .full-image { + max-width: 100%; +} +:root.gecko.fit-width .full-image { + width: 100%; +} +#ihover { + -moz-box-sizing: border-box; + box-sizing: border-box; + max-height: 100%; + max-width: 75%; + padding-bottom: 16px; +} + +/* Index/Reply Navigation */ +#navlinks { + font-size: 16px; + top: 25px; + right: 10px; +} + +/* Filter */ +.opContainer.filter-highlight { + box-shadow: inset 5px 0 rgba(255, 0, 0, .5); +} +.filter-highlight > .reply { + box-shadow: -5px 0 rgba(255, 0, 0, .5); +} + +/* Thread & Reply Hiding */ +.hide-thread-button, +.hide-reply-button { + float: left; + margin-right: 2px; +} +.stub ~ * { + display: none !important; +} +.stub input { + display: inline-block; +} + +/* QR */ +:root.hide-original-post-form #postForm, +:root.hide-original-post-form .postingMode, +:root.hide-original-post-form #togglePostForm, +#qr.autohide:not(.has-focus):not(:hover) > form { + display: none; +} +#qr select, #dump-button, .remove, .captcha-img { + cursor: pointer; +} +#qr > div { + min-width: 300px; + display: flex; + align-items: center; +} +#qr .move { + align-self: stretch; + flex: 1; +} +#qr select { + margin: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border: none; + background: none; + font: inherit; +} +#qr option { + color: #000; + background-color: #F7F7F7; +} +#qr .close { + padding: 0 3px; +} +#qr > form { + display: flex; + flex-direction: column; +} +.persona { + display: flex; +} +.persona .field { + flex: 1; +} +.persona .field:hover, +.persona .field:focus { + flex: 3; +} +#dump-button { + background: linear-gradient(#EEE, #CCC); + border: 1px solid #CCC; + margin: 0; + padding: 2px 4px 3px; + outline: none; + width: 30px; +} +#dump-button:hover, +#dump-button:focus { + background: linear-gradient(#FFF, #DDD); +} +#dump-button:active, +.dump #dump-button:not(:hover):not(:focus) { + background: linear-gradient(#CCC, #DDD); +} +:root.gecko #dump-button { + padding: 0; +} +#qr:not(.dump) #dump-list-container { + display: none; +} +#dump-list-container { + height: 100px; + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +#dump-list { + counter-reset: qrpreviews; + top: 0; + right: 0; + bottom: 0; + left: 0; + overflow: hidden; + position: absolute; + white-space: nowrap; +} +#dump-list:hover { + bottom: -12px; + overflow-x: auto; + z-index: 1; +} +#dump-list::-webkit-scrollbar { + height: 12px; +} +#dump-list::-webkit-scrollbar-thumb { + border: 1px solid; +} +.qr-preview { + background-position: 50% 20%; + background-size: cover; + border: 1px solid #808080; + color: #FFF !important; + font-size: 12px; + -moz-box-sizing: border-box; + box-sizing: border-box; + cursor: move; + display: inline-block; + height: 92px; + width: 92px; + margin: 4px; + padding: 2px; + opacity: .6; + outline: none; + overflow: hidden; + position: relative; + text-shadow: 0 1px 1px #000; + transition: opacity .25s ease-in-out; + vertical-align: top; + white-space: pre; +} +.qr-preview:hover, +.qr-preview:focus { + opacity: .9; + color: #FFF !important; +} +.qr-preview#selected { + opacity: 1; +} +.qr-preview::before { + counter-increment: qrpreviews; + content: counter(qrpreviews); + font-weight: 700; + text-shadow: 0 0 3px #000, 0 0 5px #000; + position: absolute; + top: 3px; + right: 3px; +} +.qr-preview.drag { + border-color: red; + border-style: dashed; + opacity: 1; +} +.qr-preview.over { + border-color: #FFF; + border-style: dashed; + opacity: 1; +} +.remove { + color: #E00 !important; + font-weight: 700; + padding: 3px; +} +.remove:hover::after { + content: ' Remove'; +} +.qr-preview > label { + background: rgba(0, 0, 0, .5); + right: 0; + bottom: 0; + left: 0; + position: absolute; + text-align: center; +} +.qr-preview > label > input { + margin: 1px 0; + vertical-align: bottom; +} +#add-post { + display: inline-block; + font-size: 30px; + height: 30px; + width: 30px; + line-height: 1; + text-align: center; + position: absolute; + right: 0; + bottom: 0; + z-index: 1; +} +#qr textarea { + min-height: 160px; + min-width: 100%; + display: block; +} +#qr.has-captcha textarea { + min-height: 120px; +} +.textarea { + position: relative; +} +#char-count { + color: #000; + background: hsla(0, 0%, 100%, .5); + font-size: 8pt; + position: absolute; + bottom: 1px; + right: 1px; + pointer-events: none; +} +#char-count.warning { + color: red; +} +.captcha-img { + background: #FFF; + outline: 1px solid #CCC; + outline-offset: -1px; +} +.captcha-img > img { + display: block; + height: 57px; + width: 300px; +} +#file-n-submit-container { + position: relative; +} +#file-n-submit { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + align-items: center; +} +#file-n-submit-container input[type='file'] { + /* Keep it to set an appropriate height to the container. */ + visibility: hidden; +} +#file-n-submit-container input { + margin: 0; + padding: 0; +} +#file-n-submit input[type='submit'] { + order: 1; +} +#file-n-submit.has-file #qr-no-file, +#file-n-submit:not(.has-file) #qr-filename, +#file-n-submit:not(.has-file) #qr-filesize, +#file-n-submit:not(.has-file) #qr-file-spoiler, +#file-n-submit:not(.has-file) #qr-filerm, +#qr-filename:focus ~ #qr-filesize { + display: none; +} +#qr-no-file, +#qr-filename, +#qr-filesize, +#qr-filerm, +#qr-file-spoiler { + margin: 0 2px !important; +} +#qr-no-file { + cursor: default; + flex: 1; +} +#qr-filename { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: none; + border: none !important; + color: inherit; + font: inherit; + flex: 1; + text-overflow: ellipsis; +} +#qr-filesize { + font-size: .8em; +} +#qr-filesize::before { + content: " ("; +} +#qr-filesize::after { + content: ")"; +} + +/* Menu */ +.menu-button { + position: relative; +} +.menu-button i:not(.icon-reorder) { + border-top: 6px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + display: inline-block; + margin: 2px; + vertical-align: middle; +} +@media screen and (resolution: 1dppx) { + .icon-reorder { + font-size: 14px; + } + #shortcuts .icon-reorder { + vertical-align: -1px; + } +} +#menu { + border-bottom: 0; + display: flex; + margin: 2px 0; + flex-direction: column; + position: absolute; + outline: none; +} +#menu.top { + top: 100%; +} +#menu.bottom { + bottom: 100%; +} +#menu.left { + left: 0; +} +#menu.right { + right: 0; +} +.entry { + cursor: pointer; + outline: none; + padding: 3px 7px; + position: relative; + text-decoration: none; + white-space: nowrap; +} +.entry.disabled { + color: graytext !important; +} +.entry.has-submenu { + padding-right: 20px; +} +.has-submenu::after { + content: ''; + border-left: 6px solid; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + display: inline-block; + margin: 4px; + position: absolute; + right: 3px; +} +.has-submenu:not(.focused) > .submenu { + display: none; +} +.submenu { + border-bottom: 0; + display: flex; + flex-direction: column; + position: absolute; + margin: -1px 0; +} +.submenu.top { + top: 0; +} +.submenu.bottom { + bottom: 0; +} +.submenu.left { + left: 100%; +} +.submenu.right { + right: 100%; +} +.entry input { + margin: 0; +} + +/* colored uid */ + +.posteruid.painted { + padding: 0 5px; + border-radius: 1em; + font-size: 0.8em; + cursor: pointer; +} diff --git a/src/General/Get.coffee b/src/General/Get.coffee index d7a0863dd..5b17ab062 100755 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -118,17 +118,18 @@ Get = Build.spoilerRange[boardID] = posts[0].custom_spoiler for post in posts break if post.no is postID # we found it! - if post.no > postID - # The post can be deleted by the time we check a quote. - if url = Redirect.to 'post', {boardID, postID} - $.cache url, - -> Get.archivedPost @, boardID, postID, root, context - , - withCredentials: url.archive.withCredentials - else - $.addClass root, 'warning' - root.textContent = "Post No.#{postID} was not found." - return + + if post.no isnt postID + # The post can be deleted by the time we check a quote. + if url = Redirect.to 'post', {boardID, postID} + $.cache url, + -> Get.archivedPost @, boardID, postID, root, context + , + withCredentials: url.archive.withCredentials + else + $.addClass root, 'warning' + root.textContent = "Post No.#{postID} was not found." + return board = g.boards[boardID] or new Board boardID diff --git a/src/General/Main.coffee b/src/General/Main.coffee index d7a0c5763..e3d420d5b 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -19,18 +19,6 @@ Main = Conf['CachedTitles'] = [] $.get Conf, (items) -> $.extend Conf, items - <% if (type === 'crx') { %> - unless items - new Notice 'error', $.el 'span', - innerHTML: """ - It seems like your <%= meta.name %> settings became corrupted due to a Chrome bug.
- Unfortunately, you'll have to fix it yourself. - """ - # Track resolution of this bug. - Main.logError - message: 'Chrome Storage API bug' - error: new Error '~' - <% } %> Main.initFeatures() $.on d, '4chanMainInit', Main.initStyle diff --git a/src/General/UI.coffee b/src/General/UI.coffee index 27249e351..8fbe4d97a 100755 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -122,8 +122,7 @@ UI = do -> findNextEntry: (entry, direction) -> entries = [entry.parentNode.children...] - entries.sort (first, second) -> - +(first.style.order or first.style.webkitOrder) - +(second.style.order or second.style.webkitOrder) + entries.sort (first, second) -> first.style.order - second.style.order entries[entries.indexOf(entry) + direction] keybinds: (e) => @@ -197,8 +196,7 @@ UI = do -> e.stopPropagation() @focus el ).bind @ - {style} = el - style.webkitOrder = style.order = entry.order or 100 + el.style.order = entry.order or 100 return unless subEntries $.addClass el, 'has-submenu' for subEntry in subEntries diff --git a/src/General/meta/manifest.json b/src/General/meta/manifest.json index 53039fd37..b7c607807 100755 --- a/src/General/meta/manifest.json +++ b/src/General/meta/manifest.json @@ -15,7 +15,7 @@ "run_at": "document_start" }], "homepage_url": "<%= meta.page %>", - "minimum_chrome_version": "27", + "minimum_chrome_version": "29", "permissions": [ "storage" ] diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 790303a74..a44eae537 100755 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -58,7 +58,7 @@ ImageExpand = unless post.file.isExpanded or $.hasClass thumb, 'expanding' ImageExpand.expand post return - ImageExpand.contract post + # Scroll back to the thumbnail when contracting the image # to avoid being left miles away from the relevant post. {root} = post.nodes @@ -81,6 +81,7 @@ ImageExpand = if rect.left < 0 x = -window.scrollX window.scrollBy x, y if x or y + ImageExpand.contract post contract: (post) -> $.rmClass post.nodes.root, 'expanded-image' diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index 4411ff4c9..48988d195 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -83,10 +83,8 @@ ThreadUpdater = online: -> if ThreadUpdater.online = navigator.onLine ThreadUpdater.outdateCount = 0 - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() - + ThreadUpdater.setInterval() ThreadUpdater.update() - ThreadUpdater.set 'status', null, null else ThreadUpdater.set 'timer', null @@ -113,7 +111,7 @@ ThreadUpdater = # Reset the counter when we focus this tab. ThreadUpdater.outdateCount = 0 if ThreadUpdater.seconds > ThreadUpdater.interval - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() scrollBG: -> ThreadUpdater.scrollBG = if Conf['Scroll BG'] -> true @@ -121,7 +119,7 @@ ThreadUpdater = -> not d.hidden autoUpdate: -> if ThreadUpdater.online - ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 + ThreadUpdater.timeout() else clearTimeout ThreadUpdater.timeoutID interval: -> @@ -129,25 +127,31 @@ ThreadUpdater = if val < 1 then val = 1 ThreadUpdater.interval = @value = val $.cb.value.call @ - load: -> + load: (e) -> {req} = ThreadUpdater + if e.type isnt 'loadend' # timeout or abort + req.onloadend = null + delete ThreadUpdater.req + if e.type is 'timeout' + ThreadUpdater.set 'status', 'Retrying', null + ThreadUpdater.update() + return switch req.status when 200 g.DEAD = false ThreadUpdater.parse JSON.parse(req.response).posts - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() when 404 g.DEAD = true ThreadUpdater.set 'timer', null ThreadUpdater.set 'status', '404', 'warning' - clearTimeout ThreadUpdater.timeoutID ThreadUpdater.thread.kill() $.event 'ThreadUpdate', 404: true thread: ThreadUpdater.thread else ThreadUpdater.outdateCount++ - ThreadUpdater.set 'timer', ThreadUpdater.getInterval() + ThreadUpdater.setInterval() [text, klass] = if req.status is 304 [null, null] else @@ -159,17 +163,21 @@ ThreadUpdater = delete ThreadUpdater.req - getInterval: -> + setInterval: -> i = ThreadUpdater.interval - j = Math.min ThreadUpdater.outdateCount, 10 + # Math.min/max is provably slow: http://jsperf.com/math-s-min-max-vs-homemade/5 + j = if cur = ThreadUpdater.outdateCount < 10 then cur else 10 unless d.hidden # Lower the max refresh rate limit on visible tabs. - j = Math.min j, 7 + j = if j < 7 then j else 7 ThreadUpdater.seconds = if Conf['Optional Increase'] - Math.max i, [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] + if cur = [0, 5, 10, 15, 20, 30, 60, 90, 120, 240, 300][j] > i then cur else i else i + ThreadUpdater.set 'timer', ThreadUpdater.seconds + clearTimeout ThreadUpdater.timeoutID + ThreadUpdater.timeout() intervalShortcut: -> Settings.open 'Advanced' @@ -188,27 +196,24 @@ ThreadUpdater = timeout: -> ThreadUpdater.timeoutID = setTimeout ThreadUpdater.timeout, 1000 - unless n = --ThreadUpdater.seconds - ThreadUpdater.update() - else if n <= -60 - ThreadUpdater.set 'status', 'Retrying', null - ThreadUpdater.update() - else if n > 0 - ThreadUpdater.set 'timer', n + ThreadUpdater.set 'timer', --ThreadUpdater.seconds + ThreadUpdater.update() if ThreadUpdater.seconds <= 0 update: -> return unless ThreadUpdater.online - ThreadUpdater.seconds = 0 + clearTimeout ThreadUpdater.timeoutID if Conf['Auto Update'] ThreadUpdater.set 'timer', '...' else ThreadUpdater.set 'timer', 'Update' - if ThreadUpdater.req - # abort() triggers onloadend, we don't want that. - ThreadUpdater.req.onloadend = null - ThreadUpdater.req.abort() + ThreadUpdater.req.abort() if ThreadUpdater.req url = "//api.4chan.org/#{ThreadUpdater.thread.board}/res/#{ThreadUpdater.thread}.json" - ThreadUpdater.req = $.ajax url, onloadend: ThreadUpdater.cb.load, + ThreadUpdater.req = $.ajax url, + onabort: ThreadUpdater.cb.load + onloadend: ThreadUpdater.cb.load + ontimeout: ThreadUpdater.cb.load + timeout: $.MINUTE + , whenModified: true updateThreadStatus: (title, OP) -> diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index 7e5d78b5e..90a279ee8 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -104,9 +104,10 @@ Unread = notif.onclick = -> Header.scrollToPost post.nodes.root window.focus() - setTimeout -> - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.close() + , 7 * $.SECOND onUpdate: (e) -> if e.detail[404] diff --git a/src/Posting/QuickReply.coffee b/src/Posting/QuickReply.coffee index 75caae801..9516eb667 100755 --- a/src/Posting/QuickReply.coffee +++ b/src/Posting/QuickReply.coffee @@ -163,10 +163,11 @@ QR = # Firefox automatically closes notifications # so we can't control the onclose properly. notif.onclose = -> notice.close() - setTimeout -> - notif.onclose = null - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.onclose = null + notif.close() + , 7 * $.SECOND <% } %> notifications: [] @@ -665,7 +666,7 @@ QR = @filesize = $.bytesToString file.size @nodes.label.hidden = false if QR.spoiler URL.revokeObjectURL @URL - @showFileData() + @showFileData() if @ is QR.selected unless /^image/.test file.type @nodes.el.style.backgroundImage = null return @@ -1222,9 +1223,10 @@ QR = QR.open() QR.captcha.nodes.input.focus() window.focus() - setTimeout -> - notif.close() - , 7 * $.SECOND + notif.onshow = -> + setTimeout -> + notif.close() + , 7 * $.SECOND unless Conf['Persistent QR'] or QR.cooldown.auto QR.close()