Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Many changes, v0.2.0

* Split source into sub-files, see dev\src.
* Separated the hooking stuff out into JavaScript Hooker, now a full
  project on GitHub, included here as a submodule.
* Created a build tool and updated the dev web server.
  • Loading branch information...
commit 185471ee0b9b8881d78512b2947fd10d994bcc45 1 parent 07cd467
@cowboy authored
View
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "dev/javascript-hooker"]
+ path = dev/javascript-hooker
+ url = git://github.com/cowboy/javascript-hooker.git
View
22 README.md
@@ -39,16 +39,22 @@ And for what it's worth, I've spent a LOT of time in the WebKit inspector, setti
Running in "development" mode:
-1. Clone the repo (You'll probably need [Git for Windows](http://code.google.com/p/msysgit/) first).
-1. Disable the `battlelog-hacks.user.js` extension in Chrome's "Extensions" manager.
-2. Run `start-webserver.cmd` in the `dev` directory.
-3. Drag `battlelog-hacks-dev.user.js` into the browser and click Ok/Continue/Install as-necessary.
-4. Reload Battlelog.
+1. Install [Git for Windows](http://code.google.com/p/msysgit/) if you don't already have it.
+2. In Git Bash, run `git clone git://github.com/cowboy/battlelog-hacks.git && cd battlelog-hacks && git submodule init && git submodule update`
+3. Disable the `battlelog-hacks.user.js` extension in Chrome's "Extensions" manager.
+4. Drag `battlelog-hacks-dev.user.js` into the browser and click Ok/Continue/Install as-necessary.
+5. Run `start-webserver.cmd` from the `dev` subdirectory.
+6. Edit scripts in the `dev\src` subdirectory.
+7. Reload Battlelog.
+8. Repeat steps 6-7 ad nauseum. Note that if you add or rename files, you'll need to edit `dev\node\files.js` and kill/restart the webserver (step 5).
+9. Once done, run `build.cmd` from the `dev` subdirectory to build `dist\battlelog-hacks.js`.
_Remember that once you're done developing, you'll need to disable `battlelog-hacks-dev.user.js` and re-enable `battlelog-hacks.user.js` in Chrome's "Extensions" manager. Or just leave the webserver running, always._
In lieu of a formal styleguide, take care to maintain the existing coding style. Issue a pull request when done. Found a bug? [File an issue](https://github.com/cowboy/battlelog-hacks/issues).
+_Also, please don't edit files in the "dist" subdirectory as they are generated via `build.cmd`. You'll find source code in the `dev\src` subdirectory!_
+
## Release History
10/30/2011
Initial release. Not even a version number.
@@ -59,6 +65,12 @@ Adding "development" web server and userscript.
Auto-retry join errors are now whitelisted, to avoid auto-retrying in certain situations (like when kicked from a server).
Version number is announced in a little blue box upon start.
+10/31/2011
+v0.2.0
+Split source into sub-files.
+Broke the hooking stuff out into JavaScript Hooker, included as a submodule.
+Created a build tool and updated the dev web server.
+
## License
Copyright (c) 2011 "Cowboy" Ben Alman
Licensed under the MIT license.
View
2  dev/build.cmd
@@ -0,0 +1,2 @@
+@echo off
+node\node node\build.js
1  dev/javascript-hooker
@@ -0,0 +1 @@
+Subproject commit 6e89f3a569d5aa15f3b924df6d3f8cabc4b50010
View
0  dev/webserver/LICENSE → dev/node/LICENSE
File renamed without changes
View
4 dev/node/build.js
@@ -0,0 +1,4 @@
+var fs = require("fs");
+var src = require("./files.js").concat();
+
+fs.writeFileSync("../dist/battlelog-hacks.js", src, "UTF8");
View
28 dev/node/files.js
@@ -0,0 +1,28 @@
+var fs = require("fs");
+
+exports.files = [
+ "src/intro.js",
+ "javascript-hooker/dist/ba-hooker.js",
+ "src/log.js",
+ "src/auto-retry.js",
+ "src/auto-sort.js",
+ "src/suppress-scrolltop.js",
+ "src/outro.js"
+];
+
+exports.concat = function(files) {
+ // Use defaults if files aren't specified.
+ files || (files = exports.files);
+ // Concat files.
+ return files.map(function(filepath, i) {
+ var src = fs.readFileSync(filepath, "UTF8");
+ // Chomp!
+ src = src.replace(/[\s\n]*$/, "");
+ // Remove leading comment from all but the first Battlelog Hacks internal
+ // files when building.
+ if (i > 0) {
+ src = src.replace(/^[\s\n]*\/\* Battlelog Hacks[\s\S]*?\*\/[\s\n]*/, "");
+ }
+ return src;
+ }).join("\n\n");
+};
View
0  dev/webserver/node.exe → dev/node/node.exe
File renamed without changes
View
6 dev/webserver/webserver.js → dev/node/webserver.js
@@ -1,11 +1,11 @@
var http = require('http');
-var fs = require('fs');
+var files = require("./files.js");
var port = 8000;
console.log("Starting webserver at http://localhost:" + port + "/");
console.log("\n(Close this window to exit)");
http.createServer(function(req, res) {
- res.writeHead(200, {'Content-Type': 'text/javascript'});
- res.end(fs.readFileSync('../dist/battlelog-hacks.js'));
+ res.writeHead(200, {"Content-Type": "text/javascript"});
+ res.end(files.concat());
}).listen(port);
View
99 dev/src/auto-retry.js
@@ -0,0 +1,99 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+cowboy.register("Auto-retry Join Server");
+
+(function() {
+ var join, id;
+
+ // Create an element to contain auto-retry status text.
+ var retryStatus = $("<span/>");
+
+ // Override existing "join" methods.
+ ["joinMpServer", "joinMpFriend"].forEach(function(method) {
+ cowboy.hooker.hook(launcher, method, function() {
+ cowboy.log(method, arguments);
+ // Stop auto-retrying.
+ unretry();
+ // Create a re-callable "join" function with arguments already applied.
+ var orig = cowboy.hooker.orig(launcher, method);
+ join = Function.apply.bind(orig, this, arguments);
+ });
+ });
+
+ // A list of errors that should trigger auto-retry. Painstakingly parsed from
+ // gamemanager.handleErrors.
+ var validErrors = [
+ launcher.ALERT.ERR_LAUNCH_DISABLED,
+ launcher.ALERT.ERR_EMPTY_JOINSTATE,
+ launcher.ALERT.ERR_FAILED_PERSONACALL,
+ launcher.ALERT.ERR_BACKEND_HTTP,
+ launcher.ALERT.ERR_BACKEND_ROUTE,
+ launcher.ALERT.ERR_TOO_MANY_ATTEMPTS,
+ launcher.ALERT.ERR_DISCONNECT_GAME_SERVERFULL,
+ launcher.ALERT.ERR_SERVERCONNECT_FULL,
+ launcher.ALERT.ERR_SERVER_QUEUE_FULL,
+ launcher.ALERT.ERR_SERVERCONNECT,
+ launcher.ALERT.ERR_SERVERCONNECT_WRONGPASSWORD,
+ launcher.ALERT.ERR_CONFIG_MISSMATCH,
+ launcher.ALERT.ERR_GENERIC,
+ launcher.ALERT.ERR_MATCHMAKE.START_MATCHMAKING_FAILED,
+ launcher.ALERT.ERR_DISCONNECT_GAME_NOREPLY,
+ launcher.ALERT.ERR_DISCONNECT_GAME_TIMEDOUT,
+ launcher.ALERT.ERR_INVALID_GAME_STATE_ACTION
+ ];
+
+ // Override existing error handler method.
+ cowboy.hooker.hook(launcher, "_triggerEvent", {
+ post: function(result, type, details) {
+ // Only auto-retry on valid errors.
+ if (type === "error.generic" && validErrors.indexOf(details[2]) !== -1) {
+ cowboy.log("Starting auto-retry countdown.", details);
+ // Start countdown.
+ retry();
+ }
+ }
+ });
+
+ // Start auto-retrying.
+ function retry() {
+ // Stop any currently executing auto-retry loop.
+ unretry(true);
+
+ // 10-seconds seems like a good number.
+ var count = 10;
+
+ // Attach status element to the DOM.
+ retryStatus.appendTo(".gamemanager-launchstate-gameready");
+
+ function update() {
+ // Update status text.
+ retryStatus.html(" (Auto-retry&nbsp;in&nbsp;" + count + ")");
+ // If counter has reached 0, stop auto-retrying and join.
+ if (count-- === 0) {
+ cowboy.log("Retrying now.");
+ unretry(true);
+ join();
+ }
+ }
+ // Start counter.
+ update();
+ id = setInterval(update, 1000);
+ }
+
+ // Stop auto-retrying.
+ function unretry(silent) {
+ if (!id) { return; }
+ cowboy.log("Stopping auto-retry.");
+ // Actually stop the auto-retry.
+ clearInterval(id);
+ id = null;
+ // Detach the status element from the DOM and empty it.
+ retryStatus.detach().empty();
+ }
+
+ // If user clicks "Close" button in "Could not join..." dialog, stop
+ // auto-retrying.
+ $(document).delegate("#gamemanager-state-close", "click", unretry);
+}());
View
119 dev/src/auto-sort.js
@@ -0,0 +1,119 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+cowboy.register("Auto-sort Server List");
+
+(function() {
+ // Watch for sorting element clicks.
+ $(document).delegate(".serverguide-sorter", "click", function(e, triggered) {
+ // Abort if manually triggered.
+ if (triggered) { return false; }
+ // Get current sort mode and direction.
+ var mode = sortMode(this);
+ var dir = sortDir(this);
+ cowboy.log("Storing server sorting mode: %s, dir: %s.", mode, dir);
+ // Store sort mode and direction.
+ localStorage.setItem("cb_sort_mode", mode);
+ localStorage.setItem("cb_sort_dir", dir);
+ });
+
+ // Get sort mode from a given element.
+ function sortMode(elem) {
+ var matches = /serverguide-sorting-(\w+)/.exec(elem.className);
+ return matches && matches[1];
+ }
+
+ // Get sort direction from a given element.
+ function sortDir(elem) {
+ var matches = /serverguide-sort-(\w+)/.exec(elem.className);
+ return matches && matches[1];
+ }
+
+ // Re-sort the server list based on the last stored sort mode + direction.
+ // I couldn't find a non-DOM way to do this. I feel gross.
+ cowboy.reSortServers = function() {
+ // Get stored sort mode.
+ var mode = localStorage.getItem("cb_sort_mode");
+ // If mode has never been stored, abort.
+ if (!mode) { return; }
+ // Find DOM element corresponding to this sort mode.
+ var elem = $(".serverguide-sorting-" + mode);
+ // If element doesn't exist, abort.
+ if (elem.length === 0) { return; }
+ // Get the current sort direction from the element.
+ var dir = sortDir(elem[0]);
+ // If the element has a sort direction, it has already been sorted. Abort.
+ if (dir) { return; }
+
+ // Get stored sort dir.
+ dir = localStorage.getItem("cb_sort_dir");
+ cowboy.log("Re-sorting servers using mode: %s, dir: %s.", mode, dir);
+ // Click the element once to sort up.
+ elem.trigger("click", [true]);
+ // If sorting down, click the element a second time.
+ if (dir === "down") {
+ elem.trigger("click", [true]);
+ }
+
+ // After sorting, the first server should be highlighted. Get a list of
+ // sorted server id strings.
+ var ids = serverguideSort.getAllServerSurfaceIds();
+ // If there aren't any servers, abort.
+ if (ids.length === 0) { return; }
+ // Highlight the first server (its numeric id value must be parsed from
+ // the id string).
+ serverguide.highlightServerIndex(+ids[0].split('-')[2]);
+ };
+
+ var count, id;
+ // Re-sort the server list when all server data has been retrieved.
+ function initCounter() {
+ cowboy.log("Waiting for server data to be retrieved.");
+ // Reset count to the number of servers displayed (the number of times that
+ // serverguide.refreshHighlight will be called).
+ count = serverguideSort.getAllServerSurfaceIds().length;
+ // Hook serverguide.refreshHighlight (which appears to be called every time
+ // a server's details (name, players, ping, etc) are returned)
+ cowboy.hooker.hook(serverguide, "refreshHighlight", function() {
+ // Clear any existing timeout.
+ clearTimeout(id);
+
+ if (--count === 0) {
+ // Unhook and re-sort servers.
+ done();
+ } else {
+ // Auto-done() after a timeout, in case serverguide.refreshHighlight is
+ // called less than the expected number of times.
+ id = setTimeout(done, 1000);
+ }
+ });
+
+ // Unhook and re-sort servers.
+ function done() {
+ cowboy.log("Server data has been retrieved.");
+ // Unhook.
+ cowboy.hooker.unhook(serverguide, "refreshHighlight");
+ // Re-sort servers.
+ cowboy.reSortServers();
+ }
+ }
+
+ // Hook serverguide.updateFriendsPlayingOnServers (which appears to be
+ // called every time the serverguide is refreshed) to init the counter.
+ cowboy.hooker.hook(serverguide, "updateFriendsPlayingOnServers", function() {
+ // Init counter.
+ initCounter();
+ // Not sure why serverguide.refreshHighlight gets called two extra times,
+ // but I don't really care either.
+ count += 2;
+ });
+
+ // The serverguide form is currently visible, so Battlelog was loaded on the
+ // serverguide page. Handle re-sorting servers here (because this userscript
+ // runs after the initial serverguide.onPageShow is called, and can't hook
+ // it the very first time).
+ if ($("#serverguide-filter-form").is(":visible")) {
+ initCounter();
+ }
+}());
View
21 dev/src/intro.js
@@ -0,0 +1,21 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+// Global namespace.
+window.cowboy = {
+ version: "0.2.0",
+ registry: [],
+ register: function(name) {
+ cowboy.registry.push(name);
+ },
+ loaded: function() {
+ cowboy.popup("Battlelog Hacks v" + cowboy.version + " loaded.");
+ cowboy.registry.forEach(function(name) {
+ cowboy.log("Registered: " + name);
+ });
+ }
+};
+
+// Hooker.
+var exports = cowboy.hooker = {};
View
73 dev/src/log.js
@@ -0,0 +1,73 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+// ==========================================================================
+// Logging
+// ==========================================================================
+
+(function () {
+ // Binding Array#slice to Function#call allows slice(arrayLikeObject) to
+ // return an array.
+ var slice = Function.call.bind([].slice);
+ // Binding Object#toString to Function#call allows toString(value) to
+ // return an "[object [[Class]]]" string.
+ var toString = Function.call.bind({}.toString);
+
+ cowboy.log = function() {
+ // Abort if logging disabled.
+ if (!cowboy.log.enabled) { return; }
+ // Build array of arguments, converting any Arguments object passed into
+ // an array.
+ var args = ["[COWBOY]"];
+ for (var i = 0; i < arguments.length; i++) {
+ if (toString(arguments[i]) === "[object Arguments]") {
+ // Convert Arguments object into an array for prettier logging.
+ args = args.concat(slice(arguments[i]));
+ } else if (i === 0 && typeof arguments[i] === "string") {
+ // Concatenate 1st string argument to existing "[COWBOY]" string so
+ // printf-style formatting placeholders work.
+ args[0] = args[0] + " " + arguments[0];
+ } else {
+ // Just push argument onto array.
+ args.push(arguments[i]);
+ }
+ }
+ // Actually log.
+ console.log.apply(console, args);
+ };
+
+ // Enable logging by default.
+ cowboy.log.enabled = true;
+
+ // Hook all function properties of a given object to help debugging.
+ cowboy.inspect = function(context, prop) {
+ // If context was omitted, default to window.
+ if (typeof context === "string") {
+ prop = context;
+ context = window;
+ }
+ // The object to be inspected.
+ var obj = context[prop];
+ // Iterate over all object keys.
+ Object.keys(obj).filter(function(key) {
+ // Skip any functions.
+ return typeof obj[key] === "function";
+ }).forEach(function(key) {
+ var name = prop + "." + key;
+ // Store a reference to the original function.
+ var orig = obj[key];
+ cowboy.log("Inspecting %s", name);
+ // Override it with a new function.
+ cowboy.hooker.hook(obj, key, function() {
+ cowboy.log(name, arguments);
+ });
+ });
+ };
+
+ // Log and show a little blue popup notice.
+ cowboy.popup = function(msg) {
+ comcenter.showReceipt(msg);
+ cowboy.log(msg);
+ };
+}());
View
5 dev/src/outro.js
@@ -0,0 +1,5 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+cowboy.loaded();
View
28 dev/src/suppress-scrolltop.js
@@ -0,0 +1,28 @@
+/* Battlelog Hacks
+ * http://benalman.com/
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
+
+cowboy.register("Suppress scrollTop on Serverlist Refresh");
+
+(function() {
+ var suppressScrollTop;
+
+ // Hook jQuery#scrollTop so that it may be suppressed.
+ cowboy.hooker.hook($.fn, "scrollTop", function(top) {
+ // Suppress if setting the document scrollTop to 0.
+ if (suppressScrollTop && top === 0 && this[0] === document) {
+ cowboy.log("Suppressing scrollTop.");
+ suppressScrollTop = false;
+ return cowboy.hooker.preempt(this);
+ }
+ });
+
+ // Hook Surface.ajaxNavigation.navigateTo (which appears to be called every
+ // time a new page-level AJAX request is made, maybe every AJAX request).
+ cowboy.hooker.hook(Surface.ajaxNavigation, "navigateTo", function(url) {
+ if (url.indexOf("/bf3/servers/") === 0) {
+ // Suppress scrollTop if loading new serverguide content.
+ suppressScrollTop = true;
+ }
+ });
+}());
View
3  dev/start-webserver.cmd
@@ -1,3 +1,2 @@
@echo off
-webserver\node webserver\webserver.js
-pause
+node\node node\webserver.js
View
311 dist/battlelog-hacks.js
@@ -3,15 +3,138 @@
* Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT */
// Global namespace.
-var exports = window.cowboy = {
- version: "0.1.1"
+window.cowboy = {
+ version: "0.2.0",
+ registry: [],
+ register: function(name) {
+ cowboy.registry.push(name);
+ },
+ loaded: function() {
+ cowboy.popup("Battlelog Hacks v" + cowboy.version + " loaded.");
+ cowboy.registry.forEach(function(name) {
+ cowboy.log("Registered: " + name);
+ });
+ }
};
+// Hooker.
+var exports = cowboy.hooker = {};
+
+/* JavaScript Hooker - v0.2.1 - 10/31/2011
+ * http://github.com/cowboy/javascript-hooker
+ * Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */
+
+(function(exports) {
+ // Since undefined can be overwritten, an internal reference is kept.
+ var undef;
+ // Get an array from an array-like object with slice.call(arrayLikeObject).
+ var slice = [].slice;
+
+ // I can't think of a better way to ensure a value is a specific type other
+ // than to create instances and use the `instanceof` operator.
+ function HookerOverride(v) { this.value = v; }
+ function HookerPreempt(v) { this.value = v; }
+ function HookerFilter(c, a) { this.context = c; this.args = a; }
+
+ // When a pre- or post-hook returns the result of this function, the value
+ // passed will be used in place of the original function's return value. Any
+ // post-hook override value will take precedence over a pre-hook override
+ // value.
+ exports.override = function(value) {
+ return new HookerOverride(value);
+ };
+
+ // When a pre-hook returns the result of this function, the value passed will
+ // be used in place of the original function's return value, and the original
+ // function will NOT be executed.
+ exports.preempt = function(value) {
+ return new HookerPreempt(value);
+ };
+
+ // When a pre-hook returns the result of this function, the context and
+ // arguments passed will be applied into the original function.
+ exports.filter = function(context, args) {
+ return new HookerFilter(context, args);
+ };
+
+ // Monkey-patch (hook) a method of an object.
+ exports.hook = function(obj, methodName, options) {
+ // If just a function is passed instead of an options hash, use that as a
+ // pre-hook function.
+ if (typeof options === "function") {
+ options = {pre: options};
+ }
+ // The original (current) method.
+ var orig = obj[methodName];
+ // Re-define the method.
+ obj[methodName] = function() {
+ var result, origResult, tmp;
+ // If a pre-hook function was specified, invoke it in the current context
+ // with the passed-in arguments, and store its result.
+ if (options.pre) {
+ result = options.pre.apply(this, arguments);
+ }
+
+ if (result instanceof HookerFilter) {
+ // If the pre-hook returned hooker.filter(context, args), invoke the
+ // original function with that context and arguments, and store its
+ // result.
+ origResult = result = orig.apply(result.context, result.args);
+ } else if (result instanceof HookerPreempt) {
+ // If the pre-hook returned hooker.preempt(value) just use the passed
+ // value and don't execute the original function.
+ origResult = result = result.value;
+ } else {
+ // Invoke the original function in the current context with the
+ // passed-in arguments, and store its result.
+ origResult = orig.apply(this, arguments);
+ // If the pre-hook returned hooker.override(value), use the passed
+ // value, otherwise use the original function's result.
+ result = result instanceof HookerOverride ? result.value : origResult;
+ }
+
+ if (options.post) {
+ // If a post-hook function was specified, invoke it in the current
+ // context, passing in the result of the original function as the first
+ // argument, followed by any passed-in arguments.
+ tmp = options.post.apply(this, [origResult].concat(slice.call(arguments)));
+ if (tmp instanceof HookerOverride) {
+ // If the post-hook returned hooker.override(value), use the passed
+ // value, otherwise use the previously computed result.
+ result = tmp.value;
+ }
+ }
+ // Unhook if the "once" option was specified.
+ if (options.once) {
+ exports.unhook(obj, methodName);
+ }
+ // Return the result!
+ return result;
+ };
+ // Store a reference to the original method as a property on the new one.
+ obj[methodName]._orig = orig;
+ };
+
+ // Get a reference to the original method from a hooked function.
+ exports.orig = function(obj, methodName) {
+ return obj[methodName]._orig;
+ };
+
+ // Un-monkey-patch (unhook) a method of an object.
+ exports.unhook = function(obj, methodName) {
+ var orig = exports.orig(obj, methodName);
+ // Only unhook if it hasn't already been unhooked.
+ if (orig) {
+ obj[methodName] = orig;
+ }
+ };
+}(typeof exports === "object" && exports || this));
+
// ==========================================================================
// Logging
// ==========================================================================
-(function (exports) {
+(function () {
// Binding Array#slice to Function#call allows slice(arrayLikeObject) to
// return an array.
var slice = Function.call.bind([].slice);
@@ -19,9 +142,9 @@ var exports = window.cowboy = {
// return an "[object [[Class]]]" string.
var toString = Function.call.bind({}.toString);
- exports.log = function() {
+ cowboy.log = function() {
// Abort if logging disabled.
- if (!exports.log.enabled) { return; }
+ if (!cowboy.log.enabled) { return; }
// Build array of arguments, converting any Arguments object passed into
// an array.
var args = ["[COWBOY]"];
@@ -43,89 +166,15 @@ var exports = window.cowboy = {
};
// Enable logging by default.
- exports.log.enabled = true;
-
- // Log and show a little blue popup notice.
- exports.popup = function(msg) {
- comcenter.showReceipt(msg);
- exports.log(msg);
- };
-}(typeof exports === "object" && exports || this));
-
-// ==========================================================================
-// Hook & Inspect
-// ==========================================================================
-
-(function (exports) {
- // Binding Array#slice to Function#call allows slice(arrayLikeObject) to
- // return an array.
- var slice = Function.call.bind([].slice);
-
- // Monkey-patch a function property of an object.
- exports.hook = function(context, prop, options, callback) {
- // If options object is omitted, shuffle arguments.
- if (typeof options === "function") {
- callback = options;
- options = {};
- }
- // The original function.
- var orig = context[prop];
-
- if (options.post) {
- // Execute callback after original is executed.
- context[prop] = function() {
- // Unbind if once option specified.
- if (options.once) { exports.unhook(context, prop); }
- // Invoke original function.
- var result = orig.apply(this, arguments);
- // Call callback.
- callback.apply(this, arguments);
- // Return original function's result.
- return result;
- };
- } else {
- // Execute callback before original is executed. If callback returns a
- // value, return that, otherwise invoke original function, returning its
- // result.
- context[prop] = function() {
- // Unbind if once option specified.
- if (options.once) { exports.unhook(context, prop); }
- // Call callback.
- var result = callback.apply(this, arguments);
- if (options.filter) {
- // In filter mode, pass callback's return value (which must be array-
- // like) into the original function, returning its value.
- return orig.apply(result[0], result.slice(1));
- } else if (options.preempt && result !== undefined) {
- // In preempt mode, return callback's result if it's not undefined.
- return result;
- }
- // Otherwise, teturn original function's result.
- return orig.apply(this, arguments);
- };
- }
- // Set property on new function to allow unhooking.
- context[prop]._orig = orig;
- };
-
- // Get the original function from a hooked function.
- exports.hook.orig = function(context, prop) {
- return context[prop]._orig;
- };
+ cowboy.log.enabled = true;
- // Un-monkey-patch a function property of an object.
- exports.unhook = function(context, prop) {
- var orig = exports.hook.orig(context, prop);
- // If there's an original function, restore it.
- if (orig) {
- context[prop] = orig;
- }
- };
-
- // Monkey-patch all function properties of a given object to help debugging.
- exports.inspect = function(prop, context) {
+ // Hook all function properties of a given object to help debugging.
+ cowboy.inspect = function(context, prop) {
// If context was omitted, default to window.
- context || (context = window);
+ if (typeof context === "string") {
+ prop = context;
+ context = window;
+ }
// The object to be inspected.
var obj = context[prop];
// Iterate over all object keys.
@@ -138,16 +187,20 @@ var exports = window.cowboy = {
var orig = obj[key];
cowboy.log("Inspecting %s", name);
// Override it with a new function.
- exports.hook(obj, key, function() {
+ cowboy.hooker.hook(obj, key, function() {
cowboy.log(name, arguments);
});
});
};
-}(typeof exports === "object" && exports || this));
-// ==========================================================================
-// Auto-retry Join Server
-// ==========================================================================
+ // Log and show a little blue popup notice.
+ cowboy.popup = function(msg) {
+ comcenter.showReceipt(msg);
+ cowboy.log(msg);
+ };
+}());
+
+cowboy.register("Auto-retry Join Server");
(function() {
var join, id;
@@ -157,12 +210,12 @@ var exports = window.cowboy = {
// Override existing "join" methods.
["joinMpServer", "joinMpFriend"].forEach(function(method) {
- cowboy.hook(launcher, method, function() {
+ cowboy.hooker.hook(launcher, method, function() {
cowboy.log(method, arguments);
// Stop auto-retrying.
unretry();
// Create a re-callable "join" function with arguments already applied.
- var orig = cowboy.hook.orig(launcher, method);
+ var orig = cowboy.hooker.orig(launcher, method);
join = Function.apply.bind(orig, this, arguments);
});
});
@@ -190,12 +243,14 @@ var exports = window.cowboy = {
];
// Override existing error handler method.
- cowboy.hook(launcher, "_triggerEvent", {post: true}, function(type, details) {
- // Only auto-retry on valid errors.
- if (type === "error.generic" && validErrors.indexOf(details[2]) !== -1) {
- cowboy.log("Starting auto-retry countdown.", details);
- // Start countdown.
- retry();
+ cowboy.hooker.hook(launcher, "_triggerEvent", {
+ post: function(result, type, details) {
+ // Only auto-retry on valid errors.
+ if (type === "error.generic" && validErrors.indexOf(details[2]) !== -1) {
+ cowboy.log("Starting auto-retry countdown.", details);
+ // Start countdown.
+ retry();
+ }
}
});
@@ -241,9 +296,7 @@ var exports = window.cowboy = {
$(document).delegate("#gamemanager-state-close", "click", unretry);
}());
-// ==========================================================================
-// Auto-sort Server List
-// ==========================================================================
+cowboy.register("Auto-sort Server List");
(function() {
// Watch for sorting element clicks.
@@ -307,25 +360,6 @@ var exports = window.cowboy = {
serverguide.highlightServerIndex(+ids[0].split('-')[2]);
};
- // Hook jQuery#scrollTop so that it may be suppressed.
- var suppressScrollTop;
- cowboy.hook($.fn, "scrollTop", {preempt: true}, function(top) {
- // Suppress if setting the document scrollTop to 0.
- if (suppressScrollTop && top === 0 && this[0] === document) {
- cowboy.log("Suppressing scrollTop(0).");
- return this;
- }
- });
-
- // Hook Surface.ajaxNavigation.navigateTo (which appears ot be called every
- // time a new page-level AJAX request is made, maybe every AJAX request).
- cowboy.hook(Surface.ajaxNavigation, "navigateTo", function(url) {
- if (url.indexOf("/bf3/servers/") === 0) {
- // Suppress scrollTop if loading new serverguide content.
- suppressScrollTop = true;
- }
- });
-
var count, id;
// Re-sort the server list when all server data has been retrieved.
function initCounter() {
@@ -335,7 +369,7 @@ var exports = window.cowboy = {
count = serverguideSort.getAllServerSurfaceIds().length;
// Hook serverguide.refreshHighlight (which appears to be called every time
// a server's details (name, players, ping, etc) are returned)
- cowboy.hook(serverguide, "refreshHighlight", function() {
+ cowboy.hooker.hook(serverguide, "refreshHighlight", function() {
// Clear any existing timeout.
clearTimeout(id);
@@ -353,17 +387,15 @@ var exports = window.cowboy = {
function done() {
cowboy.log("Server data has been retrieved.");
// Unhook.
- cowboy.unhook(serverguide, "refreshHighlight");
+ cowboy.hooker.unhook(serverguide, "refreshHighlight");
// Re-sort servers.
cowboy.reSortServers();
- // Un-suppress scrollTop.
- suppressScrollTop = false;
}
}
// Hook serverguide.updateFriendsPlayingOnServers (which appears to be
// called every time the serverguide is refreshed) to init the counter.
- cowboy.hook(serverguide, "updateFriendsPlayingOnServers", function() {
+ cowboy.hooker.hook(serverguide, "updateFriendsPlayingOnServers", function() {
// Init counter.
initCounter();
// Not sure why serverguide.refreshHighlight gets called two extra times,
@@ -380,4 +412,29 @@ var exports = window.cowboy = {
}
}());
-cowboy.popup("Battlelog Hacks v" + cowboy.version + " loaded.");
+cowboy.register("Suppress scrollTop on Serverlist Refresh");
+
+(function() {
+ var suppressScrollTop;
+
+ // Hook jQuery#scrollTop so that it may be suppressed.
+ cowboy.hooker.hook($.fn, "scrollTop", function(top) {
+ // Suppress if setting the document scrollTop to 0.
+ if (suppressScrollTop && top === 0 && this[0] === document) {
+ cowboy.log("Suppressing scrollTop.");
+ suppressScrollTop = false;
+ return cowboy.hooker.preempt(this);
+ }
+ });
+
+ // Hook Surface.ajaxNavigation.navigateTo (which appears to be called every
+ // time a new page-level AJAX request is made, maybe every AJAX request).
+ cowboy.hooker.hook(Surface.ajaxNavigation, "navigateTo", function(url) {
+ if (url.indexOf("/bf3/servers/") === 0) {
+ // Suppress scrollTop if loading new serverguide content.
+ suppressScrollTop = true;
+ }
+ });
+}());
+
+cowboy.loaded();
Please sign in to comment.
Something went wrong with that request. Please try again.