Tag:
%gLiteral %: %%
Quick Reply Personas One item per line. Items will be added in the relevant input's auto-completion list. Password items will always be used, since there is no password input. Lines starting with a # will be ignored.
You can use these settings with each item, separate them with semicolons:Possible items are: name, options (or equivalently email), subject and password. Wrap values of items with quotes, like this: options:"sage". Force values as defaults with the always keyword, for example: options:"sage";always. Select specific boards for an item, separated with commas, for example: options:"sage";boards:jp;always. Unread Favicon is disabled. ferongr xat- 4chanJS Mayhem Original Metro Thread Updater is disabled. Interval: seconds
Custom Cooldown Time Seconds:
Custom CSSApply CSS "
});
ref = $$('.warning', section);
for (k = 0, len1 = ref.length; k < len1; k++) {
@@ -15898,7 +15872,7 @@
}
items = {};
inputs = {};
- ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown'];
+ ref1 = ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown'];
for (q = 0, len2 = ref1.length; q < len2; q++) {
name = ref1[q];
input = $("[name='" + name + "']", section);
@@ -15911,7 +15885,9 @@
$.on(input, 'change', Settings[name]);
} else {
$.on(input, 'input', $.cb.value);
- $.on(input, 'input', Settings[name]);
+ if (name in Settings) {
+ $.on(input, 'input', Settings[name]);
+ }
}
}
ta = $('.personafield', section);
@@ -15925,10 +15901,9 @@
val = items[key];
input = inputs[key];
input.value = val;
- if (key === 'usercss' || key === 'customCooldown') {
- continue;
+ if (key in Settings && key !== 'usercss') {
+ Settings[key].call(input);
}
- Settings[key].call(input);
}
});
interval = $('input[name="Interval"]', section);
@@ -16079,13 +16054,15 @@
data = {
isReply: true,
file: {
- URL: '//i.4cdn.org/g/1334437723720.jpg',
+ url: '//i.4cdn.org/g/1334437723720.jpg',
name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg',
size: '276 KB',
sizeInBytes: 276 * 1024,
dimensions: '1280x720',
isImage: true,
- isSpoiler: true
+ isVideo: false,
+ isSpoiler: true,
+ tag: 'Loop'
}
};
return FileInfo.format(this.value, data, this.nextElementSibling);
@@ -16165,19 +16142,31 @@
if (location.hostname === 'www.google.com') {
if (location.pathname === '/recaptcha/api/fallback') {
$.ready(function() {
- return Captcha.noscript.initFrame();
- });
- } else {
- $.get('Captcha Fixes', true, function(arg) {
- var enabled;
- enabled = arg['Captcha Fixes'];
- if (enabled) {
- return $.ready(function() {
- return Captcha.fixes.init();
- });
- }
+ return Captcha.v2.initFrame();
});
}
+ $.get('Captcha Fixes', true, function(arg) {
+ var enabled;
+ enabled = arg['Captcha Fixes'];
+ if (enabled) {
+ return $.ready(function() {
+ return Captcha.fixes.init();
+ });
+ }
+ });
+ return;
+ }
+ if (location.hostname === 'www.4chan.org') {
+ $.onExists(d.documentElement, 'body', false, function() {
+ return $.addStyle(Main.cssWWW);
+ });
+ Conf = {
+ 'captchaLanguage': Config.captchaLanguage
+ };
+ $.get(Conf, function(items) {
+ $.extend(Conf, items);
+ return Captcha.language.fixPage();
+ });
return;
}
g.threads = new SimpleDict();
@@ -16247,7 +16236,10 @@
});
},
initFeatures: function() {
- var err, feature, k, len1, name, pathname, ref, ref1;
+ var err, feature, k, len1, name, pathname, ref, ref1, ref2;
+ if ((ref = location.hostname) === 'boards.4chan.org' || ref === 'sys.4chan.org') {
+ $.globalEval('document.documentElement.classList.add("js-enabled");');
+ }
switch (location.hostname) {
case 'a.4cdn.org':
return;
@@ -16261,8 +16253,8 @@
$.asap((function() {
return d.readyState !== 'loading';
}), function() {
- var URL, pathname, ref, video;
- if (Conf['404 Redirect'] && ((ref = d.title) === '4chan - Temporarily Offline' || ref === '4chan - 404 Not Found')) {
+ var URL, pathname, ref1, video;
+ if (Conf['404 Redirect'] && ((ref1 = d.title) === '4chan - Temporarily Offline' || ref1 === '4chan - 404 Not Found')) {
Redirect.init();
pathname = location.pathname.split('/');
URL = Redirect.to('file', {
@@ -16291,9 +16283,9 @@
history.replaceState(null, '', pathname.slice(0, 4).join('/') + location.hash);
}
}
- ref = Main.features;
- for (k = 0, len1 = ref.length; k < len1; k++) {
- ref1 = ref[k], name = ref1[0], feature = ref1[1];
+ ref1 = Main.features;
+ for (k = 0, len1 = ref1.length; k < len1; k++) {
+ ref2 = ref1[k], name = ref2[0], feature = ref2[1];
try {
feature.init();
} catch (_error) {
@@ -17146,6 +17138,15 @@
" 0% {transform:rotate(0deg);}\n" +
" 100% {transform:rotate(359deg);}\n" +
"}\n" +
+"noscript > div, noscript > div > div {\n" +
+" height: 545px !important;\n" +
+"}\n" +
+"noscript > div > div > div:first-child, noscript iframe {\n" +
+" height: 423px !important;\n" +
+"}\n" +
+":root:not(.js-enabled) #g-recaptcha {\n" +
+" height: auto;\n" +
+"}\n" +
"/* General */\n" +
".dialog {\n" +
" border: 1px solid;\n" +
@@ -17252,10 +17253,8 @@
" /* party hats */\n" +
" pointer-events: none;\n" +
"}\n" +
-"marquee,\n" +
-".postMessage marquee + br,\n" +
-".postMessage marquee + br + br {\n" +
-" display: none;\n" +
+":root:not(.js-enabled) #postForm {\n" +
+" display: table;\n" +
"}\n" +
"/* Anti-autoplay */\n" +
"audio.controls-added {\n" +
@@ -17311,6 +17310,9 @@
"#thread-watcher {\n" +
" z-index: 10;\n" +
"}\n" +
+":root.fixed:not(.gallery-open) #header-bar:not(.autohide) {\n" +
+" z-index: 5;\n" +
+"}\n" +
"/* Header */\n" +
".fixed.top-header body {\n" +
" padding-top: 2em;\n" +
@@ -17675,6 +17677,9 @@
".section-filter textarea {\n" +
" height: 500px;\n" +
"}\n" +
+".section-filter a, .section-advanced a {\n" +
+" text-decoration: underline;\n" +
+"}\n" +
".section-sauce textarea {\n" +
" height: 350px;\n" +
"}\n" +
@@ -17998,12 +18003,15 @@
" border: none;\n" +
" box-shadow: none;\n" +
"}\n" +
-":root.float #thread-stats > .move > span {\n" +
+":root.float #thread-stats > .move > :not(#page-count) {\n" +
" pointer-events: none;\n" +
"}\n" +
":root.float #thread-stats {\n" +
" padding: 0px 3px;\n" +
"}\n" +
+"#page-count {\n" +
+" cursor: pointer;\n" +
+"}\n" +
"/* Quote */\n" +
".catalog-thread > .comment > span.quote, #arc-list span.quote {\n" +
" color: #789922;\n" +
@@ -18257,9 +18265,7 @@
" display: none !important;\n" +
"}\n" +
"#qr select,\n" +
-"#url-button,\n" +
-"#custom-cooldown-button,\n" +
-"#dump-button,\n" +
+"#qr-filename-container > a,\n" +
".remove,\n" +
".captcha-img {\n" +
" cursor: pointer;\n" +
@@ -18271,6 +18277,10 @@
" min-width: 300px;\n" +
" border-radius: 3px 3px 0 0;\n" +
"}\n" +
+"#qr > form {\n" +
+" max-height: calc(100vh - 75px);\n" +
+" overflow-y: auto;\n" +
+"}\n" +
"#qrtab {\n" +
" border-radius: 3px 3px 0 0;\n" +
"}\n" +
@@ -18281,13 +18291,6 @@
" float: right;\n" +
" padding: 0 3px;\n" +
"}\n" +
-"#qr .warning {\n" +
-" min-height: 1.6em;\n" +
-" vertical-align: middle;\n" +
-" padding: 0 1px;\n" +
-" border-width: 1px;\n" +
-" border-style: solid;\n" +
-"}\n" +
".qr-link-container {\n" +
" text-align: center;\n" +
"}\n" +
@@ -18349,23 +18352,7 @@
" position: relative;\n" +
" top: 2px;\n" +
"}\n" +
-"/* Noscript Recaptcha */\n" +
-".captcha-img {\n" +
-" margin: 0px;\n" +
-" text-align: center;\n" +
-" background-image: #fff;\n" +
-" font-size: 0px;\n" +
-" min-height: 59px;\n" +
-" min-width: 302px;\n" +
-"}\n" +
-".captcha-input{\n" +
-" width: 100%;\n" +
-" margin: 1px 0 0;\n" +
-"}\n" +
-"#qr-captcha-iframe {\n" +
-" display: none;\n" +
-"}\n" +
-"/* Recaptcha v2 */\n" +
+"/* Captcha */\n" +
"#qr .captcha-root {\n" +
" position: relative;\n" +
"}\n" +
@@ -18389,6 +18376,21 @@
" display: block;\n" +
" width: 100%;\n" +
"}\n" +
+"#qr-captcha-iframe {\n" +
+" width: 302px;\n" +
+" height: 423px;\n" +
+" border: 0;\n" +
+" display: block;\n" +
+" margin: auto;\n" +
+"}\n" +
+".goog-bubble-content {\n" +
+" max-width: 100vw;\n" +
+" max-height: 100vh;\n" +
+" overflow: auto;\n" +
+"}\n" +
+".goog-bubble-content iframe {\n" +
+" position: static !important;\n" +
+"}\n" +
"/* File Input, Submit Button */\n" +
"#file-n-submit {\n" +
" display: -webkit-flex;\n" +
@@ -18402,6 +18404,7 @@
" background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;\n" +
" border: 1px solid #BBB;\n" +
" border-radius: 2px;\n" +
+" height: 100%;\n" +
"}\n" +
"#qr-file-button {\n" +
" width: 15%;\n" +
@@ -18579,6 +18582,8 @@
"}\n" +
".textarea {\n" +
" position: relative;\n" +
+" display: -webkit-flex;\n" +
+" display: flex;\n" +
"}\n" +
":root.webkit .textarea {\n" +
" margin-bottom: -2px;\n" +
@@ -18592,6 +18597,9 @@
" right: 1px;\n" +
" pointer-events: none;\n" +
"}\n" +
+"#char-count.warning {\n" +
+" color: red;\n" +
+"}\n" +
"/* Menu */\n" +
".menu-button:not(.fa-bars) {\n" +
" display: inline-block;\n" +
@@ -18614,7 +18622,7 @@
" height: 15px;\n" +
" text-align: center;\n" +
"}\n" +
-".menu-button + .container:not(:empty) {\n" +
+".menu-button + .container :first-child {\n" +
" margin-left: -5px;\n" +
"}\n" +
"#menu {\n" +
@@ -18897,13 +18905,6 @@
"}\n" +
".gal-fit-height .gal-image img,\n" +
".gal-fit-height .gal-image video {\n" +
-" /*\n" +
-" Chrome doesn't support viewpoint units in calc()\n" +
-" http://bugs.chromium.org/168840\n" +
-" \"It looks like the original author of viewport units in WebKit is not coming back to fix this stuff.\"\n" +
-" Well, fuck.\n" +
-" */\n" +
-" max-height: 95vh;\n" +
" max-height: calc(100vh - 25px);\n" +
"}\n" +
".gal-image iframe {\n" +
@@ -19535,7 +19536,19 @@
" font-family: sans-serif !important;\n" +
" text-shadow: 1px 1px 1px rgba(0,74,153,0.6);\n" +
"}",
- features: [['Polyfill', Polyfill], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['Custom CSS', CustomCSS], ['Linkify', Linkify], ['Reveal Spoilers', RemoveSpoilers], ['Resurrect Quotes', Quotify], ['Filter', Filter], ['Thread Hiding Buttons', ThreadHiding], ['Reply Hiding Buttons', PostHiding], ['Recursive', Recursive], ['Strike-through Quotes', QuoteStrikeThrough], ['Quick Reply', QR], ['Menu', Menu], ['Index Generator (Menu)', Index.menu], ['Report Link', ReportLink], ['Thread Hiding (Menu)', ThreadHiding.menu], ['Reply Hiding (Menu)', PostHiding.menu], ['Delete Link', DeleteLink], ['Filter (Menu)', Filter.menu], ['Download Link', DownloadLink], ['Archive Link', ArchiveLink], ['Quote Inlining', QuoteInline], ['Quote Previewing', QuotePreview], ['Quote Backlinks', QuoteBacklink], ['Mark Quotes of You', QuoteYou], ['Mark OP Quotes', QuoteOP], ['Mark Cross-thread Quotes', QuoteCT], ['Anonymize', Anonymize], ['Time Formatting', Time], ['Relative Post Dates', RelativeDates], ['File Info Formatting', FileInfo], ['Fappe Tyme', FappeTyme], ['Gallery', Gallery], ['Gallery (menu)', Gallery.menu], ['Sauce', Sauce], ['Image Expansion', ImageExpand], ['Image Expansion (Menu)', ImageExpand.menu], ['Reveal Spoiler Thumbnails', RevealSpoilers], ['Image Loading', ImageLoader], ['Image Hover', ImageHover], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['Comment Expansion', ExpandComment], ['Thread Expansion', ExpandThread], ['Thread Excerpt', ThreadExcerpt], ['Favicon', Favicon], ['Unread', Unread], ['Quote Threading', QuoteThreading], ['Thread Stats', ThreadStats], ['Thread Updater', ThreadUpdater], ['Thread Watcher', ThreadWatcher], ['Thread Watcher (Menu)', ThreadWatcher.menu], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]]
+ cssWWW: "noscript > div, noscript > div > div {\n" +
+" height: 545px !important;\n" +
+"}\n" +
+"noscript > div > div > div:first-child, noscript iframe {\n" +
+" height: 423px !important;\n" +
+"}\n" +
+":root:not(.js-enabled) #g-recaptcha {\n" +
+" height: auto;\n" +
+"}\n" +
+"#captcha-cnt {\n" +
+" height: auto;\n" +
+"}",
+ features: [['Polyfill', Polyfill], ['Captcha Language', Captcha.language], ['Redirect', Redirect], ['Header', Header], ['Catalog Links', CatalogLinks], ['Settings', Settings], ['Index Generator', Index], ['Disable Autoplay', AntiAutoplay], ['Announcement Hiding', PSAHiding], ['Fourchan thingies', Fourchan], ['Color User IDs', IDColor], ['Highlight by User ID', IDHighlight], ['Custom CSS', CustomCSS], ['Linkify', Linkify], ['Reveal Spoilers', RemoveSpoilers], ['Resurrect Quotes', Quotify], ['Filter', Filter], ['Thread Hiding Buttons', ThreadHiding], ['Reply Hiding Buttons', PostHiding], ['Recursive', Recursive], ['Strike-through Quotes', QuoteStrikeThrough], ['Quick Reply', QR], ['Menu', Menu], ['Index Generator (Menu)', Index.menu], ['Report Link', ReportLink], ['Thread Hiding (Menu)', ThreadHiding.menu], ['Reply Hiding (Menu)', PostHiding.menu], ['Delete Link', DeleteLink], ['Filter (Menu)', Filter.menu], ['Download Link', DownloadLink], ['Archive Link', ArchiveLink], ['Quote Inlining', QuoteInline], ['Quote Previewing', QuotePreview], ['Quote Backlinks', QuoteBacklink], ['Mark Quotes of You', QuoteYou], ['Mark OP Quotes', QuoteOP], ['Mark Cross-thread Quotes', QuoteCT], ['Anonymize', Anonymize], ['Time Formatting', Time], ['Relative Post Dates', RelativeDates], ['File Info Formatting', FileInfo], ['Fappe Tyme', FappeTyme], ['Gallery', Gallery], ['Gallery (menu)', Gallery.menu], ['Sauce', Sauce], ['Image Expansion', ImageExpand], ['Image Expansion (Menu)', ImageExpand.menu], ['Reveal Spoiler Thumbnails', RevealSpoilers], ['Image Loading', ImageLoader], ['Image Hover', ImageHover], ['Volume Control', Volume], ['WEBM Metadata', Metadata], ['Comment Expansion', ExpandComment], ['Thread Expansion', ExpandThread], ['Thread Excerpt', ThreadExcerpt], ['Favicon', Favicon], ['Unread', Unread], ['Quote Threading', QuoteThreading], ['Thread Stats', ThreadStats], ['Thread Updater', ThreadUpdater], ['Thread Watcher', ThreadWatcher], ['Thread Watcher (Menu)', ThreadWatcher.menu], ['Mark New IPs', MarkNewIPs], ['Index Navigation', Nav], ['Keybinds', Keybinds], ['Banner', Banner], ['Flash Features', Flash]]
};
Main.init();
diff --git a/builds/4chan-X.zip b/builds/4chan-X.zip
index 38be0486d..32aaa06aa 100644
Binary files a/builds/4chan-X.zip and b/builds/4chan-X.zip differ
diff --git a/builds/updates-beta.xml b/builds/updates-beta.xml
index d95aabceb..a4e215c31 100644
--- a/builds/updates-beta.xml
+++ b/builds/updates-beta.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/builds/updates.xml b/builds/updates.xml
index 8fee44d97..c9952f5c6 100644
--- a/builds/updates.xml
+++ b/builds/updates.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/index.html b/index.html
index a362e7f25..7527918b3 100644
--- a/index.html
+++ b/index.html
@@ -37,7 +37,8 @@ Only the latest stable version of 4chan X is available.
Other browsers
This fork of 4chan X is not guaranteed to work correctly in other browsers, but you are welcome to try your luck. Pull requests to fix the bugs you will likely find are always welcome. You may fare better with loadletter's fork , which has fewer features but less dependence on browser-specific APIs.
Beta version
New features and non-urgent bugfixes are released on the beta channel for further testing before they are moved the stable version. Please report any issues you find, and be sure to mention which version you're using. You should back up your settings regularly to prevent them from being lost due to bugs.
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 686d074f5..14a25f054 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1,7 +1,7 @@
{
"name": "4chan-X",
- "npm-shrinkwrap-version": "5.3.0",
- "node-version": "v0.10.29",
+ "npm-shrinkwrap-version": "5.4.0",
+ "node-version": "v0.10.38",
"dependencies": {
"crx": {
"version": "3.0.2",
@@ -269,6 +269,90 @@
}
}
},
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
"grunt-legacy-log": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
@@ -403,6 +487,404 @@
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz",
"dependencies": {
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.10.0.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.5.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.5.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "dependencies": {
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "dependencies": {
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz"
+ },
+ "readable-stream": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
+ },
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
+ }
+ }
+ }
+ }
+ },
"rimraf": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
@@ -455,6 +937,414 @@
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.1.tgz"
},
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "dependencies": {
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "dependencies": {
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz"
+ },
+ "readable-stream": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
"lodash": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.3.0.tgz"
@@ -507,6 +1397,414 @@
}
}
},
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "dependencies": {
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "dependencies": {
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz"
+ },
+ "readable-stream": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
"source-map": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.3.0.tgz",
@@ -564,24 +1862,38 @@
"file-sync-cmp": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz"
- }
- }
- },
- "grunt-contrib-jshint": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.1.tgz",
- "dependencies": {
- "hooker": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
},
- "jshint": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.6.3.tgz",
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
"dependencies": {
- "cli": {
- "version": "0.6.5",
- "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.5.tgz",
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
"dependencies": {
"glob": {
"version": "3.2.11",
@@ -596,8 +1908,402 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
"dependencies": {
"lru-cache": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "dependencies": {
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "dependencies": {
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz"
+ },
+ "readable-stream": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.11.2.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.7.0.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
},
"sigmund": {
"version": "1.0.0",
@@ -683,17 +2389,27 @@
}
}
},
+ "lodash": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
+ },
"minimatch": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz",
"dependencies": {
- "lru-cache": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz"
- },
- "sigmund": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
}
}
},
@@ -704,10 +2420,6 @@
"strip-json-comments": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
- },
- "underscore": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
}
}
}
@@ -765,6 +2477,404 @@
}
}
},
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.8.0.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.4.4.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz",
+ "dependencies": {
+ "inflight": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz"
+ },
+ "domutils": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz"
+ },
+ "readable-stream": {
+ "version": "1.0.33",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
"lodash": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz"
@@ -809,6 +2919,421 @@
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/grunt-markdown/-/grunt-markdown-0.7.0.tgz",
"dependencies": {
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.3.0.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-1.1.0.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz",
+ "dependencies": {
+ "inflight": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "esprima": {
+ "version": "2.2.0",
+ "from": "esprima@https://github.com/ariya/esprima/tarball/master",
+ "resolved": "https://github.com/ariya/esprima/tarball/master"
+ },
+ "minimatch": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ },
+ "peakle": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/peakle/-/peakle-0.0.1.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.3.1.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz",
+ "dependencies": {
+ "fileset": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.5.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"highlight.js": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-8.4.0.tgz"
@@ -875,6 +3400,494 @@
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/grunt-webstore-upload/-/grunt-webstore-upload-0.8.2.tgz",
"dependencies": {
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-clean": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.5.0.tgz",
+ "dependencies": {
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.8.0.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.4.4.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.9.tgz",
+ "dependencies": {
+ "inflight": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
+ "dependencies": {
+ "brace-expansion": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "once": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
+ "dependencies": {
+ "wrappy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz"
+ }
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz"
+ },
+ "domutils": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz"
+ },
+ "readable-stream": {
+ "version": "1.0.33",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "load-grunt-tasks": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-0.3.0.tgz",
+ "dependencies": {
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "globule": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.2.0.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"lodash": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz"
@@ -893,6 +3906,408 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz",
"dependencies": {
+ "grunt": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz"
+ },
+ "coffee-script": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz"
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz"
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz"
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "findup-sync": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ }
+ }
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz"
+ },
+ "inherits": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.0.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.6.5.tgz",
+ "dependencies": {
+ "jshint": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.1.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz"
+ },
+ "console-browserify": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-0.1.6.tgz"
+ },
+ "shelljs": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz"
+ },
+ "underscore": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-contrib-watch": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.5.3.tgz",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.4.3.tgz",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz"
+ }
+ }
+ }
+ }
+ },
+ "tiny-lr": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz",
+ "dependencies": {
+ "debug": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz"
+ },
+ "faye-websocket": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz"
+ },
+ "noptify": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz",
+ "dependencies": {
+ "nopt": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "qs": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz"
+ }
+ }
+ }
+ }
+ },
+ "grunt-legacy-log": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.1.tgz",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz"
+ },
+ "underscore.string": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz"
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "iconv-lite": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.16",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
+ "dependencies": {
+ "underscore": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
+ },
+ "underscore.string": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.2.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "underscore.string": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz"
+ },
+ "which": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz"
+ }
+ }
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.10.0.tgz",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz"
+ },
+ "jshint": {
+ "version": "2.5.11",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.5.11.tgz",
+ "dependencies": {
+ "cli": {
+ "version": "0.6.6",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "dependencies": {
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz"
+ },
+ "htmlparser2": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz"
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "dependencies": {
+ "dom-serializer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+ "dependencies": {
+ "domelementtype": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+ },
+ "entities": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+ }
+ }
+ }
+ }
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz"
+ },
+ "readable-stream": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
+ },
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.4.tgz"
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz"
+ }
+ }
+ },
+ "shelljs": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz"
+ },
+ "strip-json-comments": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.2.tgz"
+ },
+ "underscore": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz"
+ }
+ }
+ }
+ }
+ },
"pako": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.6.tgz"
@@ -900,8 +4315,8 @@
}
},
"load-grunt-tasks": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.1.0.tgz",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-3.2.0.tgz",
"dependencies": {
"findup-sync": {
"version": "0.2.1",
@@ -926,8 +4341,8 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"minimatch": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz",
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
@@ -946,8 +4361,8 @@
}
},
"once": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/once/-/once-1.3.1.tgz",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
"dependencies": {
"wrappy": {
"version": "1.0.1",
@@ -978,8 +4393,8 @@
}
},
"minimatch": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.1.tgz",
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
@@ -1002,8 +4417,8 @@
}
},
"npm-shrinkwrap": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-5.3.0.tgz",
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-5.4.0.tgz",
"dependencies": {
"array-find": {
"version": "0.1.1",
@@ -1548,8 +4963,8 @@
"resolved": "https://registry.npmjs.org/read-json/-/read-json-0.1.0.tgz"
},
"rimraf": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.3.2.tgz",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.3.3.tgz",
"dependencies": {
"glob": {
"version": "4.5.3",
@@ -1570,8 +4985,8 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"minimatch": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.4.tgz",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.7.tgz",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
@@ -1648,8 +5063,8 @@
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-2.0.0.tgz"
},
"semver": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.1.tgz"
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.3.tgz"
},
"sorted-object": {
"version": "1.0.0",
diff --git a/package.json b/package.json
index e3f2a0116..150be93eb 100755
--- a/package.json
+++ b/package.json
@@ -3,8 +3,8 @@
"description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": {
"name": "4chan X",
- "version": "1.10.10.0",
- "date": "2015-04-18T07:45:01.778Z",
+ "version": "1.11.0.6",
+ "date": "2015-06-21T06:05:51.745Z",
"repo": "https://github.com/ccd0/4chan-x/",
"page": "https://github.com/ccd0/4chan-x",
"downloads": "https://ccd0.github.io/4chan-x/builds/",
@@ -21,9 +21,11 @@
"*://sys.4chan.org/*",
"*://a.4cdn.org/*",
"*://i.4cdn.org/*",
+ "*://www.4chan.org/banned",
+ "*://www.4chan.org/feedback",
"https://www.google.com/recaptcha/api2/anchor?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*",
"https://www.google.com/recaptcha/api2/frame?*&k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*",
- "*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"
+ "*://www.google.com/recaptcha/api/fallback?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc*"
],
"suffix": {
"stable": "",
@@ -38,7 +40,7 @@
"dev": " dev"
},
"min": {
- "chrome": "32",
+ "chrome": "34",
"firefox": "26",
"greasemonkey": "1.14"
}
@@ -52,14 +54,14 @@
"grunt-contrib-coffee": "^0.13.0",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-copy": "^0.8.0",
- "grunt-contrib-jshint": "^0.11.1",
+ "grunt-contrib-jshint": "^0.11.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-markdown": "^0.7.0",
"grunt-shell": "^1.1.2",
"grunt-webstore-upload": "^0.8.2",
"jszip": "^2.5.0",
- "load-grunt-tasks": "^3.1.0",
- "npm-shrinkwrap": "^5.3.0"
+ "load-grunt-tasks": "^3.2.0",
+ "npm-shrinkwrap": "^5.4.0"
},
"repository": {
"type": "git",
diff --git a/src/Archive/archives.json b/src/Archive/archives.json
index 6a0640044..f1a0e0bd5 100644
--- a/src/Archive/archives.json
+++ b/src/Archive/archives.json
@@ -5,7 +5,7 @@
"http": false,
"https": true,
"software": "foolfuuka",
- "boards": ["a", "biz", "c", "co", "diy", "fit", "gd", "gif", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
+ "boards": ["a", "an", "biz", "c", "co", "diy", "fit", "gd", "gif", "h", "i", "int", "jp", "k", "m", "mlp", "out", "po", "qa", "r9k", "s4s", "sci", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
"files": ["a", "biz", "c", "co", "diy", "fit", "gd", "h", "i", "jp", "k", "m", "mlp", "po", "qa", "r9k", "s4s", "sci", "tg", "u", "v", "vg", "vp", "vr", "wsg"]
}, {
"uid": 3,
@@ -32,8 +32,8 @@
"http": true,
"https": true,
"software": "foolfuuka",
- "boards": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"],
- "files": ["c", "d", "e", "i", "lgbt", "t", "u", "w", "wg"]
+ "boards": ["c", "d", "e", "i", "lgbt", "t", "u"],
+ "files": ["c", "d", "e", "i", "lgbt", "t", "u"]
}, {
"uid": 8,
"name": "Rebecca Black Tech",
diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee
index a81f419c8..d1da7e139 100755
--- a/src/Filtering/Filter.coffee
+++ b/src/Filtering/Filter.coffee
@@ -17,12 +17,10 @@ Filter =
# Don't mix up filter flags with the regular expression.
filter = line.replace regexp[0], ''
- # Do not add this filter to the list if it's not a global one
- # and it's not specifically applicable to the current board.
+ # Comma-separated list of the boards this filter applies to.
# Defaults to global.
boards = filter.match(/boards:([^;]+)/)?[1].toLowerCase() or 'global'
- if boards isnt 'global' and g.BOARD.ID not in boards.split ','
- continue
+ boards = if boards is 'global' then null else boards.split(',')
if key in ['uniqueID', 'MD5']
# MD5 filter will use strings instead of regular expressions.
@@ -66,7 +64,7 @@ Filter =
top = filter.match(/top:(yes|no)/)?[1] or 'yes'
top = top is 'yes' # Turn it into a boolean
- @filters[key].push @createFilter regexp, op, stub, hl, top
+ @filters[key].push @createFilter regexp, boards, op, stub, hl, top
# Only execute filter types that contain valid filters.
unless @filters[key].length
@@ -77,7 +75,7 @@ Filter =
name: 'Filter'
cb: @node
- createFilter: (regexp, op, stub, hl, top) ->
+ createFilter: (regexp, boards, op, stub, hl, top) ->
test =
if typeof regexp is 'string'
# MD5 checking
@@ -91,7 +89,9 @@ Filter =
class: hl
top: top
- (value, isReply) ->
+ (value, boardID, isReply) ->
+ if boards and boardID not in boards
+ return false
if isReply and op is 'only' or !isReply and op is 'no'
return false
unless test value
@@ -103,7 +103,7 @@ Filter =
for key of Filter.filters when (value = Filter[key] @)?
# Continue if there's nothing to filter (no tripcode for example).
- for filter in Filter.filters[key] when result = filter value, @isReply
+ for filter in Filter.filters[key] when result = filter value, @board.ID, @isReply
# Hide
if result.hide and not @isFetchedQuote
if @isReply
@@ -121,12 +121,18 @@ Filter =
if !@isReply and result.top
@thread.isOnTop = true
+ isHidden: (post) ->
+ for key of Filter.filters when (value = Filter[key] post)?
+ for filter in Filter.filters[key] when result = filter value, post.boardID, post.isReply
+ return true if result.hide
+ false
+
name: (post) -> post.info.name
uniqueID: (post) -> post.info.uniqueID
tripcode: (post) -> post.info.tripcode
capcode: (post) -> post.info.capcode
- subject: (post) -> post.info.subject or undefined
- comment: (post) -> post.info.comment
+ subject: (post) -> post.info.subject
+ comment: (post) -> post.info.comment ? Build.parseComment(post)
flag: (post) -> post.info.flag
filename: (post) -> post.file?.name
dimensions: (post) -> post.file?.dimensions
diff --git a/src/General/Build.coffee b/src/General/Build.coffee
index 7bbe30d0e..0b76f84e1 100755
--- a/src/General/Build.coffee
+++ b/src/General/Build.coffee
@@ -2,10 +2,12 @@ Build =
staticPath: '//s.4cdn.org/image/'
gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif'
spoilerRange: {}
+
unescape: (text) ->
return text unless text?
- text.replace(/<[^>]*>/g, '').replace /&(amp|#039|quot|lt|gt);/g, (c) ->
- {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>'}[c]
+ text.replace(/<[^>]*>/g, '').replace /&(amp|#039|quot|lt|gt|#44);/g, (c) ->
+ {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>', ',': ','}[c]
+
shortFilename: (filename) ->
threshold = 30
ext = filename.match(/\.?[^\.]*$/)[0]
@@ -13,93 +15,100 @@ Build =
"#{filename[...threshold - 5]}(...)#{ext}"
else
filename
+
spoilerThumb: (boardID) ->
if spoilerRange = Build.spoilerRange[boardID]
# Randomize the spoiler image.
"#{Build.staticPath}spoiler-#{boardID}#{Math.floor 1 + spoilerRange * Math.random()}.png"
else
"#{Build.staticPath}spoiler.png"
+
sameThread: (boardID, threadID) ->
g.VIEW is 'thread' and g.BOARD.ID is boardID and g.THREADID is +threadID
+
postURL: (boardID, threadID, postID) ->
if Build.sameThread boardID, threadID
"#p#{postID}"
else
"/#{boardID}/thread/#{threadID}#p#{postID}"
- postFromObject: (data, boardID, suppressThumb) ->
+
+ parseJSON: (data, boardID) ->
o =
# id
postID: data.no
threadID: data.resto or data.no
boardID: boardID
- # info
- name: Build.unescape data.name
- capcode: data.capcode
- tripcode: data.trip
- uniqueID: data.id
- email: Build.unescape data.email
- subject: Build.unescape data.sub
- flagCode: data.country
- flagName: Build.unescape data.country_name
- date: data.now
- dateUTC: data.time
- comment: {innerHTML: data.com or ''}
+ isReply: !!data.resto
# thread status
isSticky: !!data.sticky
isClosed: !!data.closed
isArchived: !!data.archived
- # file
- if data.filedeleted
- o.file =
- isDeleted: true
- else if data.ext
+ # file status
+ fileDeleted: !!data.filedeleted
+ o.info =
+ subject: Build.unescape data.sub
+ email: Build.unescape data.email
+ name: Build.unescape(data.name) or ''
+ tripcode: data.trip
+ uniqueID: data.id
+ flagCode: data.country
+ flag: Build.unescape data.country_name
+ dateUTC: data.time
+ dateText: data.now
+ commentHTML: {innerHTML: data.com or ''}
+ if data.capcode
+ o.info.capcode = data.capcode.replace(/_highlight$/, '').replace(/_/g, ' ').replace(/\b\w/g, (c) -> c.toUpperCase())
+ o.capcodeHighlight = /_highlight$/.test data.capcode
+ delete o.info.uniqueID
+ if data.ext
o.file =
name: (Build.unescape data.filename) + data.ext
- timestamp: "#{data.tim}#{data.ext}"
url: if boardID is 'f'
- "//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}"
+ "#{location.protocol}//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}"
else
- "//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}"
+ "#{location.protocol}//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}"
height: data.h
width: data.w
MD5: data.md5
- size: data.fsize
- turl: "//i.4cdn.org/#{boardID}/#{data.tim}s.jpg"
+ size: $.bytesToString data.fsize
+ thumbURL: "#{location.protocol}//i.4cdn.org/#{boardID}/#{data.tim}s.jpg"
theight: data.tn_h
twidth: data.tn_w
isSpoiler: !!data.spoiler
- isDeleted: false
tag: data.tag
+ o.file.dimensions = "#{o.file.width}x#{o.file.height}" unless /\.pdf$/.test o.file.url
+ o
+
+ parseComment: (o) ->
+ html = o.info.commentHTML.innerHTML
+ .replace(/
/gi, '\n')
+ .replace(/\n\n
Rolled [^<]*<\/b>/i, '') # Rolls (/tg/)
+ .replace(/]*>/g, '')
+ o.info.comment = Build.unescape html
+
+ postFromObject: (data, boardID, suppressThumb) ->
+ o = Build.parseJSON data, boardID
Build.post o, suppressThumb
+
post: (o, suppressThumb) ->
- ###
- This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS).
- @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE
- ###
- {
- postID, threadID, boardID
- name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
- comment
- file
- } = o
- name or= ''
- subject or= ''
- isOP = postID is threadID
+ {postID, threadID, boardID, file} = o
+ {subject, email, name, tripcode, capcode, uniqueID, flagCode, flag, dateUTC, dateText, commentHTML} = o.info
{staticPath, gifIcon} = Build
### Post Info ###
if capcode
- capcodeLC = capcode.split('_')[0]
- capcodeUC = capcodeLC[0].toUpperCase() + capcodeLC[1..]
- capcodeText = capcodeUC
- capcodeLong = {'Admin': 'Administrator', 'Mod': 'Moderator'}[capcodeUC] or capcodeUC
- capcodePlural = "#{capcodeLong}s"
- capcodeDescription = "a 4chan #{capcodeLong}"
- if capcode is 'admin_emeritus'
- capcodeText = 'Admin Emeritus'
+ capcodeUC = capcode.split(' ')[0]
+ capcodeLC = capcodeUC.toLowerCase()
+ if capcode is 'Admin Emeritus'
capcodePlural = 'the Administrator Emeritus'
capcodeDescription = "4chan's founding Administrator"
+ else
+ capcodeLong = {'Admin': 'Administrator', 'Mod': 'Moderator'}[capcode] or capcode
+ capcodePlural = "#{capcodeLong}s"
+ capcodeDescription = "a 4chan #{capcodeLong}"
postLink = Build.postURL boardID, threadID, postID
quoteLink = if Build.sameThread boardID, threadID
@@ -111,17 +120,17 @@ Build =
### File Info ###
- if file and not file.isDeleted
+ if file
+ protocol = /^https?:(?=\/\/i\.4cdn\.org\/)/
+ fileURL = file.url.replace protocol, ''
shortFilename = Build.shortFilename file.name
- fileSize = $.bytesToString file.size
- fileDims = if file.url[-4..] is '.pdf' then 'PDF' else "#{file.width}x#{file.height}"
- fileThumb = if file.isSpoiler then Build.spoilerThumb boardID else file.turl
+ fileThumb = if file.isSpoiler then Build.spoilerThumb(boardID) else file.thumbURL.replace(protocol, '')
fileBlock = <%= importHTML('Build/File') %>
### Whole Post ###
- postClass = if isOP then 'op' else 'reply'
+ postClass = if o.isReply then 'reply' else 'op'
wholePost = <%= importHTML('Build/Post') %>
@@ -130,13 +139,15 @@ Build =
id: "pc#{postID}"
$.extend container, wholePost
- # Fix pathnames
+ # Fix quotelinks
for quote in $$ '.quotelink', container
href = quote.getAttribute 'href'
if (href[0] is '#') and !(Build.sameThread boardID, threadID)
quote.href = "/#{boardID}/thread/#{threadID}" + href
else if (match = href.match /^\/([^\/]+)\/thread\/(\d+)/) and (Build.sameThread match[1], match[2])
quote.href = href.match(/(#[^#]*)?$/)[0] or '#'
+ else if /^\d+(#|$)/.test(href) and not (g.VIEW is 'thread' and g.BOARD.ID is boardID) # used on /f/
+ quote.href = "/#{boardID}/thread/#{href}"
container
diff --git a/src/General/BuildTest.coffee b/src/General/BuildTest.coffee
index 3638fdaa2..8bf1c9219 100644
--- a/src/General/BuildTest.coffee
+++ b/src/General/BuildTest.coffee
@@ -38,22 +38,37 @@ BuildTest =
for postData in posts
if postData.no is post.ID
t1 = new Date().getTime()
- root = Build.postFromObject postData, post.board.ID
+ obj = Build.parseJSON postData, post.board.ID
+ root = Build.post obj
t2 = new Date().getTime()
BuildTest.time += t2 - t1
post2 = new Post root, post.thread, post.board
+ fail = false
+
x = post.normalizedOriginal
y = post2.normalizedOriginal
- if x.isEqualNode y
- c.log "#{post.fullID} correct"
- else
+ unless x.isEqualNode y
+ fail = true
c.log "#{post.fullID} differs"
- BuildTest.postsFailed++
[x2, y2] = BuildTest.firstDiff x, y
c.log x2
c.log y2
c.log x.outerHTML
c.log y.outerHTML
+
+ for key of Config.filter when not (key is 'MD5' and post.board.ID is 'f')
+ val1 = Filter[key] obj
+ val2 = Filter[key] post2
+ if val1 isnt val2
+ fail = true
+ c.log "#{post.fullID} has filter bug in #{key}"
+ c.log val1
+ c.log val2
+
+ if fail
+ BuildTest.postsFailed++
+ else
+ c.log "#{post.fullID} correct"
BuildTest.postsRemaining--
BuildTest.report() if BuildTest.postsRemaining is 0
post2.isFetchedQuote = true
@@ -61,8 +76,9 @@ BuildTest =
testAll: ->
g.posts.forEach (post) ->
- unless post.isClone or post.isFetchedQuote or $ '.abbr', post.nodes.comment
- BuildTest.testOne post
+ unless post.isClone or post.isFetchedQuote
+ unless (abbr = $ '.abbr', post.nodes.comment) and /Comment too long\./.test(abbr.textContent)
+ BuildTest.testOne post
return
postsRemaining: 0
diff --git a/src/General/Config.coffee b/src/General/Config.coffee
index b8eb7d363..02b25e2ab 100755
--- a/src/General/Config.coffee
+++ b/src/General/Config.coffee
@@ -421,13 +421,11 @@ Config =
'Open new threads or replies to a thread from the index in a new tab.'
1
]
- <% if (type === 'userscript') { %>
'Remember QR Size': [
false
'Remember the size of the Quick reply.'
1
]
- <% } %>
'Remember Spoiler': [
false
'Remember the spoiler state, instead of resetting after posting.'
@@ -485,7 +483,7 @@ Config =
]
'Captcha Fixes': [
true
- 'Make captcha more keyboard-navigable.'
+ 'Make captcha easier to use, especially with the keyboard.'
]
'Quote Links':
@@ -592,6 +590,9 @@ Config =
'Fit Height': [
true
]
+ 'Stretch to Fit': [
+ false
+ ]
'Scroll to Post': [
true
]
@@ -674,12 +675,14 @@ Config =
sauces: """
https://www.google.com/searchbyimage?image_url=%IMG
http://iqdb.org/?url=%IMG
- http://eye.swfchan.com/search/?q=%name;types:swf
+ http://eye.swfchan.com/search/?q=%name;types:swf;sandbox
#//tineye.com/search?url=%IMG
#https://www.yandex.com/images/search?rpt=imageview&img_url=%IMG
#//saucenao.com/search.php?url=%IMG
#http://3d.iqdb.org/?url=%IMG
+ # tools:
#http://regex.info/exif.cgi?imgurl=%URL
+ #//imgops.com/%URL;types:gif,jpg,png
# uploaders:
#//imgur.com/upload?url=%URL;types:gif,jpg,png,pdf;text:Upload to imgur
# "View Same" in archives:
@@ -743,11 +746,13 @@ Config =
#options:"sage";boards:jp;always
"""
+ captchaLanguage: ''
+
time: '%m/%d/%y(%a)%H:%M:%S'
backlink: '>>%id'
- fileInfo: '%l (%p%s, %r)'
+ fileInfo: '%l (%p%s, %r%g)'
favicon: 'ferongr'
@@ -830,7 +835,7 @@ Config =
'Pause/play videos in the gallery.'
]
'Slideshow': [
- 's'
+ 'Ctrl+Right'
'Toggle the gallery slideshow mode.'
]
'fappeTyme': [
diff --git a/src/General/Get.coffee b/src/General/Get.coffee
index bfd705f9e..9dffdf722 100755
--- a/src/General/Get.coffee
+++ b/src/General/Get.coffee
@@ -3,7 +3,7 @@ Get =
{OP} = thread
excerpt = "/#{thread.board}/ - " + (
OP.info.subject?.trim() or
- OP.info.comment.replace(/\n+/g, ' // ') or
+ OP.info.commentDisplay.replace(/\n+/g, ' // ') or
OP.info.nameBlock)
return "#{excerpt[...70]}..." if excerpt.length > 73
excerpt
diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee
index 82bcf0278..43cd70ca4 100755
--- a/src/General/Globals.coffee
+++ b/src/General/Globals.coffee
@@ -19,3 +19,6 @@ 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/Index.coffee b/src/General/Index.coffee
index c61f40e5b..74a4dfaab 100644
--- a/src/General/Index.coffee
+++ b/src/General/Index.coffee
@@ -431,7 +431,7 @@ Index =
Index.req = $.ajax "//a.4cdn.org/#{g.BOARD}/catalog.json",
onloadend: (e) -> Index.load e, state
,
- whenModified: true
+ whenModified: 'Index'
$.addClass Index.button, 'fa-spin'
load: (e, state) ->
diff --git a/src/General/Main.coffee b/src/General/Main.coffee
index 6de834f61..f52330270 100755
--- a/src/General/Main.coffee
+++ b/src/General/Main.coffee
@@ -2,11 +2,18 @@ Main =
init: ->
if location.hostname is 'www.google.com'
if location.pathname is '/recaptcha/api/fallback'
- $.ready -> Captcha.noscript.initFrame()
- else
- $.get 'Captcha Fixes', true, ({'Captcha Fixes': enabled}) ->
- if enabled
- $.ready -> Captcha.fixes.init()
+ $.ready -> Captcha.v2.initFrame()
+ $.get 'Captcha Fixes', true, ({'Captcha Fixes': enabled}) ->
+ if enabled
+ $.ready -> Captcha.fixes.init()
+ return
+
+ if location.hostname is 'www.4chan.org'
+ $.onExists d.documentElement, 'body', false, -> $.addStyle Main.cssWWW
+ Conf = {'captchaLanguage': Config.captchaLanguage}
+ $.get Conf, (items) ->
+ $.extend Conf, items
+ Captcha.language.fixPage()
return
g.threads = new SimpleDict()
@@ -57,6 +64,9 @@ Main =
$.onExists doc, 'body', false, Main.initStyle
initFeatures: ->
+ if location.hostname in ['boards.4chan.org', 'sys.4chan.org']
+ $.globalEval 'document.documentElement.classList.add("js-enabled");'
+
switch location.hostname
when 'a.4cdn.org'
return
@@ -298,17 +308,13 @@ Main =
$.ready ->
cb() if Main.isThisPageLegit()
- css: `<%=
- grunt.template.process(
- ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon'].map(function(name) {
- return grunt.file.read('src/General/css/'+name+'.css');
- }).join(''),
- {data: {type: type}}
- ).trim().replace(/\n+/g, '\n').split(/^/m).map(JSON.stringify).join(' +\n').replace(/`/g, '\\`')
- %>`
+ css: `<%= importCSS('font-awesome', 'noscript', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon') %>`
+
+ cssWWW: `<%= importCSS('noscript', 'www') %>`
features: [
['Polyfill', Polyfill]
+ ['Captcha Language', Captcha.language]
['Redirect', Redirect]
['Header', Header]
['Catalog Links', CatalogLinks]
diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee
index f842925d4..09d8a652c 100755
--- a/src/General/Settings.coffee
+++ b/src/General/Settings.coffee
@@ -103,6 +103,7 @@ Settings =
description = arr[1]
div = $.el 'div',
<%= html(' ${key}: ${description} ') %>
+ div.hidden = true if chrome? and key is 'Remember QR Size' # XXX not supported
input = $ 'input', div
$.on input, 'change', ->
@parentNode.parentNode.dataset.checked = @checked
@@ -314,7 +315,7 @@ Settings =
items = {}
inputs = {}
- for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']
+ for name in ['captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss', 'customCooldown']
input = $ "[name='#{name}']", section
items[name] = Conf[name]
inputs[name] = input
@@ -323,9 +324,9 @@ Settings =
else if name is 'favicon'
$.on input, 'change', $.cb.value
$.on input, 'change', Settings[name]
- else
+ else
$.on input, 'input', $.cb.value
- $.on input, 'input', Settings[name]
+ $.on input, 'input', Settings[name] if name of Settings
# Quick Reply Personas
ta = $ '.personafield', section
@@ -338,8 +339,8 @@ Settings =
for key, val of items
input = inputs[key]
input.value = val
- continue if key in ['usercss', 'customCooldown']
- Settings[key].call input
+ if key of Settings and key isnt 'usercss'
+ Settings[key].call input
return
interval = $ 'input[name="Interval"]', section
@@ -454,13 +455,15 @@ Settings =
data =
isReply: true
file:
- URL: '//i.4cdn.org/g/1334437723720.jpg'
+ url: '//i.4cdn.org/g/1334437723720.jpg'
name: 'd9bb2efc98dd0df141a94399ff5880b7.jpg'
size: '276 KB'
sizeInBytes: 276 * 1024
dimensions: '1280x720'
isImage: true
+ isVideo: false
isSpoiler: true
+ tag: 'Loop'
FileInfo.format @value, data, @nextElementSibling
favicon: ->
diff --git a/src/General/css/noscript.css b/src/General/css/noscript.css
new file mode 100644
index 000000000..01baf5523
--- /dev/null
+++ b/src/General/css/noscript.css
@@ -0,0 +1,9 @@
+noscript > div, noscript > div > div {
+ height: 545px !important;
+}
+noscript > div > div > div:first-child, noscript iframe {
+ height: 423px !important;
+}
+:root:not(.js-enabled) #g-recaptcha {
+ height: auto;
+}
diff --git a/src/General/css/style.css b/src/General/css/style.css
index f3d1bbc5b..e9ae46c30 100755
--- a/src/General/css/style.css
+++ b/src/General/css/style.css
@@ -105,10 +105,8 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) {
/* party hats */
pointer-events: none;
}
-marquee,
-.postMessage marquee + br,
-.postMessage marquee + br + br {
- display: none;
+:root:not(.js-enabled) #postForm {
+ display: table;
}
/* Anti-autoplay */
@@ -166,6 +164,9 @@ audio.controls-added {
#thread-watcher {
z-index: 10;
}
+:root.fixed:not(.gallery-open) #header-bar:not(.autohide) {
+ z-index: 5;
+}
/* Header */
.fixed.top-header body {
@@ -532,6 +533,9 @@ div[data-checked="false"] > .suboption-list {
.section-filter textarea {
height: 500px;
}
+.section-filter a, .section-advanced a {
+ text-decoration: underline;
+}
.section-sauce textarea {
height: 350px;
}
@@ -867,12 +871,15 @@ span.hide-announcement {
border: none;
box-shadow: none;
}
-:root.float #thread-stats > .move > span {
+:root.float #thread-stats > .move > :not(#page-count) {
pointer-events: none;
}
:root.float #thread-stats {
padding: 0px 3px;
}
+#page-count {
+ cursor: pointer;
+}
/* Quote */
.catalog-thread > .comment > span.quote, #arc-list span.quote {
@@ -1134,9 +1141,7 @@ input[name="Default Volume"] {
display: none !important;
}
#qr select,
-#url-button,
-#custom-cooldown-button,
-#dump-button,
+#qr-filename-container > a,
.remove,
.captcha-img {
cursor: pointer;
@@ -1148,6 +1153,10 @@ input[name="Default Volume"] {
min-width: 300px;
border-radius: 3px 3px 0 0;
}
+#qr > form {
+ max-height: calc(100vh - 75px);
+ overflow-y: auto;
+}
#qrtab {
border-radius: 3px 3px 0 0;
}
@@ -1158,13 +1167,6 @@ input[name="Default Volume"] {
float: right;
padding: 0 3px;
}
-#qr .warning {
- min-height: 1.6em;
- vertical-align: middle;
- padding: 0 1px;
- border-width: 1px;
- border-style: solid;
-}
.qr-link-container {
text-align: center;
}
@@ -1227,7 +1229,7 @@ input.field.tripped:not(:hover):not(:focus) {
top: 2px;
}
-/* Noscript Recaptcha */
+/* Recaptcha v1 */
.captcha-img {
margin: 0px;
text-align: center;
@@ -1240,9 +1242,6 @@ input.field.tripped:not(:hover):not(:focus) {
width: 100%;
margin: 1px 0 0;
}
-#qr-captcha-iframe {
- display: none;
-}
/* Recaptcha v2 */
#qr .captcha-root {
@@ -1268,6 +1267,21 @@ input.field.tripped:not(:hover):not(:focus) {
display: block;
width: 100%;
}
+#qr-captcha-iframe {
+ width: 302px;
+ height: 423px;
+ border: 0;
+ display: block;
+ margin: auto;
+}
+.goog-bubble-content {
+ max-width: 100vw;
+ max-height: 100vh;
+ overflow: auto;
+}
+.goog-bubble-content iframe {
+ position: static !important;
+}
/* File Input, Submit Button */
#file-n-submit {
@@ -1282,6 +1296,7 @@ input.field.tripped:not(:hover):not(:focus) {
background: linear-gradient(to bottom, #F8F8F8, #DCDCDC) no-repeat;
border: 1px solid #BBB;
border-radius: 2px;
+ height: 100%;
}
#qr-file-button {
width: 15%;
@@ -1462,6 +1477,8 @@ a:only-of-type > .remove {
}
.textarea {
position: relative;
+ display: -webkit-flex;
+ display: flex;
}
:root.webkit .textarea {
margin-bottom: -2px;
@@ -1475,6 +1492,9 @@ a:only-of-type > .remove {
right: 1px;
pointer-events: none;
}
+#char-count.warning {
+ color: red;
+}
/* Menu */
.menu-button:not(.fa-bars) {
@@ -1498,7 +1518,7 @@ a:only-of-type > .remove {
height: 15px;
text-align: center;
}
-.menu-button + .container:not(:empty) {
+.menu-button + .container :first-child {
margin-left: -5px;
}
#menu {
@@ -1745,13 +1765,6 @@ grunt.file.expand('src/General/img/links/*.png').map(function(file) {
}
.gal-fit-height .gal-image img,
.gal-fit-height .gal-image video {
- /*
- Chrome doesn't support viewpoint units in calc()
- http://bugs.chromium.org/168840
- "It looks like the original author of viewport units in WebKit is not coming back to fix this stuff."
- Well, fuck.
- */
- max-height: 95vh;
max-height: calc(100vh - 25px);
}
.gal-image iframe {
diff --git a/src/General/css/www.css b/src/General/css/www.css
new file mode 100644
index 000000000..01b12c399
--- /dev/null
+++ b/src/General/css/www.css
@@ -0,0 +1,3 @@
+#captcha-cnt {
+ height: auto;
+}
diff --git a/src/General/html/Build/File.html b/src/General/html/Build/File.html
index 2b1e0fed4..d48f11db2 100644
--- a/src/General/html/Build/File.html
+++ b/src/General/html/Build/File.html
@@ -1,29 +1,35 @@
-?{file}{
- ?{file.isDeleted}{
-
-
-
- }{?{boardID === "f"}{
+?{file}{
+
+ ?{boardID === "f"}{
File:
- ${file.name}
- -(${fileSize}, ${fileDims}, ${file.tag})
+ ${file.name}
+ -(${file.size}, ${file.dimensions}?{file.tag}{, ${file.tag}})
}{
File:
-
+
?{file.isSpoiler}{Spoiler Image}{${shortFilename}}
- (${fileSize}, ${fileDims})
+ (${file.size}, ${file.dimensions || "PDF"})
-
+
- }}
-
}
+ }
+
+}{
+ ?{o.fileDeleted}{
+
+
+
+
+
+ }
+}
diff --git a/src/General/html/Build/Post.html b/src/General/html/Build/Post.html
index fc2b36cca..bc9cf1852 100644
--- a/src/General/html/Build/Post.html
+++ b/src/General/html/Build/Post.html
@@ -1,5 +1,5 @@
-?{!isOP}{>>
}
-
- ?{isOP}{&{fileBlock}&{postInfo}}{&{postInfo}&{fileBlock}}
-
&{comment}
+?{o.isReply}{
>>
}
+
+ ?{o.isReply}{&{postInfo}&{fileBlock}}{&{fileBlock}&{postInfo}}
+
&{commentHTML}
diff --git a/src/General/html/Build/PostInfo.html b/src/General/html/Build/PostInfo.html
index fbd848f5c..75f7c383d 100644
--- a/src/General/html/Build/PostInfo.html
+++ b/src/General/html/Build/PostInfo.html
@@ -1,24 +1,24 @@
- ?{isOP || boardID === "f"}{
${subject} }
+ ?{!o.isReply || boardID === "f" || subject}{
${subject || ""} }
?{email}{}
${name}
?{tripcode}{ ${tripcode} }
- ?{capcode}{ ## ${capcodeText} }
+ ?{capcode}{ ## ${capcode} }
?{email}{ }
- ?{boardID === "f" && isOP || capcode}{}{ }
+ ?{boardID === "f" && !o.isReply || capcode}{}{ }
?{capcode}{ }
?{uniqueID && !capcode}{ (ID: ${uniqueID} ) }
- ?{flagCode}{ }
+ ?{flagCode}{ }
-
${date}
-
+ ${dateText}
+
No.
${postID}
?{o.isSticky}{ }
?{o.isClosed && !o.isArchived}{ }
?{o.isArchived}{ }
- ?{isOP && g.VIEW === "index"}{ [Reply ] }
+ ?{!o.isReply && g.VIEW === "index"}{ [Reply ] }
diff --git a/src/General/html/Features/Sandbox.html b/src/General/html/Features/Sandbox.html
new file mode 100644
index 000000000..bc8ddbd52
--- /dev/null
+++ b/src/General/html/Features/Sandbox.html
@@ -0,0 +1,16 @@
+
+
[sb] ${url}
+
+
+
+
diff --git a/src/General/html/Settings/Advanced.html b/src/General/html/Settings/Advanced.html
index a9ddd65a0..e6b5f38ef 100755
--- a/src/General/html/Settings/Advanced.html
+++ b/src/General/html/Settings/Advanced.html
@@ -12,6 +12,12 @@
+
+ Captcha Language
+
+
+
+
Custom Board Navigation
@@ -41,7 +47,7 @@
Time Formatting is disabled.
:
-
+
Day: %a, %A, %d, %e
Month: %m, %b, %B
Year: %y, %Y
@@ -64,6 +70,7 @@
Spoiler indicator: %p
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
Resolution: %r (Displays 'PDF' for PDF files)
+ Tag:
%g
Literal %: %%
diff --git a/src/General/html/Settings/Filter-guide.html b/src/General/html/Settings/Filter-guide.html
index 5b5f981cc..9d453ced6 100755
--- a/src/General/html/Settings/Filter-guide.html
+++ b/src/General/html/Settings/Filter-guide.html
@@ -1,6 +1,6 @@
Filter is disabled.
- Use regular expressions , one per line.
+ Use regular expressions , one per line.
Lines starting with a # will be ignored.
For example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
MD5 filtering uses exact string matching, not regular expressions.
diff --git a/src/General/html/Settings/Sauce.html b/src/General/html/Settings/Sauce.html
index ff1c76644..bf13382a3 100755
--- a/src/General/html/Settings/Sauce.html
+++ b/src/General/html/Settings/Sauce.html
@@ -3,6 +3,7 @@
You can specify a display text by appending ;text:[text] to the URL.
You can specify the applicable boards by appending ;boards:[board1],[board2].
You can specify the applicable file types by appending ;types:[extension1],[extension2].
+
You can open links with scripts and popups disabled by appending ;sandbox.
These parameters will be replaced by their corresponding values:
%TURL: Thumbnail URL.
%URL: Full image URL.
diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee
index 04ca0b1e6..921da3ea0 100755
--- a/src/General/lib/$.coffee
+++ b/src/General/lib/$.coffee
@@ -64,8 +64,8 @@ $.ajax = do ->
options.onerror?()
return
if whenModified
- r.setRequestHeader 'If-Modified-Since', lastModified[url] if url of lastModified
- $.on r, 'load', -> lastModified[url] = r.getResponseHeader 'Last-Modified'
+ r.setRequestHeader 'If-Modified-Since', lastModified[whenModified][url] if lastModified[whenModified]?[url]?
+ $.on r, 'load', -> (lastModified[whenModified] or= {})[url] = r.getResponseHeader 'Last-Modified'
if /\.json$/.test url
r.responseType = 'json'
$.extend r, options
diff --git a/src/General/lib/connection.class b/src/General/lib/connection.class
index 52e614136..00d90ae3f 100644
--- a/src/General/lib/connection.class
+++ b/src/General/lib/connection.class
@@ -1,5 +1,5 @@
class Connection
- constructor: (@target, @origin, @cb) ->
+ constructor: (@target, @origin, @cb={}) ->
$.on window, 'message', @onMessage
send: (data) =>
diff --git a/src/General/lib/fetcher.class b/src/General/lib/fetcher.class
index 49baf9257..5a143799d 100644
--- a/src/General/lib/fetcher.class
+++ b/src/General/lib/fetcher.class
@@ -126,41 +126,41 @@ class Fetcher
@threadID = +data.thread_num
o =
- # id
postID: @postID
threadID: @threadID
boardID: @boardID
- # info
- name: data.name
- capcode: switch data.capcode
- when 'M' then 'mod'
- when 'A' then 'admin'
- when 'D' then 'developer'
- tripcode: data.trip
- uniqueID: data.poster_hash
- email: data.email or ''
+ isReply: @postID isnt @threadID
+ o.info =
subject: data.title
+ email: data.email
+ name: data.name or ''
+ tripcode: data.trip
+ capcode: switch data.capcode
+ when 'M' then 'Mod'
+ when 'A' then 'Admin'
+ when 'D' then 'Developer'
+ uniqueID: data.poster_hash
flagCode: data.poster_country
- flagName: data.poster_country_name
- date: data.fourchan_date
+ flag: data.poster_country_name
dateUTC: data.timestamp
- comment: comment
- # file
+ dateText: data.fourchan_date
+ commentHTML: comment
+ delete o.info.uniqueID if o.info.capcode
if data.media?.media_filename
o.file =
name: data.media.media_filename
- timestamp: data.media.media_orig
url: data.media.media_link or data.media.remote_media_link or
- "//i.4cdn.org/#{@boardID}/#{encodeURIComponent data.media[if @boardID is 'f' then 'media_filename' else 'media_orig']}"
+ "#{location.protocol}//i.4cdn.org/#{@boardID}/#{encodeURIComponent data.media[if @boardID is 'f' then 'media_filename' else 'media_orig']}"
height: data.media.media_h
width: data.media.media_w
MD5: data.media.media_hash
- size: data.media.media_size
- turl: data.media.thumb_link or "//i.4cdn.org/#{@boardID}/#{data.media.preview_orig}"
+ size: $.bytesToString data.media.media_size
+ thumbURL: data.media.thumb_link or "#{location.protocol}//i.4cdn.org/#{@boardID}/#{data.media.preview_orig}"
theight: data.media.preview_h
twidth: data.media.preview_w
isSpoiler: data.media.spoiler is '1'
- o.file.tag = JSON.parse(data.media.exif).Tag if @boardID is 'f'
+ o.file.dimensions = "#{o.file.width}x#{o.file.height}" unless /\.pdf$/.test o.file.url
+ o.file.tag = JSON.parse(data.media.exif).Tag if @boardID is 'f' and data.media.exif
board = g.boards[@boardID] or
new Board @boardID
@@ -168,7 +168,7 @@ class Fetcher
new Thread @threadID, board
post = new Post Build.post(o), thread, board
post.kill()
- post.file.thumbURL = o.file.turl if post.file
+ post.file.thumbURL = o.file.thumbURL if post.file
post.isFetchedQuote = true
Main.callbackNodes Post, [post]
@insert post
diff --git a/src/General/lib/polyfill.coffee b/src/General/lib/polyfill.coffee
index 6f7799f06..2f78e2d59 100755
--- a/src/General/lib/polyfill.coffee
+++ b/src/General/lib/polyfill.coffee
@@ -1,19 +1,6 @@
Polyfill =
init: ->
- @notificationPermission()
@toBlob()
- @visibility()
- notificationPermission: ->
- return if !window.Notification or 'permission' of Notification or !window.webkitNotifications
- Object.defineProperty Notification, 'permission',
- get: ->
- switch webkitNotifications.checkPermission()
- when 0
- 'granted'
- when 1
- 'default'
- when 2
- 'denied'
toBlob: ->
HTMLCanvasElement::toBlob or= (cb) ->
data = atob @toDataURL()[22..]
@@ -23,12 +10,3 @@ Polyfill =
for i in [0...l] by 1
ui8a[i] = data.charCodeAt i
cb new Blob [ui8a], type: 'image/png'
- visibility: ->
- # page visibility API
- return if 'visibilityState' of d
- Object.defineProperties HTMLDocument.prototype,
- visibilityState:
- get: -> @webkitVisibilityState
- hidden:
- get: -> @webkitHidden
- $.on d, 'webkitvisibilitychange', -> $.event 'visibilitychange'
diff --git a/src/General/lib/post.class b/src/General/lib/post.class
index f62e65377..7fec2efc3 100755
--- a/src/General/lib/post.class
+++ b/src/General/lib/post.class
@@ -56,7 +56,7 @@ class Post
@nodes.nameBlock.textContent.trim()
if subject = $ '.subject', info
@nodes.subject = subject
- @info.subject = subject.textContent
+ @info.subject = subject.textContent or undefined
if name = $ '.name', info
@nodes.name = name
@info.name = name.textContent
@@ -106,22 +106,28 @@ class Post
# 'Comment too long'...
# EXIF data. (/p/)
# Rolls. (/tg/)
- # Marquees. (/pol/)
+ # Fortunes. (/s4s/)
+ bq = @nodes.comment.cloneNode true
+ for node in $$ '.abbr + br, .exif, b, .fortune', bq
+ $.rm node
+ if abbr = $ '.abbr', bq
+ $.rm abbr
+ @info.comment = @nodesToText bq
+ if abbr
+ @info.comment = @info.comment.replace /\n\n$/, ''
+
+ # Hide spoilers.
+ # Remove:
# Preceding and following new lines.
# Trailing spaces.
- bq = @nodes.comment.cloneNode true
- for node in $$ '.abbr, .exif, b, marquee', bq
- $.rm node
- @info.comment = @nodesToText bq
-
- # Get the comment's text with spoilers hidden.
- spoilers = $$ 's', bq
- @info.commentSpoilered = if spoilers.length
- for node in spoilers
- $.replace node, $.tn '[spoiler]'
- @nodesToText bq
- else
- @info.comment
+ commentDisplay = @info.comment
+ unless Conf['Remove Spoilers'] or Conf['Reveal Spoilers']
+ spoilers = $$ 's', bq
+ if spoilers.length
+ for node in spoilers
+ $.replace node, $.tn '[spoiler]'
+ commentDisplay = @nodesToText bq
+ @info.commentDisplay = commentDisplay.trim().replace /\s+$/gm, ''
nodesToText: (bq) ->
text = ""
@@ -129,7 +135,7 @@ class Post
i = 0
while node = nodes.snapshotItem i++
text += node.data or '\n'
- text.trim().replace /\s+$/gm, ''
+ text
parseQuotes: ->
@quotes = []
@@ -170,12 +176,13 @@ class Post
@file =
text: fileText
link: link
- URL: link.href
+ url: link.href
name: fileText.title or link.title or link.textContent
size: info[1]
isImage: /(jpg|png|gif)$/i.test link.href
isVideo: /webm$/i.test link.href
dimensions: info[0].match(/\d+x\d+/)?[0]
+ tag: info[0].match(/,[^,]*, ([a-z]+)\)/i)?[1]
size = +@file.size.match(/[\d.]+/)[0]
unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0]
size *= 1024 while unit-- > 0
@@ -183,7 +190,7 @@ class Post
if (thumb = $ '.fileThumb > [data-md5]', fileEl)
$.extend @file,
thumb: thumb
- thumbURL: "#{location.protocol}//i.4cdn.org/#{@board}/#{link.href.match(/(\d+)\./)[1]}s.jpg"
+ thumbURL: if m = link.href.match(/\d+(?=\.\w+$)/) then "#{location.protocol}//i.4cdn.org/#{@board}/#{m[0]}s.jpg"
MD5: thumb.dataset.md5
isSpoiler: $.hasClass thumb.parentNode, 'imgspoiler'
diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee
index 49a591e0b..cf5f3312d 100644
--- a/src/Images/Gallery.coffee
+++ b/src/Images/Gallery.coffee
@@ -112,7 +112,7 @@ Gallery =
thumb = $.el 'a',
className: 'gal-thumb'
- href: post.file.URL
+ href: post.file.url
target: '_blank'
title: post.file.name
@@ -128,53 +128,66 @@ Gallery =
Gallery.images.push thumb
$.add Gallery.nodes.thumbs, thumb
+ load: (thumb, errorCB) ->
+ ext = thumb.href.match /\w*$/
+ elType = {'webm': 'video', 'pdf': 'iframe'}[ext] or 'img'
+ file = $.el elType,
+ title: thumb.title
+ $.extend file.dataset, thumb.dataset
+ $.on file, 'error', errorCB
+ file.src = thumb.href
+ file
+
open: (thumb) ->
{nodes} = Gallery
- {name} = nodes
oldID = +nodes.current.dataset.id
newID = +thumb.dataset.id
- slideshow = Gallery.slideshow and (newID > oldID or (oldID is Gallery.images.length-1 and newID is 0))
- $.rmClass el, 'gal-highlight' if el = $ '.gal-highlight', nodes.thumbs
+ # Highlight, center selected thumbnail
+ $.rmClass el, 'gal-highlight' if el = Gallery.images[oldID]
$.addClass thumb, 'gal-highlight'
+ nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight/2 - nodes.thumbs.clientHeight/2
- elType = if /\.webm$/.test(thumb.href)
- 'video'
- else if /\.pdf$/.test(thumb.href)
- 'iframe'
+ # Load image or use preloaded image
+ if Gallery.cache?.dataset.id is ''+newID
+ file = Gallery.cache
+ $.off file, 'error', Gallery.cacheError
+ $.on file, 'error', Gallery.error
else
- 'img'
-
- $[if elType is 'iframe' then 'addClass' else 'rmClass'] doc, 'gal-pdf'
- file = $.el elType,
- title: name.download = name.textContent = thumb.title
- $.extend file.dataset, thumb.dataset
- $.on file, 'error', Gallery.error
- file.src = name.href = thumb.href
+ file = Gallery.load thumb, Gallery.error
+ # Replace old image with new one
$.off nodes.current, 'error', Gallery.error
ImageCommon.pause nodes.current
$.replace nodes.current, file
- if elType is 'video'
+ nodes.current = file
+
+ if file.nodeName is 'VIDEO'
file.loop = true
Volume.setup file
file.play() if Conf['Autoplay']
ImageCommon.addControls file if Conf['Show Controls']
+
+ doc.classList.toggle 'gal-pdf', file.nodeName is 'IFRAME'
+ Gallery.cb.setHeight()
nodes.count.textContent = +thumb.dataset.id + 1
- nodes.current = file
+ nodes.name.download = nodes.name.textContent = thumb.title
+ nodes.name.href = thumb.href
nodes.frame.scrollTop = 0
nodes.next.focus()
- if slideshow
+
+ # Continue slideshow if moving forward, stop otherwise
+ if Gallery.slideshow and (newID > oldID or (oldID is Gallery.images.length-1 and newID is 0))
Gallery.setupTimer()
else
Gallery.cb.stop()
# Scroll to post
- if Conf['Scroll to Post'] and post = (post = g.posts[file.dataset.post])?.nodes.root
- Header.scrollTo post
+ if Conf['Scroll to Post'] and (post = g.posts[file.dataset.post])
+ Header.scrollTo post.nodes.root
- # Center selected thumbnail
- nodes.thumbs.scrollTop = thumb.offsetTop + thumb.offsetHeight/2 - nodes.thumbs.clientHeight/2
+ # Preload next image
+ Gallery.cache = Gallery.load Gallery.images[(newID + 1) % Gallery.images.length], Gallery.cacheError
error: ->
if @error?.code is MediaError.MEDIA_ERR_DECODE
@@ -185,6 +198,9 @@ Gallery =
Gallery.images[@dataset.id].href = url
@src = url if Gallery.nodes.current is @
+ cacheError: ->
+ delete Gallery.cache
+
cleanupTimer: ->
clearTimeout Gallery.timeoutID
{current} = Gallery.nodes
@@ -301,6 +317,14 @@ Gallery =
setFitness: ->
(if @checked then $.addClass else $.rmClass) doc, "gal-#{@name.toLowerCase().replace /\s+/g, '-'}"
+ setHeight: ->
+ {current, frame} = Gallery.nodes
+ current.style.minHeight = if Conf['Stretch to Fit'] and (dim = g.posts[current.dataset.post]?.file.dimensions)
+ [width, height] = dim.split 'x'
+ Math.min(doc.clientHeight - 25, height / width * frame.clientWidth) + 'px'
+ else
+ null
+
setDelay: -> Gallery.delay = +@value
menu:
@@ -319,14 +343,14 @@ Gallery =
createSubEntry: (name) ->
label = UI.checkbox name, name
input = label.firstElementChild
- if name in ['Fit Width', 'Fit Height', 'Hide Thumbnails']
- $.on input, 'change', Gallery.cb.setFitness
+ $.on input, 'change', Gallery.cb.setFitness if name in ['Hide Thumbnails', 'Fit Width', 'Fit Height']
$.event 'change', null, input
$.on input, 'change', $.cb.checked
+ $.on input, 'change', Gallery.cb.setHeight if name in ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit']
el: label
createSubEntries: ->
- subEntries = (Gallery.menu.createSubEntry item for item in ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post'])
+ subEntries = (Gallery.menu.createSubEntry item for item in ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Stretch to Fit', 'Scroll to Post'])
delayLabel = $.el 'label', <%= html('Slide Delay: ') %>
delayInput = delayLabel.firstElementChild
diff --git a/src/Images/ImageCommon.coffee b/src/Images/ImageCommon.coffee
index 499808522..582428198 100644
--- a/src/Images/ImageCommon.coffee
+++ b/src/Images/ImageCommon.coffee
@@ -34,7 +34,7 @@ ImageCommon =
return true
error: (file, post, delay, cb) ->
- src = post.file.URL.split '/'
+ src = post.file.url.split '/'
URL = Redirect.to 'file',
boardID: post.board.ID
filename: src[src.length - 1]
@@ -51,10 +51,10 @@ ImageCommon =
cb URL
<% if (type === 'crx') { %>
- $.ajax post.file.URL,
+ $.ajax post.file.url,
onloadend: ->
if @status is 200
- URL = post.file.URL
+ URL = post.file.url
else
post.kill true if @status is 404
redirect()
@@ -74,7 +74,7 @@ ImageCommon =
post.kill true
redirect()
else
- URL = post.file.URL
+ URL = post.file.url
<% } %>
# Add controls, but not until the mouse is moved over the video.
diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee
index a60914c96..a2698ab01 100755
--- a/src/Images/ImageExpand.coffee
+++ b/src/Images/ImageExpand.coffee
@@ -122,7 +122,7 @@ ImageExpand =
$.rmClass post.nodes.root, 'expanded-image'
$.rmClass file.thumb, 'expanding'
$.rm file.videoControls if file.videoControls
- file.thumb.parentNode.href = file.URL
+ file.thumb.parentNode.href = file.url
file.thumb.parentNode.target = '_blank'
for x in ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView']
delete file[x]
@@ -175,7 +175,7 @@ ImageExpand =
el = file.fullImage = $.el (if isVideo then 'video' else 'img')
el.dataset.fullID = post.fullID
$.on el, 'error', ImageExpand.error
- el.src = src or file.URL
+ el.src = src or file.url
el.className = 'full-image'
$.after thumb, el
diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee
index bed03f2e4..ac551b369 100755
--- a/src/Images/ImageHover.coffee
+++ b/src/Images/ImageHover.coffee
@@ -32,7 +32,7 @@ ImageHover =
el = $.el (if isVideo then 'video' else 'img')
el.dataset.fullID = post.fullID
$.on el, 'error', error
- el.src = file.URL
+ el.src = file.url
if Conf['Restart when Opened']
ImageCommon.rewind el
diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee
index 96f243622..eff4718fd 100755
--- a/src/Images/ImageLoader.coffee
+++ b/src/Images/ImageLoader.coffee
@@ -44,7 +44,7 @@ ImageLoader =
video.setAttribute 'muted', 'muted'
video.dataset.md5 = thumb.dataset.md5
video.style[attr] = thumb.style[attr] for attr in ['height', 'width', 'maxHeight', 'maxWidth']
- video.src = file.URL
+ video.src = file.url
$.replace thumb, video
file.thumb = video
file.videoThumb = true
@@ -52,9 +52,9 @@ ImageLoader =
prefetch: (post) ->
{file} = post
return unless file
- {isImage, isVideo, thumb, URL} = file
+ {isImage, isVideo, thumb, url} = file
return if file.isPrefetched or !(isImage or isVideo) or post.isHidden or post.thread.isHidden
- type = if (match = URL.match(/\.([^.]+)$/)[1].toUpperCase()) is 'JPEG' then 'JPG' else match
+ type = if (match = url.match(/\.([^.]+)$/)[1].toUpperCase()) is 'JPEG' then 'JPG' else match
replace = Conf["Replace #{type}"] and !/spoiler/.test(thumb.src or thumb.dataset.src)
return unless replace or Conf['prefetch']
return unless [post, post.clones...].some (clone) -> doc.contains clone.nodes.root
@@ -70,11 +70,11 @@ ImageLoader =
el = $.el if isImage then 'img' else 'video'
if replace and isImage
$.on el, 'load', ->
- clone.file.thumb.src = URL for clone in post.clones
- thumb.src = URL
+ clone.file.thumb.src = url for clone in post.clones
+ thumb.src = url
# XXX https://bugzilla.mozilla.org/show_bug.cgi?id=1021289
thumb.removeAttribute 'data-src'
- el.src = URL
+ el.src = url
toggle: ->
if Conf['prefetch'] = @checked
diff --git a/src/Images/Metadata.coffee b/src/Images/Metadata.coffee
index f5f89b180..68af80a63 100644
--- a/src/Images/Metadata.coffee
+++ b/src/Images/Metadata.coffee
@@ -7,7 +7,7 @@ Metadata =
cb: @node
node: ->
- return unless @file and /webm$/i.test @file.URL
+ return unless @file and /webm$/i.test @file.url
if @isClone
el = $ '.webm-title', @file.text
else
@@ -21,7 +21,7 @@ Metadata =
load: ->
$.rmClass @parentNode, 'error'
$.addClass @parentNode, 'loading'
- CrossOrigin.binary Get.postFromNode(@).file.URL, (data) =>
+ CrossOrigin.binary Get.postFromNode(@).file.url, (data) =>
$.rmClass @parentNode, 'loading'
if data?
title = Metadata.parse data
diff --git a/src/Images/Sauce.coffee b/src/Images/Sauce.coffee
index d68f9c8d5..dea290eaf 100755
--- a/src/Images/Sauce.coffee
+++ b/src/Images/Sauce.coffee
@@ -16,26 +16,35 @@ Sauce =
name: 'Sauce'
cb: @node
+ sandbox: (url) ->
+ E.url <%= importHTML('Features/Sandbox') %>
+
+ rmOrigin: (e) ->
+ return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
+ # Work around mixed content restrictions (data: URIs have inherited origin).
+ $.open @href
+ e.preventDefault()
+
createSauceLink: (link, post) ->
return null unless link = link.trim()
parts = {}
- for part, i in link.split /;(?=(?:text|boards|types):)/
+ for part, i in link.split /;(?=(?:text|boards|types|sandbox):?)/
if i is 0
parts['url'] = part
else
- m = part.match /^(\w*):(.*)$/
+ m = part.match /^(\w*):?(.*)$/
parts[m[1]] = m[2]
parts['text'] or= parts['url'].match(/(\w+)\.\w+\//)?[1] or '?'
- ext = post.file.URL.match(/[^.]*$/)[0]
+ ext = post.file.url.match(/[^.]*$/)[0]
skip = false
for key of parts
parts[key] = parts[key].replace /%(T?URL|IMG|MD5|board|name|%|semi)/g, (parameter) ->
type = {
'%TURL': post.file.thumbURL
- '%URL': post.file.URL
- '%IMG': if ext in ['gif', 'jpg', 'png'] then post.file.URL else post.file.thumbURL
+ '%URL': post.file.url
+ '%IMG': if ext in ['gif', 'jpg', 'png'] then post.file.url else post.file.thumbURL
'%MD5': post.file.MD5
'%board': post.board.ID
'%name': post.file.name
@@ -55,10 +64,14 @@ Sauce =
return null unless !parts['boards'] or post.board.ID in parts['boards'].split ','
return null unless !parts['types'] or ext in parts['types'].split ','
+ url = parts['url']
+ url = Sauce.sandbox url if parts['sandbox']?
+
a = Sauce.link.cloneNode true
- a.href = parts['url']
+ a.href = url
a.textContent = parts['text']
a.removeAttribute 'target' if /^javascript:/i.test parts['url']
+ $.on a, 'click', Sauce.rmOrigin if parts['sandbox']?
a
node: ->
diff --git a/src/Images/Volume.coffee b/src/Images/Volume.coffee
index 5583b6299..b4248a77f 100644
--- a/src/Images/Volume.coffee
+++ b/src/Images/Volume.coffee
@@ -70,6 +70,7 @@ Volume =
$.on @nodes.thumb, 'wheel', Volume.wheel.bind(Header.hover)
wheel: (e) ->
+ return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey
return unless el = $ 'video:not([data-md5])', @
return if el.muted or not $.hasAudio el
volume = el.volume + 0.1
diff --git a/src/Linkification/Embedding.coffee b/src/Linkification/Embedding.coffee
index 03b70455d..39fdc13fd 100644
--- a/src/Linkification/Embedding.coffee
+++ b/src/Linkification/Embedding.coffee
@@ -185,7 +185,7 @@ Embedding =
el = $.el 'iframe'
el.setAttribute 'sandbox', 'allow-scripts'
content = <%= html('${a.dataset.uid} ') %>
- el.src = "data:text/html;charset=utf-8,#{encodeURIComponent content.innerHTML}"
+ el.src = E.url content
el
title:
api: (uid) -> "https://api.github.com/gists/#{uid}"
diff --git a/src/Menu/DownloadLink.coffee b/src/Menu/DownloadLink.coffee
index d7c3f9411..f5973ae78 100755
--- a/src/Menu/DownloadLink.coffee
+++ b/src/Menu/DownloadLink.coffee
@@ -14,6 +14,6 @@ DownloadLink =
order: 100
open: ({file}) ->
return false unless file
- a.href = file.URL
+ a.href = file.url
a.download = file.name
true
diff --git a/src/Menu/ReportLink.coffee b/src/Menu/ReportLink.coffee
index 23621a317..d9a2a0930 100755
--- a/src/Menu/ReportLink.coffee
+++ b/src/Menu/ReportLink.coffee
@@ -5,20 +5,26 @@ ReportLink =
a = $.el 'a',
className: 'report-link'
href: 'javascript:;'
- textContent: 'Report this post'
$.on a, 'click', ReportLink.report
+
Menu.menu.addEntry
el: a
order: 10
open: (post) ->
- ReportLink.url = unless post.isDead
- "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}"
+ unless post.isDead or (post.thread.isDead and not post.thread.isArchived)
+ a.textContent = 'Report this post'
+ ReportLink.url = "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}"
+ ReportLink.height = 200
else if Conf['Archive Report']
- Redirect.to 'report', {boardID: post.board.ID, postID: post.ID}
+ a.textContent = 'Report to archive'
+ ReportLink.url = Redirect.to 'report', {boardID: post.board.ID, postID: post.ID}
+ ReportLink.height = 350
+ else
+ ReportLink.url = ''
!!ReportLink.url
report: ->
- {url} = ReportLink
+ {url, height} = ReportLink
id = Date.now()
- set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=685,height=200"
+ set = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,resizable=1,width=700,height=#{height}"
window.open url, id, set
diff --git a/src/Miscellaneous/FileInfo.coffee b/src/Miscellaneous/FileInfo.coffee
index 74ea41f97..542fc43b7 100755
--- a/src/Miscellaneous/FileInfo.coffee
+++ b/src/Miscellaneous/FileInfo.coffee
@@ -28,10 +28,10 @@ FileInfo =
$.extend outputNode, <%= html('@{output}') %>
formatters:
- t: -> <%= html('${this.file.URL.match(/[^\/]*$/)[0]}') %>
- T: -> <%= html('&{FileInfo.formatters.t.call(this)} ') %>
- l: -> <%= html('&{FileInfo.formatters.n.call(this)} ') %>
- L: -> <%= html('&{FileInfo.formatters.N.call(this)} ') %>
+ t: -> <%= html('${this.file.url.match(/[^\/]*$/)[0]}') %>
+ T: -> <%= html('&{FileInfo.formatters.t.call(this)} ') %>
+ l: -> <%= html('&{FileInfo.formatters.n.call(this)} ') %>
+ L: -> <%= html('&{FileInfo.formatters.N.call(this)} ') %>
n: ->
fullname = @file.name
shortname = Build.shortFilename @file.name, @isReply
@@ -46,4 +46,5 @@ FileInfo =
K: -> <%= html('${Math.round(this.file.sizeInBytes/1024)} KB') %>
M: -> <%= html('${Math.round(this.file.sizeInBytes/1048576*100)/100} MB') %>
r: -> <%= html('${this.file.dimensions || "PDF"}') %>
+ g: -> <%= html('?{this.file.tag}{, ${this.file.tag}}{}') %>
'%': -> <%= html('%') %>
diff --git a/src/Miscellaneous/Fourchan.coffee b/src/Miscellaneous/Fourchan.coffee
index a2cfc9fbf..5dd401e1a 100755
--- a/src/Miscellaneous/Fourchan.coffee
+++ b/src/Miscellaneous/Fourchan.coffee
@@ -60,9 +60,10 @@ Fourchan =
code: ->
return if @isClone
- for pre, i in $$('.prettyprint', @nodes.comment) when not $.hasClass(pre, 'prettyprinted')
- $.event 'prettyprint', {ID: @fullID, i: i, html: pre.innerHTML}, window
- return
+ $.ready =>
+ for pre, i in $$('.prettyprint', @nodes.comment) when not $.hasClass(pre, 'prettyprinted')
+ $.event 'prettyprint', {ID: @fullID, i: i, html: pre.innerHTML}, window
+ return
math: ->
return if (@isClone and doc.contains @origin.nodes.root) or !$ '.math', @nodes.comment
diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee
index 161e5d6b7..ba796938a 100755
--- a/src/Miscellaneous/Keybinds.coffee
+++ b/src/Miscellaneous/Keybinds.coffee
@@ -204,6 +204,8 @@ Keybinds =
'Enter'
when 27
'Esc'
+ when 32
+ 'Space'
when 37
'Left'
when 38
@@ -212,9 +214,15 @@ Keybinds =
'Right'
when 40
'Down'
+ when 188
+ 'Comma'
+ when 190
+ 'Period'
else
if 48 <= kc <= 57 or 65 <= kc <= 90 # 0-9, A-Z
String.fromCharCode(kc).toLowerCase()
+ else if 96 <= kc <= 105 # numpad 0-9
+ String.fromCharCode(kc - 48).toLowerCase()
else
null
if key
diff --git a/src/Miscellaneous/Report.coffee b/src/Miscellaneous/Report.coffee
index 7c76af437..d2b5cdcff 100755
--- a/src/Miscellaneous/Report.coffee
+++ b/src/Miscellaneous/Report.coffee
@@ -1,19 +1,26 @@
Report =
+ css: `<%= importCSS('noscript') %>`
+
init: ->
return unless /\bmode=report\b/.test(location.search) and match = location.search.match /\bno=(\d+)/
+ Captcha.language.fixPage()
@postID = +match[1]
$.ready @ready
ready: ->
- new MutationObserver(Report.resize).observe d.body,
- childList: true
- attributes: true
- subtree: true
+ $.addStyle Report.css
Report.archive() if Conf['Archive Report']
+ if $.hasClass doc, 'js-enabled'
+ new MutationObserver(-> Report.fit '.gc-bubbleDefault').observe d.body,
+ childList: true
+ attributes: true
+ subtree: true
+ else
+ Report.fit 'body'
- resize: ->
- return unless bubble = $ '.gc-bubbleDefault'
- dy = bubble.getBoundingClientRect().bottom - doc.clientHeight
+ fit: (selector) ->
+ return unless el = $ selector, doc
+ dy = el.getBoundingClientRect().bottom - doc.clientHeight + 8
window.resizeBy 0, dy if dy > 0
archive: ->
@@ -21,14 +28,20 @@ Report =
return unless url = Redirect.to 'report', {boardID: g.BOARD.ID, postID: Report.postID}
if (message = $ 'h3') and /Report submitted!/.test(message.textContent)
- $.globalEval 'self.close = function(){};'
- window.resizeTo 685, 320
- location.replace url
+ if location.hash is '#redirect'
+ $.globalEval 'self.close = function(){};'
+ window.resizeBy 0, 350 - doc.clientHeight
+ location.replace url
return
+
link = $.el 'a',
href: url
- textContent: 'Report to fgts'
+ textContent: 'Report to archive'
$.on link, 'click', (e) ->
unless e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
- window.resizeTo 685, 320
+ window.resizeBy 0, 350 - doc.clientHeight
$.add d.body, [$.tn(' ['), link, $.tn(']')]
+
+ if types = $.id('reportTypes')
+ $.on types, 'change', (e) ->
+ $('form').action = if e.target.value in ['illegal', 'spam'] then '#redirect' else ''
diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee
index 52363ef60..fedb7ad88 100755
--- a/src/Monitoring/ThreadStats.coffee
+++ b/src/Monitoring/ThreadStats.coffee
@@ -5,11 +5,11 @@ ThreadStats =
statsHTML = <%= html(
'? / ? ' +
'?{Conf["IP Count in Stats"]}{ / ? }' +
- '?{Conf["Page Count in Stats"]}{ / ? }'
+ '?{Conf["Page Count in Stats"] && g.BOARD.ID !== "f"}{ / ? }'
) %>
statsTitle = 'Posts / Files'
statsTitle += ' / IPs' if Conf['IP Count in Stats']
- statsTitle += ' / Page' if Conf['Page Count in Stats']
+ statsTitle += ' / Page' if Conf['Page Count in Stats'] and g.BOARD.ID isnt 'f'
if Conf['Updater and Stats in Header']
@dialog = sc = $.el 'span',
@@ -31,6 +31,8 @@ ThreadStats =
@ipCountEl = $ '#ip-count', sc
@pageCountEl = $ '#page-count', sc
+ $.on @pageCountEl, 'click', ThreadStats.fetchPage if @pageCountEl
+
Thread.callbacks.push
name: 'Thread Stats'
cb: @node
@@ -41,7 +43,7 @@ ThreadStats =
@posts.forEach (post) ->
postCount++
fileCount++ if post.file
- ThreadStats.lastPost = post.info.date if Conf["Page Count in Stats"]
+ ThreadStats.lastPost = post.info.date if ThreadStats.pageCountEl
ThreadStats.thread = @
ThreadStats.fetchPage()
ThreadStats.update postCount, fileCount, @ipCount
@@ -51,23 +53,23 @@ ThreadStats =
return if e.detail[404]
{postCount, fileCount, ipCount, newPosts} = e.detail
ThreadStats.update postCount, fileCount, ipCount
- return unless Conf["Page Count in Stats"]
+ return unless ThreadStats.pageCountEl
if newPosts.length
ThreadStats.lastPost = g.posts[newPosts[newPosts.length - 1]].info.date
- if ThreadStats.lastPost > ThreadStats.lastPageUpdate and ThreadStats.pageCountEl?.textContent isnt '1'
+ if ThreadStats.pageCountEl?.textContent isnt '1'
ThreadStats.fetchPage()
update: (postCount, fileCount, ipCount) ->
{thread, postCountEl, fileCountEl, ipCountEl} = ThreadStats
postCountEl.textContent = postCount
fileCountEl.textContent = fileCount
- if ipCount? and Conf["IP Count in Stats"]
+ if ipCount? and ipCountEl
ipCountEl.textContent = ipCount
(if thread.postLimit and !thread.isSticky then $.addClass else $.rmClass) postCountEl, 'warning'
(if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning'
fetchPage: ->
- return if !Conf["Page Count in Stats"]
+ return unless ThreadStats.pageCountEl
clearTimeout ThreadStats.timeout
if ThreadStats.thread.isDead
ThreadStats.pageCountEl.textContent = 'Dead'
@@ -75,14 +77,22 @@ ThreadStats =
return
ThreadStats.timeout = setTimeout ThreadStats.fetchPage, 2 * $.MINUTE
$.ajax "//a.4cdn.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad,
- whenModified: true
+ whenModified: 'ThreadStats'
onThreadsLoad: ->
- return unless Conf["Page Count in Stats"] and @status is 200
- for page in @response
- for thread in page.threads when thread.no is ThreadStats.thread.ID
- ThreadStats.pageCountEl.textContent = page.page
- (if page.page is @response.length then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
- # Thread data may be stale (modification date given < time of last post). If so, try again on next thread update.
- ThreadStats.lastPageUpdate = new Date thread.last_modified * $.SECOND
- return
+ if @status is 200
+ for page in @response
+ for thread in page.threads when thread.no is ThreadStats.thread.ID
+ ThreadStats.pageCountEl.textContent = page.page
+ (if page.page is @response.length then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
+ ThreadStats.lastPageUpdate = new Date thread.last_modified * $.SECOND
+ ThreadStats.retry()
+ return
+ else if @status is 304
+ ThreadStats.retry()
+
+ retry: ->
+ # If thread data is stale (modification date given < time of last post), try again.
+ if ThreadStats.lastPost > ThreadStats.lastPageUpdate and ThreadStats.pageCountEl?.textContent isnt '1'
+ clearTimeout ThreadStats.timeout
+ ThreadStats.timeout = setTimeout ThreadStats.fetchPage, 5 * $.SECOND
diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee
index e884e8494..5466dd227 100755
--- a/src/Monitoring/ThreadUpdater.coffee
+++ b/src/Monitoring/ThreadUpdater.coffee
@@ -241,7 +241,7 @@ ThreadUpdater =
onloadend: ThreadUpdater.cb.load
timeout: $.MINUTE
,
- whenModified: true
+ whenModified: 'ThreadUpdater'
updateThreadStatus: (type, status) ->
return unless hasChanged = ThreadUpdater.thread["is#{type}"] isnt status
diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee
index 6e6ecc648..00dabd13c 100755
--- a/src/Monitoring/ThreadWatcher.coffee
+++ b/src/Monitoring/ThreadWatcher.coffee
@@ -20,7 +20,7 @@ ThreadWatcher =
$.on d, 'QRPostSuccessful', @cb.post
$.on sc, 'click', @toggleWatcher
- $.on @refreshButton, 'click', @fetchAllStatus
+ $.on @refreshButton, 'click', @buttonFetchAll
$.on @closeButton, 'click', @toggleWatcher
$.on d, '4chanXInitFinished', @ready
@@ -84,6 +84,9 @@ ThreadWatcher =
return unless e.button is 0 and e.altKey
ThreadWatcher.toggle @thread
e.preventDefault()
+ $.on @nodes.thumb.parentNode, 'mousedown', (e) ->
+ # Prevent highlighting thumbnail in Firefox.
+ e.preventDefault() if e.button is 0 and e.altKey
ready: ->
$.off d, '4chanXInitFinished', ThreadWatcher.ready
@@ -136,11 +139,13 @@ ThreadWatcher =
boardID = g.BOARD.ID
db.forceSync()
for threadID, data of db.data.boards[boardID] when not data?.isDead and threadID not of g.BOARD.threads
- if Conf['Auto Prune'] or not (data and typeof data is 'object')
- ThreadWatcher.db.delete {boardID, threadID}
+ if Conf['Auto Prune'] or not (data and typeof data is 'object') # corrupt data
+ db.delete {boardID, threadID}
else
+ if Conf['Show Unread Count']
+ ThreadWatcher.fetchStatus {boardID, threadID, data}
data.isDead = true
- ThreadWatcher.db.set {boardID, threadID, val: data}
+ db.set {boardID, threadID, val: data}
ThreadWatcher.refresh()
onThreadRefresh: (e) ->
thread = g.threads[e.detail.threadID]
@@ -148,9 +153,19 @@ ThreadWatcher =
# Update dead status.
ThreadWatcher.add thread
- fetchCount:
- fetched: 0
- fetching: 0
+ requests: []
+ fetched: 0
+
+ clearRequests: ->
+ ThreadWatcher.requests = []
+ ThreadWatcher.fetched = 0
+ ThreadWatcher.status.textContent = ''
+ $.rmClass ThreadWatcher.refreshButton, 'fa-spin'
+
+ abort: ->
+ for req in ThreadWatcher.requests when req.readyState isnt 4 # DONE
+ req.abort()
+ ThreadWatcher.clearRequests()
fetchAuto: ->
clearTimeout ThreadWatcher.timeout
@@ -164,6 +179,12 @@ ThreadWatcher =
db.save()
ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval
+ buttonFetchAll: ->
+ if ThreadWatcher.requests.length
+ ThreadWatcher.abort()
+ else
+ ThreadWatcher.fetchAllStatus()
+
fetchAllStatus: ->
ThreadWatcher.db.forceSync()
ThreadWatcher.unreaddb.forceSync()
@@ -176,26 +197,23 @@ ThreadWatcher =
fetchStatus: (thread, force) ->
{boardID, threadID, data} = thread
return if data.isDead and not force
- {fetchCount} = ThreadWatcher
- if fetchCount.fetching is 0
+ if ThreadWatcher.requests.length is 0
ThreadWatcher.status.textContent = '...'
$.addClass ThreadWatcher.refreshButton, 'fa-spin'
- fetchCount.fetching++
- $.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
+ req = $.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
onloadend: ->
ThreadWatcher.parseStatus.call @, thread
+ timeout: $.MINUTE
+ ,
+ whenModified: if force then false else 'ThreadWatcher'
+ ThreadWatcher.requests.push req
parseStatus: ({boardID, threadID, data}) ->
- {fetchCount} = ThreadWatcher
- fetchCount.fetched++
- if fetchCount.fetched is fetchCount.fetching
- fetchCount.fetched = 0
- fetchCount.fetching = 0
- status = ''
- $.rmClass ThreadWatcher.refreshButton, 'fa-spin'
+ ThreadWatcher.fetched++
+ if ThreadWatcher.fetched is ThreadWatcher.requests.length
+ ThreadWatcher.clearRequests()
else
- status = "#{Math.round fetchCount.fetched / fetchCount.fetching * 100}%"
- ThreadWatcher.status.textContent = status
+ ThreadWatcher.status.textContent = "#{Math.round(ThreadWatcher.fetched / ThreadWatcher.requests.length * 100)}%"
if @status is 200 and @response
isDead = !!@response.posts[0].archived
@@ -214,17 +232,23 @@ ThreadWatcher =
for postObj in @response.posts
continue unless postObj.no > lastReadPost
continue if QR.db?.get {boardID, threadID, postID: postObj.no}
+
unread++
+
continue unless QR.db and postObj.com
- regexp = /]*\bhref="(?:\/([^\/]+)\/thread\/(\d+))?(?:#p(\d+))?"/g
+
+ quotesYou = false
+ regexp = / ]*\bhref="(?:\/([^\/]+)\/thread\/)?(\d+)?(?:#p(\d+))?"/g
while match = regexp.exec postObj.com
if QR.db.get {
boardID: match[1] or boardID
threadID: match[2] or threadID
postID: match[3] or match[2] or threadID
}
- quotingYou++
- continue
+ quotesYou = true
+ break
+ if quotesYou and not Filter.isHidden(Build.parseJSON postObj, boardID)
+ quotingYou++
if isDead isnt data.isDead or unread isnt data.unread or quotingYou isnt data.quotingYou
data.isDead = isDead
@@ -238,6 +262,8 @@ ThreadWatcher =
ThreadWatcher.db.delete {boardID, threadID}
else
data.isDead = true
+ delete data.unread
+ delete data.quotingYou
ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh()
diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee
index 74e485977..80a296297 100755
--- a/src/Monitoring/Unread.coffee
+++ b/src/Monitoring/Unread.coffee
@@ -128,7 +128,7 @@ Unread =
openNotification: (post) ->
return unless Header.areNotificationsEnabled
notif = new Notification "#{post.info.nameBlock} replied to you",
- body: post.info[if Conf['Remove Spoilers'] or Conf['Reveal Spoilers'] then 'comment' else 'commentSpoilered']
+ body: post.info.commentDisplay
icon: Favicon.logo
notif.onclick = ->
Header.scrollToIfNeeded post.nodes.root, true
@@ -154,6 +154,10 @@ Unread =
Unread.update()
read: $.debounce 100, (e) ->
+ # Update the lastReadPost when hidden posts are added to the thread.
+ if !Unread.posts.size and Unread.readCount isnt Unread.thread.posts.keys.length
+ Unread.saveLastReadPost()
+
return if d.hidden or !Unread.posts.size
height = doc.clientHeight
diff --git a/src/Posting/Captcha.fixes.coffee b/src/Posting/Captcha.fixes.coffee
index a2e414cec..50fcf7143 100644
--- a/src/Posting/Captcha.fixes.coffee
+++ b/src/Posting/Captcha.fixes.coffee
@@ -1,11 +1,38 @@
Captcha.fixes =
- selectors:
- image: '.rc-imageselect-target > .rc-imageselect-tile > img'
+ imageKeys: '789456123uiojklm'.split('').concat(['Comma', 'Period'])
+
+ css: '''
+ .rc-imageselect-target > div:focus {
+ outline: 2px solid #4a90e2;
+ }
+ .rc-button-default:focus {
+ box-shadow: inset 0 0 0 2px #0063d6;
+ }
+ '''
+
+ cssNoscript: '''
+ .fbc-payload-imageselect {
+ position: relative;
+ }
+ .fbc-payload-imageselect > label {
+ position: absolute;
+ display: block;
+ height: 93.3px;
+ width: 93.3px;
+ }
+ label[data-row="0"] {top: 0px;}
+ label[data-row="1"] {top: 93.3px;}
+ label[data-row="2"] {top: 186.6px;}
+ label[data-col="0"] {left: 0px;}
+ label[data-col="1"] {left: 93.3px;}
+ label[data-col="2"] {left: 186.6px;}
+ '''
init: ->
switch location.pathname.split('/')[3]
- when 'anchor' then @initMain()
- when 'frame' then @initPopup()
+ when 'anchor' then @initMain()
+ when 'frame' then @initPopup()
+ when 'fallback' then @initNoscript()
initMain: ->
$.onExists d.body, '#recaptcha-anchor', true, (checkbox) ->
@@ -17,39 +44,71 @@ Captcha.fixes =
$.queueTask focus
initPopup: ->
- $.addStyle "#{@selectors.image}:focus {outline: 2px solid #4a90e2;}"
+ $.addStyle @css
@fixImages()
new MutationObserver(=> @fixImages()).observe d.body, {childList: true, subtree: true}
$.on d, 'keydown', @keybinds.bind(@)
+ initNoscript: ->
+ @noscript = true
+ @images = $$ '.fbc-payload-imageselect > input'
+ return unless @images.length
+
+ $.addStyle @cssNoscript
+ @addLabels()
+ $.on d, 'keydown', @keybinds.bind(@)
+ $.on $('.fbc-imageselect-challenge > form'), 'submit', @checkForm.bind(@)
+
fixImages: ->
- return unless (@images = $$ @selectors.image).length
- focus = @images[0].tabIndex isnt 0
+ @images = $$ '.rc-imageselect-target > div'
for img in @images
img.tabIndex = 0
- @focusImage() if focus
+ @addTooltips @images if @images.length
- focusImage: ->
- # XXX Image is not focusable at first in Firefox; to be refactored when I figure out why.
- img = @images[0]
- $.asap ->
- return true unless doc.contains img
- img.focus()
- d.activeElement is img
- , ->
+ addLabels: ->
+ imageSelect = $ '.fbc-payload-imageselect'
+ labels = for checkbox, i in @images
+ checkbox.id = "checkbox-#{i}"
+ label = $.el 'label',
+ htmlFor: checkbox.id
+ label.dataset.row = i // 3
+ label.dataset.col = i % 3
+ label
+ $.add imageSelect, labels
+ @addTooltips labels
+
+ addTooltips: (nodes) ->
+ for node, i in nodes
+ node.title = "#{@imageKeys[i]} or #{@imageKeys[i+9][0].toUpperCase()}#{@imageKeys[i+9][1..]}"
+ return
+
+ checkForm: (e) ->
+ n = 0
+ n++ for checkbox in @images when checkbox.checked
+ e.preventDefault() if n is 0
keybinds: (e) ->
- return unless @images and doc.contains(@images[0]) and d.activeElement
- reload = $.id 'recaptcha-reload-button'
- verify = $.id 'recaptcha-verify-button'
+ return unless @images and doc.contains(@images[0])
+
+ reload = $ '#recaptcha-reload-button, .fbc-button-reload'
+ verify = $ '#recaptcha-verify-button, .fbc-button-verify > input'
x = @images.indexOf d.activeElement
if x < 0
- return unless $('.rc-controls').contains d.activeElement
x = if d.activeElement is verify then 11 else 9
- return unless dx = {38: 9, 40: 3, 37: 11, 39: 1}[e.keyCode] # Up, Down, Left, Right
- x = (x + dx) % 12
- if x is 10
- x = if dx is 11 then 9 else 11
- (@images[x] or {9: reload, 11: verify}[x]).focus()
+ key = Keybinds.keyCode e
+
+ if !@noscript and key is 'Space' and x < 9
+ @images[x].click()
+ else if (i = @imageKeys.indexOf key) >= 0
+ @images[i % 9].click()
+ verify.focus()
+ else if dx = {'Up': 9, 'Down': 3, 'Left': 11, 'Right': 1}[key]
+ x = (x + dx) % 12
+ if x is 10
+ x = if dx is 11 then 9 else 11
+ (@images[x] or {9: reload, 11: verify}[x]).focus()
+ else
+ return
+
e.preventDefault()
e.stopPropagation()
diff --git a/src/Posting/Captcha.language.coffee b/src/Posting/Captcha.language.coffee
new file mode 100644
index 000000000..bd63efb72
--- /dev/null
+++ b/src/Posting/Captcha.language.coffee
@@ -0,0 +1,17 @@
+Captcha.language =
+ init: ->
+ return unless Conf['captchaLanguage'].trim() and d.cookie.indexOf('pass_enabled=1') < 0 and !Conf['Hide Original Post Form']
+ $.onExists doc, '#captchaFormPart', true, (node) ->
+ $.onExists node, 'iframe', true, Captcha.language.fixIframe
+
+ fixPage: ->
+ return unless Conf['captchaLanguage'].trim() and d.cookie.indexOf('pass_enabled=1') < 0
+ $.onExists doc, 'iframe', true, Captcha.language.fixIframe
+
+ fixIframe: (el) ->
+ return unless lang = Conf['captchaLanguage'].trim()
+ src = if /[?&]hl=/.test el.src
+ el.src.replace(/([?&]hl=)[^&]*/, '$1' + encodeURIComponent lang)
+ else
+ el.src + "&hl=#{encodeURIComponent lang}"
+ el.src = src unless el.src is src
diff --git a/src/Posting/Captcha.noscript.coffee b/src/Posting/Captcha.noscript.coffee
deleted file mode 100644
index 1458c1f76..000000000
--- a/src/Posting/Captcha.noscript.coffee
+++ /dev/null
@@ -1,226 +0,0 @@
-Captcha.noscript =
- lifetime: 2 * $.MINUTE
- iframeURL: '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'
-
- init: ->
- return if d.cookie.indexOf('pass_enabled=1') >= 0
- return unless @isEnabled = !!$.id 'g-recaptcha'
-
- container = $.el 'div',
- className: 'captcha-img'
- title: 'Reload reCAPTCHA'
-
- input = $.el 'input',
- className: 'captcha-input field'
- title: 'Verification'
- autocomplete: 'off'
- spellcheck: false
- @nodes = {container, input}
-
- $.on input, 'keydown', @keydown.bind @
- $.on @nodes.container, 'click', =>
- @reload()
- @nodes.input.focus()
-
- @conn = new Connection null, "#{location.protocol}//www.google.com",
- challenge: @load.bind @
- token: @save.bind @
- error: @error.bind @
-
- $.addClass QR.nodes.el, 'has-captcha'
- $.after QR.nodes.com.parentNode, [container, input]
-
- @captchas = []
- $.get 'captchas', [], ({captchas}) ->
- QR.captcha.sync captchas
- QR.captcha.clear()
- $.sync 'captchas', @sync
-
- @beforeSetup()
- @setup()
-
- initFrame: ->
- conn = new Connection window.parent, "#{location.protocol}//boards.4chan.org",
- response: (response) ->
- $.id('response').value = response
- $('.fbc-challenge > form').submit()
- conn.send
- token: $('.fbc-verification-token > textarea')?.value
- error: $('.fbc-error')?.textContent
- return unless img = $ '.fbc-payload > img'
- cb = ->
- canvas = $.el 'canvas'
- canvas.width = img.width
- canvas.height = img.height
- canvas.getContext('2d').drawImage(img, 0, 0)
- conn.send {challenge: canvas.toDataURL()}
- if img.complete
- cb()
- else
- $.on img, 'load', cb
-
- timers: {}
-
- cb:
- focus: -> QR.captcha.setup false, true
-
- beforeSetup: ->
- {container, input} = @nodes
- container.hidden = true
- input.value = ''
- input.placeholder = 'Focus to load reCAPTCHA'
- @count()
- $.on input, 'focus click', @cb.focus
-
- needed: ->
- captchaCount = @captchas.length
- captchaCount++ if QR.req
- postsCount = QR.posts.length
- postsCount = 0 if postsCount is 1 and !Conf['Auto-load captcha'] and !QR.posts[0].com and !QR.posts[0].file
- captchaCount < postsCount
-
- onNewPost: ->
-
- onPostChange: ->
-
- setup: (focus, force) ->
- return unless @isEnabled and (@needed() or force)
- if !@nodes.iframe
- @nodes.iframe = $.el 'iframe',
- id: 'qr-captcha-iframe'
- src: @iframeURL
- $.add d.body, @nodes.iframe
- @conn.target = @nodes.iframe.contentWindow
- else if !@occupied or force
- @nodes.iframe.src = @iframeURL
- @occupied = true
- @nodes.input.focus() if focus
-
- afterSetup: ->
- {container, input} = @nodes
- container.hidden = false
- input.placeholder = 'Verification'
- @count()
- $.off input, 'focus click', @cb.focus
-
- if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
- QR.nodes.el.style.top = ''
- QR.nodes.el.style.bottom = '0px'
-
- destroy: ->
- return unless @isEnabled
- $.rm @nodes.img if @nodes.img
- delete @nodes.img
- $.rm @nodes.iframe if @nodes.iframe
- delete @nodes.iframe
- delete @occupied
- @beforeSetup()
-
- sync: (captchas=[]) ->
- QR.captcha.captchas = captchas
- QR.captcha.count()
-
- getOne: ->
- @clear()
- if captcha = @captchas.shift()
- @count()
- $.set 'captchas', @captchas
- captcha.response
- else if /\S/.test @nodes.input.value
- (cb) =>
- @submitCB = cb
- @sendResponse()
- else
- null
-
- sendResponse: ->
- response = @nodes.input.value
- if /\S/.test response
- @conn.send {response}
-
- save: (token) ->
- delete @occupied
- @nodes.input.value = ''
- if @submitCB
- @submitCB token
- delete @submitCB
- if @needed() then @reload() else @destroy()
- else
- $.forceSync 'captchas'
- @captchas.push
- response: token
- timeout: @timeout
- @count()
- $.set 'captchas', @captchas
- @reload()
-
- error: (message) ->
- @occupied = true
- @nodes.input.value = ''
- if @submitCB
- @submitCB()
- delete @submitCB
- QR.error "Captcha Error: #{message}"
-
- clear: ->
- return unless @captchas.length
- $.forceSync 'captchas'
- now = Date.now()
- for captcha, i in @captchas
- break if captcha.timeout > now
- return unless i
- @captchas = @captchas[i..]
- @count()
- $.set 'captchas', @captchas
-
- load: (src) ->
- {container, input, img} = @nodes
- @occupied = true
- @timeout = Date.now() + @lifetime
- unless img
- img = @nodes.img = new Image()
- $.one img, 'load', @afterSetup.bind @
- $.on img, 'load', -> @hidden = false
- $.add container, img
- img.src = src
- input.value = ''
- @clear()
- clearTimeout @timers.expire
- @timers.expire = setTimeout @expire.bind(@), @lifetime
-
- count: ->
- count = if @captchas then @captchas.length else 0
- placeholder = @nodes.input.placeholder.replace /\ \(.*\)$/, ''
- placeholder += switch count
- when 0
- if placeholder is 'Verification' then ' (Shift + Enter to cache)' else ''
- when 1
- ' (1 cached captcha)'
- else
- " (#{count} cached captchas)"
- @nodes.input.placeholder = placeholder
- @nodes.input.alt = count # For XTRM RICE.
- clearTimeout @timers.clear
- if @captchas.length
- @timers.clear = setTimeout @clear.bind(@), @captchas[0].timeout - Date.now()
-
- expire: ->
- return unless @nodes.iframe
- if not d.hidden and (@needed() or d.activeElement is @nodes.input)
- @reload()
- else
- @destroy()
-
- reload: ->
- @nodes.iframe.src = @iframeURL
- @occupied = true
- @nodes.img?.hidden = true
-
- keydown: (e) ->
- if e.keyCode is 8 and not @nodes.input.value
- if @nodes.iframe then @reload() else @setup()
- else if e.keyCode is 13 and e.shiftKey
- @sendResponse()
- else
- return
- e.preventDefault()
diff --git a/src/Posting/Captcha.v2.coffee b/src/Posting/Captcha.v2.coffee
index 3b694940e..d44679378 100644
--- a/src/Posting/Captcha.v2.coffee
+++ b/src/Posting/Captcha.v2.coffee
@@ -5,6 +5,11 @@ Captcha.v2 =
return if d.cookie.indexOf('pass_enabled=1') >= 0
return unless @isEnabled = !!$.id 'g-recaptcha'
+ if @noscript = Conf['Force Noscript Captcha'] or not $.hasClass doc, 'js-enabled'
+ @conn = new Connection null, "#{location.protocol}//www.google.com",
+ token: (token) => @save true, token
+ $.addClass QR.nodes.el, 'noscript-captcha'
+
@captchas = []
$.get 'captchas', [], ({captchas}) ->
QR.captcha.sync captchas
@@ -25,10 +30,21 @@ Captcha.v2 =
# XXX Greasemonkey 1.x workaround to gain access to GM_* functions.
$.queueTask => @save false
+ initFrame: ->
+ if token = $('.fbc-verification-token > textarea')?.value
+ conn = new Connection window.parent, "#{location.protocol}//boards.4chan.org"
+ conn.send {token}
+
shouldFocus: false
timeouts: {}
postsCount: 0
+ noscriptURL: ->
+ url = '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'
+ if lang = Conf['captchaLanguage'].trim()
+ url += "&hl=#{encodeURIComponent lang}"
+ url
+
needed: ->
captchaCount = @captchas.length
captchaCount++ if QR.req
@@ -51,7 +67,7 @@ Captcha.v2 =
setup: (focus, force) ->
return unless @isEnabled and (@needed() or force)
- @shouldFocus = true if focus
+ @shouldFocus = true if focus and not QR.inBubble()
if @timeouts.destroy
clearTimeout @timeouts.destroy
delete @timeouts.destroy
@@ -60,6 +76,7 @@ Captcha.v2 =
if @nodes.container
if @shouldFocus and iframe = $ 'iframe', @nodes.container
iframe.focus()
+ QR.focus() # Event handler not fired in Firefox
delete @shouldFocus
return
@@ -69,6 +86,19 @@ Captcha.v2 =
childList: true
subtree: true
+ if @noscript
+ @setupNoscript()
+ else
+ @setupJS()
+
+ setupNoscript: ->
+ iframe = $.el 'iframe',
+ id: 'qr-captcha-iframe'
+ src: @noscriptURL()
+ $.add @nodes.container, iframe
+ @conn.target = iframe.contentWindow
+
+ setupJS: ->
$.globalEval '''
(function() {
function render() {
@@ -101,7 +131,7 @@ Captcha.v2 =
return
setupIFrame: (iframe) ->
- @setupTime = Date.now()
+ Captcha.language.fixIframe iframe
$.addClass QR.nodes.el, 'captcha-open'
if QR.nodes.el.getBoundingClientRect().bottom > doc.clientHeight
QR.nodes.el.style.top = null
@@ -138,22 +168,23 @@ Captcha.v2 =
else
null
- save: (pasted) ->
+ save: (pasted, token) ->
$.forceSync 'captchas'
@captchas.push
- response: $('textarea', @nodes.container).value
- timeout: (if pasted then @setupTime else Date.now()) + @lifetime
+ response: token or $('textarea', @nodes.container).value
+ timeout: Date.now() + @lifetime
$.set 'captchas', @captchas
@count()
+ focus = d.activeElement?.nodeName is 'IFRAME' and /https?:\/\/www\.google\.com\/recaptcha\//.test(d.activeElement.src)
if @needed()
- if QR.cooldown.auto or Conf['Post on Captcha Completion']
- @shouldFocus = true
- else
- QR.nodes.status.focus()
+ if focus
+ if QR.cooldown.auto or Conf['Post on Captcha Completion']
+ @shouldFocus = true
+ else
+ QR.nodes.status.focus()
@reload()
else
- focus = d.activeElement?.nodeName is 'IFRAME' and d.activeElement.src?[...38] is 'https://www.google.com/recaptcha/api2/'
if pasted
@destroy()
else
@@ -172,7 +203,7 @@ Captcha.v2 =
@captchas = @captchas[i..]
@count()
$.set 'captchas', @captchas
- @setup true
+ @setup(d.activeElement is QR.nodes.status)
count: ->
@nodes.counter.textContent = "Captchas: #{@captchas.length}"
@@ -181,9 +212,12 @@ Captcha.v2 =
@timeouts.clear = setTimeout @clear.bind(@), @captchas[0].timeout - Date.now()
reload: ->
- $.globalEval '''
- (function() {
- var container = document.querySelector("#qr .captcha-container");
- window.grecaptcha.reset(container.dataset.widgetID);
- })();
- '''
+ if @noscript
+ $('iframe', @nodes.container).src = @noscriptURL()
+ else
+ $.globalEval '''
+ (function() {
+ var container = document.querySelector("#qr .captcha-container");
+ window.grecaptcha.reset(container.dataset.widgetID);
+ })();
+ '''
diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index afc234aba..738606871 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -9,13 +9,7 @@ QR =
return if g.VIEW is 'archive'
- $.globalEval 'document.documentElement.dataset.jsEnabled = true;'
- version = if Conf['Force Noscript Captcha'] or !doc.dataset.jsEnabled
- 'noscript'
- else if Conf['Use Recaptcha v1']
- 'v1'
- else
- 'v2'
+ version = if Conf['Use Recaptcha v1'] then 'v1' else 'v2'
@captcha = Captcha[version]
$.on d, '4chanXInitFinished', @initReady
@@ -42,7 +36,7 @@ QR =
if Conf['Hide Original Post Form']
$.addClass doc, 'hide-original-post-form'
- if !doc.dataset.jsEnabled
+ unless $.hasClass doc, 'js-enabled'
# Prevent unnecessary loading of fallback iframe.
$.onExists doc, '#postForm noscript', true, $.rm
@@ -136,19 +130,25 @@ QR =
focus: ->
$.queueTask ->
- unless $$('.goog-bubble-content > iframe').some((el) -> el.getBoundingClientRect().top >= 0)
- focus = d.activeElement and QR.nodes.el.contains(d.activeElement)
- $[if focus then 'addClass' else 'rmClass'] QR.nodes.el, 'focus'
- if chrome?
- # XXX Stop anomalous scrolling on space/tab in/into captcha iframe.
- if d.activeElement and QR.nodes.el.contains(d.activeElement) and d.activeElement.nodeName is 'IFRAME'
- QR.scrollY = window.scrollY
- $.on d, 'scroll', QR.scrollLock
- else
- $.off d, 'scroll', QR.scrollLock
+ unless QR.inBubble()
+ QR.hasFocus = d.activeElement and QR.nodes.el.contains(d.activeElement)
+ QR.nodes.el.classList.toggle 'focus', QR.hasFocus
+ # XXX Stop unwanted scrolling due to captcha.
+ if QR.captcha.isEnabled and !QR.captcha.noscript
+ if QR.inCaptcha()
+ QR.scrollY = window.scrollY
+ $.on d, 'scroll', QR.scrollLock
+ else
+ $.off d, 'scroll', QR.scrollLock
+
+ inBubble: ->
+ d.activeElement in $$('.goog-bubble-content > iframe')
+
+ inCaptcha: ->
+ (d.activeElement?.nodeName is 'IFRAME' and QR.nodes.el.contains(d.activeElement)) or (QR.hasFocus and QR.inBubble())
scrollLock: ->
- if d.activeElement and QR.nodes.el.contains(d.activeElement) and d.activeElement.nodeName is 'IFRAME'
+ if QR.inCaptcha()
window.scroll window.scrollX, QR.scrollY
else
$.off d, 'scroll', QR.scrollLock
@@ -290,10 +290,10 @@ QR =
characterCount: ->
counter = QR.nodes.charCount
- count = QR.nodes.com.textLength
+ count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length
counter.textContent = count
counter.hidden = count < 1000
- (if count > 1500 then $.addClass else $.rmClass) counter, 'warning'
+ (if count > 2000 then $.addClass else $.rmClass) counter, 'warning'
getFile: ->
$.event 'QRFile', QR.selected?.file
@@ -537,14 +537,13 @@ QR =
event = if node.nodeName is 'SELECT' then 'change' else 'input'
$.on nodes[name], event, save
- <% if (type === 'userscript') { %>
- if Conf['Remember QR Size']
+ # XXX Chromium treats width and height as min-width and min-height
+ if !chrome? and Conf['Remember QR Size']
$.get 'QR Size', '', (item) ->
nodes.com.style.cssText = item['QR Size']
$.on nodes.com, 'mouseup', (e) ->
return if e.button isnt 0
$.set 'QR Size', @style.cssText
- <% } %>
QR.generatePostableThreadsList()
QR.persona.init()
diff --git a/src/Quotelinks/QuotePreview.coffee b/src/Quotelinks/QuotePreview.coffee
index 056eba03a..2c820a468 100755
--- a/src/Quotelinks/QuotePreview.coffee
+++ b/src/Quotelinks/QuotePreview.coffee
@@ -15,7 +15,7 @@ QuotePreview =
return
mouseover: (e) ->
- return if $.hasClass @, 'inlined'
+ return if $.hasClass(@, 'inlined') or !d.contains(@)
{boardID, threadID, postID} = Get.postDataFromLink @