As things stand, if you want to use Drupal with the prototype library, you have a problem, in that both jQuery and Prototype use the $() function.

On the jQuery site, there is a workaround using (see http://docs.jquery.com/Using_jQuery_with_Other_Libraries ).

However: the recommended method doesn't work with jQuery 1.0.4:

var $j = jQuery.noConflict();
     
     // Use jQuery via $j(...)
     $j(document).ready(function(){
       $j("div").hide();
     });

because the noConflict() function isn't supported in 1.0.4

What I did

In each of the files listed below, I did two things:

1. Added the following at the top:

var $jq = jQuery;

2. Change ever instance of $(...) to $jq(...)

Then, my prototype and jQuery got on fine.

List of files changed:

autocomplete.js
collapse.js
drupal.js
progress.js
tableselect.js
textarea.js
upload.js
update.js

As of jQuery 1.1.x, the first line in the files will have to read:
var $jq = jQuery.noConflict();

My _feature request_ is... is there any way that in core, we could adopt a $jq or $drupal_jq override of the jQuery $() function? That way, Drupal will be compatible with Prototype and other libraries off the shelf!

Victor Kane
http://awebfactory.com.ar

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

xhochy’s picture

FileSize
69.74 KB

I think too, that it is really necessary to remove the use of the $()-function and write into the development handbooks, that developers shouldn't use the $()-function because there can be conflicts with other JavaScript-Libraries, but as long as this problem is not solved, I created my own version of Prototype where the $()-function is renamed to $ID(), so I can be used with Drupal 5 without having any problems. I made a compressed version too, which is available at my Weblog.

John Resig’s picture

There's a lot confusion on this point, and I'd like to point out a more-ideal solution. The Drupal core, and any Drupal plugins, should use the native jQuery() function. Defining it to any other namespace (like $jq) will only cause any/all Drupal/jQuery code to become Drupal-specific; which is, generally, a poor solution.

Once the Drupal core is using jQuery() exclusively, the decision to use $() would then be left to the user; the same person who should be choosing to use Prototype or Mootools (or any other $-using library).

Additionally, any module that uses Prototype, or Mootools (or any library that explicitly overwrites Drupal/jQuery's $() function) should be re-written to use a copy of Prototype or Mootools that isn't so destructive (like the one posted by xhochy). In this respect, the JavaScript code of Drupal modules should be treated just like any other PHP module code (like completely overwriting, and breaking, a native Drupal function).

Finally, it is then left to the user, if they should chose to use Prototype, to simply call jQuery.noConflict() - allowing them to use Prototype's native $() function. While, at the same time, regular Drupal users, who wish to use jQuery's $() function, don't have to jump through any additional hoops in order to get started. But that's ok, because with this solution these two groups of users will still be able to happily co-exist.

victorkane’s picture

Excellent points!
We should work along those lines.
The only little detail is that jQuery.noConflict() is not available in the older version of jQuery that currently comes with Drupal core (5.1).
Hopefully this will change in the future.

xhochy’s picture

I found ca. 190 occurences of the $()-function in Drupal HEAD with the following command

find -name "*" -exec grep -Hn "\$(" "{}" ";"

Sadly not all occurences found by find are really $()-function-calls, some belong to a regular expression or are part of a binary file, I think that I will try to write a simple(4-10 lines) replacement script, that I would run on each of the file without those regular expressions. The other files(which cause conflict through regular expressions) have to be edited manually, but I think that shouldn't be a lot of work.

xhochy’s picture

I think most important now is to add to a handbook(I don't know where it belongs) that core implementation should only use jquery() and not $() in future to give the user the choice of the javascript library he wants.

xhochy’s picture

FileSize
25.04 KB

Ok here's my script(sry but it's in ruby but should be simple to understand) for the replacing of $() with jquery and the patch for Drupal so that $() isn't anymore used. The next step should be a upgrade to jquery 1.1.x.

xhochy’s picture

Because only one file per comment can be uploaded here's the ruby script.

pwolanin’s picture

can you post the patch as a unified diff?

xhochy’s picture

The "jquery_15.patch" should be an unified patch, or do you want the patch for prototype?

pwolanin’s picture

Perhaps I wasn't clear- "jquery_15.patch" does not seem to have been made in the unified diff format.

Please run diff as "diff -up" when making a patch: http://drupal.org/patch/create

xhochy’s picture

FileSize
44.95 KB

ok, I made a new after the Drupal instructions, here is it.

psychopath_mind’s picture

Status: Active » Postponed (maintainer needs more info)

And before it...

If i need to upgrade my Drupal 5.1 for a newer version... I will lose this replacements right? has someone know about what this will be solved on the future by the development core team? or a link about what the develompents pretend to solve this on future version?

On her last path, what is the version of JQuery?

Can you take me the source of replacement script?

Thanks!

chx’s picture

Project: jQuery libraries » Drupal core
Version: » 6.x-dev
Component: Code » javascript
Status: Postponed (maintainer needs more info) » Needs work
AmrMostafa’s picture

Title: override $() in Drupal's invocation of jQuery in order to allow compatibility with prototype » Javascript: Allow compatibility with other libraries.
Status: Needs work » Needs review
FileSize
102.14 KB

Patch updated for HEAD.

Steven’s picture

Rather than bloating the code and hampering readability by peppering the code with 'jQuery', a closure could be used to alias jQuery to $ locally instead to keep the best of both worlds.

infojunkie’s picture

@Steven: I'm trying to use a script made with scriptaculous and prototype on Drupal 5.3, but I'm a JavaScript n00b. Can you please explain further how the closure approach would be implemented? TIA.

catch’s picture

Status: Needs review » Needs work

Marking as needs work per #15.

kratib: http://docs.jquery.com/Using_jQuery_with_Other_Libraries

infojunkie’s picture

@catch: Thanks. I read that page, but IINM that still means every occurrence of $(*) in Drupal core (and modules) must be replaced with either a closure or a new jQuery variable. Which is what the original thread is all about, right? Does the closure approach save some search-and-replace?

Steven’s picture

You just wrap the entire .js file in:

(function ($) {
...
})(jQuery);

as has been proposed elsewhere.

infojunkie’s picture

That seems to work perfectly, with much less editing indeed. Note that in misc/drupal.js, one has to insert the

(function ($) {

after the line

var Drupal = Drupal || {};

Thanks for a great tip!

kourge’s picture

FileSize
13.9 KB

I wrapped the scoping anonymous function around all JS files in core and rolled this patch. All JS files are wrapped except for jQuery itself (obvious) and jQuery form (it already does the wrapping). This approach should be much cleaner (and saner) than replacing every instance of $ with jQuery. I've roughly tested this; the installer, drag-and-drop functionality, and sticky table headers are all working correctly.

kourge’s picture

Status: Needs work » Needs review
dvessel’s picture

Status: Needs review » Needs work

A lot's happened since the patch. Needs a re-roll.

dvessel’s picture

Status: Needs work » Needs review
FileSize
13.3 KB

Not sure this is the exact same. All js files wrapped except jquery.js & jquery.form.js.

dvessel’s picture

FileSize
13.28 KB

removed space in function ($).

dvessel’s picture

Time is running short.. Wait for Drupal 7?

kourge’s picture

It's not like this patch (gasp!) adds new strings, changes the database schema, ports Drupal to Java, or anything big like that. Why are patches like these always so heartbreaking? :(

quicksketch’s picture

After applying #25, I added prototype.js (1.6) to drupal_add_js() so it gets loaded along with jquery.js. The result unfortunately was not optimal. Prototype immediately throws an error when the page loads and drag and drop throws an error on every drop. This patch missed all the places where we have inline javascript added to the page (in book, poll and user) which will no longer be able to use the $ function without wrapping in a special clause. (See http://docs.jquery.com/Using_jQuery_with_Other_Libraries if you haven't).

I updated the patch to fix book, poll, and user, but generally I don't think this can get into Drupal 6. For any module that has already been ported to Drupal 6, it will need to be wrapped in (function($) { }); to make it compatible with another library. That previously wasn't a requirement since other libraries weren't even pretending to be supported. It's something that would definitely be added to the Upgrading 5.x modules to 6.x page and an API change.

quicksketch’s picture

Status: Needs review » Needs work
webchick’s picture

Version: 6.x-dev » 7.x-dev

Sounds like this represents an API change, which makes it a clear 7.x feature. Sorry, kourge/dvessel. :(

dvessel’s picture

Doh, forgot about the inline scripts. So 7 it is. No worries from me webchick, thought it was going to be a small enough change.

kourge’s picture

So does that mean the handbook page on converting 5.x modules to 6.x needs to be updated so that module authors wrap their JavaScript in a scoping function?
Also, I sort of doubt that inline functions need to be wrapped again. I think it may have something to do with the for...in loop construct; I'll investigate more on this.

starbow’s picture

subscribing

webchick’s picture

kourge: No, there's no need to update that document because this patch is an API change and thus won't be applied until 7.x. But it /will/ need to be documented in the updating 6.x to 7.x handbook page. :)

Matt V.’s picture

subscribing

mfer’s picture

subscribing.

catch’s picture

kkaefer’s picture

Status: Needs work » Needs review
FileSize
73.65 KB

Rerolled the patch. We definitely need this in D7. I added a call to jQuery.noConflict which removes jQuery from the $ variable. That means that all contrib module JS also has to wrap its code into (function($) { ... })(jQuery); to continue functioning.

kourge’s picture

Good move. This'll be a good enough motivation for maintainers. Breakage is the last thing maintainers want to happen on their modules.

Wim Leers’s picture

Subscribing.

Wim Leers’s picture

Hierarchical Select is now doing this: http://drupal.org/node/231576#comment-828004.

It's a super easy change, so I think there's no valid reason at all to prevent this patch from going in.

quicksketch’s picture

I agree, after John Resig chided us a little bit (rightly so) about Drupal's lack of wrapping the Drupal core code in these compatibility wrappers I did feel a little poorly about shooting this down for D6. Anyway, this definitely does need to happen with Drupal 7.

@kkaefer: Is there a good reason for adding this to jquery.js instead of drupal.js?

+// Allow other JavaScript libraries to use $.
+jQuery.noConflict();

I agree with the decision to essentially "force" contrib modules to use the wrappers also.

roman25’s picture

not sure if this is rellevant to this post, but there is a problem with for and while loops in javascript in the latest version... very easily reproducable,

kkaefer’s picture

It's completely unrelated. If you think you found a valid bug, please post an issue at http://drupal.org/node/add/project_issue/drupal.

lilou’s picture

Status: Needs review » Needs work

Patch no longer applied :

--- Successfully Patched ---
drupal\7.x\misc\ahah.js
drupal\7.x\misc\autocomplete.js
drupal\7.x\misc\batch.js
drupal\7.x\misc\collapse.js
drupal\7.x\misc\drupal.js
drupal\7.x\misc\form.js
drupal\7.x\misc\progress.js
drupal\7.x\misc\tableheader.js
drupal\7.x\misc\tableselect.js
drupal\7.x\misc\teaser.js
drupal\7.x\misc\textarea.js
drupal\7.x\modules\book\book.module
drupal\7.x\modules\openid\openid.js
drupal\7.x\modules\profile\profile.js
drupal\7.x\modules\system\system.js
drupal\7.x\modules\taxonomy\taxonomy.js
drupal\7.x\modules\user\user.js
drupal\7.x\modules\user\user.module
--- Failed ---
drupal\7.x\misc\jquery.js (Cannot apply hunk @@ 4 )
drupal\7.x\misc\tabledrag.js (Cannot apply hunk @@ 5 )
drupal\7.x\misc\farbtastic\farbtastic.js (Cannot apply hunk @@ 6 )
drupal\7.x\modules\block\block.js (Cannot apply hunk @@ 5 )
drupal\7.x\modules\color\color.js (Cannot apply hunk @@ 5 )
drupal\7.x\modules\comment\comment.js (Cannot apply hunk @@ 5 )
drupal\7.x\modules\poll\poll.module (Cannot apply hunk @@ 7 
mfer’s picture

At the very least this is because there is a new version of jQuery present in core.

Should Drupal.behaviors be wrapped in ;(function($) { ... })(jQuery);???

kkaefer’s picture

FileSize
17.83 KB

Updated patch; also adds some semicolons where they were missing.

kkaefer’s picture

Status: Needs work » Needs review
mfer’s picture

I'm still wondering if the Drupal.behaviors should be wrapped with ;(function($) { ... })(jQuery);

If we follow the jQuery way of wrapping it looks like Drupal.behaviors would be wrapped with ;(function() { ... })();

Can someone a little more familiar with jQuery clear this up for me?

kkaefer’s picture

Why shouldn't they be wrapped in that new scope? Behaviors can use $ as well.

mfer’s picture

After I posted that I started thinking the same thing. I'll test this later this week (most likely Friday)

webchick’s picture

Just a note to say that I'm hugely in support of this patch. We allow pluggable caching systems, pluggable image handling libraries, and even pluggable *password hashing algorithms*. It's a little silly that we don't allow pluggable JS libraries as well.

Susurrus’s picture

In regards to pluggable libraries #251578: More flexible js/css ordering and an alter operation is another step in that direction having a JS_LIBRARY_WEIGHT.

mfer’s picture

Status: Needs review » Needs work

The patch no longer applies. Specifically simpletest.js. Can someone re-roll the patch and I'll test it.

dvessel’s picture

Status: Needs work » Needs review
FileSize
18.35 KB

re-rolled

mfer’s picture

Applies cleanly. Tested in FF3 and Safari on mac and found no issues. I'll keep testing more later.

mfer’s picture

Status: Needs review » Reviewed & tested by the community

Tested in FF3, Opera 9.5b, IE6, IE7, and Chrome on PC. Did a brief review of the patch and it looks good. Marking RTBC.

On a side note: Opera is still having trouble with file uploads via the upload module both before and after the patch. This is an ongoing issue.

webchick’s picture

Awesome. Thanks for your hard work, folks!

This is a big enough change that I feel like I should get Dries's two cents before committing. So in the meantime, if someone wants to write up a blurb that can be copy/pasted into the upgrade docs, that would be lovely. ;)

chx’s picture

We define an anonymous function with a single parameter. This parameter is $. So it's now function ($) { original JS code}. Now, we call this function. If it would be assigned to a variable then we would do whatever(jquery); so that inside the function, $ = jquery. But with anonymous functions, we do not have and do not need a name, so we can just write

(function ($) { 
  original JS code 
})(jquery);

.

mfer’s picture

Any status on this issue? Anything we can do to help it along?

RobLoach’s picture

Oh, this is cool!

webchick’s picture

Status: Reviewed & tested by the community » Needs review

Actually, could we have someone re-run the same tests as quicksketch did back in #28 and make sure a mootools/prototype script gets added to a page correctly? Seems like that hasn't been done since January, and this patch has changed a few times since then.

webchick’s picture

Oh, and karma points to anyone who does this and posts back with some source code and clear instructions on how to reproduce that it's working for non-JS folks (like me :D).

mfer’s picture

Updated to follow a moving head.

mfer’s picture

Status: Needs review » Needs work
FileSize
30.47 KB

The attached file is a zip file (remove the .txt extension). It contains a basic module that adds prototype.js to every page.

To test with this enabled the console in firebug for a test site, enable this module, go to a page (like /admin/build/block), and test the JavaScript based functionality. If there is an issue firebug will start through errors. This is a basic way to see if anything is wrong.

Sadly, on /admin/build/block I got errors. This was the first page I tested. This still needs some work.

mfer’s picture

I found part of the issue, I think. According to the jQuery documentation on using noConflict jQuery.noCOnflict() needs to be called after jQuery and the other library have been loaded. If you do a drupal_add_js('/path/to/prototype.js') call jquery.js, drupal.js (containing the noConflict call), and then prototype.js will be loaded. The issue may have to do with ordering.

But, I tried manually reordering the including of the files and still found problems. Another problem is autocomplete when nothing is returned.

mfer’s picture

Now that #315798: JavaScript Patch #2: Weight is in we should have an easier time adding another library and positioning it where we need to make this work. I'm going to revisit this issue after #315801: JavaScript Patch #3: JS Alter Operation gets in.

To quote the jQuery.noConflict() documentation:

This function must be called after including the jQuery javascript file, but before including any other conflicting library, and also before actually that other conflicting library gets used, in case jQuery is included last.

When we go to add another Library it can be added with a weight of JS_LIBRARY since jQuery is added with a weight of JS_LIBRARY - 2 and drupal.js, which contains a jQuery.noConflict call (in the patches here) has a weight of JS_LIBRARY - 1.

webchick’s picture

Awesome. Looking greatly forward to seeing this fixed.

webchick’s picture

hook_js_alter() is in now. Still looking forward to seeing this fixed. ;)

Xano’s picture

I like chx's explanation in #59, but I still don't understand the technical part behind this change. I have a fair knowledge of JS (good enough for regular stuff), but there will be lots of people with less knowledge who will understand even less of this than I do. I am hoping somebody can shed some more light on this change at "Converting 6.x modules to 7.x".

Wim Leers’s picture

@Xano: It's really *that* simple. It's exactly as chx demonstrated. Just "wrap" your code that way and it'll be able to co-exist with any JS library, while you can continue to use the shorthand $ notation. I've been doing the same in Hierarchical Select for quite some time for example: hierarchical_select.js. It's just the old code, with one line added at the top of the file and one at the end of the file. That simple :)

Xano’s picture

It's not that I don't know how to use it, it's that I don't get how it works :)

kkaefer’s picture

@Xano: It wraps the code in a function. That function has a parameter called $, so in that function, $ is what the caller of that function passed into that function. That function is then called right away with jQuery as the first parameter, which subsequently is mapped to $, just inside that function.

kkaefer’s picture

FileSize
17.62 KB

Recreated patch (there were too many conflicts due to attach/detach for behaviors). I also added a function to $ that indicates when people use $ without wrapping their code.

Patch also adds semicolons where they are missing.

kkaefer’s picture

Status: Needs work » Needs review
kkaefer’s picture

When testing, please make sure that you clear your browser cache to prevent old JS files from interfering. If you don't, you might get alert messages telling you to wrap your code.

webchick’s picture

#62/#63 still applies for testing this patch.

Thanks for the re-roll, kkaefer! I'd really like to see this get in.

Xano’s picture

@Xano: It wraps the code in a function. That function has a parameter called $, so in that function, $ is what the caller of that function passed into that function. That function is then called right away with jQuery as the first parameter, which subsequently is mapped to $, just inside that function.

Thanks a lot :)

quicksketch’s picture

FileSize
22.43 KB

This looks absolutely great. I found one more call to $() in an inline function in install.php. I tested (not that it should make a difference):

- Upload and Book (ahah.js and progress.js)
- Install (timezone.js and batch.js)
- Node form (collapse.js, teaser.js, textarea.js)
- Powered by Drupal block (drupal.js)
- Menu page (tabledrag.js and tableheader.js)

As long as I didn't screw up my one change in install.php, this looks RTBC to me.

sun’s picture

Issue tags: +JavaScript
chx’s picture

Status: Needs review » Reviewed & tested by the community

Let's go with this.

webchick’s picture

So someone tried to add Prototype to their Drupal 7 site? Can someone please explain how I can do this so I can verify that it's working?

Xano’s picture

You simply include the prototype JS file and use it just like you would do in any other situation :)

Matt V.’s picture

Here's what I did to test the patch...

I downloaded prototype-1.6.0.2.js and added it to the Garland theme, by adding the following line to garland.info:

scripts[] = prototype-1.6.0.2.js

I cleared the cache and confirmed that prototype-1.6.0.2.js was loading by viewing the source of a page.

I noticed two trouble spots. When I went to node/add/page, the collapsible fieldsets were no longer clickable. Also, on admin/build/block, the draggable cross was gone and the weight fields showed.

I then applied the patch, which applied cleanly. To confirm the fix, I went back to node/add/page; the fieldsets were clickable again. When I went to admin/build/block, the blocks were draggable and the weight fields were hidden again.

I'd be glad to check other issues, if anyone can point me to other trouble spots.

kkaefer’s picture

@webchick: When I tested the patch, I downloaded the module from #65 and visited all pages I could think of that use JS. All of them worked fine (i.e. they didn't show the alert box, so it's quite obvious when something goes wrong).

webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs documentation

Excellent. Thanks, everyone! I'm very happy to announce that this is now fixed! :)

Please update the docs!

Xano’s picture

Status: Needs work » Fixed
Issue tags: -Needs documentation

Done.

Great job everyone!

quicksketch’s picture

Fantastic! Great job guys!

sun’s picture

Version: 7.x-dev » 6.9
Category: feature » bug
Status: Fixed » Needs review

This patch actually contained some fixes that made me worry about the state of Drupal 6.

Attached patch ports some coding-style niceties and fixes of this patch back to Drupal 6.

Especially (but not only) the last hunk for Drupal.behaviors.copyFieldValue() makes me wonder whether the existing code in D6 shouldn't lead to a syntax error.

sun’s picture

2nd try. Displayed in preview.

Pasqualle’s picture

@sun as I see it is not a backport patch, and not related to the original problem. Then why not create a new issue?

webchick’s picture

Version: 6.9 » 7.x-dev
Status: Needs review » Fixed

I think this was meant to be a reply to a different issue... I remember committing a similar patch a few months back, but I can't seem to find which issue it was. At any rate, restoring original properties.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

doompal’s picture

I know it's a long time ago, but was there a rationale for breaking jQuery for the sake of Prototype? What does Prototype do to justify this?

Maedi’s picture

I would also like to voice my concern over this change.

Drupal should work out of the box when it comes to writing in jQuery (or another library). Most tutorials are written with the '$' function in mind. Currently any mainstream code snippets wont work instantly with Drupal 7.

The accepted wrapper function and 'jQuery()' solutions lead to needlessly convoluted code. I think many beginner to mid-level Drupal developers will stumble upon the "$ is not a function" error caused by this change, and be genuinely confused. The majority of Drupal developers use jQuery and this majority is being given an avoidable error.

If there's someone else out there who feels the same way, then let me know and I'll start a separate issue.

sun’s picture

Please open a new issue for discussing that and link to it in here.

In case you didn't notice, this change was committed 3 years ago.

Thanks.

Maedi’s picture

Thanks sun. I've created a new issue here:
#1407256: Wrap inline JS in a self-executing closure

Yeah I'd noticed that this is an old issue though still something I'd like to see changed.

justindodge’s picture

Hey guys, if you still want to use the $ in D7, I've created a contributed module that allows you to do so.

It can be found here:
http://drupal.org/project/jquery_dollar

I know this particular issue is old and dead, but since the "Converting 6.x modules to 7.x" documentation points to this issue, I thought it would be worth mentioning here.