Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script loaders #15

Open
andrewdavey opened this issue Jul 27, 2011 · 12 comments
Open

Script loaders #15

andrewdavey opened this issue Jul 27, 2011 · 12 comments

Comments

@andrewdavey
Copy link
Owner

Investigate the different script loaders, such as LABjs, require.js and head.js.

Could Cassette provide automatic support for these? Would that even make sense, given Cassette's module-based approach to scripts?

@dotnetchris
Copy link

+1 on this, built in integration with a script loader would be a great feature. There will be many times you can't combine a 3rd party script into your scripts that you can still end up with 5-7+ requests to load js files / css files even using combining where you can. This would help make up for those files.

@andrewdavey
Copy link
Owner Author

Which loader do you use?

I'm hoping that support for different loaders can be provided by extension methods to Cassette.UI.IReferenceBuilder. I need to know more about how the loaders are called before I can implement anything for this.

@dotnetchris
Copy link

I personally have always been overwhelmed by the myriad of choices about loaders. In one of my applications we're using RejuiresJS which I know is one of the biggest heavy hitters out there. When I was looking at all the loaders Curl.js (Cujo resource loader) Curl seemed to have an amazingly simple DSL for working with it. Even supported style sheet loading, and client side templating file loading. Unfortunately it seemed not very actively developed and stuff wasn't working to use all the features, or it was something like to use the css/etc features it only worked with a Node.js server or something else that was a giant barrier for an ASP.NET app.

I think going for supporting multiple loaders could be an extremely ambitious task, it might just be most beneficial to pick one and run with it, then later worry about trying to abstract support in. I'm not too sure how well that would work given how vastly different loaders can work.

Some thoughts on top loaders would be YepNope light weight, requires cache headers to work (Cassette would hopefully make sure everything is setup right for that to work properly), RequiresJS, LABSjs. One thing I absolutely say to NOT do would be to create your own for Cassette.

@nazjunaid
Copy link
Contributor

We are using Modernizr.load which now comes bundled with MVC 3 it's loader was released standalone as yepnope.js so maybe supporting Midernizr/yepnope is a good start as it comes bundled with MVC 3.

http://www.iwantmymvc.com/html5-localstorage-modernizr-in-mvc3-tools-release

@andrewdavey
Copy link
Owner Author

What does your usage of (Modernizr|yepnope).load look like? Are you using it to load all page scripts, or just polyfills?

I'm imaging extending the Cassette reference parse to allow something like:

/// <reference unless="window.JSON" path="~/scripts/polyfill/json2.js" />
/// <reference if="Modernizr.geolocation" path="~/scripts/show-map.js" />

function useJsonParserAndLocation() { ... }

Then when you call @Assets.Scripts.Render() Cassette will generate this:

<script>
yepnope({
    test: !window.JSON,
    nope: "/_assets/scripts/scripts/polyfill/json2_abcd1234.js", // Cassette URL to required module
    complete: function() {
        yepnope({
            test: Modernizr.geolocation,
            yep: "/_assets/scripts/script-map_1234acbd.js",
            complete: function() {
                yepnope("/_assets/scripts/page_1234abcd.js"); // Load the original script that had the references.
            }
        });
    }
});
</script>

The other option is for Cassette to wrap the original script source in yepnope calls, where the final complete function contains the source script. (The module processing pipeline makes this trivial.) This is nicer because now the script is just included into the page as a normal tag. Any references are lazily loaded via yepnope.

Having just written that, it sounds pretty cool. I may try it out now! :)

EDIT: I've not used yepnope before, so let me know if I'm way off the mark, or missing a simpler way to handle things!

@nazjunaid
Copy link
Contributor

To be honest the only thing we currently using it for is Google analytic code and that's only because we copied the script tag from HTML5 Boilerplate which uses a shorter code size compared to the default Google Analytic code. So my knowledge is quite basic but your example looks OK to me.

This is our current usage. Taken from HTML5 Boilerplate.

<script>
    window._gaq = [['_setAccount','UA-403581-1'],['_trackPageview'],['_trackPageLoadTime']];
    Modernizr.load({
        load: ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'
    });
</script>

We would like to dynamically load some resources like jquery-watermark (html5 placeholder fall-back) and json2 using Modernizr just not sure how we can tie that into cassette's minification.

@andrewdavey
Copy link
Owner Author

The @Assets.Scripts.ModuleUrl("path/to/module") function will return the Cassette module URL. You can use this instead of referencing the module in the page. So for example:

<script>
yepnope({
  test: Modernizr.geolocation,
  yep: "@Assets.Scripts.ModuleUrl("~/scripts/geo/geo-stuff.js")",
  nope: "@Assets.Scripts.ModuleUrl("~/scripts/polyfill/geo-fallback.js")"
});
</script>

I'm not a fan of this kind of code, but I guess it'll be okay for basic scenarios.

@nazjunaid
Copy link
Contributor

Thanks for the tip with @Assets.Scripts.ModuleUrl didn't know about that one.

We can use that to load json2 but with jquery-watermark we need to inject that just after jquery and before and script dependent on it so I guess that needs cassette to support it.

<script>Modernizr.load({test:!window.JSON,yep:'@Assets.Scripts.ModuleUrl("~/resources/scripts/polyfill/json2.js")'});</script>

The above works for me I've crunched/minified the output to save some space.

@andrewdavey
Copy link
Owner Author

This is the issue with going down the script loader route. Once you start using a script loader you really have to use it for everything. Mixing regular includes with the loader soon gets messy.

This is why I'd like Cassette to provide an option that switches everything to using the loader, with no in-page changes. You keep all the references and render calls as they are and let Cassette generate the required yepnope stuff.

@dotnetchris
Copy link

I think that's probably the right way to start with things. I'm pretty sure yepnope also supports doing loading for everything else by using the following parameter after the nope condition.

I think the if/unless tags are a great idea.

@SLaks
Copy link
Contributor

SLaks commented Feb 15, 2012

I too would like this feature.

I'm planning on writing my own code to emit a single head.js call for all included bundle URLs (regardless of location).

I haven't yet figured out how difficult that will be.

@prabirshrestha
Copy link

Combres (http://combres.codeplex.com) does a neat trick by allowing us to call EnableClientUrls which generates the following html code that needs to be added in our page.

    <script type="text/javascript">
    var Combres=Combres||{};
    Combres.Urls=Combres.Urls||{};
    (function() {
        var urls={siteCss:'/combres.axd/siteCss/-1261607049/',dotLessCss:'/combres.axd/dotLessCss/1328800956/',siteJs:'/combres.axd/siteJs/1312894718/'};for(attrName in urls)Combres.Urls[attrName]=urls[attrName];
    })();
</script>

compressed

<script type="text/javascript">var Combres=Combres||{};Combres.Urls=Combres.Urls||{};(function(){var u={siteCss:'/combres.axd/siteCss/-1261607049/',dotLessCss:'/combres.axd/dotLessCss/1328800956/',siteJs:'/combres.axd/siteJs/1312894718/'};for(a in u) Combres.Urls[a]=u[a];})();</script>

Cassette can improve on it by allows us to specify the list of bundles optionally so only certain urls are generated and also allow us to change the global variable where it will store the hash of urls.

Then we can just reference the url using Combres.Url.siteJs and so on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants