Most of the Header is done, see changelog file.

Add 4chan and 4chan X settings links in the header's menu.

Add $.asap.

Add UI.Menu.
The menu API now requires a type ("post" or "header").

Add Get.boardsConfig.
This commit is contained in:
Nicolas Stepien 2013-02-09 18:40:18 +01:00
parent c54ddff83d
commit b6cf7220c4
7 changed files with 962 additions and 559 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,16 @@
alpha alpha
- Mayhem - Mayhem
Major rewrite of 4chan X.
New feature, the Header:
Access the list of boards directly from the Header.
Access to settings and extra features easily from the Header's menu.
Can be auto-hidden.
Added touch and multi-touch support for dragging windows. Added touch and multi-touch support for dragging windows.
The Thread Updater will pause when offline, and resume when online. The Thread Updater will pause when offline, and resume when online.
Added Thread & Post Hiding in the Menu, with individual settings. Added Thread & Post Hiding in the Menu, with individual settings.
Thread & Post Hiding Buttons can now be disabled in the settings. Thread & Post Hiding Buttons can now be disabled in the settings.
Recursive Hiding will be automatically applied when manually hiding a post. Recursive Hiding will be automatically applied when manually hiding a post.
Fix Chrome's install warning that 4chan X would execute on all domains. Fix Chrome's install warning saying that 4chan X would execute on all domains.
Fix Quote Backlinks not affecting inlined quotes. Fix Quote Backlinks not affecting inlined quotes.
Fix Quote Highlighting not affecting inlined quotes. Fix Quote Highlighting not affecting inlined quotes.

View File

@ -31,17 +31,20 @@ a[href="javascript:;"] {
/* fixed, z-index */ /* fixed, z-index */
#qp, #ihover, #qp, #ihover,
#updater, #stats, #updater, #stats,
#boardNavDesktop.reply, #header,
#qr, #watcher { #qr, #watcher {
position: fixed; position: fixed;
} }
#qp, #ihover { #qp, #ihover {
z-index: 100; z-index: 100;
} }
#menu {
z-index: 95;
}
#updater, #stats { #updater, #stats {
z-index: 90; z-index: 90;
} }
#boardNavDesktop.reply:hover { #header:hover {
z-index: 80; z-index: 80;
} }
#qr { #qr {
@ -50,38 +53,70 @@ a[href="javascript:;"] {
#watcher { #watcher {
z-index: 30; z-index: 30;
} }
#boardNavDesktop.reply { #header {
z-index: 10; z-index: 10;
} }
/* XXX support different styles */
#header-bar {
font-size: 9pt;
color: #89A;
background-color: #D6DAF0;
border-color: #B7C5D9;
border-width: 0 0 1px;
border-style: solid;
}
/* header */ /* header */
body.fourchan_x { body.fourchan_x {
margin-top: 2.5em; margin-top: 2em;
} }
#boardNavDesktop.reply { #header {
border-width: 0 0 1px;
padding: 4px;
top: 0; top: 0;
right: 0; right: 0;
left: 0; left: 0;
transition: opacity .1s ease-in-out;
-o-transition: opacity .1s ease-in-out;
-moz-transition: opacity .1s ease-in-out;
-webkit-transition: opacity .1s ease-in-out;
} }
#boardNavDesktop.reply:not(:hover) { #header-bar {
opacity: .4; padding: 4px;
transition: opacity 1.5s .5s ease-in-out; position: relative;
-o-transition: opacity 1.5s .5s ease-in-out; transition: all .1s ease-in-out;
-moz-transition: opacity 1.5s .5s ease-in-out; -o-transition: all .1s ease-in-out;
-webkit-transition: opacity 1.5s .5s ease-in-out; -moz-transition: all .1s ease-in-out;
-webkit-transition: all .1s ease-in-out;
} }
#boardNavDesktop.reply a { #header-bar.autohide:not(:hover) {
margin: -1px; transform: translateY(-100%);
-o-transform: translateY(-100%);
-moz-transform: translateY(-100%);
-webkit-transform: translateY(-100%);
transition: all .75s .25s ease-in-out;
-o-transition: all .75s .25s ease-in-out;
-moz-transition: all .75s .25s ease-in-out;
-webkit-transition: all .75s .25s ease-in-out;
} }
#settings { #toggle-header-bar {
cursor: n-resize;
left: 0;
right: 0;
bottom: -8px;
height: 10px;
position: absolute;
}
#header-bar.autohide #toggle-header-bar {
cursor: s-resize;
}
#header-bar a {
text-decoration: none;
padding: 1px;
}
#header-bar > .menu-button {
float: right; float: right;
padding: 0;
}
body > #boardNavDesktop,
#navtopright,
#boardNavDesktopFoot {
display: none !important;
} }
/* thread updater */ /* thread updater */

View File

@ -78,19 +78,19 @@ $.extend $,
value: -> value: ->
$.set @name, @value.trim() $.set @name, @value.trim()
Conf[@name] = @value Conf[@name] = @value
asap: (test, cb) ->
if test()
cb()
else
setTimeout $.asap, 25, test, cb
addStyle: (css) -> addStyle: (css) ->
style = $.el 'style', style = $.el 'style',
textContent: css textContent: css
# That's terrible. # XXX fix for scriptish:
# XXX tmp fix for scriptish:
# https://github.com/scriptish/scriptish/issues/16 # https://github.com/scriptish/scriptish/issues/16
f = -> $.asap (-> d.head), (->
# XXX Only Chrome has no d.head on document-start. $.add d.head, style
if root = d.head or d.documentElement )
$.add root, style
else
setTimeout f, 20
f()
style style
x: (path, root=d.body) -> x: (path, root=d.body) ->
# XPathResult.ANY_UNORDERED_NODE_TYPE === 8 # XPathResult.ANY_UNORDERED_NODE_TYPE === 8

View File

@ -10,6 +10,180 @@ UI = (->
move.addEventListener 'mousedown', dragstart, false move.addEventListener 'mousedown', dragstart, false
el el
class Menu
currentMenu = null
lastToggledButton = null
constructor: (@type) ->
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API
$.on d, 'AddMenuEntry', @addEntryListener.bind @
@close = close.bind @
@entries = []
makeMenu: ->
menu = $.el 'div',
className: 'reply dialog'
id: 'menu'
tabIndex: 0
$.on menu, 'click', (e) -> e.stopPropagation()
$.on menu, 'keydown', @keybinds.bind @
menu
toggle: (e, button, data) ->
e.preventDefault()
e.stopPropagation()
if currentMenu
# Close if it's already opened.
# Reopen if we clicked on another button.
previousButton = lastToggledButton
@close()
return if previousButton is button
return unless @entries.length
@open button, data
open: (button, data) ->
menu = @makeMenu()
currentMenu = menu
lastToggledButton = button
for entry in @entries
@insertEntry entry, menu, data
@focus $ '.entry', menu
$.on d, 'click', @close
$.add d.body, menu
# Position
mRect = menu.getBoundingClientRect()
bRect = button.getBoundingClientRect()
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left
cHeight = d.documentElement.clientHeight
cWidth = d.documentElement.clientWidth
top =
if bRect.top + bRect.height + mRect.height < cHeight
bTop + bRect.height + 2
else
bTop - mRect.height - 2
left =
if bRect.left + mRect.width < cWidth
bLeft
else
bLeft + bRect.width - mRect.width
{style} = menu
style.top = top + 'px'
style.left = left + 'px'
menu.focus()
insertEntry: (entry, parent, data) ->
if typeof entry.open is 'function'
return unless entry.open data
$.add parent, entry.el
return unless entry.children
if submenu = $ '.submenu', entry.el
# Reset sub menu, remove irrelevant entries.
$.rm submenu
submenu = $.el 'div',
className: 'reply dialog submenu'
for child in entry.children
@insertEntry child, submenu, data
$.add entry.el, submenu
return
close = ->
$.rm currentMenu
currentMenu = null
lastToggledButton = null
$.off d, 'click', @close
keybinds: (e) ->
entry = $ '.focused', currentMenu
while subEntry = $ '.focused', entry
entry = subEntry
switch e.keyCode
when 27 # Esc
lastToggledButton.focus()
@close()
when 13, 32 # Enter, Space
entry.click()
when 38 # Up
if next = entry.previousElementSibling
@focus next
when 40 # Down
if next = entry.nextElementSibling
@focus next
when 39 # Right
if (submenu = $ '.submenu', entry) and next = submenu.firstElementChild
@focus next
when 37 # Left
if next = $.x 'parent::*[contains(@class,"submenu")]/parent::*', entry
@focus next
else
return
e.preventDefault()
e.stopPropagation()
focus: (entry) ->
while focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
$.rmClass focused, 'focused'
for focused in $$ '.focused', entry
$.rmClass focused, 'focused'
$.addClass entry, 'focused'
# Submenu positioning.
return unless submenu = $ '.submenu', entry
sRect = submenu.getBoundingClientRect()
eRect = entry.getBoundingClientRect()
cHeight = d.documentElement.clientHeight
cWidth = d.documentElement.clientWidth
if eRect.top + sRect.height < cHeight
top = '0px'
bottom = 'auto'
else
top = 'auto'
bottom = '0px'
if eRect.right + sRect.width < cWidth
left = '100%'
right = 'auto'
else
left = 'auto'
right = '100%'
{style} = submenu
style.top = top
style.bottom = bottom
style.left = left
style.right = right
addEntry: (entry) ->
@parseEntry entry
@entries.push entry
parseEntry: (entry) ->
{el, children} = entry
$.addClass el, 'entry'
$.on el, 'focus mouseover', ((e) ->
e.stopPropagation()
@focus el
).bind @
return unless children
$.addClass el, 'has-submenu'
for child in children
@parseEntry child
return
addEntryListener: (e) ->
entry = e.detail
return if entry.type isnt @type
@addEntry entry
dragstart = (e) -> dragstart = (e) ->
# prevent text selection # prevent text selection
e.preventDefault() e.preventDefault()
@ -49,24 +223,42 @@ UI = (->
drag.call @, touch drag.call @, touch
return return
drag = (e) -> drag = (e) ->
left = e.clientX - @dx {clientX, clientY} = e
top = e.clientY - @dy
if left < 10 then left = 0 left = clientX - @dx
else if @width - left < 10 then left = null left =
if top < 10 then top = 0 if left < 10
else if @height - top < 10 then top = null 0
if left is null else if @width - left < 10
@style.left = null null
@style.right = '0%' else
else left / @screenWidth * 100 + '%'
@style.left = left / @screenWidth * 100 + '%'
@style.right = null top = clientY - @dy
if top is null top =
@style.top = null if top < 10
@style.bottom = '0%' 0
else else if @height - top < 10
@style.top = top / @screenHeight * 100 + '%' null
@style.bottom = null else
top / @screenHeight * 100 + '%'
right =
if left is null
0
else
null
bottom =
if top is null
0
else
null
{style} = @
style.left = left
style.right = right
style.top = top
style.bottom = bottom
touchend = (e) -> touchend = (e) ->
for touch in e.changedTouches for touch in e.changedTouches
if touch.identifier is @identifier if touch.identifier is @identifier
@ -99,22 +291,28 @@ UI = (->
root.addEventListener 'mousemove', o.hover, false root.addEventListener 'mousemove', o.hover, false
hover = (e) -> hover = (e) ->
height = @el.offsetHeight height = @el.offsetHeight
top = e.clientY - 120 {clientX, clientY} = e
@style.top =
if @clientHeight <= height or top <= 0 top = clientY - 120
'0px' top =
else if top + height >= @clientHeight if @clientHeight <= height or top <= 0
@clientHeight - height + 'px' 0
else else if top + height >= @clientHeight
top + 'px' @clientHeight - height
else
top
{clientX} = e
if clientX <= @clientWidth - 400 if clientX <= @clientWidth - 400
@style.left = clientX + 45 + 'px' left = clientX + 45 + 'px'
@style.right = null right = null
else else
@style.left = null left = null
@style.right = @clientWidth - clientX + 45 + 'px' right = @clientWidth - clientX + 45 + 'px'
{style} = @
style.top = top + 'px'
style.left = left
style.right = right
hoverend = -> hoverend = ->
@el.parentNode.removeChild @el @el.parentNode.removeChild @el
for event in @events for event in @events
@ -122,8 +320,10 @@ UI = (->
@root.removeEventListener 'mousemove', @hover, false @root.removeEventListener 'mousemove', @hover, false
@cb.call @ if @cb @cb.call @ if @cb
return { return {
dialog: dialog dialog: dialog
Menu: Menu
hover: hoverstart hover: hoverstart
} }
)() )()

View File

@ -1,3 +1,109 @@
Header =
init: ->
@menu = new UI.Menu 'header'
@headerEl = $.el 'div',
id: 'header'
innerHTML: '<div id=header-bar></div><div id=notifications></div>'
headerBar = $('#header-bar', @headerEl)
if $.get 'autohideHeaderBar', false
$.addClass headerBar, 'autohide'
menuButton = $.el 'a',
className: 'menu-button'
innerHTML: '[<span></span>]'
href: 'javascript:;'
$.on menuButton, 'click', @menuToggle
boardListButton = $.el 'span',
className: 'show-board-list-button'
innerHTML: '[<a href=javascript:;>+</a>]'
title: 'Toggle the board list.'
$.on boardListButton, 'click', @toggleBoardList
boardTitle = $.el 'a',
className: 'board-name'
innerHTML: "<span class=board-path>/#{g.BOARD}/</span> - <span class=board-title>...</span>"
href: "/#{g.BOARD}/"
boardList = $.el 'span',
className: 'board-list'
hidden: true
toggleBar = $.el 'div',
id: 'toggle-header-bar'
title: 'Toggle the header bar.'
$.on toggleBar, 'click', @toggleBar
$.prepend headerBar, [menuButton, boardListButton, $.tn(' '), boardTitle, boardList, toggleBar]
try
@setBoardList()
catch err
# XXX handle error
$.log err, 'Header - board list'
$.asap (-> d.body), ->
$.prepend d.body, Header.headerEl
setBoardList: ->
Get.boardsConfig (boardsConfig) ->
$('.board-title', Header.headerEl).textContent = boardsConfig[g.BOARD].title
$.ready ->
if nav = $.id 'boardNavDesktop'
$("a[href$='/#{g.BOARD}/']", nav)?.className = 'current'
$.add $('.board-list', Header.headerEl),
Array::slice.call nav.childNodes
toggleBoardList: ->
node = @firstElementChild.firstChild
if showBoardList = $.hasClass @, 'show-board-list-button'
@className = 'hide-board-list-button'
node.data = node.data.replace '+', '-'
else
@className = 'show-board-list-button'
node.data = node.data.replace '-', '+'
{headerEl} = Header
$('.board-name', headerEl).hidden = showBoardList
$('.board-list', headerEl).hidden = !showBoardList
toggleBar: ->
bool = $.id('header-bar').classList.toggle 'autohide'
$.set 'autohideHeaderBar', bool
menuToggle: (e) ->
Header.menu.toggle e, @, g
Settings =
init: ->
# 4chan X settings link
link = $.el 'a',
className: 'settings-link'
textContent: '4chan X Settings'
href: 'javascript:;'
$.on link, 'click', Settings.open
Header.menu.addEntry
el: link
# 4chan settings link
link = $.el 'a',
className: 'fourchan-settings-link'
textContent: '4chan Settings'
href: 'javascript:;'
$.on link, 'click', -> $.id('settingsWindowLink').click()
Header.menu.addEntry
el: link
open: -> !Conf['Disable 4chan\'s extension']
return unless Conf['Disable 4chan\'s extension']
settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
return if settings.disableAll
settings.disableAll = true
localStorage.setItem '4chan-settings', JSON.stringify settings
open: ->
Header.menu.close()
# Here be settings
Filter = Filter =
filters: {} filters: {}
init: -> init: ->
@ -195,7 +301,7 @@ Filter =
# Add a sub entry for each filter type. # Add a sub entry for each filter type.
entry.children.push Filter.menu.createSubEntry type[0], type[1] entry.children.push Filter.menu.createSubEntry type[0], type[1]
Menu.addEntry entry Menu.menu.addEntry entry
createSubEntry: (text, type) -> createSubEntry: (text, type) ->
el = $.el 'a', el = $.el 'a',
@ -345,7 +451,7 @@ ThreadHiding =
makeStub = $.el 'label', makeStub = $.el 'label',
innerHTML: "<input type=checkbox checked=#{Conf['Stubs']}> Make stub" innerHTML: "<input type=checkbox checked=#{Conf['Stubs']}> Make stub"
Menu.addEntry Menu.menu.addEntry
el: div el: div
open: (post) -> open: (post) ->
{thread} = post {thread} = post
@ -493,7 +599,7 @@ ReplyHiding =
makeStub = $.el 'label', makeStub = $.el 'label',
innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub" innerHTML: "<input type=checkbox name=makeStub checked=#{Conf['Stubs']}> Make stub"
Menu.addEntry Menu.menu.addEntry
el: div el: div
open: (post) -> open: (post) ->
if !post.isReply or post.isClone if !post.isReply or post.isClone
@ -619,20 +725,18 @@ Recursive =
return return
Menu = Menu =
entries: []
init: -> init: ->
# Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API @menu = new UI.Menu 'post'
$.on d, 'AddMenuEntry', (e) -> Menu.addEntry e.detail
Post::callbacks.push Post::callbacks.push
name: 'Menu' name: 'Menu'
cb: @node cb: @node
node: -> node: ->
a = Menu.makeButton @ button = Menu.makeButton @
if @isClone if @isClone
$.replace $('.menu-button', @nodes.info), a $.replace $('.menu-button', @nodes.info), button
return return
$.add @nodes.info, [$.tn('\u00A0'), a] $.add @nodes.info, [$.tn('\u00A0'), button]
makeButton: (post) -> makeButton: (post) ->
a = $.el 'a', a = $.el 'a',
@ -644,158 +748,13 @@ Menu =
$.on a, 'click', Menu.toggle $.on a, 'click', Menu.toggle
a a
makeMenu: ->
menu = $.el 'div',
className: 'reply dialog'
id: 'menu'
tabIndex: 0
$.on menu, 'click', (e) -> e.stopPropagation()
$.on menu, 'keydown', Menu.keybinds
menu
toggle: (e) -> toggle: (e) ->
e.preventDefault()
e.stopPropagation()
if Menu.currentMenu
# Close if it's already opened.
# Reopen if we clicked on another button.
{lastToggledButton} = Menu
Menu.close()
return if lastToggledButton is @
Menu.lastToggledButton = @
post = post =
if @dataset.clone if @dataset.clone
Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', @
else else
g.posts[@dataset.postid] g.posts[@dataset.postid]
Menu.open @, post Menu.menu.toggle e, @, post
open: (button, post) ->
menu = Menu.makeMenu()
Menu.currentMenu = menu
for entry in Menu.entries
Menu.insertEntry entry, menu, post
Menu.focus $ '.entry', menu
$.on d, 'click', Menu.close
$.add d.body, menu
# Position
mRect = menu.getBoundingClientRect()
bRect = button.getBoundingClientRect()
bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top
bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left
menu.style.top =
if bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight
bTop + bRect.height + 2 + 'px'
else
bTop - mRect.height - 2 + 'px'
menu.style.left =
if bRect.left + mRect.width < d.documentElement.clientWidth
bLeft + 'px'
else
bLeft + bRect.width - mRect.width + 'px'
menu.focus()
insertEntry: (entry, parent, post) ->
if typeof entry.open is 'function'
return unless entry.open post
$.add parent, entry.el
return unless entry.children
if submenu = $ '.submenu', entry.el
# Reset sub menu, remove irrelevant entries.
$.rm submenu
submenu = $.el 'div',
className: 'reply dialog submenu'
$.add entry.el, submenu
for child in entry.children
Menu.insertEntry child, submenu, post
return
close: ->
$.rm Menu.currentMenu
delete Menu.currentMenu
delete Menu.lastToggledButton
$.off d, 'click', Menu.close
keybinds: (e) ->
entry = $ '.focused', Menu.currentMenu
while subEntry = $ '.focused', entry
entry = subEntry
switch Keybinds.keyCode(e) or e.keyCode
when 'Esc'
Menu.lastToggledButton.focus()
Menu.close()
when 13, 32 # 'Enter', 'Space'
entry.click()
when 'Up'
if next = entry.previousElementSibling
Menu.focus next
when 'Down'
if next = entry.nextElementSibling
Menu.focus next
when 'Right'
if (submenu = $ '.submenu', entry) and next = submenu.firstElementChild
Menu.focus next
when 'Left'
if next = $.x 'parent::*[contains(@class,"submenu")]/parent::*', entry
Menu.focus next
else
return
e.preventDefault()
e.stopPropagation()
focus: (entry) ->
while focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
$.rmClass focused, 'focused'
for focused in $$ '.focused', entry
$.rmClass focused, 'focused'
$.addClass entry, 'focused'
# Submenu positioning.
return unless submenu = $ '.submenu', entry
sRect = submenu.getBoundingClientRect()
eRect = entry.getBoundingClientRect()
if eRect.top + sRect.height < d.documentElement.clientHeight
top = '0px'
bottom = 'auto'
else
top = 'auto'
bottom = '0px'
if eRect.right + sRect.width < d.documentElement.clientWidth
left = '100%'
right = 'auto'
else
left = 'auto'
right = '100%'
{style} = submenu
style.top = top
style.bottom = bottom
style.left = left
style.right = right
addEntry: (entry) ->
Menu.parseEntry entry
Menu.entries.push entry
parseEntry: (entry) ->
{el, children} = entry
$.addClass el, 'entry'
$.on el, 'focus mouseover', (e) ->
e.stopPropagation()
Menu.focus @
return unless children
$.addClass el, 'has-submenu'
for child in children
Menu.parseEntry child
return
ReportLink = ReportLink =
init: -> init: ->
@ -804,7 +763,7 @@ ReportLink =
href: 'javascript:;' href: 'javascript:;'
textContent: 'Report this post' textContent: 'Report this post'
$.on a, 'click', ReportLink.report $.on a, 'click', ReportLink.report
Menu.addEntry Menu.menu.addEntry
el: a el: a
open: (post) -> open: (post) ->
ReportLink.post = post ReportLink.post = post
@ -841,7 +800,7 @@ DeleteLink =
$.on fileEl, 'click', DeleteLink.delete $.on fileEl, 'click', DeleteLink.delete
!!post.file !!post.file
Menu.addEntry Menu.menu.addEntry
el: div el: div
open: (post) -> open: (post) ->
return false if post.isDead return false if post.isDead
@ -927,7 +886,7 @@ DownloadLink =
a = $.el 'a', a = $.el 'a',
className: 'download-link' className: 'download-link'
textContent: 'Download file' textContent: 'Download file'
Menu.addEntry Menu.menu.addEntry
el: a el: a
open: (post) -> open: (post) ->
return false unless post.file return false unless post.file
@ -962,7 +921,7 @@ ArchiveLink =
# Add a sub entry for each type. # Add a sub entry for each type.
entry.children.push @createSubEntry type[0], type[1] entry.children.push @createSubEntry type[0], type[1]
Menu.addEntry entry Menu.menu.addEntry entry
createSubEntry: (text, type) -> createSubEntry: (text, type) ->
el = $.el 'a', el = $.el 'a',
@ -1341,6 +1300,40 @@ Build =
container container
Get = Get =
boardsConfig: (->
boardsConfig = null
callbacks = []
parseBoardsConfig = ->
return if @status isnt 200
boardsConfig = {}
for board in JSON.parse(@response).boards
boardName = board.board
delete board.board
boardsConfig[boardName] = board
for callback in callbacks
callback boardsConfig
callbacks = null
boardsConfig.lastModified = @getResponseHeader 'Last-Modified'
$.set 'boardsConfig', boardsConfig
(cb) ->
# Configs were already loaded previously, callback and stop.
if boardsConfig
cb boardsConfig
return
# Load configs, callback, check for updates.
if boardsConfig = $.get 'boardsConfig', null
cb boardsConfig
lastModified = boardsConfig.lastModified
else
return if callbacks.push(cb) > 1
lastModified = 0
$.ajax '//api.4chan.org/boards.json', onloadend: parseBoardsConfig,
headers: 'If-Modified-Since': lastModified
)()
postFromRoot: (root) -> postFromRoot: (root) ->
link = $ 'a[title="Highlight this post"]', root link = $ 'a[title="Highlight this post"]', root
board = link.pathname.split('/')[1] board = link.pathname.split('/')[1]

View File

@ -158,6 +158,7 @@ class Post
# Get quote/backlinks to this post # Get quote/backlinks to this post
# and paint them (Dead). # and paint them (Dead).
for quotelink in Get.allQuotelinksLinkingTo @ for quotelink in Get.allQuotelinksLinkingTo @
continue if $.hasClass quotelink, 'deadlink'
$.add quotelink, $.tn '\u00A0(Dead)' $.add quotelink, $.tn '\u00A0(Dead)'
$.addClass quotelink, 'deadlink' $.addClass quotelink, 'deadlink'
return return
@ -267,10 +268,6 @@ Main =
g.THREAD = +pathname[3] g.THREAD = +pathname[3]
switch location.hostname switch location.hostname
when 'boards.4chan.org'
Main.initHeader()
return if g.VIEW is 'catalog'
Main.initFeatures()
when 'sys.4chan.org' when 'sys.4chan.org'
return return
when 'images.4chan.org' when 'images.4chan.org'
@ -280,41 +277,25 @@ Main =
location.href = url if url location.href = url if url
return return
initHeader: -> return if g.VIEW is 'catalog'
$.addStyle Main.css $.addStyle Main.css
Main.header = $.el 'div', $.asap (-> d.body), (->
className: 'reply' $.addClass d.body, $.engine
innerHTML: '<div class=extra></div>' $.addClass d.body, 'fourchan_x'
$.ready Main.initHeaderReady )
initHeaderReady: -> try
header = Main.header Header.init()
$.prepend d.body, header catch err
# XXX handle error
$.log err, 'Header'
if nav = $.id 'boardNavDesktop' try
header.id = nav.id Settings.init()
$.prepend header, nav catch err
nav.id = nav.className = null # XXX handle error
nav.lastElementChild.hidden = true $.log err, 'Settings'
settings = $.el 'span',
id: 'settings'
innerHTML: '[<a href=javascript:;>Settings</a>]'
$.on settings.firstElementChild, 'click', Main.settings
$.add nav, settings
$("a[href$='/#{g.BOARD}/']", nav)?.className = 'current'
$.addClass d.body, $.engine
$.addClass d.body, 'fourchan_x'
# disable the mobile layout
$('link[href*=mobile]', d.head)?.disabled = true
$.id('boardNavDesktopFoot')?.hidden = true
initFeatures: ->
if Conf['Disable 4chan\'s extension']
settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
settings.disableAll = true
localStorage.setItem '4chan-settings', JSON.stringify settings
if Conf['Resurrect Quotes'] if Conf['Resurrect Quotes']
try try
@ -497,9 +478,9 @@ Main =
# XXX handle error # XXX handle error
$.log err, 'Thread Updater' $.log err, 'Thread Updater'
$.ready Main.initFeaturesReady $.ready Main.initReady
initFeaturesReady: -> initReady: ->
if d.title is '4chan - 404 Not Found' if d.title is '4chan - 404 Not Found'
if Conf['404 Redirect'] and g.VIEW is 'thread' if Conf['404 Redirect'] and g.VIEW is 'thread'
location.href = Redirect.to location.href = Redirect.to
@ -508,7 +489,8 @@ Main =
postID: location.hash postID: location.hash
return return
return unless $.id 'navtopright' # disable the mobile layout
$('link[href*=mobile]', d.head)?.disabled = true
threads = [] threads = []
posts = [] posts = []
@ -543,9 +525,6 @@ Main =
$.log err.stack $.log err.stack
return return
settings: ->
alert 'Here be settings'
css: """<%= grunt.file.read('css/style.css') %>""" css: """<%= grunt.file.read('css/style.css') %>"""
Main.init() Main.init()