diff --git a/Makefile b/Makefile index 9cbdc589b..87682d56f 100644 --- a/Makefile +++ b/Makefile @@ -20,19 +20,17 @@ template := node tools/template.js template_deps := package.json tools/template.js node_modules/lodash/package.json cat := node tools/cat.js cat_deps := tools/cat.js -jshint_deps := .jshintrc node_modules/jshint/package.json -parts := top API classes General Filtering Quotelinks Posting Images Linkification Menu Monitoring Archive Miscellaneous bottom +parts := Config API classes General Filtering Quotelinks Posting Images Linkification Menu Monitoring Archive Miscellaneous bottom -js_parts := $(foreach p,$(subst API,API_crx API_userscript,$(parts)),tmp/parts/$(p).js) +intermediate := LICENSE src/meta/fbegin.js tmp/declaration.js tmp/globals.js $(foreach p,$(parts),tmp/$(p).js) src/meta/fend.js define sorted_dir sources_$1 := $$(sort $$(wildcard src/$1/*.coffee)) endef -sources_top := \ - src/General/Config.coffee \ - src/General/Globals.coffee +sources_Config := \ + src/General/Config.coffee sources_API := \ src/General/$$.coffee \ @@ -46,7 +44,7 @@ sources_classes := \ sources_General := \ $(foreach n, \ - Polyfill Header Index Build Get UI BuildTest \ + Polyfill Header Index Build Get UI Build.Test \ ,src/General/$(n).coffee) $(foreach d, \ @@ -72,10 +70,9 @@ sources_bottom := \ src/General/Settings.coffee \ src/General/Main.coffee -imports_top := \ +imports_Config := \ src/Archive/archives.json \ - src/css/custom.css \ - version.json + src/css/custom.css imports_Monitoring := \ src/meta/icon128.png imports_Miscellaneous := \ @@ -106,15 +103,13 @@ testbds := $(foreach f,$(filter-out %.crx %.zip,$(bds)),test$(f)) $(foreach t,cr testcrx := $(foreach f,$(filter %.crx %.zip,$(bds)),test$(f)) -jshint := $(foreach f,script-crx eventPage script-userscript,.events/jshint.$(f)) +jshint := $(foreach f,globals $(subst API,API_crx API_userscript,$(parts)),.events/jshint.$(f)) -jshint_parts := $(foreach p,$(subst API,API_crx API_userscript,$(parts)),.events/jshint_p.$(p)) - -default : jshint_parts install +default : jshint install all : jshint bds install -.events tmp tmp/parts testbuilds builds : +.events tmp testbuilds builds : $(MKDIR) .events/npm : npm-shrinkwrap.json | .events @@ -133,32 +128,34 @@ tmp/font-awesome.css : src/css/font-awesome.css $(imports_font_awesome) $(templa tmp/style.css : src/css/style.css $(imports_style) $(template_deps) | tmp $(template) $< $@ +tmp/declaration.js : $(wildcard src/*/*.coffee) tools/declare.js | tmp + node tools/declare.js + +tmp/globals.js : src/General/globals.js version.json $(template_deps) | tmp + $(template) $< $@ + define rules_part -tmp/parts/$1.jst : $$(sources_$1) $(cat_deps) | tmp/parts +tmp/$1.jst : $$(sources_$1) $(cat_deps) | tmp $(cat) $$(sources_$1) $$@ -tmp/parts/$1.coffee : tmp/parts/$1.jst $$(filter-out %.coffee,$$(wildcard src/$1/*.* src/$1/*/*.* src/$1/*/*/*.*)) $$(imports_$1) .tests_enabled $(template_deps) +tmp/$1.coffee : tmp/$1.jst $$(filter-out %.coffee,$$(wildcard src/$1/*.* src/$1/*/*.* src/$1/*/*/*.*)) $$(imports_$1) .tests_enabled $(template_deps) $(template) $$< $$@ -tmp/parts/$1.js : tmp/parts/$1.coffee $(coffee_deps) +tmp/$1.js : tmp/$1.coffee $(coffee_deps) tools/globalize.js $(coffee) $$< + node tools/globalize.js $$@ $$(sources_$1) endef $(foreach i,$(parts),$(eval $(call rules_part,$(i)))) -tmp/parts/API_%.coffee : tmp/parts/API.jst $(template_deps) +tmp/API_%.coffee : tmp/API.jst $(template_deps) $(template) $< $@ type=$* -tmp/parts/API_%.js : tmp/parts/API_%.coffee $(coffee_deps) +tmp/API_%.js : tmp/API_%.coffee $(coffee_deps) $(coffee) $< - -tmp/script-crx.js : $(filter-out tmp/parts/API_userscript.js,$(js_parts)) tools/cat-coffee.js - node tools/cat-coffee.js $(filter-out tmp/parts/API_userscript.js,$(js_parts)) $@ - -tmp/script-userscript.js : $(filter-out tmp/parts/API_crx.js,$(js_parts)) tools/cat-coffee.js - node tools/cat-coffee.js $(filter-out tmp/parts/API_crx.js,$(js_parts)) $@ + node tools/globalize.js $@ $(sources_API) tmp/eventPage.js : src/General/eventPage.coffee $(coffee_deps) | tmp $(coffee) -o tmp src/General/eventPage.coffee @@ -168,8 +165,8 @@ define rules_channel testbuilds/crx$1 : $$(MKDIR) -testbuilds/crx$1/script.js : src/meta/botproc.js LICENSE src/meta/usestrict.js tmp/script-crx.js $(cat_deps) | testbuilds/crx$1 - $(cat) src/meta/botproc.js LICENSE src/meta/usestrict.js tmp/script-crx.js $$@ +testbuilds/crx$1/script.js : src/meta/botproc.js $(subst API,API_crx,$(intermediate)) $(cat_deps) | testbuilds/crx$1 + $(cat) src/meta/botproc.js $(subst API,API_crx,$(intermediate)) $$@ testbuilds/crx$1/eventPage.js : tmp/eventPage.js | testbuilds/crx$1 $$(CP) @@ -194,8 +191,8 @@ testbuilds/$(name)$1.crx : testbuilds/$(name)$1.crx.zip package.json tools/sign. testbuilds/$(name)$1.meta.js : src/meta/metadata.js src/meta/icon48.png version.json $(template_deps) | testbuilds $(template) $$< $$@ type=userscript channel=$1 -testbuilds/$(name)$1.user.js : src/meta/botproc.js testbuilds/$(name)$1.meta.js LICENSE src/meta/usestrict.js tmp/script-userscript.js $(cat_deps) - $(cat) src/meta/botproc.js testbuilds/$(name)$1.meta.js LICENSE src/meta/usestrict.js tmp/script-userscript.js $$@ +testbuilds/$(name)$1.user.js : src/meta/botproc.js testbuilds/$(name)$1.meta.js $(subst API,API_userscript,$(intermediate)) $(cat_deps) + $(cat) src/meta/botproc.js testbuilds/$(name)$1.meta.js $(subst API,API_userscript,$(intermediate)) $$@ endef @@ -212,17 +209,10 @@ builds/% : testbuilds/% $(jshint) | builds test.html : README.md template.jst tools/markdown.js node_modules/marked/package.json node_modules/lodash/package.json node tools/markdown.js -tmp/parts/.jshintrc : src/meta/jshint.json $(template_deps) | tmp/parts - $(template) $< $@ stage=parts +tmp/.jshintrc : src/meta/jshint.json tmp/declaration.js tmp/globals.js $(template_deps) | tmp + $(template) $< $@ -.jshintrc : src/meta/jshint.json $(template_deps) - $(template) $< $@ stage=full - -.events/jshint.% : tmp/%.js .jshintrc node_modules/jshint/package.json | .events - $(BIN)jshint $< - echo -> $@ - -.events/jshint_p.% : tmp/parts/%.js tmp/parts/.jshintrc node_modules/jshint/package.json | .events +.events/jshint.% : tmp/%.js tmp/.jshintrc node_modules/jshint/package.json | .events $(BIN)jshint $< echo -> $@ @@ -235,11 +225,11 @@ install.json : .SECONDARY : -.PHONY: default all clean cleanall testbds bds jshint jshint_parts install +.PHONY: default all clean cleanall testbds bds jshint install clean : $(RMDIR) tmp testbuilds .events - $(RM) .jshintrc .tests_enabled + $(RM) .tests_enabled cleanall : clean $(RMDIR) builds @@ -252,6 +242,4 @@ bds : $(bds) jshint : $(jshint) -jshint_parts : $(jshint_parts) - install : .events/install diff --git a/package.json b/package.json index 356c49111..f1d850062 100644 --- a/package.json +++ b/package.json @@ -40,15 +40,6 @@ "GM_openInTab", "GM_xmlhttpRequest" ], - "globals": [ - "$$", - "c", - "Conf", - "d", - "doc", - "E", - "g" - ], "min": { "chrome": "33", "firefox": "26", diff --git a/src/General/$.coffee b/src/General/$.coffee index 29b9d1616..f9b92092f 100644 --- a/src/General/$.coffee +++ b/src/General/$.coffee @@ -593,6 +593,3 @@ $.clear = (cb) -> $.delete $.listValues().map (key) -> key.replace g.NAMESPACE, '' cb?() <% } %> - -$$ = (selector, root=d.body) -> - [root.querySelectorAll(selector)...] diff --git a/src/General/BuildTest.coffee b/src/General/Build.Test.coffee similarity index 77% rename from src/General/BuildTest.coffee rename to src/General/Build.Test.coffee index f088106ca..05d9eede8 100644 --- a/src/General/BuildTest.coffee +++ b/src/General/Build.Test.coffee @@ -1,5 +1,5 @@ <% if (readJSON('.tests_enabled')) { %> -BuildTest = +Build.Test = init: -> return if !Conf['Menu'] or g.VIEW not in ['index', 'thread'] @@ -27,11 +27,11 @@ BuildTest = x2 = x.childNodes[i] y2 = y.childNodes[i] return [x2, y2] unless x2 and y2 - return BuildTest.firstDiff(x2, y2) unless x2.isEqualNode y2 + return Build.Test.firstDiff(x2, y2) unless x2.isEqualNode y2 i++ testOne: (post) -> - BuildTest.postsRemaining++ + Build.Test.postsRemaining++ $.cache "//a.4cdn.org/#{post.board.ID}/thread/#{post.thread.ID}.json", -> {posts} = @response Build.spoilerRange[post.board.ID] = posts[0].custom_spoiler @@ -41,7 +41,7 @@ BuildTest = obj = Build.parseJSON postData, post.board.ID root = Build.post obj t2 = new Date().getTime() - BuildTest.time += t2 - t1 + Build.Test.time += t2 - t1 post2 = new Post root, post.thread, post.board fail = false @@ -50,7 +50,7 @@ BuildTest = unless x.isEqualNode y fail = true c.log "#{post.fullID} differs" - [x2, y2] = BuildTest.firstDiff x, y + [x2, y2] = Build.Test.firstDiff x, y c.log x2 c.log y2 c.log x.outerHTML @@ -66,11 +66,11 @@ BuildTest = c.log val2 if fail - BuildTest.postsFailed++ + Build.Test.postsFailed++ else c.log "#{post.fullID} correct" - BuildTest.postsRemaining-- - BuildTest.report() if BuildTest.postsRemaining is 0 + Build.Test.postsRemaining-- + Build.Test.report() if Build.Test.postsRemaining is 0 post2.isFetchedQuote = true Main.callbackNodes Post, [post2] @@ -78,7 +78,7 @@ BuildTest = g.posts.forEach (post) -> unless post.isClone or post.isFetchedQuote unless (abbr = $ '.abbr', post.nodes.comment) and /Comment too long\./.test(abbr.textContent) - BuildTest.testOne post + Build.Test.testOne post return postsRemaining: 0 @@ -86,18 +86,18 @@ BuildTest = time: 0 report: -> - if BuildTest.postsFailed - new Notice 'warning', "#{BuildTest.postsFailed} post(s) differ (#{BuildTest.time} ms)", 30 + if Build.Test.postsFailed + new Notice 'warning', "#{Build.Test.postsFailed} post(s) differ (#{Build.Test.time} ms)", 30 else - new Notice 'success', "All correct (#{BuildTest.time} ms)", 5 - BuildTest.postsFailed = BuildTest.time = 0 + new Notice 'success', "All correct (#{Build.Test.time} ms)", 5 + Build.Test.postsFailed = Build.Test.time = 0 cb: testOne: -> - BuildTest.testOne g.posts[@dataset.fullID] + Build.Test.testOne g.posts[@dataset.fullID] Menu.menu.close() testAll: -> - BuildTest.testAll() + Build.Test.testAll() Header.menu.close() <% } %> diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee deleted file mode 100644 index fb1f6fc34..000000000 --- a/src/General/Globals.coffee +++ /dev/null @@ -1,24 +0,0 @@ -Conf = {} -c = console -d = document -doc = d.documentElement -g = - VERSION: '<%= readJSON('version.json').version %>' - NAMESPACE: '<%= meta.name %>.' - boards: {} - -E = do -> - str = {'&': '&', "'": ''', '"': '"', '<': '<', '>': '>'} - r = String::replace - regex = /[&"'<>]/g - fn = (x) -> - str[x] - (text) -> r.call text, regex, fn - -E.cat = (templates) -> - html = '' - html += x.innerHTML for x in templates - html - -E.url = (content) -> - "data:text/html;charset=utf-8,#{encodeURIComponent content.innerHTML}" diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 44182c7c8..0fbbb0811 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -470,7 +470,7 @@ Main = ['Flash Features', Flash] ['Reply Pruning', ReplyPruning] <% if (readJSON('.tests_enabled')) { %> - ['Build Test', BuildTest] + ['Build Test', Build.Test] <% } %> ] diff --git a/src/General/globals.js b/src/General/globals.js new file mode 100644 index 000000000..4c5514464 --- /dev/null +++ b/src/General/globals.js @@ -0,0 +1,42 @@ +var Conf, c, d, doc, g, E, $$; + +Conf = {}; +c = console; +d = document; +doc = d.documentElement; +g = { + VERSION: '<%= readJSON('version.json').version %>', + NAMESPACE: '<%= meta.name %>.', + boards: {} +}; + +E = (function() { + var str = {'&': '&', "'": ''', '"': '"', '<': '<', '>': '>'}; + var r = String.prototype.replace; + var regex = /[&"'<>]/g; + var fn = function (x) { + return str[x]; + }; + return function(text) { + return r.call(text, regex, fn); + }; +})(); + +E.cat = function(templates) { + var html = ''; + for (var i = 0, len = templates.length; i < len; i++) { + html += templates[i].innerHTML; + } + return html; +}; + +E.url = function (content) { + return 'data:text/html;charset=utf-8,' + encodeURIComponent(content.innerHTML); +}; + +$$ = function(selector, root) { + if (root == null) { + root = d.body; + } + return [].slice.call(root.querySelectorAll(selector)); +}; diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 8da4b80e5..d9c1adda3 100644 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -195,7 +195,7 @@ Keybinds = <% if (readJSON('.tests_enabled')) { %> when 't' return unless threadRoot - BuildTest.testAll() + Build.Test.testAll() <% } %> else return diff --git a/src/meta/fbegin.js b/src/meta/fbegin.js new file mode 100644 index 000000000..230e4d98b --- /dev/null +++ b/src/meta/fbegin.js @@ -0,0 +1,3 @@ +'use strict'; + +(function() { diff --git a/src/meta/fend.js b/src/meta/fend.js new file mode 100644 index 000000000..aec0816be --- /dev/null +++ b/src/meta/fend.js @@ -0,0 +1 @@ +}).call(this); diff --git a/src/meta/jshint.json b/src/meta/jshint.json index da7801c8a..8e01d236d 100644 --- a/src/meta/jshint.json +++ b/src/meta/jshint.json @@ -20,16 +20,10 @@ "unsafeWindow": true, "chrome": true<%= meta.grants.map(x => `,\n "${x}": true`).join('') -%><% if (stage === 'parts') { %><%= - meta.globals.map(x => `,\n "${x}": true`).join('') %><%= - (ls('src') - .map(x => ls(`src/${x}`)) - .reduce((x,y) => x.concat(y)) - .filter(x => /^[$A-Z]\w*\.coffee/.test(x)) - .map(x => x.split('.')[0]) - .map(x => `,\n "${x}": true`).join('') - ) -%><% } %> + read('tmp/declaration.js').match(/^var (.*);/)[1].split(', ').map(x => `,\n "${x}": true`).join('') +%><%= + read('tmp/globals.js').match(/^var (.*);/)[1].split(', ').map(x => `,\n "${x}": true`).join('') +%> } } diff --git a/src/meta/usestrict.js b/src/meta/usestrict.js deleted file mode 100644 index ad9a93a7c..000000000 --- a/src/meta/usestrict.js +++ /dev/null @@ -1 +0,0 @@ -'use strict'; diff --git a/tools/cat-coffee.js b/tools/cat-coffee.js deleted file mode 100644 index d7932ba78..000000000 --- a/tools/cat-coffee.js +++ /dev/null @@ -1,56 +0,0 @@ -var fs = require('fs'); - -var inputFiles = process.argv.slice(2, -1); - -var allVars = []; -var allHelperNames = []; -var allHelperValues = {}; -var allBodies = []; - -for (var file of inputFiles) { - var inputText = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n'); - - var parts = inputText.match(/^\(function\(\) {\n var ([\w$]+(?:, [\w$]+)*)((?:,\n [\w$]+ = .*)*);\n\n([^]*)\n\n}\)\.call\(this\);\n$/); - if (!parts) throw new Error(`${file}: unexpected format`); - - var vars = parts[1].split(', '); - for (var v of vars) { - if (allVars.indexOf(v) >= 0) { - throw new Error(`${file}: reused variable name ${v}`); - } - if (allHelperNames.indexOf(v) >= 0) { - throw new Error(`${file}: variable clashes with helper ${v}`); - } - allVars.push(v); - } - - var helpers = parts[2].split(',\n ').slice(1); - for (var h of helpers) { - var hparts = h.match(/^([\w$]+) = (.*)$/); - var hn = hparts[1]; - var hv = hparts[2]; - if (allVars.indexOf(hn) >= 0) { - throw new Error(`${file}: helper clashes with variable ${v}`); - } - if (allHelperNames.indexOf(hn) >= 0) { - if (allHelperValues[hn] !== hv) { - throw new Error(`${file}: redefined helper ${hn}`); - } - } else { - allHelperNames.push(hn); - allHelperValues[hn] = hv; - } - } - - var body = parts[3]; - allBodies.push(body); -} - -var varText = allVars.sort().join(', '); -var helperText = allHelperNames.map(hn => `,\n ${hn} = ${allHelperValues[hn]}`).join(''); -var bodyText = allBodies.join('\n\n'); - -var outputText = `(function() {\n var ${varText}${helperText};\n\n${bodyText}\n\n}).call(this);\n`; - -var outputName = process.argv[process.argv.length - 1]; -fs.writeFileSync(outputName, outputText); diff --git a/tools/declare.js b/tools/declare.js new file mode 100644 index 000000000..d946b970d --- /dev/null +++ b/tools/declare.js @@ -0,0 +1,10 @@ +var fs = require('fs'); + +var names = []; +for (var d of fs.readdirSync('src')) { + for (var f of fs.readdirSync(`src/${d}`)) { + var m = f.match(/^([$A-Z]\w*)\.coffee$/); + if (m) names.push(m[1]); + } +} +fs.writeFileSync('tmp/declaration.js', `var ${names.sort().join(', ')};\n`); diff --git a/tools/globalize.js b/tools/globalize.js new file mode 100644 index 000000000..4b66d6e22 --- /dev/null +++ b/tools/globalize.js @@ -0,0 +1,54 @@ +var fs = require('fs'); + +var filename = process.argv[2]; +var sources = process.argv.slice(3); + +// Extract variables to be made global from source file list +// e.g. ImageExpand from src/Images/ImageExpand.coffee +// but not QR.post or eventPage +var names = []; +for (var f of sources) { + f = f.match(/[^/]*$/)[0]; + var m = f.match(/^([$A-Z]\w*)\.coffee$/); + if (m) names.push(m[1]); +} + +var script = fs.readFileSync(filename, 'utf8').replace(/\r\n/g, '\n'); + +var replaced = 0; + +script = script.replace( + + // matches declaration at the start of the function, not including helper function assignments + / *\bvar\s+[\w$]+(,\s*[\w$]+)*(,\s*|;\n)/, + + function(declaration) { + var parts = declaration.split(/([\w$]+)(?=[,;])/); + + for (var name of names) { + var i = parts.indexOf(name); + if (i < 0) { + throw new Error(`${filename}: ${name} not found`); + } else if (i !== 1) { + // not first: remove variable and separator before it + parts.splice(i - 1, 2); + } else if (!(i === parts.length - 2 && parts[parts.length - 1][0] === ';')) { + // not last: remove variable and separator after it + parts.splice(i, 2); + } else { + // removing only variable: nuke whole declaration + parts = []; + } + } + + replaced++; + return parts.join(''); + } + +); + +if (replaced !== 1) { + throw new Error(`${filename}: no declaration found`); +} + +fs.writeFileSync(filename, script);