Allow drupal_add_js to load external Javascript files

ontwerpwerk - October 25, 2006 - 16:03
Project:Drupal
Version:7.x-dev
Component:javascript
Category:feature request
Priority:normal
Assigned:Unassigned
Status:duplicate
Description

Suppose you want to build a module with google maps, then you would need to load an external javascript file like so:

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key={insert long API key here}" type="text/javascript"></script>

Currently drupal_add_js does not allow loading of external .js files, which you would have to bypass with drupal_set_html_head which is IMHO not practical.

So I'm hoping this can be solved by expanding the drupal_add_js function to enable this kind of use.

---

I'm not really sure if this is a feature request or a bug, my initial reaction was that this would be a critical bug because module developers who are told to use drupal_add_js would certainly fail in these scenarios, and it is a great plus for drupal if stuff like adding google maps is easy

#1

hass - May 5, 2007 - 12:24
Version:x.y.z» 6.x-dev

this is required for some much more modules, too. for e.g. Google Analytics.

#2

ontwerpwerk - May 8, 2007 - 10:08
Status:active» patch (code needs review)

This patch tries to solve this by adding an extra scope to the javascript loaders

AttachmentSize
drupal_external_js.patch1.52 KB

#3

hass - May 8, 2007 - 11:42

This patch looks like RTBC...

Additional something i found - it seems not possibly to add JS inline code in the closure variable. This is for e.g. required for Google Analytics and some other statistic modules. This seems currently not possible or i was only unsuccessful... 'header' and 'footer' works but 'closure' not.

#4

budda - May 8, 2007 - 22:24

It would certainly make referencing remote javascript urls cleaner inside drupal modules. So the feature request gets a +1 for me to use in the Google Analytics module.

#5

hass - May 9, 2007 - 21:12
Status:patch (code needs review)» patch (reviewed & tested by the community)

There was a bug inside the patch. I fixed the ordering of JS files embedded into HTML and tested successful with Google Analytics under D51. Nevertheless the patch is build against today HEAD.

This is the example code for current Google Analytics module.

drupal_add_js('http' . $prefix . '.google-analytics.com/urchin.js', 'external', 'footer');
drupal_add_js("<!--\n_uacct = \"".$id."\";{$segmentation}{$codesnippet}urchinTracker();\n// -->\n", 'inline', 'footer');

With the patch provided in #2 the order of JS ends with the following wrong code order:

<script type="text/javascript"><!--
_uacct = "UA-736352-1";__utmSetVar('');urchinTracker();
// -->
</script>
<script type="text/javascript" src="http://www.google-analytics.com/urchin.js"></script>

This is now fixed with this patch and produces the following output. This loads first the external files with the functions possibly required by the types "inline" or "settings":

AttachmentSize
drupal_add_js_external.patch1.35 KB

#6

hass - May 9, 2007 - 21:15

here is the missing part of my last posting:

<script type="text/javascript" src="http://www.google-analytics.com/urchin.js"></script>
<script type="text/javascript"><!--
_uacct = "UA-736352-1";__utmSetVar('');urchinTracker();
// -->
</script>

#7

ChrisKennedy - May 10, 2007 - 00:46

This looks good to me.

#8

Steven - May 13, 2007 - 04:13

Can't you work around this with drupal_set_html_head()? If so, I'd like to not commit this to 5.x, as it's an API change and 5.x modules that use it will break without it.

#9

hass - May 13, 2007 - 10:17

This was original build as a feature for D6 for not using a workaround like drupal_set_html_head()... if you'd like to commit to D5, well - we are happy, too. drupal_set_html_head() may work.

#10

Gurpartap Singh - May 13, 2007 - 18:45
Status:patch (reviewed & tested by the community)» patch (code needs work)

case 'external': could be placed after case 'inline':? Because it would be the rarest option used in drupal_add_js and this is just a tiny performance tweak :) Changing status..

#11

hass - May 13, 2007 - 18:58
Status:patch (code needs work)» patch (code needs review)

Sorry, but you are wrong. Read my above posting, please. we first need to add JS code - no matter if - internal or external and then we must add inline code or settings code. if we do different, we cannot call a function from inline code that is inside the included JS files. therefor this order is a *must*.

#12

Gurpartap Singh - May 14, 2007 - 02:27

Well, if you think so, you are incorrect. With this order you can't accomplish what you are trying to explain. You are not passing all the JS and options altogether to drupal_add_js where it decides which JS code to output first and what second. This order is just normal syntax.

#13

ontwerpwerk - May 15, 2007 - 08:48

I think the problems of adding inline scripts to closures and order of loading external and internal scripts is not the most important part of this issue.

So adding the new scope to a position in the switch that is fastest would be the best start, but I don't believe the few extra cycles that will save compared to my patch will matter that much.

#14

hass - May 15, 2007 - 18:32

if my code looks like

<script type="text/javascript"><!--
_uacct = "UA-736352-1";__utmSetVar('');urchinTracker();
// -->
</script>

<script type="text/javascript" src="http://www.google-analytics.com/urchin.js"></script>

i get a function urchinTracker() is not defined error.

If i use my patch the order is done in the following way and i have NO function undefined error.

<script type="text/javascript" src="http://www.google-analytics.com/urchin.js"></script>

<script type="text/javascript"><!--
_uacct = "UA-736352-1";__utmSetVar('');urchinTracker();
// -->
</script>

The order is therefor very important - if you have functions that are executed one page load, isn't it?

#15

Gurpartap Singh - May 15, 2007 - 19:17

Yes it's important, and to accomplish that, you really don't need to patch drupal. Just use it like:

drupal_add_js(..., 'external');
drupal_add_js(..., 'inline');

So, your point is not related here. :) And the patch seems to be fine too. :)

#16

hass - May 15, 2007 - 21:04

Yes it's important, and to accomplish that, you really don't need to patch drupal. Just use it like:

drupal_add_js(..., 'external');
drupal_add_js(..., 'inline');

So, your point is not related here. :) And the patch seems to be fine too. :)

Sorry i don't understand you. There is currently no drupal_add_js(..., 'external'); in drupal core. So we need to patch drupal. why are you writing we don't need to patch? We need to patch.

If you think the patch is fine set to RTBC, please. So we have a chance to get this into D6 core. D6 code freeze is only 2 weeks away... THX

#17

Gurpartap Singh - May 15, 2007 - 21:41

I mean you dont have to patch anything to get the series of external or inline, up or down. Sure you have to patch to get the external option in. Hope you get it. Don't worry, whatever has to, will get in on time.

#18

bradrice - June 9, 2007 - 12:50

I created this workaround until the external is added to drupal_add_js.

I needed to add Amazon affiliate script at the end of my document similar to the earlier post that needed a Google Analytics post. I did it by creating a text file called affiliate.js. All it contained was:

Then I used a php include to include it at the bottom of my post:

include('sites/js/affiliate.js');

You can see what it does at: http://www.bradrice.com/drupal/node/8

I would still like to see the code updated to allow for external links, but this is a temporary workaround.

#19

profix898 - June 9, 2007 - 14:53

Here is a slightly extended patch which
1. adds documentation for the 'external' option to drupal_add_js()
2. fixes a small coding-style issue (was present before the patch)
3. allows the external files to be preprocessed optionally

I really hope we can get support for 'external' javascript into D6. Its a much needed feature IMO, esp. for integration modules.

AttachmentSize
drupal_add_js_external2.patch3.51 KB

#20

profix898 - June 9, 2007 - 15:00

@Steven's question 'Can't you work around this with drupal_set_html_head()?'

Here is why I think this is not a satisfying solution ...
- drupal_set_html_head does not check for duplicated includes (drupal_add_js does)
- drupal_set_html_head doesnt cascade with other js includes. It always adds the files at the beginning of the head section, even before jquery.js, etc. Thats especially bad if you have an external application to embed, which also depends on jquery. You must include jquery manually in this case and it will be added twice in the end.

#21

hass - June 9, 2007 - 18:52

@profix898: are you sure preprocess will work with this patch? I don't think so... maybe i missed something, but you need to send a HTTP request to an external box, save the content to disk and so on and so on... I think this is no good idea to integrate external scripts into your local scripts... this will break many external scripts, too. And finally if the remote site change the file your local cache is not updated and therefor may not work until regenerated.

calling external HTTP request is problematic... if the remote server is down your server may goes down while it starts with tons of hanging HTTP request to the JS file on the external site.

#22

profix898 - June 9, 2007 - 19:42

@hass: drupal_build_js_cache() simply uses $contents .= _drupal_compress_js(file_get_contents($path). ';'); to read the file and AFAIK file_get_contents() also works for remote files. I think it should work that way, yes. I also dont think this would break the external script. If the script changes frequently you cant still call drupal_add_js() with preprocessing disabled and the cache is not updated for local modified files either.

BUT I agree about the HTTP request being problematic. If the external source is unavailable the server will hang instead of the browser. So lets remove that option. Attached patch is quite similar to yours but fixes the coding-style issue and adds docs.

AttachmentSize
drupal_add_js_external3.patch3.21 KB

#23

Steven - June 9, 2007 - 21:16
Status:patch (code needs review)» patch (code needs work)

Why does the 'external' option allow an array to be passed in, when this is not done for local files? (at least according to the doxygen). This would be inconsistent.

As for the drupal_set_html_head() workaround, please read my comment carefully. The original post asks whether this a bug report or a feature request. My point was that since the bug fix requires an API change, it was unlikely to be backported to 5.x, but if there was a workaround available, that there would no real reason to do so.

#24

hass - June 9, 2007 - 21:21

@Stephen: as stated above - we are not talking about a Bugfix. This is a Feature Request for D6, nothing else.

#25

profix898 - June 9, 2007 - 22:37
Status:patch (code needs work)» patch (code needs review)

Sorry, I was wrong :( The 'external' option does - of course - not allow an array to be passed. Changed the doxygen accordingly ...

AttachmentSize
drupal_add_js_external4.patch3.2 KB

#26

profix898 - June 30, 2007 - 23:13

I still think this is an important feature and should go in ...

#27

hass - October 13, 2007 - 13:52

For Google Analytics feature request in http://drupal.org/node/182334 we need this fixed and i'm bumping this up now to get into D6.

#28

hass - October 13, 2007 - 14:07

Updated patch for latest HEAD. No changes - only updated to remove hunks.

AttachmentSize
drupal_add_js_external_2007101301.patch3.27 KB

#29

Stutzer - November 27, 2007 - 05:40

Simple decision

drupal_add_js('</script><script type="text/javascript" src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABCDEFG" >', 'inline');

#30

Rob Loach - December 4, 2007 - 13:25

Subscribing...

#31

Rob Loach - December 7, 2007 - 16:07
Status:patch (code needs review)» patch (reviewed & tested by the community)

Updated to HEAD and tested it by using the following contents inside a new node with the PHP Filter:

<?php
 
// Add an external Javascript file
 
drupal_add_js('http://maps.google.com/maps?file=api&amp;v=2&amp;key=GOOGLEKEYHERE', 'external');

 
// Add the Javascript to test it
 
drupal_add_js('window.onload = function() {
      if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById("map_canvas"));
        map.setCenter(new GLatLng(37.4419, -122.1419), 13);
      }
  }'
, 'inline');

  echo
'<div id="map_canvas" style="width:500px; height:300px;"></div>';
?>

It displays a Google Map by using an external Javascript file from Google. You'll need a Google Maps API key generated here.

AttachmentSize
drupal_add_js_external_6.patch3.24 KB

#32

Gábor Hojtsy - December 7, 2007 - 16:58
Version:6.x-dev» 7.x-dev

API changes are not accepted in Drupal 6, unless they fix bugs. This adds a new feature, so postponed. Just as it was with 5.x (see above), this came too late. Please make sure you get this into 7.x early.

#33

itaine - December 27, 2007 - 12:59

Robby Rob If its not too much to ask, could you update the patch for the first RC? Too bad this one didn't make 6!

#34

seanr - January 16, 2008 - 22:27

Subscribing. Why is it so hard to add such a seemingly simple feature? Don't get why this wasn't committed ages ago.

#35

Rob Loach - February 8, 2008 - 13:54
Title:drupal_add_js does not load external javascript» Allow drupal_add_js to load external Javascript files
Status:patch (reviewed & tested by the community)» patch (code needs work)

This patch will have to be moved to HEAD. It would also be great if somehow we got caching working on the external scripts.

#36

hass - February 8, 2008 - 16:36
Status:patch (code needs work)» patch (reviewed & tested by the community)

Please make caching for external an extra patch. this one is RTBC

#37

Rob Loach - February 8, 2008 - 16:54

Discussion of adding caching for the external Javascript files can continue here: Cache External Javascript.

#38

Rob Loach - February 28, 2008 - 05:49
Status:patch (reviewed & tested by the community)» patch (code needs work)

Patch is getting hunk failures.

#39

Rob Loach - February 28, 2008 - 06:05
Status:patch (code needs work)» patch (code needs review)

Rerolled to HEAD...

To test:

  1. Fresh Drupal install
  2. Apply the patch
  3. Enable the PHP input filter module at admin/build/modules
  4. Create a new page at node/add/page using the new PHP Input Filter with the following code:
    <?php
     
    // Add an external Javascript file
     
    drupal_add_js('http://maps.google.com/maps?file=api&amp;v=2&amp;key=GOOGLEKEYHERE', 'external');

     
    // Add the Javascript to test it
     
    drupal_add_js('$(document).ready(function() {
          if (GBrowserIsCompatible()) {
            var map = new GMap2(document.getElementById("map_canvas"));
            map.setCenter(new GLatLng(37.4419, -122.1419), 13);
          }
      });'
    , 'inline');

      echo
    '<div id="map_canvas" style="width:500px; height:300px;"></div>';
    ?>
  5. Save the page and view it
  6. You should see a Google Map

As you can see, in this code, we call drupal_add_js using the external flag, meaning it will load the Google Maps API externally without having to hold it on the local server.

AttachmentSize
91250.patch3.03 KB

#40

Arancaytar - February 28, 2008 - 12:18

@hass: drupal_build_js_cache() simply uses $contents .= _drupal_compress_js(file_get_contents($path). ';'); to read the file and AFAIK file_get_contents() also works for remote files.

No it doesn't. This is why Drupal implements its own HTTP client function with drupal_http_request and uses only this to get remote files. file_get_contents can be enabled for URLs, but most hosts shut this off for security, to ensure that you only access a URL when you really mean to.

I know the patch no longer supports preprocessing for remote files, but this is an important aspect of retrieving external data anywhere in core, so it bears repeating.

#41

adityaw - March 2, 2008 - 16:34

Or try this instead :

<?php
  $path
= drupal_get_path('module', 'mymodule');
 
drupal_add_js($path .'/downloadscript.js');
?>

then in downloadscript.js:

var source= ('http://www.google.com/uds/api?file=uds.js&amp;v=1.0&key=MYAPIKEY');
document.write ("<scr"+"ipt type='text/javascript' src='"+source);
document.write ("'><\/scr"+"ipt>");

#42

Rob Loach - March 2, 2008 - 23:40

That work-around is a horrible hack, and still results in the same issue at hand. Although it successfully allows us to use external scripts though drupal_add_js, it doesn't do it cleanly at all. We should be able to put the script URL right into the method call.

I think caching and preprocessing these external scripts should be handled in a seperate issue than what this patch is doing. The cache would download the referenced script to the files directory, and then reference the JS file in the files directory. This seems like a lot of work for one patch, so splitting it into two will help the situation. Could we get a review of this patch?

#43

Arancaytar - March 3, 2008 - 09:51

#39 works for me. The following test code was used in a PHP-evaluated node:

<?php drupal_add_js('http://ermarian.net/style/jquery.form', 'external'); ?>
<form id="testform">
  <input type="submit" value="Start" />
</form>

<script type="text/javascript">
  $(document).ready(function() {
    $('#testform').ajaxForm({beforeSubmit: function() {alert("Forms extension present.");}});
  });
</script>

The jQuery forms extension is not in Drupal, so pre-patch (with the URL included as a relative path) the missing library will cause an error. Post-patch, clicking on the button will pop up the alert attached to it via ajaxForm.

#44

Wim Leers - March 3, 2008 - 13:13

Subscribing.

#45

Arancaytar - March 4, 2008 - 16:11

jQuery forms extension is not in Drupal

Whoops, correction: The jQuery forms extension is only loaded when a form requiring AHAH behavior is built. The test code above will not work if an AHAH form is present on the page - it will work without the patch just as it will with the patch.

#46

Rob Loach - March 5, 2008 - 11:32

So, has anyone reviewed this patch successfully? If so, can we get a RTBC?

#47

Arancaytar - March 5, 2008 - 11:35

I've tested it. Perhaps we can get a second review?

#48

Rob Loach - May 12, 2008 - 14:31

Updated to HEAD. Test still applies:

  1. Fresh Drupal HEAD install
  2. Apply the patch
  3. Enable the PHP input filter module at admin/build/modules
  4. Create a new page at node/add/page using the new PHP Input Filter with the following code:
    <?php
     
    // Add an external Javascript file
     
    drupal_add_js('http://maps.google.com/maps?file=api&amp;v=2&amp;key=GOOGLEKEYHERE', 'external');

     
    // Add the Javascript to test it
     
    drupal_add_js('$(document).ready(function() {
          if (GBrowserIsCompatible()) {
            var map = new GMap2(document.getElementById("map_canvas"));
            map.setCenter(new GLatLng(37.4419, -122.1419), 13);
          }
      });'
    , 'inline');

      echo
    '<div id="map_canvas" style="width:500px; height:300px;"></div>';
    ?>
  5. Save the page and view it
  6. You should see a Google Map
AttachmentSize
91250.patch3.09 KB

#49

Rob Loach - May 12, 2008 - 15:19

The attached patch also caches the external Javascript files locally. Reviews would be great.

AttachmentSize
91250.patch3.95 KB

#50

hass - May 12, 2008 - 18:43

@Rob: caching of external files should go into an extra patch http://drupal.org/node/91250#comment-722222. See your own comment below this comment.

#51

Rob Loach - May 12, 2008 - 22:18

Then this patch should suffice for this issue...

AttachmentSize
91250.patch3.09 KB

#52

wretched sinner... - May 12, 2008 - 23:46

obligatory sub comment

#53

Rob Loach - May 13, 2008 - 05:30

Similar thread can be found here: http://drupal.org/node/251578

#54

Dries - June 3, 2008 - 18:39

Rob et al, it would be great if you could share your thoughts on #251578 at http://drupal.org/node/251578. It seems to fix this order, as well fix a problem with the order in which Javascript files are shown. We might conclude that this patch is a duplicate ... ?

#55

andypost - June 4, 2008 - 21:47

Interesting discussion but everybody forget about collisions of variable names and security.

#56

Rob Loach - June 5, 2008 - 18:41
Status:patch (code needs review)» duplicate

I ported the code over from the $type = 'external' code to just check if the path given is a valid absolute URL in the reinvention of drupal_add_js.

 
 

Drupal is a registered trademark of Dries Buytaert.