jMod on Github

I recently uploaded my first official release of jMod, a new javascript library designed for userscripts, and this seemed like the place to get some feedback on it. I've posted this same discussion over at greasy fork, but I figured I might as well post it here too.

It's designed to handle a lot of the headaches associated with writing userscripts, and it's packed with a bunch of helpful features that will cut your work in half!

One of my favorite features is that it handles a lot of scope/permission problems (authors using firefox know what i'm talking about). And it can even extend a jQuery instance to use GM_xmlhttpRequest for native cross-origin support.

It can also generate an entire tabbed settings dialog for you! Using your choice of either localStorage (single domain), sessionStorage or GM_storage (multi domain).

I could go on and on about this, but here is the gitgub readme:

jMod

Click Here For Full Documentation

Settings Demo

jMod is a library of useful tools for userscript authors with features ranging from popup notifications, to a full settings dialog generator.


Overview




ToDo / Goals




##Overview

Lightweight and versatile, jMod can be loaded anywhere without having to worry about the native CSS affecting the UI. jMod loads a FULLY namespaced bootstrap stylesheet that has been trimmed down to its bare essentials. Thats it! jMod does not depend on jQuery. However, loading a copy of jQuery can enhance several of its features.

jMod can be loaded as a required script in your meta block, or as a script element.

##Events
Full List of jMod Events

One of jMod's most useful features is handling loading events for you. When run at "document-start", scripts actually execute before the DOM exists. This prevents you from interacting with the document until it is created. jMod takes care of this for you.

jMod.CSS = 'custom css'; // Added to CSS stack until DOM exists

// Start DOM interactions
function onDOMReadyCB(){
    console.log('onDOMReadyCB');
}
jMod.onDOMReady = onDOMReadyCB;

// jMod fully initialized
function onReadyCB(){
    console.log('onReadyCB');
}
jMod.onReady = onReadyCB;

// Page is ready
function onPageReadyCB(){
    console.log('onPageReadyCB');
}
jMod.onPageReady = onPageReadyCB;

// Page is loaded
function loadCB(){
    console.log('loadCB');
}
jMod.load = loadCB;

The following four methods are all functionally equivalent:

// Execute function when jMod is fully loaded and CSS is added
jMod.onReady = function(){
    console.log('onReady');
}
jMod(function(){
    console.log('onReady');
});
jMod('onReady', function(){
    console.log('onReady');
});
jMod.Events.addListener('onReady', function(){
    console.log('onReady');
}, true);


jMod Event Log

jMod Event Log




##Settings
Settings Demo

Tutorial



jMod Settings Example

jMod Settings Example




##Notifications



jMod Notifications Example

jMod Notifications Example

##jQuery
Although jMod is designed to run without using jQuery, there are a few jQuery specific enhancements built in.

###GM_xmlhttpRequest in jQuery Ajax Requests
jMod can extend any instance of jQuery to use GM_xmlhttpRequest as its default data transmission method. This allows you to reliably make cross-origin requests without any additionally flags. Doing this affects every ajax request made by jQuery.

Documentation

if($){
    $(document).ready(function() {
        function test_jQueryFunctions(){
            jMod.jQueryExtensions.addCrossOriginSupport($);

            // Test $.ajax()
            console.log('Test $.ajax("http://google.com")');
            $.ajax({
                    url: 'http://google.com',
                    contentType: 'text/html',
                    type: 'GET',
                    dataType: 'html',
                    onprogress: function(response){
                        console.log('onprogress response', response);
                    },
                    onreadystatechange: function(response){
                        console.log('onreadystatechange response', response);
                    }
                })
                .done(function(data, textStatus, jqXHR) {
                    console.log("$.ajax() success: ", jqXHR);
                })
                .fail(function() {
                    console.log("$.ajax() error");
                });

            // Test $(element).load()
            console.log('Test $(element).load("http://google.com #hplogo")');
            var tmpDiv = document.createElement('div');
            tmpDiv.id = 'tmpDiv';
            document.body.appendChild(tmpDiv);

            $('#tmpDiv').load('http://google.com #hplogo', function(responseText, textStatus, jqXHR){
                console.log('$(element).load() ' + textStatus, jqXHR);
            });
        }

        test_jQueryFunctions();
    });
} else {
    console.log('Test Failed! No jQuery');
}

##CSS
jMod loads in fully namespaced versions of several popular CSS libraries. They will not interact or change the web page in any way until the namespace is added to an element.

###Bootstrap 3.3.2
The bootstrap stylesheet is namespaced with the class ".jmod-na". Many of its standard components have been removed, while others have been heavily modified. For example, tooltip classes have been renamed to avoid having the content page's Bootstrap instance try and interact with it.

###Font Awesome
The bootstrap stylesheet is namespaced with the class ".jmod-fa", and defines the font-face as "jModFontAwesome". It doesn't use the same namespace as the other libraries to avoid overriding a page's font awesome instance when doing so is undesirable.

###Libraries Used

Re: @jgjake2:

... and this seemed like the place to get some feedback on it.

Impressive library and appears that you have spent a good deal of time developing this.

Off the top of my head a few things come to mind:

  • Incompatibility with some Add-ons. Unfortunately your demo page only comes up on a clean browser profile. I disabled the usual suspects with security related Add-ons and it still didn't come up... at this time I don't have a spare moment to go debugging with you and other Add-ons
  • Few things related to security in current Firefox release show up in the Browser Console... be careful as Moz and possibly GM may be changing unsafeWindow a bit.
  • Nice combination specific to inclusion of some node projects and a few mashups from USO/OUJS.
  • Care to consider sharing this in the Libraries section too with a webhook on your GH repo for greater exposure? ... Please ensure that you follow the Terms of Service though with a // @exclude * in your UserScript metadata block... otherwise it will get removed by moderation.

Thanks for sharing and it looks promising. :)

Re: @Marti:

Thank you so much for the feedback!

...Incompatibility with some Add-ons.

....damn it. I tried so hard to do everything in my power to make sure that jMod doesn't interfere/interact with anything; or conversely, be interfered with. What kind of add-ons do you have? How could I reproduce your error so I can program around it?
I've tested jMod in several versions of firefox, chrome and opera and i've never had an issue :/ so this is weird and kinda makes me mad.

...Few things related to security

I've spent a LOT of time developing methods of working around FF security issues. I made a full post about it here. What pops up in your console?

...Nice combination specific to inclusion of some node projects and a few mashups from USO/OUJS

Anything in particular you want included?

...Care to consider sharing this in the Libraries section

I was planning on it. I am just waiting till I feel the project as a whole is ready for that kind of release. There is still some CSS work to do and there are a few other features I'm working on that I'm very excited about. Also you pointed out a few problems that need to be addressed before I can even consider adding it to the libraries section.

Re: @jgjake2:

What kind of add-ons do you have?

Lots... will have to narrow it down to which ones as well as about:config changes. Like I said earlier I don't have enough time just yet to do the Add-on walk-about testing. Perhaps sometime next week will work for me.

What pops up in your console?

unsafeWindow and localStorage is what I recall from earlier... again depends on what active Add-ons I had. NoScript is the usual culprit for enforcing certain security restrictions but Moz is taking on that challenge as well. Will take some time to iron out the details for you... but not until at least next week.

Anything in particular you want included?

Don't know yet... testing stopped once the Add-on incompatibility was discovered. I'll try to redo my profile in case migration is a factor in the results... that takes a bit to do.

Re: @Marti:
Once again I really appreciate the feedback. It's weird considering jMod is only being loaded as a script in that instance. The only thing I can think of that could cause a problem like that is if one of the add-ons is creating an unsafeWindow object when none exists in the public scope. This would break jMod considering it only checks if unsafeWindow exists before using it. So I modified it with the following:

var isWindowInstance = function(win){
    return Object.prototype.toString.call(win).replace(/^\[object |\]$/g,'').toLowerCase() === "window";
};

...

.call(this, "undefined"!==typeof unsafeWindow&&unsafeWindow.top&&isWindowInstance(unsafeWindow)?unsafeWindow:("undefined"!==typeof window&&window.top&&isWindowInstance(window)?window:this))

That should fix the problem if my logic is correct.

Thanks again. Let me know if you have anymore thoughts or concerns.

The demo page didn't work for me either, the problem was that I had the Firefox addon Cookie Controller enabled, which blocks cookies and DOM storage. However this might not be relevant, as this addon is not really popular.
This is the error in the console:

  • jMod - jModReady 185.31msj Mod.full.js (line 2937)
  • jMod.Settings Test settings.js (line 4)
  • SecurityError: The operation is insecure. ...rn (localStorage?localStorage:(window.localStorage?window.localStorage:unsafeWin... jMod.full.js (line 4244)
  • jMod - onPageReady 1024.30ms jMod.full.js (line 2937)

Re: @cuzi:

Cookie Controller enabled, which blocks cookies and DOM storage.

Yup that is partially it. Thanks for that... save me some time next week. :)

, which blocks cookies and DOM storage.

When disabled my Firefox (Fx) takes over since that Add-on uses the existing about: prefs e.g. the settings persist.

as this addon is not really popular.

There's also about:data in SeaMonkey (SM) so it's quite common for some browsers, especially Moz, to have these settings enhanced. Fx is about:permissions.

Re: @jgjake2:

Anything in particular you want included?

So there's one thing... fallback to DOM storage e.g try using the GM_* API first and if all else fails perhaps let the user know gently... perhaps with console.warn statement or better. This is how one of our earlier projects works.

Re: @Marti:
Thank you @cuzi! Thats everything I needed to know about the error.

fallback to DOM storage e.g try using the GM_* API first

Actaully jMod does exactly that. It uses the default storage engine (defined at jMod.Config.API.Storage.engine - GM_Storage by default).

If unavailable, it proceeds to the next available storage engine.

However, in this case jMod resorts to using localStorage and Cookie Controller is just being a jerk about it.

SecurityError: The operation is insecure. ...rn (localStorage?localStorage:(window.localStorage?window.localStorage:unsafeWin... jMod.full.js (line 4244)

This points me directly to problem (and hopefully the solution). The error occurs on a line of code attempting to find the window's/unsafeWindow's localStorage instance, which is throwing a permission error. I believe a little try-catch magic should do the trick; just have to do it to both localStorage and sessionStorage. Also, it wouldn't hurt to make the get/set functions for each a little safer, considering they don't even check if the stor property is valid before using it.

let the user know...with console.warn statement or better

Of course, I'm kind of a nut about logging. I had to write an unnecessarily complicated and convoluted logging system just to let me control verbosity levels. I just never though a problem would arise from simply looking at the localStorage object. I thought testing for existence would be enough. I'll have the fix up by tomorrow with some other minor improvements.

And i'll make sure to test jMod with Cookie Controller from now on ;)

Thanks again @Marti and @cuzi. Ya'll are awesome!