Skip to content
This repository has been archived by the owner on Dec 31, 2022. It is now read-only.

Strange problem in Drupal #13

Closed
jcisio opened this issue Jan 5, 2011 · 13 comments
Closed

Strange problem in Drupal #13

jcisio opened this issue Jan 5, 2011 · 13 comments

Comments

@jcisio
Copy link

jcisio commented Jan 5, 2011

I'm integrating LABjs into Drupal 7. However I have a problem. Here are my code:
<script type="text/javascript" src="http://d7.ttcn/sites/all/libraries/labjs/LAB.min.js?lekatr"></script>
<script type="text/javascript">

</script>

<script type="text/javascript">
<!--//--><![CDATA[//><!--
$LAB.wait(function() {
jQuery.extend(Drupal.settings, {"basePath":"\/"},"overlay":{"... strip..."});
});
//--><!]]>
</script>

However, the code in jQuery.extend(...) has some problem. I don't know why. That line looks like never executed. I put an "alert(0)" before that line, it pops up, after that line, it does not.

Is that the correct way to do? I have a few more inline script block, I use $LAB.wait(...) for all of them.

@getify
Copy link
Owner

getify commented Jan 5, 2011

Unfortunately, you can't split up the $LAB.script() calls and the $LAB.wait() call like that, because that will act as two independent LAB chains and won't get your desired effect.

You could do it like this:

<script type="text/javascript" src="http://d7.ttcn/sites/all/libraries/labjs/LAB.min.js?lekatr"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
var _lab = $LAB
.setOptions({AlwaysPreserveOrder:true})
.script("/misc/jquery.js")
.script("/misc/jquery.once.js")
.script("/misc/drupal.js")
.script("/misc/ui/jquery.ui.core.min.js")
.script("/misc/jquery.ba-bbq.js")
.script("/modules/overlay/overlay-parent.js")
.script("/modules/contextual/contextual.js")
.script("/misc/jquery.cookie.js")
.script("/modules/toolbar/toolbar.js");
//--><!]]>
</script>

<script type="text/javascript">
<!--//--><![CDATA[//><!--
_lab.wait(function() {
    jQuery.extend(Drupal.settings, {"basePath":"\/"},"overlay":{"... strip..."});
});
//--><!]]>
</script>

The difference here is we "continue" the original $LAB chain by saving the ultimate return value (technically the return value from the last .script() call) int _lab variable, and we then chain the .wait() off it later.

However, I would say in general, it's not a good idea to split the chains up like this, as there's a possibility that you can create race conditions (not really a case I've tested extensively). So if you do it that way, be careful and test thoroughly.

Another approach, which should be a little more solid, but is based on a very similar concept (simulated chaining) is found here:

https://gist.github.com/704226

-OR-

https://gist.github.com/475431

@jcisio
Copy link
Author

jcisio commented Jan 5, 2011

Thanks, Kyle, that works.

About the queuing, I can't use it. Developers put inline JS everywhere. I can grab all JS files and put in one chain without side effect, but for inline JS blocks, it's more difficult. However, I'll do more test about merging all inline JS blocks.

@jcisio
Copy link
Author

jcisio commented Jan 6, 2011

Anyway, is it better to have an event callback like this?
$LAB.ready(function() {
// code will be executed when all scripts are executed
// ...
});
It is like _lab.wait() but we can have many $LAB.script().script()... chains.

@getify
Copy link
Owner

getify commented Jan 6, 2011

Initially, it was an important design decision for LABjs to have each $LAB chain completely independent. This is still currently my philosophy and it precludes features which deliberately tie the chains together, even for a single callback.

Syntactically, it introduces an API complexity/confusion that is undesirable. If $LAB.ready() fires when all current chains are done, what happens if you start another $LAB chain after it (or inside it)... will it clear out that event? Lots of issues arise in this line of questioning.

Basically, I'm not convinced that the use-case for multiple chains is all that strong (except in a few scenarios), and so I encourage people to consolidate their code into as few chains as possible. Really, you shouldn't have any code in separate chains if that code has any relationship to code being loaded in another chain. There's "simulated chaining" (with a for-loop) if you have the need to build up parts of a chain in contextually separate blocks of code and run the chain all at once once the chains parts are all known.

So, while I've thought about a $LAB.ready() thing before, I think it's counter to the philosophy, API design, and best-practices that are put forth from LABjs.

I'm not aware of any use-case that would be a strong argument for such a thing... can you explain why you think it's necessary?

@jcisio
Copy link
Author

jcisio commented Jan 6, 2011

When I think about $LAB.ready(), I suppose that there is no $LAB.script() inside it. I didn't thought about the case when there is a $LAB.script() after a $LAB.ready(). Indeed, I thought that the browser is fast enough (or there is a small delay before $LAB.ready()) so that all $LAB.script() are called (that mean LABjs knows all scripts in the page) before $LAB.ready().

When you ask about this case, I know that I'm wrong ;-) because your decision is "as fast as the browser will allow", then there is no delay... But I wonder if a few ms delay for $LAB.ready() could be enough so that all $LAB.script() are called before .ready()?

In a page there are many JS: some is in header, some is in footer, some is in the content for inline ads (the availability of JS depends on content, not on page, not all content have ads). But in fact I can postprocess to consolidate all JS file in one call, reorder JS (normally ads JS does not depend on other JS, thus it is placed 1st). So, currently I don't have any use case for multiple chain, it's just for convenient.

Thanks for your help. FYI I've created a new project page to enable LABjs in Drupal automatically http://drupal.org/project/labjs . Version for Drupal 7 was out, for Drupal 6 is coming soon. Maybe there will be more use cases.

@getify
Copy link
Owner

getify commented Jan 6, 2011

I saw the Drupal LABjs project! Thanks so much for doing that!

I understand that it's common (especially in CMS's) for people to strewn their <script> tags (both inline script blocks and external script references) about the entire HTML.

That's why I gave you the link on the "queuing"... basically, you could have the drupal find all the different script blocks (inline and external src) and replace that with the appropriate call to $LAB.queue() as shown, just like you do with $LAB.script() or $LAB.wait() right now.

Then, in the master footer of the page, you have one call to $LAB.executeQueue() which causes the entire set of all scripts from across all the different snippets to start loading and executing, just like if you'd done a single $LAB chain at the end manually.

In my opinion, this is the best of both worlds for CMS's, because plugins and content authors get to keep leaving their scripts inline strewn about their content as makes sense to them, and the CMS takes care of re-writing all <script>...</script> and <script src="..."></script> blocks to calls to the $LAB.queue() manager, and the content/plugin authors never know the difference.

@jcisio
Copy link
Author

jcisio commented Jan 7, 2011

The following code doesnot work:


<title>TTCN D7 test</title>
<script type="text/javascript" src="http://d7.ttcn/sites/all/libraries/labjs/LAB.min.js?lenopg"></script>
<script type="text/javascript">
var _lab = $LAB
.setOptions({AlwaysPreserveOrder:true})
.script("http://d7.ttcn/file1.js");
</script>
<script type="text/javascript" src="http://partner.googleadservices.com/gampad/google_service.js"></script>
<script type="text/javascript">
_lab.wait(function() {
alert(0);
});
</script>

Test

I load all scripts with LABjs, but not google_server.js (I want it to be blocking one, and there are ads block with document.write() that requires this file that I can't put in .wait()). If all files are in cache, the code "alert(0)" is not executed.

@getify
Copy link
Owner

getify commented Jan 7, 2011

yeah, like i said, it's not particularly a supported use-case to have significant delays in the middle of a chain's operation. That's why queuing up all your script and inline scripts (into .wait()) and then firing off the chain all at once at the end is the preferred route. The two links I've sent you above show exactly how to do that.

@jcisio
Copy link
Author

jcisio commented Jan 7, 2011

I've reread the queuing code snipset, but sorry I can't figure how to use it in my case.

I don't want (or it is impossible) to load all scripts with LABjs. There is an inline JS block that I can't put in .wait() because of document.write(). This inline block requires an external script. Thus I have to load this external script in the traditional way.

@getify
Copy link
Owner

getify commented Jan 7, 2011

i undersstand that you only want to load some scripts with LABjs. as far as i'm concerned, if you have to, that's fine. if there's a script that has a document.write in it, you should definitely only load that with a regular script tag. (hint: you shold also pressure the creator of that script to stop using document.write()... it's terrible for performance!)

if you're wanting is to have a regular script block in the markup have some waiting affect on the code you're loading with LABjs, this is not possible. the definition of a dynamic script loader is that it makes script loading independent from the page's other resource loading (including manual script tags).

if you're just wanting to sometimes use the queueing (for LABjs loadable scripts) and other times not, do something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>TTCN D7 test</title>
<script src="http://d7.ttcn/sites/all/libraries/labjs/LAB.min.js?lenopg"></script>
<script type="text/javascript">$LAB.setGlobalDefaults({AlwaysPreserveOrder:true});</script>
...
<script>$LAB.queue("http://d7.ttcn/file1.js");</script>
...
<script src="http://partner.googleadservices.com/gampad/google_service.js"></script>
...
<script>$LAB.queue(function() {
  alert(0);
});</script>
...
</head>
<body>Test</body>
...
<script>$LAB.executeQueue();</script>
</html>

As for how, in your original markup, to distinguish what code should be queued for LABjs and what should be left alone, have it be something like this as the original markup before you alter it with your Drupal module:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>TTCN D7 test</title>
...
<script rel="LABjs" src="http://d7.ttcn/file1.js"></script>
...
<script src="http://partner.googleadservices.com/gampad/google_service.js"></script>
...
<script rel="LABjs">
  alert(0);
</script>
...
</head>
<body>Test</body>
</html>

So, your original markup just starts off with script blocks strewn throughout, like shown. For the script blocks where you want LABjs to attach and queue (either for an external src loading, or for an inline snippet), you just mark the tag with something like rel="LABjs", that way your Drupal module knows it should rewrite that tag. And you inject the LABjs loading at the beginning, including the setting of global defaults, and you inject the executeQueue() call at the very bottom of the page.

I dunno if this helps or not, but that's how I'd approach a CMS module for LABjs.

@jcisio
Copy link
Author

jcisio commented Jan 7, 2011

That helps much! I try with queuing and it works. The conclusion could be we shouldn't use var _lab = $LAB... because it is not stable. About the document.write(), that's Google and I don't bother to tell...

The working code is as follow, I try to make it more comprehensive:


<title>TTCN D7 test</title>
<script type="text/javascript" src="http://d7.ttcn/LAB.min.js"></script>
<script type="text/javascript">$LAB.setGlobalDefaults({AlwaysPreserveOrder:true});</script>
<script>
$LAB.queue("http://d7.ttcn/file1.js");
$LAB.queue("http://d7.ttcn/file2.js");
</script>
<script type="text/javascript" src="http://partner.googleadservices.com/gampad/google_service.js"></script>
<script type="text/javascript">
$LAB.queue(function() {
// do some good thing
});
</script>


Test
<script type="text/javascript">
// this is evil, and it requires google_service.js
// it calls a function to add more JS file and do some document.write()
</script>
<script type="text/javascript">$LAB.executeQueue();</script>


My approach to rewrite a JavaScript is to verify if there is a comment // LABjs exclude at the begin, that way it get a great penetration with zero conf for mostly other modules.

Then when will the queuing functionality officially get in? ;-)

@getify
Copy link
Owner

getify commented Jan 7, 2011

At the moment, I don't have specific plans to include it "officially". It's not a lot of extra size, but it is only a functionality that's actually useful if people are using it inside CMS's like you are, and in that case, I think them adding in the little extra snippet is probably the best idea.

Previously, before I released v1.1.12 (actually, v1.1.11), we didn't have "conditional chaining", and there was a lot more of a use-case for this queuing. But now that conditoinal chaining is directly supported, this queuing is only necessary if you need to spread out the building of your chain across the entire HTML block. I don't generally recommend that approach, but in the specific case of CMS's, it's the best (or only) option.

@getify
Copy link
Owner

getify commented Aug 9, 2011

queueing was added in 2.0

This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants