4

This object seems to be available in all Stack Exchange pages and contains interesting information about the actual page, site, user, among others. Just type StackExchange on the console to check it out.

For example, with StackExchange.options.locale I can detect if the site is pt.stackoverflow (yes, here location.hostname would be the same), and with StackExchange.options.user.isRegistered if the user is logged or not.

Here, a simplified script:

// ==UserScript==
// @name        (SE) Inspect global object
// @match       *://*.stackoverflow.com/*
// @grant none
// ==/UserScript==

var start_up = function( $ ) {
    // Run only on individual posts
    if( ( StackExchange.options.routeName.indexOf('Questions/Show') === -1 ) ) 
        return;

    /* do our thing */
};

function with_jquery( f ) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.textContent = "(" + f.toString() + ")(jQuery)";
    document.body.appendChild(script);
};

with_jquery( start_up );

In Chrome, the object is recognized at root level, side by side with with_jquery, but in Firefox it only works inside the start_up. Brock Adams mentions that the object may not be available when the script fires. Can we somewhat rely on that object? Could @run-at document-end help here?

In a related note, I've seen a couple of scripts that deal with Comments failing in some circumstances. In this one I added a setTimeout and it finally prevented the error to occur. But that got me thinking that maybe it's beneficial that we detect that StackExchange ended its initialization before firing any action, similar to this crazy AJAX detection that the omnipresent Brock does :)

I think a TL;DR would be, I'm trying to make this work in Tamper and Grease:

if( ( StackExchange.options.routeName.indexOf('Questions/Show') === -1 ) ) 
    with_jquery( start_up );
else
    with_jquery( something_else );
2
  • doc-end is the default. I'll try to remember to take a deeper look at this in the next few days. Sep 29, 2014 at 22:50
  • Cool, I guess you imagine that, but most of my research ends up in your answers, it's not serial upvote ;)
    – brasofilo
    Sep 29, 2014 at 22:53

2 Answers 2

2

The question is still not clear, please rewrite it like:

  1. I do X.
  2. I expect Y.
  3. But I get Z.

Update:

From the comments: Apparently the OP's problem really was that accessing StackExchange.options.routeName in the script, outside the injected code, caused a ReferenceError, when using Scriptish, not Tampermonkey and Greasemonkey as stated in the question.

This is because Scriptish does not yet break the sandbox the same way that Greasemonkey and Tampermonkey do in @grant none mode.

Solutions in decreasing order of preference/robustness:

  • Don't use @grant none mode if you can help it. Keep script code sandboxed or inject it.
  • Inject all code that depends on the target page's javascript.
  • Don't use Scriptish for code needing @grant none until such time as Scriptish more fully emulates the abysmal design decisions that Greasemonkey made with @grant.

Old answer, may be useful to any others with similar problems:

  • The question's current code seems to work as expected on both browsers (Using Tampermonkey on Chrome).

  • A setTimeout or AJAX tricks don't seem to be needed to wait for, or access, that object.

  • If you mean that you can't use start_up from the console, that's the nature of the particular with_jquery() function being used. It wraps your function in parentheses () which effectively makes it anonymous.

I recommend never doing jQuery that way from a userscript. 99% of the time, use @require and a @grant setting other than none.

On friendly, jQuery powered pages, where heavy interaction with page objects is desired (like Stack Exchange sites), use the page's jQuery like this:

// ==UserScript==
// @name        (SE) Inspect global object
// @match       *://*.stackoverflow.com/*
// @match       *://*.stackapps.com/*
// @grant none
// ==/UserScript==

//var start_up = function( $ ) {    // BAD
function start_up ( $ ) {           // GOOD
    console.log (
        "===> ",
        typeof StackExchange.options.routeName,
        StackExchange.options.routeName
    );
};

withPages_jQuery (start_up);

function withPages_jQuery (NAMED_FunctionToRun) {
    //--- Use named functions for clarity and debugging...
    var funcText        = NAMED_FunctionToRun.toString ();
    var funcName        = funcText.replace (/^function\s+(\w+)\s*\((.|\n|\r)+$/, "$1");
    var script          = document.createElement ("script");
    script.textContent  = funcText + "\n\n";
    script.textContent += 'jQuery(document).ready( function () {' + funcName + '(jQuery);} );';
    document.body.appendChild (script);
};

This allows you to see your function (start_up) and use or debug it from the console.

9
  • Ooook, food for thought. I'll try to revise my XYZ tomorrow. I did see this method of yours, but as there are some others around, wasn't sure. Saw what you did there: the BAD, the GOOD & the UGLY.
    – brasofilo
    Oct 2, 2014 at 1:55
  • I think the TL;DR added to the question sums up my issue. But then, it doesn't hurt doing things inside start_up().
    – brasofilo
    Oct 2, 2014 at 23:03
  • That's a different problem than was originally stated! Anyway, your TL;DR code works in both GM and TM if @grant none is set. So, what's the problem? Oct 2, 2014 at 23:31
  • I may not be expressing things right, but I originally said In Chrome, the object is recognized at root level, side by side with (the function) with_jquery but in Firefox it only works inside the start_up. For me, it produces a ReferenceError (undefined). I'm using Scriptish, if that makes any difference.
    – brasofilo
    Oct 2, 2014 at 23:39
  • Yes, Scriptish makes a HUGE difference! You need to state these things in the question along with an exact recipe to duplicate the problem. I'm pretty sure that Scriptish has not fully implemented the, beyond F'd up, behavior that GM has with @grant settings. TM only emulated that abominable approach for compatibility. Oct 3, 2014 at 0:16
  • sh!t, sorry for that, I realized that just now, am moving away from it and going standard with GM. Thanks for the heads up.
    – brasofilo
    Oct 3, 2014 at 0:19
  • It happens. In the future if you work out an MCVE to post in your question, it helps everyone arrive at the answer sooner, with 79% less head-scratching. ;) Oct 3, 2014 at 1:46
  • So used to answer and to recommend MCVE to folks when commenting on questions and, alas, my asking skills went downhill :)
    – brasofilo
    Oct 3, 2014 at 1:48
  • I think @grant none was one of the causes of my woes. Published an answer that hopefully clarifies the question too.
    – brasofilo
    Oct 3, 2014 at 11:18
1

I'll report my findings based on Brock's answer and my tests.

1) I was using Scriptish. Well, don't. Once I moved to Greasemonkey many things that were working on Chrome's Tampermonkey (where I started to develop) started to work on Firefox.

2) Chrome recognizes StackExchange at root level with @grant none. Firefox does not.

3) To make it cross browser compatible, two options:

If something needs to be granted, inspect for SE object inside the callback function. Note that I'm not passing jQuery to the callback, as the page already has it and it's recognized throughout the script. This way is nice to pass GM storage objects or some other values to the callback.

// ==UserScript==
// @name    (SE) Inspect global object
// @match   *://*.stackoverflow.com/*
// @grant   GM_getValue
// ==/UserScript==

var obj = {'name':'inspect', 'when':'now'};
with_jquery( start_up, JSON.stringify(obj) );

function with_jquery( callback, obj ) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.textContent = '(' + callback.toString() + ')(' + obj + ')';
    document.body.appendChild(script);
}

function start_up( obj ) {
    function filter_so_check_page() {
        var route = false;
        switch( StackExchange.options.routeName ) {
            case 'Questions/List':
            case 'Questions/ListByTag':
                route = 'questions';
                break;
            case 'Unanswered/List':
            case 'Unanswered/ListByTag':
                route = 'unanswered';
                break;
            case 'Home/Index':
                route = 'homepage';
                break;
        }
        return route;
    }
    console.log( 'running at ' + filter_so_check_page() + ' with jQuery v' + $.fn.jquery );
    console.log( 'received', obj );
}

If we have @grant none, these are the modifications:

// @grant   none
// ==/UserScript==

var the_page = filter_so_check_page();

if( the_page == 'questions' || the_page == 'unanswered' ) {
    var obj = {'page':the_page, 'when':'now'};
    with_jquery( start_up, JSON.stringify(obj) );
}

function start_up( obj ) {
    console.log( 'running at ' + obj.page + ' with jQuery v' + $.fn.jquery );
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .