prototype: Fix $(form).serialize() in Safari and add support for extending specific tags to Element.addMethods. Closes #7358.

This commit is contained in:
Sam Stephenson 2007-01-27 19:45:34 +00:00
parent 5345085b33
commit 7044da8138
6 changed files with 158 additions and 40 deletions

View File

@ -1,5 +1,7 @@
*SVN*
* Fix $(form).serialize() in Safari and add support for extending specific tags to Element.addMethods. Closes #7358. [Andrew Dupont]
* Add String.prototype.startsWith, String.prototype.endsWith, and String.prototype.include. Closes #7075. [Tobie Langel]
* Improve performance of String.prototype.escapeHTML by using a cached div and text node. Closes #6937. [altblue]

View File

@ -41,26 +41,31 @@ document.getElementsByClassName = function(className, parentElement) {
if (!window.Element)
var Element = new Object();
Element.extend = function(element) {
if (!element || _nativeExtensions || element.nodeType == 3) return element;
if (!element._extended && element.tagName && element != window) {
var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
if (element.tagName == 'FORM')
Object.extend(methods, Form.Methods);
if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
Object.extend(methods, Form.Element.Methods);
Object.extend(methods, Element.Methods.Simulated);
for (var property in methods) {
var value = methods[property];
if (typeof value == 'function' && !(property in element))
element[property] = cache.findOrStore(value);
}
var F = Prototype.BrowserFeatures;
if (!element || !element.tagName || element.nodeType == 3 ||
element._extended || F.SpecificElementExtensions || element == window)
return element;
var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
T = Element.Methods.ByTag;
// extend methods for all tags (Safari doesn't need this)
if (!F.ElementExtensions) {
Object.extend(methods, Element.Methods),
Object.extend(methods, Element.Methods.Simulated);
}
// extend methods for specific tags
if (T[tagName]) Object.extend(methods, T[tagName]);
for (var property in methods) {
var value = methods[property];
if (typeof value == 'function' && !(property in element))
element[property] = cache.findOrStore(value);
}
element._extended = true;
return element;
};
@ -471,6 +476,8 @@ Element.Methods.Simulated = {
}
};
Element.Methods.ByTag = {};
// IE is missing .innerHTML support for TABLE-related elements
if (document.all && !window.opera){
Element.Methods.update = function(element, html) {
@ -510,19 +517,36 @@ if (document.all && !window.opera){
Object.extend(Element, Element.Methods);
var _nativeExtensions = false;
if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
var className = 'HTML' + tag + 'Element';
if(window[className]) return;
var klass = window[className] = {};
klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
});
if (!Prototype.BrowserFeatures.ElementExtensions &&
document.createElement('div').__proto__) {
window.HTMLElement = {};
window.HTMLElement.prototype = document.createElement('div').__proto__;
Prototype.BrowserFeatures.ElementExtensions = true;
}
Element.addMethods = function(methods) {
Object.extend(Element.Methods, methods || {});
var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
if (arguments.length == 2) {
var tagName = methods;
methods = arguments[1];
}
if (!tagName)
Object.extend(Element.Methods, methods || {});
else {
if (tagName.constructor == Array) {
tagName.each(extend);
}
else extend(tagName);
}
function extend(tagName) {
tagName = tagName.toUpperCase();
if (!Element.Methods.ByTag[tagName])
Element.Methods.ByTag[tagName] = {};
Object.extend(Element.Methods.ByTag[tagName], methods);
}
function copy(methods, destination, onlyIfAbsent) {
onlyIfAbsent = onlyIfAbsent || false;
var cache = Element.extend.cache;
@ -533,16 +557,44 @@ Element.addMethods = function(methods) {
}
}
if (typeof HTMLElement != 'undefined') {
function findDOMClass(tagName) {
var klass;
var trans = {
"OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
"FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
"DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
"H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
"INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
"TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
"TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
"TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
"FrameSet", "IFRAME": "IFrame"
};
if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
if (window[klass]) return window[klass];
klass = 'HTML' + tagName + 'Element';
if (window[klass]) return window[klass];
klass = 'HTML' + tagName.capitalize() + 'Element';
if (window[klass]) return window[klass];
window[klass] = {};
window[klass].prototype = document.createElement(tagName).__proto__;
return window[klass];
}
if (F.ElementExtensions) {
copy(Element.Methods, HTMLElement.prototype);
copy(Element.Methods.Simulated, HTMLElement.prototype, true);
copy(Form.Methods, HTMLFormElement.prototype);
[HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
copy(Form.Element.Methods, klass.prototype);
});
_nativeExtensions = true;
}
}
if (F.SpecificElementExtensions) {
for (var tag in Element.Methods.ByTag) {
var klass = findDOMClass(tag);
if (typeof klass == "undefined") continue;
copy(T[tag], klass.prototype);
}
}
};
var Toggle = new Object();
Toggle.display = Element.toggle;

View File

@ -156,6 +156,15 @@ Form.Element.Methods = {
}
Object.extend(Form.Element, Form.Element.Methods);
Object.extend(Element.Methods.ByTag, {
"FORM": Object.clone(Form.Methods),
"INPUT": Object.clone(Form.Element.Methods),
"SELECT": Object.clone(Form.Element.Methods),
"TEXTAREA": Object.clone(Form.Element.Methods)
});
/*--------------------------------------------------------------------------*/
var Field = Form.Element;
var $F = Form.Element.getValue;
@ -304,4 +313,3 @@ Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
return Form.serialize(this.element);
}
});

6
src/prototype.js vendored
View File

@ -3,7 +3,11 @@
var Prototype = {
Version: '<%= PROTOTYPE_VERSION %>',
BrowserFeatures: {
XPath: !!document.evaluate
XPath: !!document.evaluate,
ElementExtensions: !!window.HTMLElement,
SpecificElementExtensions:
(document.createElement('div').__proto__ !==
document.createElement('form').__proto__)
},
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

View File

@ -256,6 +256,18 @@
var testVar = 'to be updated';
Element.addMethods("LI", {
pancakes: function(element) { return "pancakes"; }
});
Element.addMethods("DIV", {
waffles: function(element) { return "waffles"; }
});
Element.addMethods($w("li div"), {
orangeJuice: function(element) { return "orange juice"; }
});
new Test.Unit.Runner({
testDollarFunction: function() { with(this) {
@ -271,6 +283,7 @@
var elt = $('testdiv');
assertIdentical(elt, $(elt));
assertRespondsTo('hide', elt);
assertRespondsTo('childOf', elt);
}},
testGetElementsByClassName: function() {with(this) {
@ -282,7 +295,7 @@
testInsertWithTR: function() {with(this) {
new Insertion.After('second_row', '<tr id="third_row"><td>Third Row</td></tr>');
assert($('second_row').childOf('table'));
assert($('second_row').descendantOf('table'));
}},
testElementVisible: function(){with(this) {
@ -801,8 +814,27 @@
elem.scrollTo();
assertEqual(Position.page(elem)[1], 0);
window.scrollTo(0, 0);
}}
}},
testSpecificElementMethods: function() {with(this) {
var elem = $('navigation_test_f');
assert(Element.Methods.ByTag[elem.tagName]);
assertRespondsTo('pancakes', elem);
assertEqual("pancakes", elem.pancakes());
var elem2 = $('test-visible');
assert(Element.Methods.ByTag[elem2.tagName]);
assertUndefined(elem2.pancakes);
assertRespondsTo('waffles', elem2);
assertEqual("waffles", elem2.waffles());
assertRespondsTo('orangeJuice', elem);
assertRespondsTo('orangeJuice', elem2);
assertEqual("orange juice", elem.orangeJuice());
assertEqual("orange juice", elem2.orangeJuice());
}}
}, 'testlog');
// ]]>

View File

@ -280,7 +280,27 @@
testFormSerializeWorksWithNonFormElements: function() {with(this) {
assertEqual('nvm%5B%5D=One&nvm%5B%5D=Three&evu=&evm%5B%5D=&evm%5B%5D=Three', Form.serialize('form_fieldset'));
assertEqual('vu=1&vm%5B%5D=1&vm%5B%5D=3&nvu=One&nvm%5B%5D=One&nvm%5B%5D=Three&evu=&evm%5B%5D=&evm%5B%5D=Three', Form.serialize('form_wrapper'));
}}
}},
testFormMethodsOnExtendedElements: function() {with(this) {
assertEqual(Form.serialize('form'), $('form').serialize());
assertEqual(Form.Element.serialize('input_enabled'), $('input_enabled').serialize());
assertNotEqual($('form').serialize, $('input_enabled').serialize);
Element.addMethods('INPUT', { anInputMethod: function(input) { return 'input' } });
Element.addMethods('SELECT', { aSelectMethod: function(select) { return 'select' } });
document.getElementById('tf_text')._extended = false;
document.getElementById('tf_selectOne')._extended = false;
assert($('tf_text').anInputMethod);
assert(!$('tf_text').aSelectMethod);
assertEqual('input', $('tf_text').anInputMethod());
assert($('tf_selectOne').aSelectMethod);
assert(!$('tf_selectOne').anInputMethod);
assertEqual('select', $('tf_selectOne').aSelectMethod());
}}
}, 'testlog');
// ]]>