how should I write my define to work with curl.js? - javascript

I'm reading Addy Osmani's excellent blog post about writing AMD modules. I start with a simple chunk of js that I lifted from his post:
define('modTest', [],
// module definition function
function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
I took out the dependencies on foo and bar. Just want a simple object that logs to the console.
So I save that in /js/modTest.js and then try to load it:
curl(['/js/modTest.js'])
.then(function(mt) {
console.log("Load complete");
console.log("mt:");
console.log(mt);
mt.doStuff()
}, function(ex) {alert(ex.message);})
Result: error: Multiple anonymous defines in URL. OK that didn't work. Tried adding in a namespace: define('myCompany/modTest', [],, same result. Tried adding an empty string in the dependency array, same result.
Also tried curl(['modTest.js'], function(dep){console.log(dep)}); with the same result.
Is the code in Addy's blog post incorrect? Am I doing something wrong? Maybe a bug in curl?
Update 5/24: I ditched curl.js in favor of require.js. Zero odd errors, very little work to change over. I did have to deal with amdefine a bit to get my code running client and server side (one object is in both places, so grunt had to be configured to take care of that). My defines generally look like:
define(->
class AlphaBravo
...
And never have any trouble loading.

You asked curl() to fetch a module called "/js/modTest.js". It found the file and loaded it and found a module named "modTest", so it complained. :) (That error message is horribly wrong, though!)
Here's how you can fix it (pick one):
1) Remove the ID from your define(). The ID is not recommended. It's typically only used by AMD build tools and when declaring modules inside test harnesses.
2) Refer to the module by the ID you gave it in the define(). (Again, the ID is not recommended in most cases.)
curl(['modTest'], doSomething);
3) Map a package (or a path) to the folder with your application's modules. It's not clear to me what that would be from your example since modTest appears to be a stand-alone module. However, if you were to decide to organize your app's files under an "app" package, you packages config might look like this:
packages: [ { name: 'app', location: 'app' } ]
Then, when you have code that relies on the modTest module, you can get to it via an ID of "app/modTest".
curl(['app/modTest'], doSomething);
I hope that helps clear things up!
Fwiw, Addy's example could actually work with the right configuration, but I don't see any configuration in that post (or my eyes missed it). Something like this might work:
packages: [ { name: 'app', location: '.' } ]
-- John

I've just had a similar problem which turned out to be the include order I was using for my other libraries. I'm loading handlebars.js, crossroads.js, jquery and a few other libraries into my project in the traditional way (script tags in head) and found that when I place the curl.js include first, I get this error, but when I include it last, I do not get this error.
My head tag now looks like this:
<script type="text/javascript" src="/js/lib/jquery.js"></script>
<script type="text/javascript" src="/js/lib/signals.js"></script>
<script type="text/javascript" src="/js/lib/crossroads.js"></script>
<script type="text/javascript" src="/js/lib/handlebars.js"></script>
<script type="text/javascript" src="/js/lib/curl.js"></script>
<script type="text/javascript" src="/js/main.js"></script>

You have a problem with your define call. It is NAMED
See AMD spec for full story on how to write defines, but here is what I would expect to see in your js/modTest.js file:
define(/* this is where the difference is */ function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
Now, the boring explanation:
CurlJS is awesome. In fact, after dealing with both, RequireJS and CurlJS, I would say CurlJS is awesome-er than RequireJS in one category - reliability of script execution ordering. So you are on the right track.
On of the major things that are different about CurlJS is that it uses "find at least one anonymous define per loaded module, else it's error" logic. RequireJS uses a timeout, where it effectively ignores cases where nothing was defined in a given file, but blows up on caught loading / parsing errors.
That difference is what is getting you here. CurlJS expects at least one anonymous (as in NOT-named) define per loaded module. It still handles named defines fine, as expected. The second you move the contents of "js/modTest.js" into inline code, you will have to "name" the define. But, that's another story.

Related

How to Add Global/Public Variables to grunt-contrib-uglify Output

Okay, so I am way new to Grunt and Node.js. I am building a site, and decided that the 'main.js' file was getting way too big. So, I split it up, and I am now trying to use Grunt to piece all of these JS files back together.
The issue that I have is that I need to make some global variables available to all of the various functions in all of these JS files. To be more specific, every page on our site is identified via an id in the body tag:
<body id="home">
Many of these JS files contain if statements that ensure certain functions only run if the appropriate page is loaded. For example:
if (page == 'home') {
var title = "Home Page"
$('.page-title').text(title);
}
Notice the page variable? That guy is the one that I need to make available to all of these files (after grunt-contrib-uglify merges them together). So, I figured I'd assign a new "unique" variable name, and make it global.
I noticed that grunt-contrib-uglify has a 'wrap' option listed in its documentation. However, no examples are given as to how to use it.
Can anyone tell me:
- How to use the 'wrap' option in 'grunt-contrib-uglify'
- If this is the right grunt plugin for what I am trying to do?
One idea I had (as a last resort) is to create a before.js and after.js and put the beginning and end (respectively) of what I wish to wrap around the other files in each. But, I think the 'wrap' option is what I need, yes?
UPDATE: Here is a link to my "merged" JS file:
main.js
And a link to my Gruntfile:
Gruntfile.js
I have been having the same problem an searching for a solution. But I think I found an answer.
Use this in your gruntfile:
uglify: {
options: {
wrap: true
}
}
The documentation for the wrap property indicates that the variables will be made available in a global variable, and looking at the generated code that does seem to to be the case. Passing a string value to the parameter does seem to create a global variable with that name.
However, wrap: true seems to make all objects and properties available in the global scope. So instead of globals.page.title (which I can't get to work, anyway), you can just use page.title. Much, much easier and simpler.
If this suits your purposes, I'd recommend doing this instead.
Ok this one is tricky, I have been stucked for a while...
Way you do this with grunt-contrib-uglify for frontend JS
create multiple files like
SomeClass.js
OtherClass.js
main.js
and use some module (grunt-file-dependencies or grunt-contrib-concat) and setup it to concat your files. Then setup uglify in your Gruntfile.js like
...
uglify: {
options: {
wrap: "myPublicObject",
...
},
In file (main.js for example) exports variable has to be assigned, the entire file might look like this:
var otherClassInst = new OtherClass();
var someClassInst = new SomeClass();
exports = otherClassInst;
Now what it exactly does
Uglify will pick superior context (this) and define property on it named myPublicObject. Then it wrap your sourcecode with function and create exports variable here (DO NOT DECLARE var exports anywhere). At the end it will assign what you exported to that property. If you dont assign anything (you dont use exports =) inital value is {}, so the property with void object exists anyway.
To make this super-clear,
if you put your code into page like <script src="myminifiedfile.min.js"></script>, then superior context is window =>
window.myPublicObject is instance of OtherClass while window.someClassInst, window.SomeClass and window.OtherClass are undefined.
this is unlikely, but if you just copy content of minified result and wrap it with different function, object you exported will be visible only via this["myPublicObject"] => uglify wrap doesn't make things globaly accessible, it makes them accessible in superior context.

DOJO reference error: declare is not defined

I was following the jsfiddle link http://jsfiddle.net/phusick/894af and when I put the same code into my application, I was getting "reference error: declare is not defined". I have following declarations on top of my js file:
dojo.require("dojo._base.declare");
dojo.require("dojox.form.CheckedMultiSelect");
Thanks in advance for your help.
With Dojo AMD you can tell which module maps to which parameter, for example dojo/_base/declare which is mapped to a variable called declare.
However, in non-AMD code you don't have this possibility. In stead of that you have to do the following:
dojo.require('dojo._base.declare'); // Import
dojo.declare(/** Parameters */); // Use
And actually, modules in dojo/_base are already inside the Dojo core if I'm not mistaken, so you could leave away the dojo.require() line in this case.
For the following AMD code:
require(["dojo/_base/declare"], function(declare) {
var MyCheckedMultiSelect = declare(CheckedMultiSelect, {
/** Stuff */
});
});
You can write the following in non-AMD:
var MyCheckedMultiSelect = dojo.declare(CheckedMultiSelect, {
/** Stuff */
});
However, make sure that when you're running Dojo 1.7, that you disable async mode, for example:
<script>
dojoConfig = {
parseOnLoad: false,
async: true
};
</script>
This rule applies to most, if not all, modules in dojo/_base and several DOM modules, for example:
dojo/_base/xhr: Methods like put(), get(), ... become dojo.xhrGet(), dojo.xhrPut(), ...
dojo/_base/lang: Methods like mixin(), hitch(), ... become dojo.mixin(), dojo.hitch(), ...
dojo/dom: Methods like byId() become dojo.byId()
dojo/on: You have to use dojo.connect() for this
dijit/registry: Methods like byId() become dijit.byId()
...
However, if you're using Dojo 1.7, then you should probably just leave the code in AMD even if all other code is written in non-AMD code. Eventually you will have to upgrade all your code to AMD-syntax, if you're now investing time to convert the code to non-AMD and you later have to convert it to AMD again, you're doing the same work twice.

Starting with RequireJS, communication between modules

I am an ActionScript 3 developer who is just making his first way in building a large-scale JavaScript app.
So I understand modules and understand that AMD is a good pattern to use. I read about RequireJS and implemented it. However, what I still don't understand is how to achieve Cross-Module communication. I understand that there should be some kind of mediator...
I read articles and posts and still couldn't understand how to implement it simply.
Here is my code, simplified:
main.js
require(["Player", "AssetsManager"], function (player, manager) {
player.loadXML();
});
Player.js
define(function () {
function parseXml(xml)
{
// NOW HERE IS THE PROBLEM -- how do I call AssetsManager from here???
AssetsManager.queueDownload($(xml).find("prop").text());
}
return {
loadXML: function () {
//FUNCTION TO LOAD THE XML HERE, WHEN LOADED CALL parseXml(xml)
}
}
});
AssetsManager.js
define(function () {
var arrDownloadQueue = [];
return {
queueDownload: function(path) {
arrDownloadQueue.push(path);
}
}
});
Any "for dummies" help will be appreciated :)
Thank you.
To load up modules from another modules that you define(), you would simply set the first parameter as an array, with your module names in it. So let's say, in your code, you wanted to load Player.js into AssetsManager.js, you would simply include the string Player in the array.
This is simply possible because define's abstract implementation is equivalent to require, only that the callback passed to define expects a value to be returned, and that it will add a "module" to a list of dependencies that you can load up.
AssetsManager.js
define(['Player'], function (player) {
//... Your code.
});
However, if I can add to it, I personally prefer the use of require inside of the callback passed to define to grab the dependency that you want to load, instead of passing parameter to the callback.
So here's my suggestion:
define(['Player'], function () {
var player = require('Player');
});
And this is because it's much more in tune with CommonJS.
And this is how main.js would look like formatted to be more CommonJS-friendly:
require(["Player", "AssetsManager"], function () {
var player = require('Player');
var manager = require('AssetsManager');
player.loadXML();
});
But the CommonJS way of doing things is just a personal preference. My rationale for it is that the order in which you input the dependency names in the array might change at any time, and i wouldn't want to have to step through both the array and the parameters list.
Another rationale of mine (though, it's just pedantic), is that I come from the world of Node.js, where modules are loaded via require().
But it's up to you.
(This would be a reply to skizeey's answer, but I don't have enough reputation for that)
Another way of solving this problem without pulling in Player's AssetManager dependency via require is to pass the AssetManager instance that main.js already has around. One way of accomplishing this might be to make Player's loadXML function accept an AssetManager parameter that then gets passed to parseXml, which then uses it. Another way might be for Player to have a variable to hold an AssetManager which gets read by parseXml. It could be set directly or a function to store an AssetManager in the variable could be used, called say, setAssetManager. This latter way has an extra consideration though - you then need to handle the case of that variable not being set before calling loadXml. This concept is generally called "dependency injection".
To be clear I'm not advising this over using AMD to load it in. I just wanted to provide you with more options; perhaps this technique may come in handier for you when solving another problem, or may help somebody else. :)

How can I combine my JavaScript files and still have my callbacks wait for a ready state?

I have lots of functions and event handlers that are split across multiple javascript files which are included on different pages throughout my site.
For performance reasons I want to combine all of those files into 1 file that is global across the site.
The problem is I will have event handlers called on elements that won't necessarily exist and same function names.
This is an example of a typical javascript file...
$(document).ready(function(){
$('#blah').keypress(function(e){
if (e.which == 13) {
checkMap();
return false;
}
});
});
function checkMap() {
// code
}
function loadMap() {
// code
}
I would need to seperate this code into an object that is called on that specific page.
My thoughts are I could re-write it like this:
(function($) {
$.homepage = {
checkMap: function(){
// code
},
loadMap: function(){
//code
}
};
})(jQuery);
And then on the page that requires it I could call $.homepage.checkMap() etc.
But then how would I declare event handlers like document.ready without containing it in it's own function?
First of all: Depending on how much code you have, you should consider, if serving all your code in one file is really a good idea. It's okay to save http-requests, but if you load a huge chunk of code, from which you use 5% on a single page, you might be better of by keeping those js files separated (especially in mobile environments!).
Remember, you can let the browser cache those files. Depending on how frequent your code changes, and how much of the source changes, you might want to separate your code into stable core-functionality and additional .js packages for special purposes. This way you might be better off traffic- and maintainance-wise.
Encapsulating your functions into different objects is a good idea to prevent unnecessary function-hoisting and global namespace pollution.
Finally you can prevent calling needless event handlers by either:
Introducing some kind of pagetype which helps you decide calling only the necessary functions.
or
checking for the existence of certain elements like this if( $("specialelement").length > 0 ){ callhandlers}
to speed up your JS, you could use the Google Closure Compiler. It minifies and optimizes your code.
I think that all you need is a namespace for you application. A namespace is a simple JSON object that could look like this:
var myApp = {
homepage : {
showHeader : function(){},
hideHeader : function(){},
animationDelay : 3400,
start : function(){} // the function that start the entire homepage logic
},
about : {
....
}
}
You can split it in more files:
MyApp will contain the myApp = { } object, maybe with some useful utilities like object.create or what have you.
Homepage.js will contain myApp.homepage = { ... } with all the methods of your homepage page.
The list goes on and on with the rest of the pages.
Think of it as packages. You don't need to use $ as the main object.
<script src="myapp.js"></script>
<script src="homepage.js"></script>
<-....->
<script>
myApp.homepage.start();
</script>
Would be the way I would use the homepage object.
When compressing with YUI, you should have:
<script src="scripts.min.js"></script>
<script>
myApp.homepage.start();
</script>
Just to make sure I've understood you correctly, you have one js file with all your code, but you want to still be in control of what is executed on a certain page?
If that is the case, then the Terrific JS framework could interest you. It allows you to apply javascript functionality to a module. A module is a component on your webpage, like the navigation, header, a currency converter. Terrific JS scans the dom and executes the js for the modules it finds so you don't have to worry about execution. Terrific JS requires OOCSS naming conventions to identify modules. It's no quick solution to your problem but it will help if you're willing to take the time. Here are some more links you may find useful:
Hello World Example:
http://jsfiddle.net/brunschgi/uzjSM/
Blogpost on using:
http://thomas.junghans.co.za/blog/2011/10/14/using-terrificjs-in-your-website/
I would use something like YUI compressor to merge all files into one min.js file that is minified. If you are looking for performance both merging and minifiying is the way to go. http://developer.yahoo.com/yui/compressor/
Example:
Javascript input files: jquery.js, ads.js support.js
run yui with jquery.js, ads.js, support.js output it into min.js
Javascript output files: min.js
then use min.js in your html code.

RequireJS Flawed By Example?

Looking into RequireJS but unlike Head.JS which downloads in undetermined order but evaluates in a determine order, RequireJS seems different
Normally RequireJS loads and evaluates scripts in an undetermined order.
Then it shows how to prefix order! to the script names for explicit ordering etc..
Then in the examples:
require(["jquery", "jquery.alpha", "jquery.beta"], function($) {
//the jquery.alpha.js and jquery.beta.js plugins have been loaded.
$(function() {
$('body').alpha().beta();
});
});
So if jquery.alpha is downloaded and evaluated before jquery then surely this would cause a problem? Forgetting any client code usage such as function body above, if like most plugin they attach to jQuery.fn then at stage of evaluation then jQuery will undefined in this scenario.
What am I missing here?
RequireJS is not designed to load plain javascript, but to load defined modules. The module format looks something like:
define(['a', 'b'], function(a, b) {
return { zzz: 123 };
});
The important thing to note is that all of the module code is inside an anonymous function. So if the file is run in an arbitrary order, it doesn't matter, because all it does is register the module. The module code is then run in dependency order, with the return value becoming the module object, which is passed as a parameter to code that uses the module.
If you are trying to load plain files, this will not work correctly for you. There is the order plugin to force load order in that case.
It should be noted that that example uses the custom made version of "requirejs and jquery" packaged together, which I believe means that jquery will always be available first.
If you have problems, you can always wrap your plugins within a module definition and make sure they depend on jquery themselves, again ensuring the order is correct:
/* SPECIAL WRAPPING CODE START */
define(['jquery'], function(jQuery) {
// .... plugin code ....
/* SPECIAL WRAPPING CODE END */
});
You are correct, without something to aid in the order an exception will occur. The good news is RequireJS has an Order plug-in to help in this.
I'm currently evaluating RequireJS...
And Here Is An Example of One of My Files:
The 'order!' command will load files for you sequentially. You can (then) use the callback to load other (support) files.
<script src="Loaders/RequireJS/requireJS.js" type="text/javascript"></script>
<script src="Loaders/RequireJS/order.js" type="text/javascript"></script>
<script type="text/javascript">
require(["Loaders/RequireJS/order!././Includes/JavaScript/jQuery/Core/jquery-1.3.2.js",
"Loaders/RequireJS/order!././Includes/JavaScript/jQuery/Core/jquery.tools.min.js",
"Loaders/RequireJS/order!././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tmpl.js"], function() {
require(["././Includes/JavaScript/jQuery/jGeneral.js",
"././Includes/JavaScript/jQuery/autocomplete.js",
"././Includes/JavaScript/jQuery/jquery.ErrorWindow.js",
"././Includes/JavaScript/jQuery/jquery.ValidationBubble.js",
"././Includes/JavaScript/jQuery/jquery.Tootltip.js",
"././Includes/JavaScript/jQuery/jquery.Extensions.js",
"././Includes/JavaScript/jQuery/jquery.Toaster.js"], null);
require(["././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.dimensions.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jQuery.Color.Animations.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.corners.min.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tipsy.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.numberformatter-1.1.0.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tipsy.js"], null);
});
</script>
In All Honesty:
I'm looking at various asynchronous resource-loaders and I'm having a hard-time finding one that does everything I need. I'm also finding the documentation in each one lacking.

Categories

Resources