...Unfortunately, we found a conflict where both Prototype.js and
javascript from one of our third-parties have a global variable
conflict. Both are using the same global variable: Hash...
On the Caja front: supporting Prototype in Caja turned out to be
harder than we expected - but we have a plan, and this time we really
do expect to have it working in the near future :-)
Cheers,
Ben (Caja tech lead).
That's pretty clever, Ken. The only potential drawback I can think of
is that it depends upon Prototype being loaded last, but that's hardly
a big deal.
:Dan Dorman
If Prototype is loaded first, (in the example of the Hash situation) you
just have to make sure you're own code references the Hash you want to
use--Prototype.Hash or that 3rd-party Hash.
The effort to convert all of Prototype's internal references (e.g. find
and replace "Selector" with "Prototype.Selector", "Template" with
"Prototype.Template", etc.) would be a matter of minutes, would add
maybe two or three kilobytes, and could be quickly certified through
Prototype's own unit tests.
I think the true request on this subject is that the Prototype Group
make an official release of /some/ version of Prototype that resolves
namespace conflicts. If it is the Caja branch that Tobie mentioned, that
would be awesome. It would also be a powerful bonus if a namespaced
Prototype release was 100% backwards compatible.
- Ken Snyder
In theory, Prototype could namespace all objects /and prototypes/ and
use a noConflict() or expose() concept to provide backwards
compatibility. My only concern is the increased file size and parse time
of prototype.js.
Along those lines, what is the advantage to using expose() instead of
noConflict()? Maybe expose() is faster in the backwards-compatible case?
- Ken
The tricky part is "taking care of" native objects : )
Array.prototype, String.prototype, etc.
Two obvious ways to solve this issue seem to be:
// 1) wrappers
(function(){
eval(Prototype.imports);
$A([1,2,3,4,5]).invoke('toString');
$S('someString').strip();
})();
// 2) namespaced methods
Prototype.lang.invoke([1,2,3], 'toString');
Prototype.lang.strip('someString');
I think the latter one is uglier. The former one is just not as
convenient as it is now.
Thoughts?
--
kangax
From an academic standpoint, solving hard problems is fun. But from a
practical, makes-a-real-difference standpoint, I simply fail to see
collision avoidance as a major issue.
I'm not sure what you mean by a "wider appeal".
I think we need to deprecate element extension for the reason of
creating a more robust code base.
I completely agree with your comment. When I said "wider" appeal, I
meant appeal to multi-library use and collision avoidance.
@kangax, the issues with Element#wrap, Element#replace, and
Element#select seem to be based on a short-sighted naming decision. For
example, if we used names like Element#wrapNode, Element#replaceNode,
Element#selectNodes there would be no problem.
Your point about iframes, non-standard DOM elements, and new elements is
well taken. Needing to manipulate those types of elements is arguably an
edge-case, but I agree that it demonstrates how fragile it is to extend
Element.prototype.
But what are the arguments for avoiding modifying prototypes on
Function, Array, and String? Is there more to it than trying to avoid
naming collisions?
- Ken
It's not much of an issue if all the other libraries provide
"noconflict" options, but prototype should really play nice too and
provide it's own version otherwise it makes prototype look like the
bully in the playground. :)
@Jeff, when you say "this" which issue do you mean?
But what are the arguments for avoiding modifying prototypes on
Function, Array, and String? Is there more to it than trying to avoid
naming collisions?
In a Rails app of mine, I needed to use a an effect that was supplied
by jquery.
It's not much of an issue if all the other libraries provide
"noconflict" options, but prototype should really play nice too and
provide it's own version otherwise it makes prototype look like the
bully in the playground. :)
I hardly increased my download and page rendering time. I was able to
swap out scripty and use a subset of jquery in it's place. Pretty
much the same amount (if not less) to download. For not seeing my
final solution, you seem to know a lot about it. :)
I always try to contribute back to the "ecosystem" when possible, but
jquery's noconflict option made things easier for me in the end. I've
only integrated two JS libs the once, so it's not a common case, but
it's a case for me regardless.
1. prototype.js can be made backwards compatible with a few loops
2. Current code can be ported to the "2.0" version with a
find-and-replace based approach
I'm sure there are other approaches besides the Decorator-pattern and
the dollar-prefix approach. And I agree with Jeff: I think the very
first and most important steps are for the Prototype team to define the
goals for moving forward and choose an approach to meet those goals.
Whether defining the goals and approaches is collaborative isn't as
important to me--but here are my top goals:
- Robust and future proof. Avoid some of the "fragile" practices we
discussed on this thread. Create hooks in all parts of the API to allow
additional extensions (e.g. Element.addMethods, Class.addMethods and I
am dying to see Event.addMethods).
- File size / Modularity. Simplifying and giving options for modularity
by reducing the interdependence between modules. Many times I've wished
I could use the Element and Selector code without including the rest of
the library.
- Backwards compatibility / easy porting. If Prototype fundamentally
changes, it will basically be a fork because so many apps rely on
Prototype 1.6 that it will probably never go away. It is certainly nice
to be able to use new features right away but have time to update code
that uses deprecated features.
Once we define the goals and approach, building a roadmap with specific
features is intuitive.
- Ken
what's the reason for `eval`?Mislav,
pastie.org seems to be having some trouble right now, so here is an
alternate version: http://kendsnyder.com/sandbox/prototype/namespace1.php
Example usage:
|// static call within Prototype namespace
Prototype.$S.strip(' test1 '); // "test1"
// instance call within Prototype namespace
Prototype.$S(' test2 ').strip(); // "test2"
// dump Prototype objects to global namespace
Prototype.expose();
// static call in global namespace
$S.strip(' test3 '); // "test3"
// instance call within Prototype namespace
$S(' test4 ').strip(); // "test4"
// copy instance methods to String.prototype using "$" prefix
Prototype.addPrototypes('$');
' test5 '.$strip(); // "test5"
// copy instance methods to String.prototype using no prefix
Prototype.addPrototypes('');
' test6 '.strip(); // "test6"|
kangax wrote:
> ...
>> Hmm. If we are going to fundamentally change the library (e.g. require
>> $S() or $A() to access Prototype string methods) why not just use a "$"
>> prefix instead? For example, Array#$each, String#$strip,
>> Function#$bind. I think such an approach was suggested many months ago,
>> but I don't recall. Some advantages of a dollar-prefix approach:
>>
>> 1. prototype.js can be made backwards compatible with a few loops
>> 2. Current code can be ported to the "2.0" version with a
>> find-and-replace based approach
>>
> Ken,
> I'm not sure I understand how we get the above mentioned benefits by
> changing String#foo to String#$foo.
> Could you explain?
>
I mean getting those benefits doing something like the following:
1. Backwards compatibility
// define dollar prefix functions
var ArrayMethods = {
foo: function() {
doSomething(this);
}
};
// copy methods to Array.prototype with a dollar prefix
for (var method in ArrayMethods) {
Array.prototype['$' + method] = ArrayMethods[method];
};
// copy methods to Array.prototype without the prefix
// (intended for compatibility with "deprecated" API)
for (var method in ArrayMethods {
Array.prototype[method] = ArrayMethods[method];
};
2. Find-and-replace code--To convert deprecated code, use a text editor
to replace ".foo(" with ".$foo(" in existing applications.
The dollar-prefix doesn't solve all the problems with prototyping, but
it avoids naming conflicts with other code.
Anyhow, kangax, thanks for clarifying. What you say makes a lot of sense.
- Ken Snyder
pastie.org seems to be having some trouble right now, so here is an
alternate version: http://kendsnyder.com/sandbox/prototype/namespace1.php
It was intended to illustrate that it is /possible/ to define all the
prototype internals as static methods, use namespacing and expose
static, instance and prototype versions as desired. It would avoid
global naming conflicts if needed, avoid prototype naming conflicts if
needed, and allow optional backwards compatibility.
It is not a solution--just a brainstorm that might give others some ideas.
@Mislav Your suggestions about dropping the "$" prefix, using
"Prototype.String" instead of "Prototype.$String", and self-serve
shortcuts are great.
@JDD Here is a copy of the code on gist: http://gist.github.com/5141
- Ken