Merge branch 'v3' of git://github.com/MayhemYDG/4chan-x into v3

Conflicts:
	Gruntfile.js
	src/config.coffee
	src/css/style.css
	src/features.coffee
	src/main.coffee
This commit is contained in:
Zixaphir 2013-04-23 23:36:12 -07:00
commit a067ad6262
16 changed files with 963 additions and 534 deletions

View File

@ -1,3 +1,10 @@
## 3.2.0 - *2013-04-23*
- The top and bottom original board lists are now optional, disabled by default.
- The button to show a hidden announcement is now inside the header's menu.
- Reorganized Header menu:<br>![menu](img/changelog/3.2.0/0.png)
- Added the `board-replace` setting to Custom Board Navigation ricing.
- Added the option `Cooldown Prediction`, enabled by default.
- Added the option `Hide Unread Count at (0)`, disabled by default.
### 3.1.4 - *2013-04-17*

View File

@ -189,7 +189,7 @@ module.exports = (grunt) ->
grunt.registerTask 'reloadPkg', 'Reload the package', ->
# Update the `pkg` object with the new version.
pkg = grunt.file.readJSON('package.json')
concatOptions.process.data = pkg
grunt.config.data.pkg = concatOptions.process.data = pkg
grunt.log.ok('pkg reloaded.')
grunt.registerTask 'updcl', 'Update the changelog', (i) ->

194
Gruntfile.js Normal file
View File

@ -0,0 +1,194 @@
module.exports = function(grunt) {
var pkg = grunt.file.readJSON('package.json');
var concatOptions = {
process: {
data: pkg
}
};
var shellOptions = {
stdout: true,
stderr: true,
failOnError: true
};
// Project configuration.
grunt.initConfig({
pkg: pkg,
concat: {
coffee: {
options: concatOptions,
src: [
'src/config.coffee',
'src/globals.coffee',
'lib/ui.coffee',
'lib/$.coffee',
'lib/polyfill.coffee',
'src/features.coffee',
'src/qr.coffee',
'src/report.coffee',
'src/databoard.coffee',
'src/main.coffee'
],
dest: 'tmp-<%= pkg.type %>/script.coffee'
},
crx: {
options: concatOptions,
files: {
'builds/crx/manifest.json': 'src/manifest.json',
'builds/crx/script.js': [
'src/banner.js',
'tmp-<%= pkg.type %>/script.js'
]
}
},
userjs: {
options: concatOptions,
src: [
'src/metadata.js',
'src/banner.js',
'tmp-<%= pkg.type %>/script.js'
],
dest: 'builds/<%= pkg.name %>.js'
},
userscript: {
options: concatOptions,
files: {
'builds/<%= pkg.name %>.meta.js': 'src/metadata.js',
'builds/<%= pkg.name %>.user.js': [
'src/metadata.js',
'src/banner.js',
'tmp-<%= pkg.type %>/script.js'
]
}
}
},
copy: {
crx: {
src: 'img/*.png',
dest: 'builds/crx/',
expand: true,
flatten: true
}
},
coffee: {
script: {
src: 'tmp-<%= pkg.type %>/script.coffee',
dest: 'tmp-<%= pkg.type %>/script.js'
}
},
concurrent: {
build: ['build-crx', 'build-userjs', 'build-userscript']
},
shell: {
commit: {
options: shellOptions,
command: [
'git checkout <%= pkg.meta.mainBranch %>',
'git commit -am "Release <%= pkg.meta.name %> v<%= pkg.version %>."',
'git tag -a <%= pkg.version %> -m "<%= pkg.meta.name %> v<%= pkg.version %>."',
'git tag -af stable-v3 -m "<%= pkg.meta.name %> v<%= pkg.version %>."'
].join(' && ')
},
push: {
options: shellOptions,
command: 'git push origin --tags -f && git push origin --all'
}
},
watch: {
all: {
options: {
interrupt: true
},
files: [
'Gruntfile.js',
'package.json',
'lib/**/*',
'src/**/*',
'css/**/*',
'img/**/*'
],
tasks: 'build'
}
},
compress: {
crx: {
options: {
archive: 'builds/<%= pkg.name %>.zip',
level: 9,
pretty: true
},
expand: true,
flatten: true,
src: 'builds/crx/*',
dest: '/'
}
},
clean: {
builds: 'builds',
tmpcrx: 'tmp-crx',
tmpuserjs: 'tmp-userjs',
tmpuserscript: 'tmp-userscript'
}
});
grunt.loadNpmTasks('grunt-bump');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-coffee');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('default', ['build']);
grunt.registerTask('set-build', 'Set the build type variable', function(type) {
pkg.type = type;
grunt.log.ok('pkg.type = %s', type);
});
grunt.registerTask('build', ['concurrent:build']);
grunt.registerTask('build-crx', [
'set-build:crx',
'concat:coffee',
'coffee:script',
'concat:crx',
'copy:crx',
'clean:tmpcrx'
]);
grunt.registerTask('build-userjs', [
'set-build:userjs',
'concat:coffee',
'coffee:script',
'concat:userjs',
'clean:tmpuserjs'
]);
grunt.registerTask('build-userscript', [
'set-build:userscript',
'concat:coffee',
'coffee:script',
'concat:userscript',
'clean:tmpuserscript'
]);
grunt.registerTask('release', ['shell:commit', 'shell:push', 'build-crx', 'compress:crx']);
grunt.registerTask('patch', ['bump', 'reloadPkg', 'updcl:3', 'release']);
grunt.registerTask('minor', ['bump:minor', 'reloadPkg', 'updcl:2', 'release']);
grunt.registerTask('major', ['bump:major', 'reloadPkg', 'updcl:1', 'release']);
grunt.registerTask('reloadPkg', 'Reload the package', function() {
// Update the `pkg` object with the new version.
pkg = grunt.file.readJSON('package.json');
grunt.config.data.pkg = concatOptions.process.data = pkg;
grunt.log.ok('pkg reloaded.');
});
grunt.registerTask('updcl', 'Update the changelog', function(i) {
// i is the number of #s for markdown.
var version = new Array(+i + 1).join('#') + ' ' + pkg.version + ' - *' + grunt.template.today('yyyy-mm-dd') + '*';
grunt.file.write('CHANGELOG.md', version + '\n\n' + grunt.file.read('CHANGELOG.md'));
grunt.log.ok('Changelog updated for v' + pkg.version + '.');
});
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "4chan X",
"version": "3.1.4",
"version": "3.2.0",
"manifest_version": 2,
"description": "Cross-browser extension for productive lurking on 4chan.",
"icons": {

File diff suppressed because one or more lines are too long

BIN
img/changelog/3.2.0/0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,6 +1,6 @@
{
"name": "4chan-X",
"version": "3.1.4",
"version": "3.2.0",
"description": "Cross-browser extension for productive lurking on 4chan.",
"meta": {
"name": "4chan X",
@ -20,8 +20,8 @@
"grunt-bump": "~0.0.2",
"grunt-concurrent": "~0.2.0",
"grunt-contrib-clean": "~0.4.1",
"grunt-contrib-coffee": "~0.6.7",
"grunt-contrib-compress": "~0.4.10",
"grunt-contrib-coffee": "~0.7.0",
"grunt-contrib-compress": "~0.5.0",
"grunt-contrib-concat": "~0.2.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-watch": "~0.3.1",

View File

@ -9,10 +9,6 @@ Config =
false
'Link to external catalog instead of the internal one.'
]
'Custom Board Navigation': [
true
'Show custom links instead of the full board list.'
]
'QR Shortcut': [
false,
'Adds a small [QR] link in the header.'
@ -257,6 +253,10 @@ Config =
true
'Prevent "flood detected" errors.'
]
'Cooldown Prediction': [
true
'Decrease the cooldown time by taking into account upload speed. Disable it if it\'s inaccurate for you.'
]
'Quote Links':
'Quote Backlinks': [
@ -388,13 +388,13 @@ http://iqdb.org/?url=%TURL
'Custom CSS': false
'Boards Navigation': 'Sticky top'
'Header auto-hide': false
'Footer auto-hide': true
'Header catalog links': false
Header:
'Fixed Header': true
'Header auto-hide': false
'Bottom Header': false
'Header catalog links': false
'Bottom Board List': false
'Custom Board Navigation': false
boardnav: '[ toggle-all ] [current-title]'

View File

@ -47,6 +47,9 @@ a[href="javascript:;"] {
.warning {
color: red;
}
#boardNavDesktop {
display: none !important;
}
/* 4chan style fixes */
.opContainer, .op {
@ -367,6 +370,9 @@ a[href="javascript:;"] {
a.hide-announcement {
float: left;
}
#toggleMsgBtn {
display: none;
}
/* Unread */
#unread-line {
@ -868,4 +874,4 @@ a:only-of-type > .remove {
.export, .import {
cursor: pointer;
text-decoration: none !important;
}
}

View File

@ -2,6 +2,20 @@ PSAHiding =
init: ->
return if !Conf['Announcement Hiding']
entry =
type: 'header'
el: $.el 'a',
textContent: 'Show announcement'
className: 'show-announcement'
href: 'javascript:;'
order: 50
open: ->
if $.id('globalMessage')?.hidden
return true
false
$.event 'AddMenuEntry', entry
$.on entry.el, 'click', PSAHiding.toggle
$.addClass doc, 'hide-announcement'
$.on d, '4chanXInitFinished', @setup
@ -14,12 +28,12 @@ PSAHiding =
return
PSAHiding.btn = btn = $.el 'a',
title: 'Toggle announcement.'
innerHTML: '<span></span>'
innerHTML: '<span>[&nbsp;-&nbsp;]</span>'
title: 'Hide announcement.'
className: 'hide-announcement'
href: 'javascript:;'
$.on btn, 'click', PSAHiding.toggle
text = PSAHiding.trim psa
$.get 'hiddenPSAs', [], (item) ->
PSAHiding.sync item['hiddenPSAs']
$.before psa, btn
@ -33,19 +47,21 @@ PSAHiding =
$.get 'hiddenPSAs', [], ({hiddenPSAs}) ->
if hide
hiddenPSAs.push text
hiddenPSAs = hiddenPSAs[-5..]
else
$.event 'CloseMenu'
i = hiddenPSAs.indexOf text
hiddenPSAs.splice i, 1
hiddenPSAs = hiddenPSAs[-5..]
PSAHiding.sync hiddenPSAs
$.set 'hiddenPSAs', hiddenPSAs
sync: (hiddenPSAs) ->
{btn} = PSAHiding
psa = $.id 'globalMessage'
[psa.hidden, btn.firstChild.textContent, btn.className] = if PSAHiding.trim(psa) in hiddenPSAs
[true, '[\u00A0+\u00A0]', 'show-announcement']
psa = $.id 'globalMessage'
psa.hidden = PSAHiding.btn.hidden = if PSAHiding.trim(psa) in hiddenPSAs
true
else
[false, '[\u00A0-\u00A0]', 'hide-announcement']
false
if hr = $.x 'following-sibling::hr', psa
hr.hidden = psa.hidden
trim: (psa) ->
psa.textContent.replace(/\W+/g, '').toLowerCase()

View File

@ -27,7 +27,11 @@ CatalogLinks =
set: (useCatalog) ->
path = if useCatalog then 'catalog' else ''
for a in $$ 'a', $.id('board-list')
for a in $$ """
#board-list a[href*="boards.4chan.org"],
#boardNavDesktop a[href*="boards.4chan.org"],
#boardNavDesktopFoot a[href*="boards.4chan.org"]
"""
board = a.pathname.split('/')[1]
continue if ['f', 'status', '4chan'].contains(board) or !board
if Conf['External Catalog']

View File

@ -1,26 +1,45 @@
Header =
init: ->
@menu = new UI.Menu 'header'
@menuButton = $.el 'span',
className: 'menu-button'
innerHTML: '<i></i>'
@menu = new UI.Menu 'header'
barFixedToggler = $.el 'label',
innerHTML: '<input type=checkbox name="Fixed Header"> Fixed Header'
headerToggler = $.el 'label',
innerHTML: '<input type=checkbox name="Header auto-hide"> Auto-hide header'
barPositionToggler = $.el 'label',
innerHTML: '<input type=checkbox name="Bottom header"> Bottom header'
customNavToggler = $.el 'label',
innerHTML: '<input type=checkbox name="Custom Board Navigation"> Custom board navigation'
footerToggler = $.el 'label',
innerHTML: "<input type=checkbox #{unless Conf['Bottom Board List'] then 'checked' else ''}> Hide Footer Nav"
editCustomNav = $.el 'a',
textContent: 'Edit custom board navigation'
href: 'javascript:;'
@headerToggler = headerToggler.firstElementChild
@barFixedToggler = barFixedToggler.firstElementChild
@barPositionToggler = barPositionToggler.firstElementChild
@headerToggler = headerToggler.firstElementChild
@footerToggler = footerToggler.firstElementChild
@customNavToggler = customNavToggler.firstElementChild
$.on @menuButton, 'click', @menuToggle
$.on window, 'load hashchange', Header.hashScroll
$.on @headerToggler, 'change', @toggleBarVisibility
$.on @menuButton, 'click', @menuToggle
$.on @barFixedToggler, 'change', @toggleBarFixed
$.on @barPositionToggler, 'change', @toggleBarPosition
$.on @headerToggler, 'change', @toggleBarVisibility
$.on @footerToggler, 'change', @toggleFooterVisibility
$.on @customNavToggler, 'change', @toggleCustomNav
$.on editCustomNav, 'click', @editCustomNav
{createSubEntry} = Header
subEntries = []
for setting in ['Sticky top', 'Sticky bottom', 'Top']
subEntries.push createSubEntry setting
@setBarFixed Conf['Fixed Header']
@setBarVisibility Conf['Header auto-hide']
@setBarPosition Conf['Bottom Header']
subEntries.push {el: headerToggler}
$.sync 'Fixed Header', Header.setBarFixed
$.sync 'Bottom Header', Header.setBarPosition
$.sync 'Header auto-hide', Header.setBarVisibility
@addShortcut Header.menuButton
@ -29,32 +48,38 @@ Header =
el: $.el 'span',
textContent: 'Header'
order: 105
subEntries: subEntries
@footerToggler = $.el 'label',
innerHTML: "<input type=checkbox #{if Conf['Footer auto-hide'] then 'checked' else ''}> Hide Footer Nav"
$.on @footerToggler.firstElementChild, 'change', @toggleFooterVisibility
$.event 'AddMenuEntry',
type: 'header'
el: @footerToggler
order: 100
subEntries: [
{el: barFixedToggler}
{el: headerToggler}
{el: barPositionToggler}
{el: footerToggler}
{el: customNavToggler}
{el: editCustomNav}
]
$.on window, 'load hashchange', Header.hashScroll
$.on d, 'CreateNotification', @createNotification
$.asap (-> d.body), ->
$.asap (-> d.body), =>
return unless Main.isThisPageLegit()
# Wait for #boardNavMobile instead of #boardNavDesktop,
# it might be incomplete otherwise.
$.asap (-> $.id 'boardNavMobile'), Header.setBoardList
$.asap (-> $.id('boardNavMobile') or d.readyState is 'complete'), Header.setBoardList
$.prepend d.body, @bar
$.ready ->
$.add d.body, Header.hover
Header.footer = footer = $.id 'boardNavDesktopFoot'
Header.setFooterVisibility Conf['Footer auto-hide']
$.sync 'Footer auto-hide', Header.setFooterVisibility
$.ready =>
if a = $ "a[href*='/#{g.BOARD}/']", $.id 'boardNavDesktopFoot'
a.className = 'current'
$.add d.body, @hover
@footer = $.id 'boardNavDesktopFoot'
@setFooterVisibility !Conf['Bottom Board List']
$.sync 'Bottom Board List', Header.setFooterVisibility
bar: $.el 'div',
id: 'header-bar'
notify: $.el 'div',
id: 'notifications'
shortcuts: $.el 'span',
@ -66,61 +91,33 @@ Header =
toggle: $.el 'div',
id: 'scroll-marker'
createSubEntry: (setting) ->
label = $.el 'label',
textContent: "#{setting}"
$.on label, 'click', Header.setBarPosition
el: label
setBoardList: ->
Header.nav = nav = $.id 'boardNavDesktop'
nav.id = 'header-bar'
if a = $ "a[href*='/#{g.BOARD}/']", nav
fourchannav = $.id 'boardNavDesktop'
if a = $ "a[href*='/#{g.BOARD}/']", fourchannav
a.className = 'current'
boardList = $.el 'span',
id: 'board-list'
innerHTML: "<span id=custom-board-list></span><span id=full-board-list hidden>[<a href=javascript:; class='hide-board-list-button'> - </a>]#{fourchannav.innerHTML}</span>"
fullBoardList = $ '#full-board-list', boardList
btn = $ '.hide-board-list-button', fullBoardList
$.on btn, 'click', Header.toggleBoardList
$.add boardList, fullBoardList = $.el 'span',
id: 'full-board-list'
$.rm $ '#navtopright', fullBoardList
$.add boardList, fullBoardList
$.add Header.bar, [boardList, Header.shortcuts, Header.notify, Header.toggle]
Header.setBarPosition.call textContent: "#{Conf['Boards Navigation']}"
$.sync 'Boards Navigation', Header.changeBarPosition
Header.setCustomNav Conf['Custom Board Navigation']
Header.generateBoardList Conf['boardnav']
Header.setBarVisibility Conf['Header auto-hide']
$.sync 'Header auto-hide', Header.setBarVisibility
$.add fullBoardList, [nav.childNodes...]
$.add nav, [boardList, Header.shortcuts, Header.bar, Header.toggle]
if Conf['Custom Board Navigation']
fullBoardList.hidden = true
customBoardList = $.el 'span',
id: 'custom-board-list'
$.add boardList, customBoardList
Header.generateBoardList Conf['boardnav']
$.sync 'boardnav', Header.generateBoardList
btn = $.el 'span',
className: 'hide-board-list-button'
innerHTML: '[<a href=javascript:;> - </a>]\u00A0'
$.on btn, 'click', Header.toggleBoardList
$.prepend fullBoardList, btn
else
fullBoardList.hidden = false
$.sync 'Custom Board Navigation', Header.setCustomNav
$.sync 'boardnav', Header.generateBoardList
generateBoardList: (text) ->
unless list = $ '#custom-board-list', Header.nav
# init'd with the custom board list disabled.
return
list = $ '#custom-board-list', Header.bar
$.rmAll list
return unless text
as = $$('#full-board-list a', Header.nav)[0...-2] # ignore the Settings and Home links
as = $$('#full-board-list a', Header.bar)[0...-2] # ignore the Settings and Home links
nodes = text.match(/[\w@]+(-(all|title|replace|full|index|catalog|text:"[^"]+"))*|[^\w@]+/g).map (t) ->
if /^[^\w@]/.test t
return $.tn t
@ -159,46 +156,51 @@ Header =
$.add list, nodes
toggleBoardList: ->
{nav} = Header
custom = $ '#custom-board-list', nav
full = $ '#full-board-list', nav
{bar} = Header
custom = $ '#custom-board-list', bar
full = $ '#full-board-list', bar
showBoardList = !full.hidden
custom.hidden = !showBoardList
full.hidden = showBoardList
setBarPosition: ->
setBarPosition: (bottom) ->
Header.barPositionToggler.checked = bottom
if bottom
$.rmClass doc, 'top'
$.addClass doc, 'bottom'
else
$.rmClass doc, 'bottom'
$.addClass doc, 'top'
toggleBarPosition: ->
$.event 'CloseMenu'
Header.changeBarPosition @textContent
Header.setBarPosition @checked
Conf['Boards Navigation'] = @textContent
$.set 'Boards Navigation', @textContent
Conf['Bottom Header'] = @checked
$.set 'Bottom Header', @checked
changeBarPosition: (setting) ->
$.rmClass doc, 'top'
$.rmClass doc, 'fixed'
$.rmClass doc, 'bottom'
$.rmClass Header.nav, 'dialog'
switch setting
when 'Sticky top'
$.addClass doc, 'top'
$.addClass doc, 'fixed'
$.addClass Header.nav, 'dialog'
when 'Sticky bottom'
$.addClass doc, 'fixed'
$.addClass doc, 'bottom'
$.addClass Header.nav, 'dialog'
when 'Top'
$.addClass doc, 'top'
setBarFixed: (fixed) ->
Header.barFixedToggler.checked = fixed
if fixed
$.addClass doc, 'fixed'
$.addClass Header.bar, 'dialog'
else
$.rmClass doc, 'fixed'
$.rmClass Header.bar, 'dialog'
toggleBarFixed: ->
$.event 'CloseMenu'
Header.setBarFixed @checked
Conf['Fixed Header'] = @checked
$.set 'Fixed Header', @checked
setBarVisibility: (hide) ->
Header.headerToggler.checked = hide
$.event 'CloseMenu'
(if hide then $.addClass else $.rmClass) Header.nav, 'autohide'
setFooterVisibility: (hide) ->
Header.footerToggler.firstElementChild.checked = hide
Header.footer.hidden = hide
(if hide then $.addClass else $.rmClass) Header.bar, 'autohide'
toggleBarVisibility: (e) ->
return if e.type is 'mousedown' and e.button isnt 0 # not LMB
@ -215,20 +217,43 @@ Header =
'The header bar will remain visible.'
new Notification 'info', message, 2
setFooterVisibility: (hide) ->
Header.footerToggler.checked = hide
Header.footer.hidden = hide
toggleFooterVisibility: ->
$.event 'CloseMenu'
hide = if @nodeName is 'INPUT'
@checked
else
!Header.footer.hidden
!!Header.footer.hidden
Header.setFooterVisibility hide
$.set 'Footer auto-hide', hide
$.set 'Bottom Board List', hide
message = if hide
'The bottom navigation will now be hidden.'
else
'The bottom navigation will remain visible.'
new Notification 'info', message, 2
setCustomNav: (show) ->
Header.customNavToggler.checked = show
cust = $ '#custom-board-list', Header.bar
full = $ '#full-board-list', Header.bar
btn = $ '.hide-board-list-button', full
[cust.hidden, full.hidden, btn.hidden] = if show
[false, true, false]
else
[true, false, true]
toggleCustomNav: ->
$.cb.checked.call @
Header.setCustomNav @checked
editCustomNav: ->
Settings.open 'Rice'
settings = $.id 'fourchanx-settings'
$('input[name=boardnav]', settings).focus()
hashScroll: ->
return unless (hash = @location.hash) and post = $.id hash[1..]
return if (Get.postFromRoot post).isHidden
@ -236,7 +261,7 @@ Header =
scrollToPost: (post) ->
{top} = post.getBoundingClientRect()
if Conf['Boards Navigation'] is 'Sticky top'
if Conf['Fixed Header'] and not Conf['Bottom Header']
headRect = Header.bar.getBoundingClientRect()
top += - headRect.top - headRect.height
(if $.engine is 'webkit' then d.body else doc).scrollTop += top

View File

@ -263,7 +263,7 @@ QR =
elapsed = Math.floor (now - start) / $.SECOND
if elapsed >= 0 # clock changed since then?
seconds = Math.max seconds, types[type] - elapsed
if hasFile and upSpd
if Conf['Cooldown Prediction'] and hasFile and upSpd
seconds -= Math.floor post.file.size / upSpd * upSpdAccuracy
seconds = Math.max seconds, 0
unless start <= now <= cooldown.timeout

View File

@ -153,7 +153,7 @@ class Post
unless strong = $ 'strong.warning', @nodes.info
strong = $.el 'strong',
className: 'warning'
textContent: '[Deleted]'
textContent: if @isReply then '[Deleted]' else '[Dead]'
$.after $('input', @nodes.info), strong
strong.textContent = if file then '[File deleted]' else '[Deleted]'