+ `);
+
+ assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
+ });
+
it('should return true when textless text content is outside the region', function () {
var checkArgs = checkSetup(
'
Introduction
'
From 7c810e2462f41a7181218c108a5d5f8c95e122ba Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Tue, 16 Apr 2024 13:15:47 -0500
Subject: [PATCH 014/400] fix(region): replaces tabs for spaces in prior commit
my IDE was adding tabs ew, replaced with spaces
Refs: #4145
---
test/checks/navigation/region.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/checks/navigation/region.js b/test/checks/navigation/region.js
index ca19f35da..9e6f1acd5 100644
--- a/test/checks/navigation/region.js
+++ b/test/checks/navigation/region.js
@@ -49,7 +49,7 @@ describe('region', function () {
it('should return false when img content is outside the region', function () {
var checkArgs = checkSetup(`
-
Content
+
Content
`);
assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
@@ -58,7 +58,7 @@ describe('region', function () {
it('should return true when img content outside of the region is decorative, via an empty alt attr', function () {
var checkArgs = checkSetup(`
-
Content
+
Content
`);
assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
@@ -67,7 +67,7 @@ describe('region', function () {
it('should return true when img content outside of the region is decorative, via a presentation role', function () {
var checkArgs = checkSetup(`
Content
+
Content
`);
assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));
From 551ba24efa4108c18fe1a81ae9107d694fa9a5aa Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Fri, 19 Apr 2024 09:26:13 -0500
Subject: [PATCH 015/400] fix(aria-valid-attr-value): aria-controls &
aria-haspopup incomplete
if an element has both aria-controls and aria-haspopup mark it incomplete as we are unsure if the DOM element will be added dynamically later
Refs: #4363
---
lib/checks/aria/aria-valid-attr-value-evaluate.js | 15 +++++++++++++--
lib/checks/aria/aria-valid-attr-value.json | 3 ++-
locales/_template.json | 3 ++-
test/checks/aria/valid-attr-value.js | 9 +++++++++
4 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/lib/checks/aria/aria-valid-attr-value-evaluate.js b/lib/checks/aria/aria-valid-attr-value-evaluate.js
index 36e824afc..79ea53fef 100644
--- a/lib/checks/aria/aria-valid-attr-value-evaluate.js
+++ b/lib/checks/aria/aria-valid-attr-value-evaluate.js
@@ -36,12 +36,23 @@ export default function ariaValidAttrValueEvaluate(node, options, virtualNode) {
const preChecks = {
// aria-controls should only check if element exists if the element
- // doesn't have aria-expanded=false or aria-selected=false (tabs)
+ // doesn't have aria-expanded=false, aria-selected=false (tabs),
+ // or aria-haspopup (may load later)
// @see https://github.com/dequelabs/axe-core/issues/1463
+ // @see https://github.com/dequelabs/axe-core/issues/4363
'aria-controls': () => {
+ const hasPopup =
+ ['false', null].includes(virtualNode.attr('aria-haspopup')) === false;
+
+ if (hasPopup) {
+ needsReview = `aria-controls="${virtualNode.attr('aria-controls')}"`;
+ messageKey = 'controlsWithinPopup';
+ }
+
return (
virtualNode.attr('aria-expanded') !== 'false' &&
- virtualNode.attr('aria-selected') !== 'false'
+ virtualNode.attr('aria-selected') !== 'false' &&
+ hasPopup === false
);
},
// aria-current should mark as needs review if any value is used that is
diff --git a/lib/checks/aria/aria-valid-attr-value.json b/lib/checks/aria/aria-valid-attr-value.json
index d533ad667..df7f2be6d 100644
--- a/lib/checks/aria/aria-valid-attr-value.json
+++ b/lib/checks/aria/aria-valid-attr-value.json
@@ -15,7 +15,8 @@
"noIdShadow": "ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}",
"ariaCurrent": "ARIA attribute value is invalid and will be treated as \"aria-current=true\": ${data.needsReview}",
"idrefs": "Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}",
- "empty": "ARIA attribute value is ignored while empty: ${data.needsReview}"
+ "empty": "ARIA attribute value is ignored while empty: ${data.needsReview}",
+ "controlsWithinPopup": "Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}"
}
}
}
diff --git a/locales/_template.json b/locales/_template.json
index a911c6014..0029d133a 100644
--- a/locales/_template.json
+++ b/locales/_template.json
@@ -538,7 +538,8 @@
"noIdShadow": "ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}",
"ariaCurrent": "ARIA attribute value is invalid and will be treated as \"aria-current=true\": ${data.needsReview}",
"idrefs": "Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}",
- "empty": "ARIA attribute value is ignored while empty: ${data.needsReview}"
+ "empty": "ARIA attribute value is ignored while empty: ${data.needsReview}",
+ "controlsWithinPopup": "Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}"
}
},
"aria-valid-attr": {
diff --git a/test/checks/aria/valid-attr-value.js b/test/checks/aria/valid-attr-value.js
index a9e3a3afc..e2605ecd8 100644
--- a/test/checks/aria/valid-attr-value.js
+++ b/test/checks/aria/valid-attr-value.js
@@ -110,6 +110,15 @@ describe('aria-valid-attr-value', function () {
assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));
});
+ it('should return undefined on aria-controls with aria-haspopup as we cannot determine if it is in the DOM later', function () {
+ var vNode = queryFixture(
+ 'Button'
+ );
+ assert.isUndefined(
+ validAttrValueCheck.call(checkContext, null, null, vNode)
+ );
+ });
+
it('should pass on aria-owns and aria-expanded=false when the element is not in the DOM', function () {
var vNode = queryFixture(
'Button'
From 08ddcbc16795ca99efdb6e2c8d24b4561bd65af8 Mon Sep 17 00:00:00 2001
From: EvilKarter
Date: Mon, 22 Apr 2024 17:55:24 +0200
Subject: [PATCH 016/400] chore: Update local de.json (#4408)
Update local de.json
---
locales/de.json | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/locales/de.json b/locales/de.json
index cab4da906..8f35d0c65 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -498,7 +498,8 @@
},
"aria-required-children": {
"pass": {
- "default": "Alle benötigten ARIA Kinder sind vorhanden."
+ "default": "Alle benötigten ARIA Kinder sind vorhanden.",
+ "aria-busy": "Element hat ein aria-busy-Attribut, daher ist es erlaubt, erforderliche ARIA Kinder wegzulassen"
},
"fail": {
"singular": "Benötigte ARIA Kindrolle nicht vorhanden: ${data.values}",
@@ -864,7 +865,10 @@
"fail": "Die viewport-Einstellungen im -Tag blockieren das Zoomen auf mobilen Geräten."
},
"target-offset": {
- "pass": "Das Ziel hat genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px, der mindestens ${data.minOffset}px beträgt.",
+ "pass": {
+ "default": "Das Ziel hat genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px, der mindestens ${data.minOffset}px beträgt.",
+ "large": "Das Ziel überschreitet bei weitem die Mindestgröße von ${data.minOffset}px."
+ },
"fail": "Das Ziel hat nicht genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px statt mindestens ${data.minOffset}px.",
"incomplete": {
"default": "Element mit negativem Tabindex hat nicht genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px statt mindestens ${data.minOffset}px. Ist dies ein Ziel?",
@@ -874,7 +878,8 @@
"target-size": {
"pass": {
"default": "Das Steuerelement hat eine ausreichende Größe (${data.width}px x ${data.height}px, sollte mindestens ${data.minSize}px x ${data.minSize}px sein).",
- "obscured": "Das Steuerelement wird ignoriert, da es vollständig verdeckt ist und daher nicht angeklickt werden kann."
+ "obscured": "Das Steuerelement wird ignoriert, da es vollständig verdeckt ist und daher nicht angeklickt werden kann.",
+ "large": "Das Ziel überschreitet bei weitem die Mindestgröße von ${data.minSize}px."
},
"fail": {
"default": "Das Ziel hat eine unzureichende Größe (${data.width}px x ${data.height}px, sollte mindestens ${data.minSize}px x ${data.minSize}px sein).",
@@ -884,7 +889,8 @@
"default": "Element mit negativem Tabindex hat unzureichende Größe (${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist dies ein Ziel?",
"contentOverflow": "Elementgröße konnte aufgrund von Überlaufinhalten nicht genau bestimmt werden",
"partiallyObscured": "Element mit negativem Tabindex hat unzureichende Größe, weil es teilweise verdeckt ist (kleinster Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist dies ein Ziel?",
- "partiallyObscuredNonTabbable": "Das Ziel hat eine unzureichende Größe, weil es teilweise von einem Nachbarn mit negativem Tabindex verdeckt wird (der kleinste Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist der Nachbar ein Ziel?"
+ "partiallyObscuredNonTabbable": "Das Ziel hat eine unzureichende Größe, weil es teilweise von einem Nachbarn mit negativem Tabindex verdeckt wird (der kleinste Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist der Nachbar ein Ziel?",
+ "tooManyRects": "Die Zielgröße konnte nicht ermittelt werden, da zu viele überlappende Elemente vorhanden sind."
}
},
"header-present": {
From 69f778b71b4c78f89334dcb0c75bedc2005609f5 Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Mon, 22 Apr 2024 11:11:12 -0500
Subject: [PATCH 017/400] fix(aria-roles): correct abstract roles (types) for
aria-roles
Too many aria roles were set to widget or otherwise were incorrect, added a comment source for where I got my information for which abstract roles were the type of which roles
Refs: #4371
---
lib/standards/aria-roles.js | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/lib/standards/aria-roles.js b/lib/standards/aria-roles.js
index 340fdb9b1..035990967 100644
--- a/lib/standards/aria-roles.js
+++ b/lib/standards/aria-roles.js
@@ -1,4 +1,5 @@
// Source: https://www.w3.org/TR/wai-aria-1.1/#roles
+// Source for abstract roles: https://www.w3.org/TR/wai-aria/#abstract_roles and https://www.w3.org/WAI/ARIA/1.2/class-diagram/rdf_model.svg
/* easiest way to see allowed roles is to filter out the global ones
from the list of inherited states and properties. The dpub spec
@@ -17,13 +18,13 @@
*/
const ariaRoles = {
alert: {
- type: 'widget',
+ type: 'structure',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
},
alertdialog: {
- type: 'widget',
+ type: 'window',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded', 'aria-modal'],
superclassRole: ['alert', 'dialog'],
@@ -31,7 +32,7 @@ const ariaRoles = {
},
application: {
// Note: spec difference
- type: 'landmark',
+ type: 'structure',
// Note: aria-expanded is not in the 1.1 spec but is
// consistently supported in ATs and was added in 1.2
allowedAttrs: ['aria-activedescendant', 'aria-expanded'],
@@ -119,7 +120,7 @@ const ariaRoles = {
nameFromContent: true
},
combobox: {
- type: 'widget',
+ type: 'composite',
requiredAttrs: ['aria-expanded', 'aria-controls'],
allowedAttrs: [
'aria-owns',
@@ -169,7 +170,7 @@ const ariaRoles = {
prohibitedAttrs: ['aria-label', 'aria-labelledby']
},
dialog: {
- type: 'widget',
+ type: 'window',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded', 'aria-modal'],
superclassRole: ['window'],
@@ -301,7 +302,7 @@ const ariaRoles = {
superclassRole: ['section']
},
listbox: {
- type: 'widget',
+ type: 'composite',
requiredOwned: ['group', 'option'],
allowedAttrs: [
'aria-multiselectable',
@@ -328,7 +329,7 @@ const ariaRoles = {
nameFromContent: true
},
log: {
- type: 'widget',
+ type: 'structure',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
@@ -340,7 +341,7 @@ const ariaRoles = {
superclassRole: ['landmark']
},
marquee: {
- type: 'widget',
+ type: 'structure',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
@@ -696,7 +697,7 @@ const ariaRoles = {
accessibleNameRequired: true
},
status: {
- type: 'widget',
+ type: 'structure',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded'],
superclassRole: ['section']
@@ -816,7 +817,7 @@ const ariaRoles = {
superclassRole: ['section']
},
timer: {
- type: 'widget',
+ type: 'structure',
// Spec difference: Aria-expanded removed in 1.2
allowedAttrs: ['aria-expanded'],
superclassRole: ['status']
From ffe37f6f3dfe58f945718581dcdc5c2aef74f498 Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Mon, 22 Apr 2024 12:48:42 -0500
Subject: [PATCH 018/400] fix(aria-roles): table with role application is
structure
Previously there was a test to confirm a table with role of application was a data table, but the spec indicates that has an anstract role of structure meaning this should be a structure table instead
This commit is just a test correction
Refs: #4421
---
test/commons/table/is-data-table.js | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/test/commons/table/is-data-table.js b/test/commons/table/is-data-table.js
index c3607faf0..d78c25e7b 100644
--- a/test/commons/table/is-data-table.js
+++ b/test/commons/table/is-data-table.js
@@ -32,6 +32,14 @@ describe('table.isDataTable', function () {
assert.isFalse(axe.commons.table.isDataTable(node));
});
+ it('should be false if the table has role=application, which is an astract role of structure', function () {
+ fixture.innerHTML = '
';
+
+ var node = fixture.querySelector('table');
+ axe.testUtils.flatTreeSetup(fixture.firstChild);
+ assert.isFalse(axe.commons.table.isDataTable(node));
+ });
+
it('should be true if the table is inside an editable area', function () {
fixture.innerHTML =
'
' +
@@ -71,13 +79,6 @@ describe('table.isDataTable', function () {
});
describe('should be true if the table has a landmark role', function () {
- it('application', function () {
- fixture.innerHTML = '
';
-
- var node = fixture.querySelector('table');
- axe.testUtils.flatTreeSetup(fixture.firstChild);
- assert.isTrue(axe.commons.table.isDataTable(node));
- });
it('banner', function () {
fixture.innerHTML = '
';
From aa1aa20043b03bb24783a46e040931804f2a1c5a Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Mon, 22 Apr 2024 13:17:17 -0500
Subject: [PATCH 019/400] fix(aria-roles): inline widgets consider composite
type
widget not inline rule corrected isWidgetType check to consider composite widget types
---
lib/rules/widget-not-inline-matches.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/rules/widget-not-inline-matches.js b/lib/rules/widget-not-inline-matches.js
index 61e5f2270..1e6a7602d 100644
--- a/lib/rules/widget-not-inline-matches.js
+++ b/lib/rules/widget-not-inline-matches.js
@@ -18,7 +18,8 @@ const matchesFns = [
];
function isWidgetType(vNode) {
- return getRoleType(vNode) === 'widget';
+ const roleType = getRoleType(vNode);
+ return roleType === 'widget' || roleType === 'composite';
}
function isNotAreaElement(vNode) {
From a82371226d202899325ac661106519e70008cefe Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Tue, 23 Apr 2024 08:56:45 -0500
Subject: [PATCH 020/400] fix(aria-valid-attr-value): more thorough test
coverage
- messageKey added to unit test
- integration test
Refs: #4363
---
test/checks/aria/valid-attr-value.js | 4 ++++
.../rules/aria-valid-attr-value/aria-valid-attr-value.html | 3 +++
.../rules/aria-valid-attr-value/aria-valid-attr-value.json | 3 ++-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/test/checks/aria/valid-attr-value.js b/test/checks/aria/valid-attr-value.js
index e2605ecd8..956c3b496 100644
--- a/test/checks/aria/valid-attr-value.js
+++ b/test/checks/aria/valid-attr-value.js
@@ -117,6 +117,10 @@ describe('aria-valid-attr-value', function () {
assert.isUndefined(
validAttrValueCheck.call(checkContext, null, null, vNode)
);
+ assert.deepEqual(checkContext._data, {
+ messageKey: 'controlsWithinPopup',
+ needsReview: 'aria-controls="test"'
+ });
});
it('should pass on aria-owns and aria-expanded=false when the element is not in the DOM', function () {
diff --git a/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html b/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html
index 42e60bae4..1542a403b 100644
--- a/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html
+++ b/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html
@@ -360,4 +360,7 @@
Possible False Positives
I'm not checked
I'm actually valid
I'm not really gone
+
+ May have injected html to control dynamically later, who knows!
+
diff --git a/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json b/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json
index 8fb15e119..f0b46c6fc 100644
--- a/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json
+++ b/test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json
@@ -236,6 +236,7 @@
["#incomplete5"],
["#incomplete6"],
["#incomplete7"],
- ["#incomplete8"]
+ ["#incomplete8"],
+ ["#incomplete9"]
]
}
From 3a90bb70c8db087b2f03cc30a4aee756995c311c Mon Sep 17 00:00:00 2001
From: Steven Lambert <2433219+straker@users.noreply.github.com>
Date: Tue, 23 Apr 2024 08:11:37 -0600
Subject: [PATCH 021/400] fix(target-size): pass for element that has nearby
elements that are obscured (#4422)
Had to update how we handled the too many rects break early since it
would return an empty array, which when looking at the lengths of the
arrays in `getOffset` made it difficult to know which case needed to be
handled (returned empty due to too many rects or returned empty because
there wasn't any visible rect). Talked to Wilco and we agreed that when
we encountered too many rects we could throw and handle the error case
in both checks.
Closes: #4387
---
lib/checks/mobile/target-offset-evaluate.js | 23 ++++++++++++--
lib/checks/mobile/target-offset.json | 3 +-
lib/checks/mobile/target-size-evaluate.js | 6 ++--
lib/commons/math/get-offset.js | 2 +-
lib/commons/math/split-rects.js | 2 +-
locales/_template.json | 3 +-
test/checks/mobile/target-offset.js | 20 +++++++++++--
test/commons/math/get-offset.js | 30 ++++++++++++++++---
test/commons/math/split-rects.js | 7 +++--
.../full/target-size/too-many-rects.js | 1 +
10 files changed, 81 insertions(+), 16 deletions(-)
diff --git a/lib/checks/mobile/target-offset-evaluate.js b/lib/checks/mobile/target-offset-evaluate.js
index 8e45fc816..949906576 100644
--- a/lib/checks/mobile/target-offset-evaluate.js
+++ b/lib/checks/mobile/target-offset-evaluate.js
@@ -20,8 +20,27 @@ export default function targetOffsetEvaluate(node, options, vNode) {
continue;
}
// the offset code works off radius but we want our messaging to reflect diameter
- const offset =
- roundToSingleDecimal(getOffset(vNode, vNeighbor, minOffset / 2)) * 2;
+ let offset = null;
+ try {
+ offset = getOffset(vNode, vNeighbor, minOffset / 2);
+ } catch (err) {
+ if (err.message.startsWith('splitRects')) {
+ this.data({
+ messageKey: 'tooManyRects',
+ closestOffset: 0,
+ minOffset
+ });
+ return undefined;
+ }
+
+ throw err;
+ }
+
+ if (offset === null) {
+ continue;
+ }
+
+ offset = roundToSingleDecimal(offset) * 2;
if (offset + roundingMargin >= minOffset) {
continue;
}
diff --git a/lib/checks/mobile/target-offset.json b/lib/checks/mobile/target-offset.json
index c06ee11d7..498ea64dc 100644
--- a/lib/checks/mobile/target-offset.json
+++ b/lib/checks/mobile/target-offset.json
@@ -14,7 +14,8 @@
"fail": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px.",
"incomplete": {
"default": "Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is this a target?",
- "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?"
+ "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?",
+ "tooManyRects": "Could not get the target size because there are too many overlapping elements"
}
}
}
diff --git a/lib/checks/mobile/target-size-evaluate.js b/lib/checks/mobile/target-size-evaluate.js
index 6ee45bc9c..85561055b 100644
--- a/lib/checks/mobile/target-size-evaluate.js
+++ b/lib/checks/mobile/target-size-evaluate.js
@@ -131,8 +131,10 @@ function getLargestUnobscuredArea(vNode, obscuredNodes) {
const obscuringRects = obscuredNodes.map(
({ boundingClientRect: rect }) => rect
);
- const unobscuredRects = splitRects(nodeRect, obscuringRects);
- if (unobscuredRects.length === 0) {
+ let unobscuredRects;
+ try {
+ unobscuredRects = splitRects(nodeRect, obscuringRects);
+ } catch (err) {
return null;
}
diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js
index 664871fda..957d4e43c 100644
--- a/lib/commons/math/get-offset.js
+++ b/lib/commons/math/get-offset.js
@@ -18,7 +18,7 @@ export default function getOffset(vTarget, vNeighbor, minRadiusNeighbour = 12) {
const neighborRects = getTargetRects(vNeighbor);
if (!targetRects.length || !neighborRects.length) {
- return 0;
+ return null;
}
const targetBoundingBox = targetRects.reduce(getBoundingRect);
diff --git a/lib/commons/math/split-rects.js b/lib/commons/math/split-rects.js
index ca4ef232a..ea6ba5397 100644
--- a/lib/commons/math/split-rects.js
+++ b/lib/commons/math/split-rects.js
@@ -18,7 +18,7 @@ export default function splitRects(outerRect, overlapRects) {
// a performance bottleneck
// @see https://github.com/dequelabs/axe-core/issues/4359
if (uniqueRects.length > 4000) {
- return [];
+ throw new Error('splitRects: Too many rects');
}
}
return uniqueRects;
diff --git a/locales/_template.json b/locales/_template.json
index a911c6014..6ea35122f 100644
--- a/locales/_template.json
+++ b/locales/_template.json
@@ -872,7 +872,8 @@
"fail": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px.",
"incomplete": {
"default": "Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is this a target?",
- "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?"
+ "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?",
+ "tooManyRects": "Could not get the target size because there are too many overlapping elements"
}
},
"target-size": {
diff --git a/test/checks/mobile/target-offset.js b/test/checks/mobile/target-offset.js
index 367968f44..747d9a4fa 100644
--- a/test/checks/mobile/target-offset.js
+++ b/test/checks/mobile/target-offset.js
@@ -98,6 +98,21 @@ describe('target-offset tests', () => {
assert.closeTo(checkContext._data.closestOffset, 24, 0.2);
});
+ it('ignores obscured widget elements as neighbors', () => {
+ const checkArgs = checkSetup(`
+
+
+
+
+
+
diff --git a/test/integration/full/target-size/shadow-dom.js b/test/integration/full/target-size/shadow-dom.js
new file mode 100644
index 000000000..557ad32f9
--- /dev/null
+++ b/test/integration/full/target-size/shadow-dom.js
@@ -0,0 +1,39 @@
+describe('target-size shadow dom test', () => {
+ 'use strict';
+ let results;
+
+ before(done => {
+ axe.testUtils.awaitNestedLoad(async () => {
+ const options = {
+ runOnly: ['target-size'],
+ elementRef: true
+ };
+ const context = {
+ // ignore the mocha links
+ exclude: '#mocha'
+ };
+ results = await axe.run(context, options);
+ done();
+ });
+ });
+
+ describe('violations', function () {
+ it('should find 0', function () {
+ assert.lengthOf(results.violations, 0);
+ });
+ });
+
+ describe('passes', function () {
+ it('should find 2', function () {
+ assert.lengthOf(results.passes[0].nodes, 2);
+ });
+ });
+
+ it('should find 0 inapplicable', function () {
+ assert.lengthOf(results.inapplicable, 0);
+ });
+
+ it('should find 0 incomplete', function () {
+ assert.lengthOf(results.incomplete, 0);
+ });
+});
From a55740f38763a5bc222a5ae0e5aea07603c85a66 Mon Sep 17 00:00:00 2001
From: Steven Lambert <2433219+straker@users.noreply.github.com>
Date: Wed, 24 Apr 2024 08:04:42 -0600
Subject: [PATCH 023/400] docs: add readme to locales dir to note _template is
generated (#4425)
Just clarifying that the `_template.json` file is generated so
contributors don't edit it without knowing. The rest of the text is
mostly copied from the root level README, which I also corrected
gramatical mistakes.
---
README.md | 4 ++--
locales/README.md | 9 +++++++++
2 files changed, 11 insertions(+), 2 deletions(-)
create mode 100644 locales/README.md
diff --git a/README.md b/README.md
index 1f6ccfec4..8848b0a3e 100644
--- a/README.md
+++ b/README.md
@@ -103,9 +103,9 @@ or equivalently:
This will create a new build for axe, called `axe..js` and `axe..min.js`. If you want to build all localized versions, simply pass in `--all-lang` instead. If you want to build multiple localized versions (but not all of them), you can pass in a comma-separated list of languages to the `--lang` flag, like `--lang=nl,ja`.
-To create a new translation for axe, start by running `grunt translate --lang=`. This will create a json file fin the `./locales` directory, with the default English text in it for you to translate. Alternatively, you could copy `./locales/_template.json`. We welcome any localization for axe-core. For details on how to contribute, see the Contributing section below. For details on the message syntax, see [Check Message Template](/doc/check-message-template.md).
+To create a new translation for axe, start by running `grunt translate --lang=`. This will create a json file in the `./locales` directory, with the default English text in it for you to translate. Alternatively, you could copy `./locales/_template.json`. We welcome any localization for axe-core. For details on how to contribute, see the Contributing section below. For details on the message syntax, see [Check Message Template](/doc/check-message-template.md).
-To update existing translation file, re-run `grunt translate --lang=`. This will add new messages used in English and remove messages which were not used in English.
+To update an existing translation file, re-run `grunt translate --lang=`. This will add new messages used in English and remove messages which were not used in English.
Additionally, locale can be applied at runtime by passing a `locale` object to `axe.configure()`. The locale object must be of the same shape as existing locales in the `./locales` directory. For example:
diff --git a/locales/README.md b/locales/README.md
new file mode 100644
index 000000000..059e4db10
--- /dev/null
+++ b/locales/README.md
@@ -0,0 +1,9 @@
+# Localizations
+
+We welcome any localization for axe-core. For details on how to contribute, see the [Contributing section](../README.md#contributing) of the main README. For details on the message syntax, see [Check Message Template](../doc/check-message-template.md).
+
+To create a new translation for axe, start by running `grunt translate --lang=`. This will create a json file with the default English text in it for you to translate. Alternatively, you could copy `_template.json`.
+
+To update an existing translation file, re-run `grunt translate --lang=`. This will add new messages used in English and remove messages which were not used in English.
+
+`_template.json` is a generated file which is created every time axe is built. It's compiled using each rules' `description` and `help` properties as well as each checks' `metadata.messages` property. To update the `_template.json` file you'll need to update the corresponding [rule](../lib/rules) or [check](../lib/checks) metadata file and rebuild.
From 61cbf3098c3e4af050d5d48d61f6e73d856b2700 Mon Sep 17 00:00:00 2001
From: Steven Lambert <2433219+straker@users.noreply.github.com>
Date: Wed, 24 Apr 2024 08:11:34 -0600
Subject: [PATCH 024/400] docs: update required develop node version to 18
(#4424)
Was the only reference to a required node version I could find in the
docs. Our node version script is already set to 18.
Closes: https://github.com/dequelabs/axe-core/issues/4323
---
doc/developer-guide.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/developer-guide.md b/doc/developer-guide.md
index 07abffb21..5b3233770 100644
--- a/doc/developer-guide.md
+++ b/doc/developer-guide.md
@@ -31,7 +31,7 @@ Axe 3.0 supports open Shadow DOM: see our virtual DOM APIs and test utilities fo
### Environment Pre-requisites
-1. You must have NodeJS version 12 or higher installed.
+1. You must have NodeJS version 18 or higher installed.
1. Install npm development dependencies. In the root folder of your axe-core repository, run `npm install`
### Building axe.js
From 2d185f65e11c79cddcd48ac66a672866e3f083a3 Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Thu, 25 Apr 2024 08:14:31 -0500
Subject: [PATCH 025/400] fix(aria-roles): address pr comments
- learned sometimes we intentionally spec change
- added `window` as a supported type, and `composite` which was missing from the readme
- undid my change to `isWidgetType`
Refs: #4371
---
doc/standards-object.md | 6 ++++--
lib/rules/widget-not-inline-matches.js | 3 +--
lib/standards/aria-roles.js | 10 ++++++----
3 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/doc/standards-object.md b/doc/standards-object.md
index a439b1ed4..cd34776d9 100644
--- a/doc/standards-object.md
+++ b/doc/standards-object.md
@@ -67,9 +67,11 @@ The [`ariaRoles`](../lib/standards/aria-roles.js) object defines valid ARIA role
- `type` - string(required). [The role type](https://www.w3.org/TR/wai-aria-1.1/#roles_categorization). Valid types are:
- `abstract`
- - `widget`
- - `structure`
+ - `composite`
- `landmark`
+ - `structure`
+ - `widget`
+ - `window`
- `requiredContext` - array(optional). List of required parent roles.
- `requiredOwned` - array(optional). List of required owned roles.
- `requiredAttrs` - array(optional). List of required attributes.
diff --git a/lib/rules/widget-not-inline-matches.js b/lib/rules/widget-not-inline-matches.js
index 1e6a7602d..61e5f2270 100644
--- a/lib/rules/widget-not-inline-matches.js
+++ b/lib/rules/widget-not-inline-matches.js
@@ -18,8 +18,7 @@ const matchesFns = [
];
function isWidgetType(vNode) {
- const roleType = getRoleType(vNode);
- return roleType === 'widget' || roleType === 'composite';
+ return getRoleType(vNode) === 'widget';
}
function isNotAreaElement(vNode) {
diff --git a/lib/standards/aria-roles.js b/lib/standards/aria-roles.js
index 035990967..7a6d32577 100644
--- a/lib/standards/aria-roles.js
+++ b/lib/standards/aria-roles.js
@@ -1,5 +1,5 @@
// Source: https://www.w3.org/TR/wai-aria-1.1/#roles
-// Source for abstract roles: https://www.w3.org/TR/wai-aria/#abstract_roles and https://www.w3.org/WAI/ARIA/1.2/class-diagram/rdf_model.svg
+// Source for abstract roles (types): https://www.w3.org/TR/wai-aria/#abstract_roles
/* easiest way to see allowed roles is to filter out the global ones
from the list of inherited states and properties. The dpub spec
@@ -32,7 +32,7 @@ const ariaRoles = {
},
application: {
// Note: spec difference
- type: 'structure',
+ type: 'landmark',
// Note: aria-expanded is not in the 1.1 spec but is
// consistently supported in ATs and was added in 1.2
allowedAttrs: ['aria-activedescendant', 'aria-expanded'],
@@ -120,7 +120,8 @@ const ariaRoles = {
nameFromContent: true
},
combobox: {
- type: 'composite',
+ // Note: spec difference
+ type: 'widget',
requiredAttrs: ['aria-expanded', 'aria-controls'],
allowedAttrs: [
'aria-owns',
@@ -302,7 +303,8 @@ const ariaRoles = {
superclassRole: ['section']
},
listbox: {
- type: 'composite',
+ // Note: spec difference
+ type: 'widget',
requiredOwned: ['group', 'option'],
allowedAttrs: [
'aria-multiselectable',
From 417e6b1a03d3359db37d87a67f5b2ee9687107c2 Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Thu, 25 Apr 2024 08:23:20 -0500
Subject: [PATCH 026/400] fix(aria-roles): reverted test change as well
test change was only for data tables, but we didn't want to keep that type change
Refs: #4371
---
test/commons/table/is-data-table.js | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/test/commons/table/is-data-table.js b/test/commons/table/is-data-table.js
index d78c25e7b..c3607faf0 100644
--- a/test/commons/table/is-data-table.js
+++ b/test/commons/table/is-data-table.js
@@ -32,14 +32,6 @@ describe('table.isDataTable', function () {
assert.isFalse(axe.commons.table.isDataTable(node));
});
- it('should be false if the table has role=application, which is an astract role of structure', function () {
- fixture.innerHTML = '
';
-
- var node = fixture.querySelector('table');
- axe.testUtils.flatTreeSetup(fixture.firstChild);
- assert.isFalse(axe.commons.table.isDataTable(node));
- });
-
it('should be true if the table is inside an editable area', function () {
fixture.innerHTML =
'
' +
@@ -79,6 +71,13 @@ describe('table.isDataTable', function () {
});
describe('should be true if the table has a landmark role', function () {
+ it('application', function () {
+ fixture.innerHTML = '
';
+
+ var node = fixture.querySelector('table');
+ axe.testUtils.flatTreeSetup(fixture.firstChild);
+ assert.isTrue(axe.commons.table.isDataTable(node));
+ });
it('banner', function () {
fixture.innerHTML = '
';
From facd65f6f528122ba9c748e587ae3cf1ebd2a22a Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Thu, 25 Apr 2024 13:35:08 -0500
Subject: [PATCH 027/400] fix(aria-roles): exception for focus-order-semantics
add `window` to has-widget-role, renamed
Refs: #4371
---
.../has-widget-or-window-role-evaluate.js | 26 +
...le.json => has-widget-or-window-role.json} | 4 +-
lib/checks/aria/has-widget-role-evaluate.js | 20 -
lib/rules/focus-order-semantics.json | 2 +-
locales/_template.json | 2 +-
test/checks/aria/has-widget-or-window-role.js | 118 ++++
test/checks/aria/has-widget-role.js | 572 ------------------
7 files changed, 148 insertions(+), 596 deletions(-)
create mode 100644 lib/checks/aria/has-widget-or-window-role-evaluate.js
rename lib/checks/aria/{has-widget-role.json => has-widget-or-window-role.json} (67%)
delete mode 100644 lib/checks/aria/has-widget-role-evaluate.js
create mode 100644 test/checks/aria/has-widget-or-window-role.js
delete mode 100644 test/checks/aria/has-widget-role.js
diff --git a/lib/checks/aria/has-widget-or-window-role-evaluate.js b/lib/checks/aria/has-widget-or-window-role-evaluate.js
new file mode 100644
index 000000000..676b7d6f6
--- /dev/null
+++ b/lib/checks/aria/has-widget-or-window-role-evaluate.js
@@ -0,0 +1,26 @@
+import { getRoleType } from '../../commons/aria';
+
+const acceptedRoles = {
+ widget: true,
+ composite: true,
+ window: true
+};
+
+/**
+ * Check if an elements `role` attribute uses any widget, composite, window abstract role values.
+ *
+ * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property.
+ *
+ * @memberof checks
+ * @return {Boolean} True if the element uses a `widget`, `composite`, or `window` abstract role (type). False otherwise.
+ */
+// # TODO: change to abstract role for widget and window
+function hasWidgetOrWindowRoleEvaluate(node) {
+ const role = node.getAttribute('role');
+ if (role === null) {
+ return false;
+ }
+ return !!acceptedRoles[getRoleType(role)];
+}
+
+export default hasWidgetOrWindowRoleEvaluate;
diff --git a/lib/checks/aria/has-widget-role.json b/lib/checks/aria/has-widget-or-window-role.json
similarity index 67%
rename from lib/checks/aria/has-widget-role.json
rename to lib/checks/aria/has-widget-or-window-role.json
index bda9ad343..be3f30f7d 100644
--- a/lib/checks/aria/has-widget-role.json
+++ b/lib/checks/aria/has-widget-or-window-role.json
@@ -1,6 +1,6 @@
{
- "id": "has-widget-role",
- "evaluate": "has-widget-role-evaluate",
+ "id": "has-widget-or-window-role",
+ "evaluate": "has-widget-or-window-role-evaluate",
"options": [],
"metadata": {
"impact": "minor",
diff --git a/lib/checks/aria/has-widget-role-evaluate.js b/lib/checks/aria/has-widget-role-evaluate.js
deleted file mode 100644
index c3344ef20..000000000
--- a/lib/checks/aria/has-widget-role-evaluate.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import { getRoleType } from '../../commons/aria';
-
-/**
- * Check if an elements `role` attribute uses any widget or composite role values.
- *
- * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property.
- *
- * @memberof checks
- * @return {Boolean} True if the element uses a `widget` or `composite` role. False otherwise.
- */
-function hasWidgetRoleEvaluate(node) {
- const role = node.getAttribute('role');
- if (role === null) {
- return false;
- }
- const roleType = getRoleType(role);
- return roleType === 'widget' || roleType === 'composite';
-}
-
-export default hasWidgetRoleEvaluate;
diff --git a/lib/rules/focus-order-semantics.json b/lib/rules/focus-order-semantics.json
index 8c73c2b9f..65bc65725 100644
--- a/lib/rules/focus-order-semantics.json
+++ b/lib/rules/focus-order-semantics.json
@@ -9,6 +9,6 @@
"help": "Elements in the focus order should have an appropriate role"
},
"all": [],
- "any": ["has-widget-role", "valid-scrollable-semantics"],
+ "any": ["has-widget-or-window-role", "valid-scrollable-semantics"],
"none": []
}
diff --git a/locales/_template.json b/locales/_template.json
index 6ea35122f..2c09e751c 100644
--- a/locales/_template.json
+++ b/locales/_template.json
@@ -576,7 +576,7 @@
},
"fail": "Element does not have global ARIA attribute"
},
- "has-widget-role": {
+ "has-widget-or-window-role": {
"pass": "Element has a widget role.",
"fail": "Element does not have a widget role."
},
diff --git a/test/checks/aria/has-widget-or-window-role.js b/test/checks/aria/has-widget-or-window-role.js
new file mode 100644
index 000000000..2adae9971
--- /dev/null
+++ b/test/checks/aria/has-widget-or-window-role.js
@@ -0,0 +1,118 @@
+describe('has-widget-or-window-role', function () {
+ 'use strict';
+
+ let node;
+ const fixture = document.getElementById('fixture');
+ const checkContext = axe.testUtils.MockCheckContext();
+ const evaluate = currentNode =>
+ axe.testUtils
+ .getCheckEvaluate('has-widget-or-window-role')
+ .call(checkContext, currentNode);
+ const roles = {
+ widget: {
+ button: true,
+ checkbox: true,
+ gridcell: true,
+ link: true,
+ menuitem: true,
+ menuitemcheckbox: true,
+ menuitemradio: true,
+ option: true,
+ progressbar: true,
+ radio: true,
+ scrollbar: true,
+ searchbox: true,
+ slider: true,
+ spinbutton: true,
+ switch: true,
+ tab: true,
+ tabpanel: true,
+ textbox: true,
+ treeitem: true
+ },
+ composite: {
+ combobox: true,
+ grid: true,
+ listbox: true,
+ menu: true,
+ menubar: true,
+ radiogroup: true,
+ tablist: true,
+ tree: true,
+ treegrid: true,
+
+ application: false,
+ article: false,
+ cell: false,
+ columnheader: false,
+ definition: false,
+ directory: false,
+ document: false,
+ feed: false,
+ figure: false,
+ group: false,
+ heading: false,
+ img: false,
+ list: false,
+ listitem: false,
+ math: false,
+ none: false,
+ note: false,
+ presentation: false,
+ row: false,
+ rowgroup: false,
+ rowheader: false,
+ table: false,
+ term: false,
+ toolbar: false
+ },
+ window: {
+ alertdialog: true,
+ dialog: true
+ },
+ landmark: {
+ banner: false,
+ complimentary: false,
+ contentinfo: false,
+ form: false,
+ name: false,
+ navigation: false,
+ region: false,
+ search: false
+ }
+ };
+
+ afterEach(function () {
+ node.innerHTML = '';
+ checkContext._data = null;
+ });
+
+ it('should return false for elements with no role', function () {
+ node = document.createElement('div');
+ fixture.appendChild(node);
+
+ assert.isFalse(evaluate(node));
+ });
+
+ it('should return false for elements with nonsensical roles', function () {
+ node = document.createElement('div');
+ node.setAttribute('role', 'buttonbuttonbutton');
+ fixture.appendChild(node);
+
+ assert.isFalse(evaluate(node));
+ });
+
+ Object.keys(roles).forEach(category => {
+ describe(category, function () {
+ Object.keys(roles[category]).forEach(role => {
+ it(`should return ${roles[category][role]} for role="${role}"`, function () {
+ node = document.createElement('div');
+ node.setAttribute('role', role);
+ fixture.appendChild(node);
+
+ assert.equal(evaluate(node), roles[category][role]);
+ });
+ });
+ });
+ });
+});
diff --git a/test/checks/aria/has-widget-role.js b/test/checks/aria/has-widget-role.js
deleted file mode 100644
index f7324cadc..000000000
--- a/test/checks/aria/has-widget-role.js
+++ /dev/null
@@ -1,572 +0,0 @@
-describe('has-widget-role', function () {
- 'use strict';
-
- var fixture = document.getElementById('fixture');
- var node;
- var checkContext = axe.testUtils.MockCheckContext();
-
- afterEach(function () {
- node.innerHTML = '';
- checkContext._data = null;
- });
-
- it('should return false for elements with no role', function () {
- node = document.createElement('div');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for elements with nonsensical roles', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'buttonbuttonbutton');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- // Widget roles
- it('should return true for role=button', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'button');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=checkbox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'checkbox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=gridcell', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'gridcell');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=link', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'link');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=menuitem', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'menuitem');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=menuitemcheckbox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'menuitemcheckbox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=menuitemradio', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'menuitemradio');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=option', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'option');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=progressbar', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'progressbar');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=radio', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'radio');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=scrollbar', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'scrollbar');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=searchbox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'searchbox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=slider', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'slider');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=spinbutton', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'spinbutton');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=switch', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'switch');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=tab', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'tab');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=tabpanel', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'tabpanel');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=textbox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'textbox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=treeitem', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'treeitem');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- // Composite widget roles
- it('should return true for role=combobox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'combobox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=grid', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'grid');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=listbox', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'listbox');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=menu', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'menu');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=menubar', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'menubar');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=radiogroup', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'radiogroup');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=tablist', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'tablist');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=tree', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'tree');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return true for role=treegrid', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'treegrid');
- fixture.appendChild(node);
- assert.isTrue(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=application', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'application');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=article', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'article');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=cell', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'cell');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=columnheader', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'columnheader');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=definition', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'definition');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=directory', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'directory');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=document', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'document');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=feed', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'feed');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=figure', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'figure');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=group', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'group');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=heading', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'heading');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=img', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'img');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=list', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'list');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=listitem', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'listitem');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=math', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'math');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=none', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'none');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=note', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'note');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=presentation', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'presentation');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=row', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'row');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=rowgroup', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'rowgroup');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=rowheader', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'rowheader');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=table', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'table');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=term', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'term');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=toolbar', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'toolbar');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- // Landmark Roles
- it('should return false for role=banner', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'banner');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=complementary', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'complementary');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=contentinfo', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'contentinfo');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=form', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'form');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=main', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'main');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=navigation', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'navigation');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=region', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'region');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-
- it('should return false for role=search', function () {
- node = document.createElement('div');
- node.setAttribute('role', 'search');
- fixture.appendChild(node);
- assert.isFalse(
- axe.testUtils.getCheckEvaluate('has-widget-role').call(checkContext, node)
- );
- });
-});
From bfa95ae2ff233de32390777f735cd35ef1e50fa4 Mon Sep 17 00:00:00 2001
From: Ava Gaiety W
Date: Thu, 25 Apr 2024 14:35:45 -0500
Subject: [PATCH 028/400] fix(aria-roles): updated locales
better pass/fail english message, removed outdated translations
Refs: #4371
---
lib/checks/aria/has-widget-or-window-role.json | 4 ++--
locales/_template.json | 4 ++--
locales/da.json | 4 ----
locales/de.json | 4 ----
locales/el.json | 4 ----
locales/es.json | 4 ----
locales/eu.json | 4 ----
locales/fr.json | 4 ----
locales/he.json | 4 ----
locales/it.json | 4 ----
locales/ja.json | 4 ----
locales/ko.json | 4 ----
locales/no_NB.json | 4 ----
locales/pl.json | 4 ----
locales/pt_BR.json | 4 ----
locales/zh_CN.json | 4 ----
locales/zh_TW.json | 4 ----
17 files changed, 4 insertions(+), 64 deletions(-)
diff --git a/lib/checks/aria/has-widget-or-window-role.json b/lib/checks/aria/has-widget-or-window-role.json
index be3f30f7d..f5fed0b46 100644
--- a/lib/checks/aria/has-widget-or-window-role.json
+++ b/lib/checks/aria/has-widget-or-window-role.json
@@ -5,8 +5,8 @@
"metadata": {
"impact": "minor",
"messages": {
- "pass": "Element has a widget role.",
- "fail": "Element does not have a widget role."
+ "pass": "Element has a widget or window role.",
+ "fail": "Element does not have a widget or window role."
}
}
}
diff --git a/locales/_template.json b/locales/_template.json
index 2c09e751c..cbdf75ed6 100644
--- a/locales/_template.json
+++ b/locales/_template.json
@@ -577,8 +577,8 @@
"fail": "Element does not have global ARIA attribute"
},
"has-widget-or-window-role": {
- "pass": "Element has a widget role.",
- "fail": "Element does not have a widget role."
+ "pass": "Element has a widget or window role.",
+ "fail": "Element does not have a widget or window role."
},
"invalidrole": {
"pass": "ARIA role is valid",
diff --git a/locales/da.json b/locales/da.json
index 56c9a5ccd..a8f54214e 100644
--- a/locales/da.json
+++ b/locales/da.json
@@ -365,10 +365,6 @@
"plural": "'aria-errormessage'-værdi ${data.values}` bør bruge en teknik til at annoncere beskeden (fx 'aria-live', 'aria-describedby', 'role=alert', osv.)"
}
},
- "has-widget-role": {
- "pass": "Elementet har en 'widget'-rolle.",
- "fail": "Elementet har ikke en 'widget'-rolle."
- },
"invalidrole": {
"pass": "ARIA-rollen er korrekt",
"fail": "Rollen skal være en af de mulige ARIA-roller"
diff --git a/locales/de.json b/locales/de.json
index 8f35d0c65..bd8a24b0a 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -576,10 +576,6 @@
},
"fail": "Das Element hat keine globalen ARIA Attribute."
},
- "has-widget-role": {
- "pass": "Element hat eine widget-Rolle.",
- "fail": "Das Element besitzt keine widget-Rolle."
- },
"invalidrole": {
"pass": "ARIA Rolle ist gültig.",
"fail": {
diff --git a/locales/el.json b/locales/el.json
index f8f805c42..4355fafeb 100644
--- a/locales/el.json
+++ b/locales/el.json
@@ -539,10 +539,6 @@
},
"fail": "Το στοιχείο δεν έχει καθολικό χαρακτηριστικό ARIA"
},
- "has-widget-role": {
- "pass": "Το στοιχείο έχει ρόλο widget.",
- "fail": "Το στοιχείο δεν έχει ρόλο widget."
- },
"invalidrole": {
"pass": "Ο ρόλος ARIA είναι έγκυρος",
"fail": {
diff --git a/locales/es.json b/locales/es.json
index 7a0b5c0b3..1f5aace76 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -356,10 +356,6 @@
"plural": "En aria-errormessage, valor ${data.values}`, se debe usar una técnica para anunciar el mensaje (p. ej., aria-live, aria-describedby, role=alert, etc.)"
}
},
- "has-widget-role": {
- "pass": "El elemento tiene un rol de widget.",
- "fail": "El elemento no tiene un rol de widget."
- },
"invalidrole": {
"pass": "El rol ARIA es válido",
"fail": "El rol debe ser uno de los roles ARIA válidos"
diff --git a/locales/eu.json b/locales/eu.json
index 03b534cfc..e4130d375 100644
--- a/locales/eu.json
+++ b/locales/eu.json
@@ -356,10 +356,6 @@
"plural": "aria-errormessagen, bailioa ${data.values}`, mezua iragartzeko teknika bat erabili behar da (adibidez: aria-live, aria-describedby, role = alert, etab.)."
}
},
- "has-widget-role": {
- "pass": "Elementuak widget rola du.",
- "fail": "Elementuak ez du widget rolik."
- },
"invalidrole": {
"pass": "ARIA rola baliozkoa da",
"fail": "Rolak baliozko ARIA rola izan behar du"
diff --git a/locales/fr.json b/locales/fr.json
index 9bd71725e..b57078518 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -519,10 +519,6 @@
},
"fail": "L’élément n’a pas d’attribut ARIA global"
},
- "has-widget-role": {
- "pass": "L’élément a un rôle widget.",
- "fail": "L’élément n’a pas de rôle widget."
- },
"invalidrole": {
"pass": "Le rôle ARIA est valide",
"fail": {
diff --git a/locales/he.json b/locales/he.json
index fd0d2eaa5..5502bac62 100644
--- a/locales/he.json
+++ b/locales/he.json
@@ -529,10 +529,6 @@
},
"fail": "לאלמנט אין תכונת ARIA גלובלית: "
},
- "has-widget-role": {
- "pass": "לאלמנט יש תפקיד של וגדג'ט.",
- "fail": "לאלמנט אין תפקיד של וגדג'ט."
- },
"invalidrole": {
"pass": "תפקיד ARIA קביל",
"fail": {
diff --git a/locales/it.json b/locales/it.json
index 6f1b72325..6aa14e609 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -573,10 +573,6 @@
},
"fail": "L'elemento non ha un attributo ARIA globale"
},
- "has-widget-role": {
- "pass": "L'elemento ha un ruolo widget.",
- "fail": "L'elemento non ha un ruolo widget."
- },
"invalidrole": {
"pass": "Il ruolo ARIA è valido",
"fail": {
diff --git a/locales/ja.json b/locales/ja.json
index 161fdba32..ce1f6e0eb 100644
--- a/locales/ja.json
+++ b/locales/ja.json
@@ -575,10 +575,6 @@
},
"fail": "要素にはグローバルなARIA属性が指定されていません"
},
- "has-widget-role": {
- "pass": "要素にはwidgetロールが存在します",
- "fail": "要素にはwidgetロールが存在しません"
- },
"invalidrole": {
"pass": "ARIAロールが有効です",
"fail": {
diff --git a/locales/ko.json b/locales/ko.json
index 9c304d1d7..d86ffeed5 100644
--- a/locales/ko.json
+++ b/locales/ko.json
@@ -524,10 +524,6 @@
},
"fail": "엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있지 않습니다."
},
- "has-widget-role": {
- "pass": "엘리먼트가 위젯 역할(role)을 가지고 있습니다.",
- "fail": "엘리먼트가 위젯 역할(role)을 가지고 있지 않습니다."
- },
"invalidrole": {
"pass": "ARIA 역할(role)이 유효합니다.",
"fail": {
diff --git a/locales/no_NB.json b/locales/no_NB.json
index 4dbf8dbe9..ed73ec0bb 100644
--- a/locales/no_NB.json
+++ b/locales/no_NB.json
@@ -365,10 +365,6 @@
"plural": "'aria-errormessage'-verdier ${data.values}` skal bruke en metode for å annonsere beskeden (f.eks. 'aria-live', 'aria-describedby', 'role=alert', osv.)"
}
},
- "has-widget-role": {
- "pass": "Elementet har en 'widget'-rolle.",
- "fail": "Elementet har ikke en 'widget'-rolle."
- },
"invalidrole": {
"pass": "ARIA-rollen er korrekt",
"fail": "Rollen skal være en av de mulige ARIA-roller"
diff --git a/locales/pl.json b/locales/pl.json
index 1caafcabd..7c2f2d1f7 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -575,10 +575,6 @@
},
"fail": "Element nie ma ogólnego atrybutu ARIA"
},
- "has-widget-role": {
- "pass": "Element ma rolę widżetu.",
- "fail": "Element nie ma roli widżetu."
- },
"invalidrole": {
"pass": "Rola ARIA jest poprawna.",
"fail": {
diff --git a/locales/pt_BR.json b/locales/pt_BR.json
index 404d5ab29..68d4cf2da 100644
--- a/locales/pt_BR.json
+++ b/locales/pt_BR.json
@@ -504,10 +504,6 @@
},
"fail": "O elemento não tem atributo ARIA global"
},
- "has-widget-role": {
- "pass": "Elemento tem um 'role' de 'widget'.",
- "fail": "Elemento não tem um 'role' de 'widget'."
- },
"invalidrole": {
"pass": "O ARIA 'role' é válido",
"fail": {
diff --git a/locales/zh_CN.json b/locales/zh_CN.json
index ef5e6876d..089cf0ce3 100644
--- a/locales/zh_CN.json
+++ b/locales/zh_CN.json
@@ -576,10 +576,6 @@
},
"fail": "元素没有全局 ARIA 属性"
},
- "has-widget-role": {
- "pass": "元素有小部件角色",
- "fail": "元素没有小部件角色"
- },
"invalidrole": {
"pass": "ARIA 角色有效",
"fail": {
diff --git a/locales/zh_TW.json b/locales/zh_TW.json
index 37a9d3262..ba6c50f4c 100644
--- a/locales/zh_TW.json
+++ b/locales/zh_TW.json
@@ -573,10 +573,6 @@
},
"fail": "元素沒有全域 ARIA 屬性"
},
- "has-widget-role": {
- "pass": "元素具有小部件角色",
- "fail": "元素沒有小部件角色"
- },
"invalidrole": {
"pass": "ARIA 角色有效",
"fail": {
From 29e7e2a32f952978178885d3e8e424290db587b3 Mon Sep 17 00:00:00 2001
From: Steven Lambert <2433219+straker@users.noreply.github.com>
Date: Thu, 25 Apr 2024 16:47:53 -0600
Subject: [PATCH 029/400] revert(color-contrast): revert upgrade to colorjs.io
v0.5.0 to work with prototype.js (#4429)
This [puts back the v0.4.3
code](https://github.com/dequelabs/axe-core/pull/4366) in `color.js` to
handle colorjs.io not handling rad and turn values in hsl.
I also decided to use core-js `Array.from` polyfill rather than our own
internal one since I needed to bring it in outside of the `polyfill.js`
file and could then add it as an import and reuse it in both places.
Closes: https://github.com/dequelabs/axe-core/issues/4428
---
lib/commons/color/color.js | 41 +-
lib/core/imports/index.js | 10 +-
lib/core/imports/polyfills.js | 91 +-
package-lock.json | 16 +-
package.json | 2 +-
test/commons/color/color.js | 18 +
.../full/contrast/prototype-lib-1.7.3.js | 8074 +++++++++++++++++
test/integration/full/contrast/prototype.html | 32 +
test/integration/full/contrast/prototype.js | 26 +
9 files changed, 8210 insertions(+), 100 deletions(-)
create mode 100644 test/integration/full/contrast/prototype-lib-1.7.3.js
create mode 100644 test/integration/full/contrast/prototype.html
create mode 100644 test/integration/full/contrast/prototype.js
diff --git a/lib/commons/color/color.js b/lib/commons/color/color.js
index 5201628d3..c6cb6496a 100644
--- a/lib/commons/color/color.js
+++ b/lib/commons/color/color.js
@@ -1,6 +1,7 @@
-import { Colorjs } from '../../core/imports';
+import { Colorjs, ArrayFrom } from '../../core/imports';
const hexRegex = /^#[0-9a-f]{3,8}$/i;
+const hslRegex = /hsl\(\s*([-\d.]+)(rad|turn)/;
/**
* @class Color
@@ -129,9 +130,37 @@ export default class Color {
* @instance
*/
parseString(colorString) {
+ // Colorjs {
+ const value = angle + unit;
+
+ switch (unit) {
+ case 'rad':
+ return match.replace(value, radToDeg(angle));
+ case 'turn':
+ return match.replace(value, turnToDeg(angle));
+ }
+ });
+
try {
+ // revert prototype.js override of Array.from
+ // in order to get color-contrast working
+ // @see https://github.com/dequelabs/axe-core/issues/4428
+ let prototypeArrayFrom;
+ if ('Prototype' in window && 'Version' in window.Prototype) {
+ prototypeArrayFrom = Array.from;
+ Array.from = ArrayFrom;
+ }
+
// srgb values are between 0 and 1
const color = new Colorjs(colorString).to('srgb');
+
+ if (prototypeArrayFrom) {
+ Array.from = prototypeArrayFrom;
+ prototypeArrayFrom = null;
+ }
+
this.r = color.r;
this.g = color.g;
this.b = color.b;
@@ -328,3 +357,13 @@ export default class Color {
function clamp(value, min, max) {
return Math.min(Math.max(min, value), max);
}
+
+// convert radians to degrees
+function radToDeg(rad) {
+ return (rad * 180) / Math.PI;
+}
+
+// convert turn to degrees
+function turnToDeg(turn) {
+ return turn * 360;
+}
diff --git a/lib/core/imports/index.js b/lib/core/imports/index.js
index 80aa717d5..4a23f352f 100644
--- a/lib/core/imports/index.js
+++ b/lib/core/imports/index.js
@@ -6,6 +6,7 @@ import doT from '@deque/dot';
import emojiRegexText from 'emoji-regex';
import memoize from 'memoizee';
import Color from 'colorjs.io';
+import ArrayFrom from 'core-js-pure/actual/array/from';
// prevent striping newline characters from strings (e.g. failure
// summaries). value must be synced with build/configure.js
@@ -17,4 +18,11 @@ doT.templateSettings.strip = false;
* @namespace imports
* @memberof axe
*/
-export { CssSelectorParser, doT, emojiRegexText, memoize, Color as Colorjs };
+export {
+ CssSelectorParser,
+ doT,
+ emojiRegexText,
+ memoize,
+ Color as Colorjs,
+ ArrayFrom
+};
diff --git a/lib/core/imports/polyfills.js b/lib/core/imports/polyfills.js
index 5725632e4..7ad8adb4a 100644
--- a/lib/core/imports/polyfills.js
+++ b/lib/core/imports/polyfills.js
@@ -3,6 +3,7 @@ import { Uint32Array } from 'typedarray';
import 'weakmap-polyfill';
import hasOwn from 'core-js-pure/actual/object/has-own';
import values from 'core-js-pure/actual/object/values';
+import ArrayFrom from 'core-js-pure/actual/array/from';
if (!('hasOwn' in Object)) {
Object.hasOwn = hasOwn;
@@ -177,95 +178,7 @@ if (!Array.prototype.some) {
}
if (!Array.from) {
- Object.defineProperty(Array, 'from', {
- value: (function () {
- var toStr = Object.prototype.toString;
- var isCallable = function (fn) {
- return (
- typeof fn === 'function' || toStr.call(fn) === '[object Function]'
- );
- };
- var toInteger = function (value) {
- var number = Number(value);
- if (isNaN(number)) {
- return 0;
- }
- if (number === 0 || !isFinite(number)) {
- return number;
- }
- return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
- };
- var maxSafeInteger = Math.pow(2, 53) - 1;
- var toLength = function (value) {
- var len = toInteger(value);
- return Math.min(Math.max(len, 0), maxSafeInteger);
- };
-
- // The length property of the from method is 1.
- return function from(arrayLike /*, mapFn, thisArg */) {
- // 1. Let C be the this value.
- var C = this;
-
- // 2. Let items be ToObject(arrayLike).
- var items = Object(arrayLike);
-
- // 3. ReturnIfAbrupt(items).
- if (arrayLike == null) {
- throw new TypeError(
- 'Array.from requires an array-like object - not null or undefined'
- );
- }
-
- // 4. If mapfn is undefined, then let mapping be false.
- var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
- var T;
- if (typeof mapFn !== 'undefined') {
- // 5. else
- // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
- if (!isCallable(mapFn)) {
- throw new TypeError(
- 'Array.from: when provided, the second argument must be a function'
- );
- }
-
- // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
- if (arguments.length > 2) {
- T = arguments[2];
- }
- }
-
- // 10. Let lenValue be Get(items, "length").
- // 11. Let len be ToLength(lenValue).
- var len = toLength(items.length);
-
- // 13. If IsConstructor(C) is true, then
- // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
- // 14. a. Else, Let A be ArrayCreate(len).
- var A = isCallable(C) ? Object(new C(len)) : new Array(len);
-
- // 16. Let k be 0.
- var k = 0;
- // 17. Repeat, while k < len… (also steps a - h)
- var kValue;
- while (k < len) {
- kValue = items[k];
- if (mapFn) {
- A[k] =
- typeof T === 'undefined'
- ? mapFn(kValue, k)
- : mapFn.call(T, kValue, k);
- } else {
- A[k] = kValue;
- }
- k += 1;
- }
- // 18. Let putStatus be Put(A, "length", len, true).
- A.length = len;
- // 20. Return A.
- return A;
- };
- })()
- });
+ Array.from = ArrayFrom;
}
if (!String.prototype.includes) {
diff --git a/package-lock.json b/package-lock.json
index 0947cc245..c337ac05d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,10 +20,10 @@
"browser-driver-manager": "1.0.4",
"chai": "^4.3.7",
"chalk": "^4.x",
- "chromedriver": "*",
+ "chromedriver": "latest",
"clean-jsdoc-theme": "^4.2.17",
"clone": "^2.1.2",
- "colorjs.io": "^0.5.0",
+ "colorjs.io": "^0.4.3",
"conventional-commits-parser": "^5.0.0",
"core-js": "^3.27.1",
"css-selector-parser": "^1.4.1",
@@ -3655,9 +3655,9 @@
"dev": true
},
"node_modules/colorjs.io": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.0.tgz",
- "integrity": "sha512-qekjTiBLM3F/sXKks/ih5aWaHIGu+Ftel0yKEvmpbKvmxpNOhojKgha5uiWEUOqEpRjC1Tq3nJRT7WgdBOxIGg==",
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.4.3.tgz",
+ "integrity": "sha512-Jr6NiWFZCuSECl23Bhe4jvDldQsE0ErnWrdl3xIUFy+Bkp0l8r5qt/iZlNH47/xxGP5izcyC8InjoUoI4Po+Pg==",
"dev": true
},
"node_modules/colors": {
@@ -15845,9 +15845,9 @@
"dev": true
},
"colorjs.io": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.0.tgz",
- "integrity": "sha512-qekjTiBLM3F/sXKks/ih5aWaHIGu+Ftel0yKEvmpbKvmxpNOhojKgha5uiWEUOqEpRjC1Tq3nJRT7WgdBOxIGg==",
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.4.3.tgz",
+ "integrity": "sha512-Jr6NiWFZCuSECl23Bhe4jvDldQsE0ErnWrdl3xIUFy+Bkp0l8r5qt/iZlNH47/xxGP5izcyC8InjoUoI4Po+Pg==",
"dev": true
},
"colors": {
diff --git a/package.json b/package.json
index e746e267c..9983cadde 100644
--- a/package.json
+++ b/package.json
@@ -130,7 +130,7 @@
"chromedriver": "latest",
"clean-jsdoc-theme": "^4.2.17",
"clone": "^2.1.2",
- "colorjs.io": "^0.5.0",
+ "colorjs.io": "^0.4.3",
"conventional-commits-parser": "^5.0.0",
"core-js": "^3.27.1",
"css-selector-parser": "^1.4.1",
diff --git a/test/commons/color/color.js b/test/commons/color/color.js
index 1c1230e7f..3c1722c11 100644
--- a/test/commons/color/color.js
+++ b/test/commons/color/color.js
@@ -166,6 +166,15 @@ describe('color.Color', () => {
assert.equal(c.alpha, 1);
});
+ it('supports negative rad on hue', () => {
+ const c = new Color();
+ c.parseColorFnString('hsl(-3.49rad, 40%, 50%)');
+ assert.equal(c.red, 77);
+ assert.equal(c.green, 179);
+ assert.equal(c.blue, 145);
+ assert.equal(c.alpha, 1);
+ });
+
it('supports turn on hue', () => {
const c = new Color();
c.parseColorFnString('hsl(0.444turn, 40%, 50%)');
@@ -174,6 +183,15 @@ describe('color.Color', () => {
assert.equal(c.blue, 144);
assert.equal(c.alpha, 1);
});
+
+ it('supports negative turn on hue', () => {
+ const c = new Color();
+ c.parseColorFnString('hsl(-0.556turn, 40%, 50%)');
+ assert.equal(c.red, 77);
+ assert.equal(c.green, 179);
+ assert.equal(c.blue, 144);
+ assert.equal(c.alpha, 1);
+ });
});
describe('with hwb()', () => {
diff --git a/test/integration/full/contrast/prototype-lib-1.7.3.js b/test/integration/full/contrast/prototype-lib-1.7.3.js
new file mode 100644
index 000000000..641442695
--- /dev/null
+++ b/test/integration/full/contrast/prototype-lib-1.7.3.js
@@ -0,0 +1,8074 @@
+/* eslint-disable */
+/* Prototype JavaScript framework, version 1.7.3
+ * (c) 2005-2010 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.7.3',
+
+ Browser: (function () {
+ var ua = navigator.userAgent;
+ var isOpera =
+ Object.prototype.toString.call(window.opera) == '[object Opera]';
+ return {
+ IE: !!window.attachEvent && !isOpera,
+ Opera: isOpera,
+ WebKit: ua.indexOf('AppleWebKit/') > -1,
+ Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
+ MobileSafari: /Apple.*Mobile/.test(ua)
+ };
+ })(),
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+
+ SelectorsAPI: !!document.querySelector,
+
+ ElementExtensions: (function () {
+ var constructor = window.Element || window.HTMLElement;
+ return !!(constructor && constructor.prototype);
+ })(),
+ SpecificElementExtensions: (function () {
+ if (typeof window.HTMLDivElement !== 'undefined') return true;
+
+ var div = document.createElement('div'),
+ form = document.createElement('form'),
+ isSupported = false;
+
+ if (div['__proto__'] && div['__proto__'] !== form['__proto__']) {
+ isSupported = true;
+ }
+
+ div = form = null;
+
+ return isSupported;
+ })()
+ },
+
+ ScriptFragment: '',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function () {},
+
+ K: function (x) {
+ return x;
+ }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+/* Based on Alex Arnell's inheritance implementation. */
+
+var Class = (function () {
+ var IS_DONTENUM_BUGGY = (function () {
+ for (var p in { toString: 1 }) {
+ if (p === 'toString') return false;
+ }
+ return true;
+ })();
+
+ function subclass() {}
+ function create() {
+ var parent = null,
+ properties = $A(arguments);
+ if (Object.isFunction(properties[0])) parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass();
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0, length = properties.length; i < length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+ return klass;
+ }
+
+ function addMethods(source) {
+ var ancestor = this.superclass && this.superclass.prototype,
+ properties = Object.keys(source);
+
+ if (IS_DONTENUM_BUGGY) {
+ if (source.toString != Object.prototype.toString)
+ properties.push('toString');
+ if (source.valueOf != Object.prototype.valueOf)
+ properties.push('valueOf');
+ }
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i],
+ value = source[property];
+ if (
+ ancestor &&
+ Object.isFunction(value) &&
+ value.argumentNames()[0] == '$super'
+ ) {
+ var method = value;
+ value = (function (m) {
+ return function () {
+ return ancestor[m].apply(this, arguments);
+ };
+ })(property).wrap(method);
+
+ value.valueOf = (function (method) {
+ return function () {
+ return method.valueOf.call(method);
+ };
+ })(method);
+
+ value.toString = (function (method) {
+ return function () {
+ return method.toString.call(method);
+ };
+ })(method);
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+
+ return {
+ create: create,
+ Methods: {
+ addMethods: addMethods
+ }
+ };
+})();
+(function () {
+ var _toString = Object.prototype.toString,
+ _hasOwnProperty = Object.prototype.hasOwnProperty,
+ NULL_TYPE = 'Null',
+ UNDEFINED_TYPE = 'Undefined',
+ BOOLEAN_TYPE = 'Boolean',
+ NUMBER_TYPE = 'Number',
+ STRING_TYPE = 'String',
+ OBJECT_TYPE = 'Object',
+ FUNCTION_CLASS = '[object Function]',
+ BOOLEAN_CLASS = '[object Boolean]',
+ NUMBER_CLASS = '[object Number]',
+ STRING_CLASS = '[object String]',
+ ARRAY_CLASS = '[object Array]',
+ DATE_CLASS = '[object Date]',
+ NATIVE_JSON_STRINGIFY_SUPPORT =
+ window.JSON &&
+ typeof JSON.stringify === 'function' &&
+ JSON.stringify(0) === '0' &&
+ typeof JSON.stringify(Prototype.K) === 'undefined';
+
+ var DONT_ENUMS = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ];
+
+ var IS_DONTENUM_BUGGY = (function () {
+ for (var p in { toString: 1 }) {
+ if (p === 'toString') return false;
+ }
+ return true;
+ })();
+
+ function Type(o) {
+ switch (o) {
+ case null:
+ return NULL_TYPE;
+ case void 0:
+ return UNDEFINED_TYPE;
+ }
+ var type = typeof o;
+ switch (type) {
+ case 'boolean':
+ return BOOLEAN_TYPE;
+ case 'number':
+ return NUMBER_TYPE;
+ case 'string':
+ return STRING_TYPE;
+ }
+ return OBJECT_TYPE;
+ }
+
+ function extend(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+ }
+
+ function inspect(object) {
+ try {
+ if (isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ }
+
+ function toJSON(value) {
+ return Str('', { '': value }, []);
+ }
+
+ function Str(key, holder, stack) {
+ var value = holder[key];
+ if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+
+ var _class = _toString.call(value);
+
+ switch (_class) {
+ case NUMBER_CLASS:
+ case BOOLEAN_CLASS:
+ case STRING_CLASS:
+ value = value.valueOf();
+ }
+
+ switch (value) {
+ case null:
+ return 'null';
+ case true:
+ return 'true';
+ case false:
+ return 'false';
+ }
+
+ var type = typeof value;
+ switch (type) {
+ case 'string':
+ return value.inspect(true);
+ case 'number':
+ return isFinite(value) ? String(value) : 'null';
+ case 'object':
+ for (var i = 0, length = stack.length; i < length; i++) {
+ if (stack[i] === value) {
+ throw new TypeError(
+ "Cyclic reference to '" + value + "' in object"
+ );
+ }
+ }
+ stack.push(value);
+
+ var partial = [];
+ if (_class === ARRAY_CLASS) {
+ for (var i = 0, length = value.length; i < length; i++) {
+ var str = Str(i, value, stack);
+ partial.push(typeof str === 'undefined' ? 'null' : str);
+ }
+ partial = '[' + partial.join(',') + ']';
+ } else {
+ var keys = Object.keys(value);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i],
+ str = Str(key, value, stack);
+ if (typeof str !== 'undefined') {
+ partial.push(key.inspect(true) + ':' + str);
+ }
+ }
+ partial = '{' + partial.join(',') + '}';
+ }
+ stack.pop();
+ return partial;
+ }
+ }
+
+ function stringify(object) {
+ return JSON.stringify(object);
+ }
+
+ function toQueryString(object) {
+ return $H(object).toQueryString();
+ }
+
+ function toHTML(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ }
+
+ function keys(object) {
+ if (Type(object) !== OBJECT_TYPE) {
+ throw new TypeError();
+ }
+ var results = [];
+ for (var property in object) {
+ if (_hasOwnProperty.call(object, property)) results.push(property);
+ }
+
+ if (IS_DONTENUM_BUGGY) {
+ for (var i = 0; (property = DONT_ENUMS[i]); i++) {
+ if (_hasOwnProperty.call(object, property)) results.push(property);
+ }
+ }
+
+ return results;
+ }
+
+ function values(object) {
+ var results = [];
+ for (var property in object) results.push(object[property]);
+ return results;
+ }
+
+ function clone(object) {
+ return extend({}, object);
+ }
+
+ function isElement(object) {
+ return !!(object && object.nodeType == 1);
+ }
+
+ function isArray(object) {
+ return _toString.call(object) === ARRAY_CLASS;
+ }
+
+ var hasNativeIsArray =
+ typeof Array.isArray == 'function' &&
+ Array.isArray([]) &&
+ !Array.isArray({});
+
+ if (hasNativeIsArray) {
+ isArray = Array.isArray;
+ }
+
+ function isHash(object) {
+ return object instanceof Hash;
+ }
+
+ function isFunction(object) {
+ return _toString.call(object) === FUNCTION_CLASS;
+ }
+
+ function isString(object) {
+ return _toString.call(object) === STRING_CLASS;
+ }
+
+ function isNumber(object) {
+ return _toString.call(object) === NUMBER_CLASS;
+ }
+
+ function isDate(object) {
+ return _toString.call(object) === DATE_CLASS;
+ }
+
+ function isUndefined(object) {
+ return typeof object === 'undefined';
+ }
+
+ extend(Object, {
+ extend: extend,
+ inspect: inspect,
+ toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
+ toQueryString: toQueryString,
+ toHTML: toHTML,
+ keys: Object.keys || keys,
+ values: values,
+ clone: clone,
+ isElement: isElement,
+ isArray: isArray,
+ isHash: isHash,
+ isFunction: isFunction,
+ isString: isString,
+ isNumber: isNumber,
+ isDate: isDate,
+ isUndefined: isUndefined
+ });
+})();
+Object.extend(
+ Function.prototype,
+ (function () {
+ var slice = Array.prototype.slice;
+
+ function update(array, args) {
+ var arrayLength = array.length,
+ length = args.length;
+ while (length--) array[arrayLength + length] = args[length];
+ return array;
+ }
+
+ function merge(array, args) {
+ array = slice.call(array, 0);
+ return update(array, args);
+ }
+
+ function argumentNames() {
+ var names = this.toString()
+ .match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
+ .replace(/\s+/g, '')
+ .split(',');
+ return names.length == 1 && !names[0] ? [] : names;
+ }
+
+ function bind(context) {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+
+ if (!Object.isFunction(this))
+ throw new TypeError('The object is not callable.');
+
+ var nop = function () {};
+ var __method = this,
+ args = slice.call(arguments, 1);
+
+ var bound = function () {
+ var a = merge(args, arguments);
+ var c = this instanceof bound ? this : context;
+ return __method.apply(c, a);
+ };
+
+ nop.prototype = this.prototype;
+ bound.prototype = new nop();
+
+ return bound;
+ }
+
+ function bindAsEventListener(context) {
+ var __method = this,
+ args = slice.call(arguments, 1);
+ return function (event) {
+ var a = update([event || window.event], args);
+ return __method.apply(context, a);
+ };
+ }
+
+ function curry() {
+ if (!arguments.length) return this;
+ var __method = this,
+ args = slice.call(arguments, 0);
+ return function () {
+ var a = merge(args, arguments);
+ return __method.apply(this, a);
+ };
+ }
+
+ function delay(timeout) {
+ var __method = this,
+ args = slice.call(arguments, 1);
+ timeout = timeout * 1000;
+ return window.setTimeout(function () {
+ return __method.apply(__method, args);
+ }, timeout);
+ }
+
+ function defer() {
+ var args = update([0.01], arguments);
+ return this.delay.apply(this, args);
+ }
+
+ function wrap(wrapper) {
+ var __method = this;
+ return function () {
+ var a = update([__method.bind(this)], arguments);
+ return wrapper.apply(this, a);
+ };
+ }
+
+ function methodize() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return (this._methodized = function () {
+ var a = update([this], arguments);
+ return __method.apply(null, a);
+ });
+ }
+
+ var extensions = {
+ argumentNames: argumentNames,
+ bindAsEventListener: bindAsEventListener,
+ curry: curry,
+ delay: delay,
+ defer: defer,
+ wrap: wrap,
+ methodize: methodize
+ };
+
+ if (!Function.prototype.bind) extensions.bind = bind;
+
+ return extensions;
+ })()
+);
+
+(function (proto) {
+ function toISOString() {
+ return (
+ this.getUTCFullYear() +
+ '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) +
+ '-' +
+ this.getUTCDate().toPaddedString(2) +
+ 'T' +
+ this.getUTCHours().toPaddedString(2) +
+ ':' +
+ this.getUTCMinutes().toPaddedString(2) +
+ ':' +
+ this.getUTCSeconds().toPaddedString(2) +
+ 'Z'
+ );
+ }
+
+ function toJSON() {
+ return this.toISOString();
+ }
+
+ if (!proto.toISOString) proto.toISOString = toISOString;
+ if (!proto.toJSON) proto.toJSON = toJSON;
+})(Date.prototype);
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function (str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+var PeriodicalExecuter = Class.create({
+ initialize: function (callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function () {
+ this.timer = setInterval(
+ this.onTimerEvent.bind(this),
+ this.frequency * 1000
+ );
+ },
+
+ execute: function () {
+ this.callback(this);
+ },
+
+ stop: function () {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function () {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ this.currentlyExecuting = false;
+ } catch (e) {
+ this.currentlyExecuting = false;
+ throw e;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function (value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(
+ String.prototype,
+ (function () {
+ var NATIVE_JSON_PARSE_SUPPORT =
+ window.JSON &&
+ typeof JSON.parse === 'function' &&
+ JSON.parse('{"test": true}').test;
+
+ function prepareReplacement(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function (match) {
+ return template.evaluate(match);
+ };
+ }
+
+ function isNonEmptyRegExp(regexp) {
+ return regexp.source && regexp.source !== '(?:)';
+ }
+
+ function gsub(pattern, replacement) {
+ var result = '',
+ source = this,
+ match;
+ replacement = prepareReplacement(replacement);
+
+ if (Object.isString(pattern)) pattern = RegExp.escape(pattern);
+
+ if (!(pattern.length || isNonEmptyRegExp(pattern))) {
+ replacement = replacement('');
+ return replacement + source.split('').join(replacement) + replacement;
+ }
+
+ while (source.length > 0) {
+ match = source.match(pattern);
+ if (match && match[0].length > 0) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ (result += source), (source = '');
+ }
+ }
+ return result;
+ }
+
+ function sub(pattern, replacement, count) {
+ replacement = prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function (match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ }
+
+ function scan(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ }
+
+ function truncate(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length
+ ? this.slice(0, length - truncation.length) + truncation
+ : String(this);
+ }
+
+ function strip() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ }
+
+ function stripTags() {
+ return this.replace(
+ /<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi,
+ ''
+ );
+ }
+
+ function stripScripts() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ }
+
+ function extractScripts() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
+ matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function (scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ }
+
+ function evalScripts() {
+ return this.extractScripts().map(function (script) {
+ return eval(script);
+ });
+ }
+
+ function escapeHTML() {
+ return this.replace(/&/g, '&')
+ .replace(//g, '>');
+ }
+
+ function unescapeHTML() {
+ return this.stripTags()
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/&/g, '&');
+ }
+
+ function toQueryParams(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return {};
+
+ return match[1].split(separator || '&').inject({}, function (hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift()),
+ value = pair.length > 1 ? pair.join('=') : pair[0];
+
+ if (value != undefined) {
+ value = value.gsub('+', ' ');
+ value = decodeURIComponent(value);
+ }
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ } else hash[key] = value;
+ }
+ return hash;
+ });
+ }
+
+ function toArray() {
+ return this.split('');
+ }
+
+ function succ() {
+ return (
+ this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1)
+ );
+ }
+
+ function times(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ }
+
+ function camelize() {
+ return this.replace(/-+(.)?/g, function (match, chr) {
+ return chr ? chr.toUpperCase() : '';
+ });
+ }
+
+ function capitalize() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ }
+
+ function underscore() {
+ return this.replace(/::/g, '/')
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+ .replace(/-/g, '_')
+ .toLowerCase();
+ }
+
+ function dasherize() {
+ return this.replace(/_/g, '-');
+ }
+
+ function inspect(useDoubleQuotes) {
+ var escapedString = this.replace(/[\x00-\x1f\\]/g, function (character) {
+ if (character in String.specialChar) {
+ return String.specialChar[character];
+ }
+ return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes)
+ return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, "\\'") + "'";
+ }
+
+ function unfilterJSON(filter) {
+ return this.replace(filter || Prototype.JSONFilter, '$1');
+ }
+
+ function isJSON() {
+ var str = this;
+ if (str.blank()) return false;
+ str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
+ str = str.replace(
+ /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ ']'
+ );
+ str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+ return /^[\],:{}\s]*$/.test(str);
+ }
+
+ function evalJSON(sanitize) {
+ var json = this.unfilterJSON(),
+ cx =
+ /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g;
+ if (cx.test(json)) {
+ json = json.replace(cx, function (a) {
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ });
+ }
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) {}
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ }
+
+ function parseJSON() {
+ var json = this.unfilterJSON();
+ return JSON.parse(json);
+ }
+
+ function include(pattern) {
+ return this.indexOf(pattern) > -1;
+ }
+
+ function startsWith(pattern, position) {
+ position = Object.isNumber(position) ? position : 0;
+ return this.lastIndexOf(pattern, position) === position;
+ }
+
+ function endsWith(pattern, position) {
+ pattern = String(pattern);
+ position = Object.isNumber(position) ? position : this.length;
+ if (position < 0) position = 0;
+ if (position > this.length) position = this.length;
+ var d = position - pattern.length;
+ return d >= 0 && this.indexOf(pattern, d) === d;
+ }
+
+ function empty() {
+ return this == '';
+ }
+
+ function blank() {
+ return /^\s*$/.test(this);
+ }
+
+ function interpolate(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+
+ return {
+ gsub: gsub,
+ sub: sub,
+ scan: scan,
+ truncate: truncate,
+ strip: String.prototype.trim || strip,
+ stripTags: stripTags,
+ stripScripts: stripScripts,
+ extractScripts: extractScripts,
+ evalScripts: evalScripts,
+ escapeHTML: escapeHTML,
+ unescapeHTML: unescapeHTML,
+ toQueryParams: toQueryParams,
+ parseQuery: toQueryParams,
+ toArray: toArray,
+ succ: succ,
+ times: times,
+ camelize: camelize,
+ capitalize: capitalize,
+ underscore: underscore,
+ dasherize: dasherize,
+ inspect: inspect,
+ unfilterJSON: unfilterJSON,
+ isJSON: isJSON,
+ evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
+ include: include,
+ startsWith: String.prototype.startsWith || startsWith,
+ endsWith: String.prototype.endsWith || endsWith,
+ empty: empty,
+ blank: blank,
+ interpolate: interpolate
+ };
+ })()
+);
+
+var Template = Class.create({
+ initialize: function (template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function (object) {
+ if (object && Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function (match) {
+ if (object == null) return match[1] + '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object,
+ expr = match[3],
+ pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[')
+ ? match[2].replace(/\\\\]/g, ']')
+ : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring(
+ '[' == match[3] ? match[1].length : match[0].length
+ );
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = {};
+
+var Enumerable = (function () {
+ function each(iterator, context) {
+ try {
+ this._each(iterator, context);
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ }
+
+ function eachSlice(number, iterator, context) {
+ var index = -number,
+ slices = [],
+ array = this.toArray();
+ if (number < 1) return array;
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index + number));
+ return slices.collect(iterator, context);
+ }
+
+ function all(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = true;
+ this.each(function (value, index) {
+ result = result && !!iterator.call(context, value, index, this);
+ if (!result) throw $break;
+ }, this);
+ return result;
+ }
+
+ function any(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = false;
+ this.each(function (value, index) {
+ if ((result = !!iterator.call(context, value, index, this))) throw $break;
+ }, this);
+ return result;
+ }
+
+ function collect(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+ this.each(function (value, index) {
+ results.push(iterator.call(context, value, index, this));
+ }, this);
+ return results;
+ }
+
+ function detect(iterator, context) {
+ var result;
+ this.each(function (value, index) {
+ if (iterator.call(context, value, index, this)) {
+ result = value;
+ throw $break;
+ }
+ }, this);
+ return result;
+ }
+
+ function findAll(iterator, context) {
+ var results = [];
+ this.each(function (value, index) {
+ if (iterator.call(context, value, index, this)) results.push(value);
+ }, this);
+ return results;
+ }
+
+ function grep(filter, iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter)) filter = new RegExp(RegExp.escape(filter));
+
+ this.each(function (value, index) {
+ if (filter.match(value))
+ results.push(iterator.call(context, value, index, this));
+ }, this);
+ return results;
+ }
+
+ function include(object) {
+ if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1)
+ return true;
+
+ var found = false;
+ this.each(function (value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ }
+
+ function inGroupsOf(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function (slice) {
+ while (slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ }
+
+ function inject(memo, iterator, context) {
+ this.each(function (value, index) {
+ memo = iterator.call(context, memo, value, index, this);
+ }, this);
+ return memo;
+ }
+
+ function invoke(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function (value) {
+ return value[method].apply(value, args);
+ });
+ }
+
+ function max(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function (value, index) {
+ value = iterator.call(context, value, index, this);
+ if (result == null || value >= result) result = value;
+ }, this);
+ return result;
+ }
+
+ function min(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function (value, index) {
+ value = iterator.call(context, value, index, this);
+ if (result == null || value < result) result = value;
+ }, this);
+ return result;
+ }
+
+ function partition(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var trues = [],
+ falses = [];
+ this.each(function (value, index) {
+ (iterator.call(context, value, index, this) ? trues : falses).push(value);
+ }, this);
+ return [trues, falses];
+ }
+
+ function pluck(property) {
+ var results = [];
+ this.each(function (value) {
+ results.push(value[property]);
+ });
+ return results;
+ }
+
+ function reject(iterator, context) {
+ var results = [];
+ this.each(function (value, index) {
+ if (!iterator.call(context, value, index, this)) results.push(value);
+ }, this);
+ return results;
+ }
+
+ function sortBy(iterator, context) {
+ return this.map(function (value, index) {
+ return {
+ value: value,
+ criteria: iterator.call(context, value, index, this)
+ };
+ }, this)
+ .sort(function (left, right) {
+ var a = left.criteria,
+ b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ })
+ .pluck('value');
+ }
+
+ function toArray() {
+ return this.map();
+ }
+
+ function zip() {
+ var iterator = Prototype.K,
+ args = $A(arguments);
+ if (Object.isFunction(args.last())) iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function (value, index) {
+ return iterator(collections.pluck(index));
+ });
+ }
+
+ function size() {
+ return this.toArray().length;
+ }
+
+ function inspect() {
+ return '#';
+ }
+
+ return {
+ each: each,
+ eachSlice: eachSlice,
+ all: all,
+ every: all,
+ any: any,
+ some: any,
+ collect: collect,
+ map: collect,
+ detect: detect,
+ findAll: findAll,
+ select: findAll,
+ filter: findAll,
+ grep: grep,
+ include: include,
+ member: include,
+ inGroupsOf: inGroupsOf,
+ inject: inject,
+ invoke: invoke,
+ max: max,
+ min: min,
+ partition: partition,
+ pluck: pluck,
+ reject: reject,
+ sortBy: sortBy,
+ toArray: toArray,
+ entries: toArray,
+ zip: zip,
+ size: size,
+ inspect: inspect,
+ find: detect
+ };
+})();
+
+function $A(iterable) {
+ if (!iterable) return [];
+ if ('toArray' in Object(iterable)) return iterable.toArray();
+ var length = iterable.length || 0,
+ results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+Array.from = $A;
+
+(function () {
+ var arrayProto = Array.prototype,
+ slice = arrayProto.slice,
+ _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
+
+ function each(iterator, context) {
+ for (var i = 0, length = this.length >>> 0; i < length; i++) {
+ if (i in this) iterator.call(context, this[i], i, this);
+ }
+ }
+ if (!_each) _each = each;
+
+ function clear() {
+ this.length = 0;
+ return this;
+ }
+
+ function first() {
+ return this[0];
+ }
+
+ function last() {
+ return this[this.length - 1];
+ }
+
+ function compact() {
+ return this.select(function (value) {
+ return value != null;
+ });
+ }
+
+ function flatten() {
+ return this.inject([], function (array, value) {
+ if (Object.isArray(value)) return array.concat(value.flatten());
+ array.push(value);
+ return array;
+ });
+ }
+
+ function without() {
+ var values = slice.call(arguments, 0);
+ return this.select(function (value) {
+ return !values.include(value);
+ });
+ }
+
+ function reverse(inline) {
+ return (inline === false ? this.toArray() : this)._reverse();
+ }
+
+ function uniq(sorted) {
+ return this.inject([], function (array, value, index) {
+ if (
+ 0 == index ||
+ (sorted ? array.last() != value : !array.include(value))
+ )
+ array.push(value);
+ return array;
+ });
+ }
+
+ function intersect(array) {
+ return this.uniq().findAll(function (item) {
+ return array.indexOf(item) !== -1;
+ });
+ }
+
+ function clone() {
+ return slice.call(this, 0);
+ }
+
+ function size() {
+ return this.length;
+ }
+
+ function inspect() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+
+ function indexOf(item, i) {
+ if (this == null) throw new TypeError();
+
+ var array = Object(this),
+ length = array.length >>> 0;
+ if (length === 0) return -1;
+
+ i = Number(i);
+ if (isNaN(i)) {
+ i = 0;
+ } else if (i !== 0 && isFinite(i)) {
+ i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
+ }
+
+ if (i > length) return -1;
+
+ var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0);
+ for (; k < length; k++) if (k in array && array[k] === item) return k;
+ return -1;
+ }
+
+ function lastIndexOf(item, i) {
+ if (this == null) throw new TypeError();
+
+ var array = Object(this),
+ length = array.length >>> 0;
+ if (length === 0) return -1;
+
+ if (!Object.isUndefined(i)) {
+ i = Number(i);
+ if (isNaN(i)) {
+ i = 0;
+ } else if (i !== 0 && isFinite(i)) {
+ i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
+ }
+ } else {
+ i = length;
+ }
+
+ var k = i >= 0 ? Math.min(i, length - 1) : length - Math.abs(i);
+
+ for (; k >= 0; k--) if (k in array && array[k] === item) return k;
+ return -1;
+ }
+
+ function concat(_) {
+ var array = [],
+ items = slice.call(arguments, 0),
+ item,
+ n = 0;
+ items.unshift(this);
+ for (var i = 0, length = items.length; i < length; i++) {
+ item = items[i];
+ if (Object.isArray(item) && !('callee' in item)) {
+ for (var j = 0, arrayLength = item.length; j < arrayLength; j++) {
+ if (j in item) array[n] = item[j];
+ n++;
+ }
+ } else {
+ array[n++] = item;
+ }
+ }
+ array.length = n;
+ return array;
+ }
+
+ function wrapNative(method) {
+ return function () {
+ if (arguments.length === 0) {
+ return method.call(this, Prototype.K);
+ } else if (arguments[0] === undefined) {
+ var args = slice.call(arguments, 1);
+ args.unshift(Prototype.K);
+ return method.apply(this, args);
+ } else {
+ return method.apply(this, arguments);
+ }
+ };
+ }
+
+ function map(iterator) {
+ if (this == null) throw new TypeError();
+ iterator = iterator || Prototype.K;
+
+ var object = Object(this);
+ var results = [],
+ context = arguments[1],
+ n = 0;
+
+ for (var i = 0, length = object.length >>> 0; i < length; i++) {
+ if (i in object) {
+ results[n] = iterator.call(context, object[i], i, object);
+ }
+ n++;
+ }
+ results.length = n;
+ return results;
+ }
+
+ if (arrayProto.map) {
+ map = wrapNative(Array.prototype.map);
+ }
+
+ function filter(iterator) {
+ if (this == null || !Object.isFunction(iterator)) throw new TypeError();
+
+ var object = Object(this);
+ var results = [],
+ context = arguments[1],
+ value;
+
+ for (var i = 0, length = object.length >>> 0; i < length; i++) {
+ if (i in object) {
+ value = object[i];
+ if (iterator.call(context, value, i, object)) {
+ results.push(value);
+ }
+ }
+ }
+ return results;
+ }
+
+ if (arrayProto.filter) {
+ filter = Array.prototype.filter;
+ }
+
+ function some(iterator) {
+ if (this == null) throw new TypeError();
+ iterator = iterator || Prototype.K;
+ var context = arguments[1];
+
+ var object = Object(this);
+ for (var i = 0, length = object.length >>> 0; i < length; i++) {
+ if (i in object && iterator.call(context, object[i], i, object)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (arrayProto.some) {
+ some = wrapNative(Array.prototype.some);
+ }
+
+ function every(iterator) {
+ if (this == null) throw new TypeError();
+ iterator = iterator || Prototype.K;
+ var context = arguments[1];
+
+ var object = Object(this);
+ for (var i = 0, length = object.length >>> 0; i < length; i++) {
+ if (i in object && !iterator.call(context, object[i], i, object)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (arrayProto.every) {
+ every = wrapNative(Array.prototype.every);
+ }
+
+ Object.extend(arrayProto, Enumerable);
+
+ if (arrayProto.entries === Enumerable.entries) {
+ delete arrayProto.entries;
+ }
+
+ if (!arrayProto._reverse) arrayProto._reverse = arrayProto.reverse;
+
+ Object.extend(arrayProto, {
+ _each: _each,
+
+ map: map,
+ collect: map,
+ select: filter,
+ filter: filter,
+ findAll: filter,
+ some: some,
+ any: some,
+ every: every,
+ all: every,
+
+ clear: clear,
+ first: first,
+ last: last,
+ compact: compact,
+ flatten: flatten,
+ without: without,
+ reverse: reverse,
+ uniq: uniq,
+ intersect: intersect,
+ clone: clone,
+ toArray: clone,
+ size: size,
+ inspect: inspect
+ });
+
+ var CONCAT_ARGUMENTS_BUGGY = (function () {
+ return [].concat(arguments)[0][0] !== 1;
+ })(1, 2);
+
+ if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
+
+ if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
+ if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
+})();
+function $H(object) {
+ return new Hash(object);
+}
+
+var Hash = Class.create(
+ Enumerable,
+ (function () {
+ function initialize(object) {
+ this._object = Object.isHash(object)
+ ? object.toObject()
+ : Object.clone(object);
+ }
+
+ function _each(iterator, context) {
+ var i = 0;
+ for (var key in this._object) {
+ var value = this._object[key],
+ pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator.call(context, pair, i);
+ i++;
+ }
+ }
+
+ function set(key, value) {
+ return (this._object[key] = value);
+ }
+
+ function get(key) {
+ if (this._object[key] !== Object.prototype[key]) return this._object[key];
+ }
+
+ function unset(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ }
+
+ function toObject() {
+ return Object.clone(this._object);
+ }
+
+ function keys() {
+ return this.pluck('key');
+ }
+
+ function values() {
+ return this.pluck('value');
+ }
+
+ function index(value) {
+ var match = this.detect(function (pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ }
+
+ function merge(object) {
+ return this.clone().update(object);
+ }
+
+ function update(object) {
+ return new Hash(object).inject(this, function (result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ }
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+
+ value = String.interpret(value);
+
+ value = value.gsub(/(\r)?\n/, '\r\n');
+ value = encodeURIComponent(value);
+ value = value.gsub(/%20/, '+');
+ return key + '=' + value;
+ }
+
+ function toQueryString() {
+ return this.inject([], function (results, pair) {
+ var key = encodeURIComponent(pair.key),
+ values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values)) {
+ var queryValues = [];
+ for (var i = 0, len = values.length, value; i < len; i++) {
+ value = values[i];
+ queryValues.push(toQueryPair(key, value));
+ }
+ return results.concat(queryValues);
+ }
+ } else results.push(toQueryPair(key, values));
+ return results;
+ }).join('&');
+ }
+
+ function inspect() {
+ return (
+ '#'
+ );
+ }
+
+ function clone() {
+ return new Hash(this);
+ }
+
+ return {
+ initialize: initialize,
+ _each: _each,
+ set: set,
+ get: get,
+ unset: unset,
+ toObject: toObject,
+ toTemplateReplacements: toObject,
+ keys: keys,
+ values: values,
+ index: index,
+ merge: merge,
+ update: update,
+ toQueryString: toQueryString,
+ inspect: inspect,
+ toJSON: toObject,
+ clone: clone
+ };
+ })()
+);
+
+Hash.from = $H;
+Object.extend(
+ Number.prototype,
+ (function () {
+ function toColorPart() {
+ return this.toPaddedString(2, 16);
+ }
+
+ function succ() {
+ return this + 1;
+ }
+
+ function times(iterator, context) {
+ $R(0, this, true).each(iterator, context);
+ return this;
+ }
+
+ function toPaddedString(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ }
+
+ function abs() {
+ return Math.abs(this);
+ }
+
+ function round() {
+ return Math.round(this);
+ }
+
+ function ceil() {
+ return Math.ceil(this);
+ }
+
+ function floor() {
+ return Math.floor(this);
+ }
+
+ return {
+ toColorPart: toColorPart,
+ succ: succ,
+ times: times,
+ toPaddedString: toPaddedString,
+ abs: abs,
+ round: round,
+ ceil: ceil,
+ floor: floor
+ };
+ })()
+);
+
+function $R(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var ObjectRange = Class.create(
+ Enumerable,
+ (function () {
+ function initialize(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ }
+
+ function _each(iterator, context) {
+ var value = this.start,
+ i;
+ for (i = 0; this.include(value); i++) {
+ iterator.call(context, value, i);
+ value = value.succ();
+ }
+ }
+
+ function include(value) {
+ if (value < this.start) return false;
+ if (this.exclusive) return value < this.end;
+ return value <= this.end;
+ }
+
+ return {
+ initialize: initialize,
+ _each: _each,
+ include: include
+ };
+ })()
+);
+
+var Abstract = {};
+
+var Try = {
+ these: function () {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+};
+
+var Ajax = {
+ getTransport: function () {
+ return (
+ Try.these(
+ function () {
+ return new XMLHttpRequest();
+ },
+ function () {
+ return new ActiveXObject('Msxml2.XMLHTTP');
+ },
+ function () {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ ) || false
+ );
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function (iterator, context) {
+ this.responders._each(iterator, context);
+ },
+
+ register: function (responder) {
+ if (!this.include(responder)) this.responders.push(responder);
+ },
+
+ unregister: function (responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function (callback, request, transport, json) {
+ this.each(function (responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function () {
+ Ajax.activeRequestCount++;
+ },
+ onComplete: function () {
+ Ajax.activeRequestCount--;
+ }
+});
+Ajax.Base = Class.create({
+ initialize: function (options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || {});
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function ($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function (url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.isString(this.options.parameters)
+ ? this.options.parameters
+ : Object.toQueryString(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ params += (params ? '&' : '') + '_method=' + this.method;
+ this.method = 'post';
+ }
+
+ if (params && this.method === 'get') {
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ }
+
+ this.parameters = params.toQueryParams();
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(
+ this.method.toUpperCase(),
+ this.url,
+ this.options.asynchronous
+ );
+
+ if (this.options.asynchronous)
+ this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body =
+ this.method == 'post' ? this.options.postBody || params : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function () {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !(readyState == 4 && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function () {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ Accept: 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] =
+ this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (
+ this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0, 2005])[1] < 2005
+ )
+ headers['Connection'] = 'close';
+ }
+
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i + 1];
+ else
+ $H(extras).each(function (pair) {
+ headers[pair.key] = pair.value;
+ });
+ }
+
+ for (var name in headers)
+ if (headers[name] != null)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function () {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300) || status == 304;
+ },
+
+ getStatus: function () {
+ try {
+ if (this.transport.status === 1223) return 204;
+ return this.transport.status || 0;
+ } catch (e) {
+ return 0;
+ }
+ },
+
+ respondToReadyState: function (readyState) {
+ var state = Ajax.Request.Events[readyState],
+ response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (
+ this.options['on' + response.status] ||
+ this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
+ Prototype.emptyFunction
+ )(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (
+ this.options.evalJS == 'force' ||
+ (this.options.evalJS &&
+ this.isSameOrigin() &&
+ contentType &&
+ contentType.match(
+ /^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i
+ ))
+ )
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(
+ response,
+ response.headerJSON
+ );
+ Ajax.Responders.dispatch(
+ 'on' + state,
+ this,
+ response,
+ response.headerJSON
+ );
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function () {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return (
+ !m ||
+ m[0] ==
+ '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ })
+ );
+ },
+
+ getHeader: function (name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) {
+ return null;
+ }
+ },
+
+ evalResponse: function () {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function (exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events = [
+ 'Uninitialized',
+ 'Loading',
+ 'Loaded',
+ 'Interactive',
+ 'Complete'
+];
+
+Ajax.Response = Class.create({
+ initialize: function (request) {
+ this.request = request;
+ var transport = (this.transport = request.transport),
+ readyState = (this.readyState = transport.readyState);
+
+ if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if (readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function () {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) {
+ return '';
+ }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function () {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) {
+ return null;
+ }
+ },
+
+ getResponseHeader: function (name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function () {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function () {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+
+ try {
+ json = decodeURIComponent(escape(json));
+ } catch (e) {}
+
+ try {
+ return json.evalJSON(
+ this.request.options.sanitizeJSON || !this.request.isSameOrigin()
+ );
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function () {
+ var options = this.request.options;
+ if (
+ !options.evalJSON ||
+ (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank()
+ )
+ return null;
+ try {
+ return this.responseText.evalJSON(
+ options.sanitizeJSON || !this.request.isSameOrigin()
+ );
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function ($super, container, url, options) {
+ this.container = {
+ success: container.success || container,
+ failure: container.failure || (container.success ? null : container)
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = function (response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }.bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function (responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if ((receiver = $(receiver))) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = {};
+ insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ } else options.insertion(receiver, responseText);
+ } else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function ($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = this.options.frequency || 2;
+ this.decay = this.options.decay || 1;
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function () {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function () {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function (response) {
+ if (this.options.decay) {
+ this.decay =
+ response.responseText == this.lastText
+ ? this.decay * this.options.decay
+ : 1;
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent
+ .bind(this)
+ .delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function () {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+
+(function (GLOBAL) {
+ var UNDEFINED;
+ var SLICE = Array.prototype.slice;
+
+ var DIV = document.createElement('div');
+
+ function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+
+ if (Object.isString(element)) element = document.getElementById(element);
+ return Element.extend(element);
+ }
+
+ GLOBAL.$ = $;
+
+ if (!GLOBAL.Node) GLOBAL.Node = {};
+
+ if (!GLOBAL.Node.ELEMENT_NODE) {
+ Object.extend(GLOBAL.Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+ }
+
+ var ELEMENT_CACHE = {};
+
+ function shouldUseCreationCache(tagName, attributes) {
+ if (tagName === 'select') return false;
+ if ('type' in attributes) return false;
+ return true;
+ }
+
+ var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function () {
+ try {
+ var el = document.createElement('');
+ return el.tagName.toLowerCase() === 'input' && el.name === 'x';
+ } catch (err) {
+ return false;
+ }
+ })();
+
+ var oldElement = GLOBAL.Element;
+ function Element(tagName, attributes) {
+ attributes = attributes || {};
+ tagName = tagName.toLowerCase();
+
+ if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(
+ document.createElement(tagName),
+ attributes
+ );
+ }
+
+ if (!ELEMENT_CACHE[tagName])
+ ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName));
+
+ var node = shouldUseCreationCache(tagName, attributes)
+ ? ELEMENT_CACHE[tagName].cloneNode(false)
+ : document.createElement(tagName);
+
+ return Element.writeAttribute(node, attributes);
+ }
+
+ GLOBAL.Element = Element;
+
+ Object.extend(GLOBAL.Element, oldElement || {});
+ if (oldElement) GLOBAL.Element.prototype = oldElement.prototype;
+
+ Element.Methods = { ByTag: {}, Simulated: {} };
+
+ var methods = {};
+
+ var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' };
+ function inspect(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+
+ var attribute, value;
+ for (var property in INSPECT_ATTRIBUTES) {
+ attribute = INSPECT_ATTRIBUTES[property];
+ value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ }
+
+ return result + '>';
+ }
+
+ methods.inspect = inspect;
+
+ function visible(element) {
+ return $(element).getStyle('display') !== 'none';
+ }
+
+ function toggle(element, bool) {
+ element = $(element);
+ if (typeof bool !== 'boolean') bool = !Element.visible(element);
+ Element[bool ? 'show' : 'hide'](element);
+
+ return element;
+ }
+
+ function hide(element) {
+ element = $(element);
+ element.style.display = 'none';
+ return element;
+ }
+
+ function show(element) {
+ element = $(element);
+ element.style.display = '';
+ return element;
+ }
+
+ Object.extend(methods, {
+ visible: visible,
+ toggle: toggle,
+ hide: hide,
+ show: show
+ });
+
+ function remove(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ }
+
+ var SELECT_ELEMENT_INNERHTML_BUGGY = (function () {
+ var el = document.createElement('select'),
+ isBuggy = true;
+ el.innerHTML = '';
+ if (el.options && el.options[0]) {
+ isBuggy = el.options[0].nodeName.toUpperCase() !== 'OPTION';
+ }
+ el = null;
+ return isBuggy;
+ })();
+
+ var TABLE_ELEMENT_INNERHTML_BUGGY = (function () {
+ try {
+ var el = document.createElement('table');
+ if (el && el.tBodies) {
+ el.innerHTML = '
test
';
+ var isBuggy = typeof el.tBodies[0] == 'undefined';
+ el = null;
+ return isBuggy;
+ }
+ } catch (e) {
+ return true;
+ }
+ })();
+
+ var LINK_ELEMENT_INNERHTML_BUGGY = (function () {
+ try {
+ var el = document.createElement('div');
+ el.innerHTML = '';
+ var isBuggy = el.childNodes.length === 0;
+ el = null;
+ return isBuggy;
+ } catch (e) {
+ return true;
+ }
+ })();
+
+ var ANY_INNERHTML_BUGGY =
+ SELECT_ELEMENT_INNERHTML_BUGGY ||
+ TABLE_ELEMENT_INNERHTML_BUGGY ||
+ LINK_ELEMENT_INNERHTML_BUGGY;
+
+ var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
+ var s = document.createElement('script'),
+ isBuggy = false;
+ try {
+ s.appendChild(document.createTextNode(''));
+ isBuggy = !s.firstChild || (s.firstChild && s.firstChild.nodeType !== 3);
+ } catch (e) {
+ isBuggy = true;
+ }
+ s = null;
+ return isBuggy;
+ })();
+
+ function update(element, content) {
+ element = $(element);
+
+ var descendants = element.getElementsByTagName('*'),
+ i = descendants.length;
+ while (i--) purgeElement(descendants[i]);
+
+ if (content && content.toElement) content = content.toElement();
+
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
+ element.text = content;
+ return element;
+ }
+
+ if (ANY_INNERHTML_BUGGY) {
+ if (tagName in INSERTION_TRANSLATIONS.tags) {
+ while (element.firstChild) element.removeChild(element.firstChild);
+
+ var nodes = getContentFromAnonymousElement(
+ tagName,
+ content.stripScripts()
+ );
+ for (var i = 0, node; (node = nodes[i]); i++) element.appendChild(node);
+ } else if (
+ LINK_ELEMENT_INNERHTML_BUGGY &&
+ Object.isString(content) &&
+ content.indexOf(' -1
+ ) {
+ while (element.firstChild) element.removeChild(element.firstChild);
+
+ var nodes = getContentFromAnonymousElement(
+ tagName,
+ content.stripScripts(),
+ true
+ );
+
+ for (var i = 0, node; (node = nodes[i]); i++) element.appendChild(node);
+ } else {
+ element.innerHTML = content.stripScripts();
+ }
+ } else {
+ element.innerHTML = content.stripScripts();
+ }
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ }
+
+ function replace(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) {
+ content = content.toElement();
+ } else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ var INSERTION_TRANSLATIONS = {
+ before: function (element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function (element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function (element, node) {
+ element.appendChild(node);
+ },
+ after: function (element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+
+ tags: {
+ TABLE: ['
', '
', 1],
+ TBODY: ['
', '
', 2],
+ TR: ['
', '
', 3],
+ TD: ['
', '
', 4],
+ SELECT: ['', 1]
+ }
+ };
+
+ var tags = INSERTION_TRANSLATIONS.tags;
+
+ Object.extend(tags, {
+ THEAD: tags.TBODY,
+ TFOOT: tags.TBODY,
+ TH: tags.TD
+ });
+
+ function replace_IE(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode,
+ tagName = parent.tagName.toUpperCase();
+
+ if (tagName in INSERTION_TRANSLATIONS.tags) {
+ var nextSibling = Element.next(element);
+ var fragments = getContentFromAnonymousElement(
+ tagName,
+ content.stripScripts()
+ );
+
+ parent.removeChild(element);
+
+ var iterator;
+ if (nextSibling)
+ iterator = function (node) {
+ parent.insertBefore(node, nextSibling);
+ };
+ else
+ iterator = function (node) {
+ parent.appendChild(node);
+ };
+
+ fragments.each(iterator);
+ } else {
+ element.outerHTML = content.stripScripts();
+ }
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ }
+
+ if ('outerHTML' in document.documentElement) replace = replace_IE;
+
+ function isContent(content) {
+ if (Object.isUndefined(content) || content === null) return false;
+
+ if (Object.isString(content) || Object.isNumber(content)) return true;
+ if (Object.isElement(content)) return true;
+ if (content.toElement || content.toHTML) return true;
+
+ return false;
+ }
+
+ function insertContentAt(element, content, position) {
+ position = position.toLowerCase();
+ var method = INSERTION_TRANSLATIONS[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ method(element, content);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var tagName = (
+ position === 'before' || position === 'after'
+ ? element.parentNode
+ : element
+ ).tagName.toUpperCase();
+
+ var childNodes = getContentFromAnonymousElement(
+ tagName,
+ content.stripScripts()
+ );
+
+ if (position === 'top' || position === 'after') childNodes.reverse();
+
+ for (var i = 0, node; (node = childNodes[i]); i++) method(element, node);
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ function insert(element, insertions) {
+ element = $(element);
+
+ if (isContent(insertions)) insertions = { bottom: insertions };
+
+ for (var position in insertions)
+ insertContentAt(element, insertions[position], position);
+
+ return element;
+ }
+
+ function wrap(element, wrapper, attributes) {
+ element = $(element);
+
+ if (Object.isElement(wrapper)) {
+ $(wrapper).writeAttribute(attributes || {});
+ } else if (Object.isString(wrapper)) {
+ wrapper = new Element(wrapper, attributes);
+ } else {
+ wrapper = new Element('div', wrapper);
+ }
+
+ if (element.parentNode) element.parentNode.replaceChild(wrapper, element);
+
+ wrapper.appendChild(element);
+
+ return wrapper;
+ }
+
+ function cleanWhitespace(element) {
+ element = $(element);
+ var node = element.firstChild;
+
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ }
+
+ function empty(element) {
+ return $(element).innerHTML.blank();
+ }
+
+ function getContentFromAnonymousElement(tagName, html, force) {
+ var t = INSERTION_TRANSLATIONS.tags[tagName],
+ div = DIV;
+
+ var workaround = !!t;
+ if (!workaround && force) {
+ workaround = true;
+ t = ['', '', 0];
+ }
+
+ if (workaround) {
+ div.innerHTML = ' ' + t[0] + html + t[1];
+ div.removeChild(div.firstChild);
+ for (var i = t[2]; i--; ) div = div.firstChild;
+ } else {
+ div.innerHTML = html;
+ }
+
+ return $A(div.childNodes);
+ }
+
+ function clone(element, deep) {
+ if (!(element = $(element))) return;
+ var clone = element.cloneNode(deep);
+ if (!HAS_UNIQUE_ID_PROPERTY) {
+ clone._prototypeUID = UNDEFINED;
+ if (deep) {
+ var descendants = Element.select(clone, '*'),
+ i = descendants.length;
+ while (i--) descendants[i]._prototypeUID = UNDEFINED;
+ }
+ }
+ return Element.extend(clone);
+ }
+
+ function purgeElement(element) {
+ var uid = getUniqueElementID(element);
+ if (uid) {
+ Element.stopObserving(element);
+ if (!HAS_UNIQUE_ID_PROPERTY) element._prototypeUID = UNDEFINED;
+ delete Element.Storage[uid];
+ }
+ }
+
+ function purgeCollection(elements) {
+ var i = elements.length;
+ while (i--) purgeElement(elements[i]);
+ }
+
+ function purgeCollection_IE(elements) {
+ var i = elements.length,
+ element,
+ uid;
+ while (i--) {
+ element = elements[i];
+ uid = getUniqueElementID(element);
+ delete Element.Storage[uid];
+ delete Event.cache[uid];
+ }
+ }
+
+ if (HAS_UNIQUE_ID_PROPERTY) {
+ purgeCollection = purgeCollection_IE;
+ }
+
+ function purge(element) {
+ if (!(element = $(element))) return;
+ purgeElement(element);
+
+ var descendants = element.getElementsByTagName('*'),
+ i = descendants.length;
+
+ while (i--) purgeElement(descendants[i]);
+
+ return null;
+ }
+
+ Object.extend(methods, {
+ remove: remove,
+ update: update,
+ replace: replace,
+ insert: insert,
+ wrap: wrap,
+ cleanWhitespace: cleanWhitespace,
+ empty: empty,
+ clone: clone,
+ purge: purge
+ });
+
+ function recursivelyCollect(element, property, maximumLength) {
+ element = $(element);
+ maximumLength = maximumLength || -1;
+ var elements = [];
+
+ while ((element = element[property])) {
+ if (element.nodeType === Node.ELEMENT_NODE)
+ elements.push(Element.extend(element));
+
+ if (elements.length === maximumLength) break;
+ }
+
+ return elements;
+ }
+
+ function ancestors(element) {
+ return recursivelyCollect(element, 'parentNode');
+ }
+
+ function descendants(element) {
+ return Element.select(element, '*');
+ }
+
+ function firstDescendant(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType !== Node.ELEMENT_NODE)
+ element = element.nextSibling;
+
+ return $(element);
+ }
+
+ function immediateDescendants(element) {
+ var results = [],
+ child = $(element).firstChild;
+
+ while (child) {
+ if (child.nodeType === Node.ELEMENT_NODE)
+ results.push(Element.extend(child));
+
+ child = child.nextSibling;
+ }
+
+ return results;
+ }
+
+ function previousSiblings(element) {
+ return recursivelyCollect(element, 'previousSibling');
+ }
+
+ function nextSiblings(element) {
+ return recursivelyCollect(element, 'nextSibling');
+ }
+
+ function siblings(element) {
+ element = $(element);
+ var previous = previousSiblings(element),
+ next = nextSiblings(element);
+ return previous.reverse().concat(next);
+ }
+
+ function match(element, selector) {
+ element = $(element);
+
+ if (Object.isString(selector))
+ return Prototype.Selector.match(element, selector);
+
+ return selector.match(element);
+ }
+
+ function _recursivelyFind(element, property, expression, index) {
+ (element = $(element)),
+ (expression = expression || 0),
+ (index = index || 0);
+ if (Object.isNumber(expression)) {
+ (index = expression), (expression = null);
+ }
+
+ while ((element = element[property])) {
+ if (element.nodeType !== 1) continue;
+ if (expression && !Prototype.Selector.match(element, expression))
+ continue;
+ if (--index >= 0) continue;
+
+ return Element.extend(element);
+ }
+ }
+
+ function up(element, expression, index) {
+ element = $(element);
+
+ if (arguments.length === 1) return $(element.parentNode);
+ return _recursivelyFind(element, 'parentNode', expression, index);
+ }
+
+ function down(element, expression, index) {
+ if (arguments.length === 1) return firstDescendant(element);
+ (element = $(element)),
+ (expression = expression || 0),
+ (index = index || 0);
+
+ if (Object.isNumber(expression)) (index = expression), (expression = '*');
+
+ var node = Prototype.Selector.select(expression, element)[index];
+ return Element.extend(node);
+ }
+
+ function previous(element, expression, index) {
+ return _recursivelyFind(element, 'previousSibling', expression, index);
+ }
+
+ function next(element, expression, index) {
+ return _recursivelyFind(element, 'nextSibling', expression, index);
+ }
+
+ function select(element) {
+ element = $(element);
+ var expressions = SLICE.call(arguments, 1).join(', ');
+ return Prototype.Selector.select(expressions, element);
+ }
+
+ function adjacent(element) {
+ element = $(element);
+ var expressions = SLICE.call(arguments, 1).join(', ');
+ var siblings = Element.siblings(element),
+ results = [];
+ for (var i = 0, sibling; (sibling = siblings[i]); i++) {
+ if (Prototype.Selector.match(sibling, expressions)) results.push(sibling);
+ }
+
+ return results;
+ }
+
+ function descendantOf_DOM(element, ancestor) {
+ (element = $(element)), (ancestor = $(ancestor));
+ if (!element || !ancestor) return false;
+ while ((element = element.parentNode))
+ if (element === ancestor) return true;
+ return false;
+ }
+
+ function descendantOf_contains(element, ancestor) {
+ (element = $(element)), (ancestor = $(ancestor));
+ if (!element || !ancestor) return false;
+ if (!ancestor.contains) return descendantOf_DOM(element, ancestor);
+ return ancestor.contains(element) && ancestor !== element;
+ }
+
+ function descendantOf_compareDocumentPosition(element, ancestor) {
+ (element = $(element)), (ancestor = $(ancestor));
+ if (!element || !ancestor) return false;
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+ }
+
+ var descendantOf;
+ if (DIV.compareDocumentPosition) {
+ descendantOf = descendantOf_compareDocumentPosition;
+ } else if (DIV.contains) {
+ descendantOf = descendantOf_contains;
+ } else {
+ descendantOf = descendantOf_DOM;
+ }
+
+ Object.extend(methods, {
+ recursivelyCollect: recursivelyCollect,
+ ancestors: ancestors,
+ descendants: descendants,
+ firstDescendant: firstDescendant,
+ immediateDescendants: immediateDescendants,
+ previousSiblings: previousSiblings,
+ nextSiblings: nextSiblings,
+ siblings: siblings,
+ match: match,
+ up: up,
+ down: down,
+ previous: previous,
+ next: next,
+ select: select,
+ adjacent: adjacent,
+ descendantOf: descendantOf,
+
+ getElementsBySelector: select,
+
+ childElements: immediateDescendants
+ });
+
+ var idCounter = 1;
+ function identify(element) {
+ element = $(element);
+ var id = Element.readAttribute(element, 'id');
+ if (id) return id;
+
+ do {
+ id = 'anonymous_element_' + idCounter++;
+ } while ($(id));
+
+ Element.writeAttribute(element, 'id', id);
+ return id;
+ }
+
+ function readAttribute(element, name) {
+ return $(element).getAttribute(name);
+ }
+
+ function readAttribute_IE(element, name) {
+ element = $(element);
+
+ var table = ATTRIBUTE_TRANSLATIONS.read;
+ if (table.values[name]) return table.values[name](element, name);
+
+ if (table.names[name]) name = table.names[name];
+
+ if (name.include(':')) {
+ if (!element.attributes || !element.attributes[name]) return null;
+ return element.attributes[name].value;
+ }
+
+ return element.getAttribute(name);
+ }
+
+ function readAttribute_Opera(element, name) {
+ if (name === 'title') return element.title;
+ return element.getAttribute(name);
+ }
+
+ var PROBLEMATIC_ATTRIBUTE_READING = (function () {
+ DIV.setAttribute('onclick', []);
+ var value = DIV.getAttribute('onclick');
+ var isFunction = Object.isArray(value);
+ DIV.removeAttribute('onclick');
+ return isFunction;
+ })();
+
+ if (PROBLEMATIC_ATTRIBUTE_READING) {
+ readAttribute = readAttribute_IE;
+ } else if (Prototype.Browser.Opera) {
+ readAttribute = readAttribute_Opera;
+ }
+
+ function writeAttribute(element, name, value) {
+ element = $(element);
+ var attributes = {},
+ table = ATTRIBUTE_TRANSLATIONS.write;
+
+ if (typeof name === 'object') {
+ attributes = name;
+ } else {
+ attributes[name] = Object.isUndefined(value) ? true : value;
+ }
+
+ for (var attr in attributes) {
+ name = table.names[attr] || attr;
+ value = attributes[attr];
+ if (table.values[attr]) {
+ value = table.values[attr](element, value);
+ if (Object.isUndefined(value)) continue;
+ }
+ if (value === false || value === null) element.removeAttribute(name);
+ else if (value === true) element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+
+ return element;
+ }
+
+ var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () {
+ if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) {
+ return false;
+ }
+ var checkbox = document.createElement('');
+ checkbox.checked = true;
+ var node = checkbox.getAttributeNode('checked');
+ return !node || !node.specified;
+ })();
+
+ function hasAttribute(element, attribute) {
+ attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return !!(node && node.specified);
+ }
+
+ function hasAttribute_IE(element, attribute) {
+ if (attribute === 'checked') {
+ return element.checked;
+ }
+ return hasAttribute(element, attribute);
+ }
+
+ GLOBAL.Element.Methods.Simulated.hasAttribute =
+ PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ? hasAttribute_IE : hasAttribute;
+
+ function classNames(element) {
+ return new Element.ClassNames(element);
+ }
+
+ var regExpCache = {};
+ function getRegExpForClassName(className) {
+ if (regExpCache[className]) return regExpCache[className];
+
+ var re = new RegExp('(^|\\s+)' + className + '(\\s+|$)');
+ regExpCache[className] = re;
+ return re;
+ }
+
+ function hasClassName(element, className) {
+ if (!(element = $(element))) return;
+
+ var elementClassName = element.className;
+
+ if (elementClassName.length === 0) return false;
+ if (elementClassName === className) return true;
+
+ return getRegExpForClassName(className).test(elementClassName);
+ }
+
+ function addClassName(element, className) {
+ if (!(element = $(element))) return;
+
+ if (!hasClassName(element, className))
+ element.className += (element.className ? ' ' : '') + className;
+
+ return element;
+ }
+
+ function removeClassName(element, className) {
+ if (!(element = $(element))) return;
+
+ element.className = element.className
+ .replace(getRegExpForClassName(className), ' ')
+ .strip();
+
+ return element;
+ }
+
+ function toggleClassName(element, className, bool) {
+ if (!(element = $(element))) return;
+
+ if (Object.isUndefined(bool)) bool = !hasClassName(element, className);
+
+ var method = Element[bool ? 'addClassName' : 'removeClassName'];
+ return method(element, className);
+ }
+
+ var ATTRIBUTE_TRANSLATIONS = {};
+
+ var classProp = 'className',
+ forProp = 'for';
+
+ DIV.setAttribute(classProp, 'x');
+ if (DIV.className !== 'x') {
+ DIV.setAttribute('class', 'x');
+ if (DIV.className === 'x') classProp = 'class';
+ }
+
+ var LABEL = document.createElement('label');
+ LABEL.setAttribute(forProp, 'x');
+ if (LABEL.htmlFor !== 'x') {
+ LABEL.setAttribute('htmlFor', 'x');
+ if (LABEL.htmlFor === 'x') forProp = 'htmlFor';
+ }
+ LABEL = null;
+
+ function _getAttr(element, attribute) {
+ return element.getAttribute(attribute);
+ }
+
+ function _getAttr2(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ }
+
+ function _getAttrNode(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : '';
+ }
+
+ function _getFlag(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ }
+
+ DIV.onclick = Prototype.emptyFunction;
+ var onclickValue = DIV.getAttribute('onclick');
+
+ var _getEv;
+
+ if (String(onclickValue).indexOf('{') > -1) {
+ _getEv = function (element, attribute) {
+ var value = element.getAttribute(attribute);
+ if (!value) return null;
+ value = value.toString();
+ value = value.split('{')[1];
+ value = value.split('}')[0];
+ return value.strip();
+ };
+ } else if (onclickValue === '') {
+ _getEv = function (element, attribute) {
+ var value = element.getAttribute(attribute);
+ if (!value) return null;
+ return value.strip();
+ };
+ }
+
+ ATTRIBUTE_TRANSLATIONS.read = {
+ names: {
+ class: classProp,
+ className: classProp,
+ for: forProp,
+ htmlFor: forProp
+ },
+
+ values: {
+ style: function (element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function (element) {
+ return element.title;
+ }
+ }
+ };
+
+ ATTRIBUTE_TRANSLATIONS.write = {
+ names: {
+ className: 'class',
+ htmlFor: 'for',
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ },
+
+ values: {
+ checked: function (element, value) {
+ value = !!value;
+ element.checked = value;
+ return value ? 'checked' : null;
+ },
+
+ style: function (element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ ATTRIBUTE_TRANSLATIONS.has = { names: {} };
+
+ Object.extend(
+ ATTRIBUTE_TRANSLATIONS.write.names,
+ ATTRIBUTE_TRANSLATIONS.read.names
+ );
+
+ var CAMEL_CASED_ATTRIBUTE_NAMES = $w(
+ 'colSpan rowSpan vAlign dateTime ' +
+ 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'
+ );
+
+ for (var i = 0, attr; (attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]); i++) {
+ ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr;
+ ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr;
+ }
+
+ Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, {
+ href: _getAttr2,
+ src: _getAttr2,
+ type: _getAttr,
+ action: _getAttrNode,
+ disabled: _getFlag,
+ checked: _getFlag,
+ readonly: _getFlag,
+ multiple: _getFlag,
+ onload: _getEv,
+ onunload: _getEv,
+ onclick: _getEv,
+ ondblclick: _getEv,
+ onmousedown: _getEv,
+ onmouseup: _getEv,
+ onmouseover: _getEv,
+ onmousemove: _getEv,
+ onmouseout: _getEv,
+ onfocus: _getEv,
+ onblur: _getEv,
+ onkeypress: _getEv,
+ onkeydown: _getEv,
+ onkeyup: _getEv,
+ onsubmit: _getEv,
+ onreset: _getEv,
+ onselect: _getEv,
+ onchange: _getEv
+ });
+
+ Object.extend(methods, {
+ identify: identify,
+ readAttribute: readAttribute,
+ writeAttribute: writeAttribute,
+ classNames: classNames,
+ hasClassName: hasClassName,
+ addClassName: addClassName,
+ removeClassName: removeClassName,
+ toggleClassName: toggleClassName
+ });
+
+ function normalizeStyleName(style) {
+ if (style === 'float' || style === 'styleFloat') return 'cssFloat';
+ return style.camelize();
+ }
+
+ function normalizeStyleName_IE(style) {
+ if (style === 'float' || style === 'cssFloat') return 'styleFloat';
+ return style.camelize();
+ }
+
+ function setStyle(element, styles) {
+ element = $(element);
+ var elementStyle = element.style,
+ match;
+
+ if (Object.isString(styles)) {
+ elementStyle.cssText += ';' + styles;
+ if (styles.include('opacity')) {
+ var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1];
+ Element.setOpacity(element, opacity);
+ }
+ return element;
+ }
+
+ for (var property in styles) {
+ if (property === 'opacity') {
+ Element.setOpacity(element, styles[property]);
+ } else {
+ var value = styles[property];
+ if (property === 'float' || property === 'cssFloat') {
+ property = Object.isUndefined(elementStyle.styleFloat)
+ ? 'cssFloat'
+ : 'styleFloat';
+ }
+ elementStyle[property] = value;
+ }
+ }
+
+ return element;
+ }
+
+ function getStyle(element, style) {
+ element = $(element);
+ style = normalizeStyleName(style);
+
+ var value = element.style[style];
+ if (!value || value === 'auto') {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+
+ if (style === 'opacity') return value ? parseFloat(value) : 1.0;
+ return value === 'auto' ? null : value;
+ }
+
+ function getStyle_Opera(element, style) {
+ switch (style) {
+ case 'height':
+ case 'width':
+ if (!Element.visible(element)) return null;
+
+ var dim = parseInt(getStyle(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()]) return dim + 'px';
+
+ return Element.measure(element, style);
+
+ default:
+ return getStyle(element, style);
+ }
+ }
+
+ function getStyle_IE(element, style) {
+ element = $(element);
+ style = normalizeStyleName_IE(style);
+
+ var value = element.style[style];
+ if (!value && element.currentStyle) {
+ value = element.currentStyle[style];
+ }
+
+ if (style === 'opacity') {
+ if (!STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity_IE(element);
+ else return value ? parseFloat(value) : 1.0;
+ }
+
+ if (value === 'auto') {
+ if ((style === 'width' || style === 'height') && Element.visible(element))
+ return Element.measure(element, style) + 'px';
+ return null;
+ }
+
+ return value;
+ }
+
+ function stripAlphaFromFilter_IE(filter) {
+ return (filter || '').replace(/alpha\([^\)]*\)/gi, '');
+ }
+
+ function hasLayout_IE(element) {
+ if (!element.currentStyle || !element.currentStyle.hasLayout)
+ element.style.zoom = 1;
+ return element;
+ }
+
+ var STANDARD_CSS_OPACITY_SUPPORTED = (function () {
+ DIV.style.cssText = 'opacity:.55';
+ return /^0.55/.test(DIV.style.opacity);
+ })();
+
+ function setOpacity(element, value) {
+ element = $(element);
+ if (value == 1 || value === '') value = '';
+ else if (value < 0.00001) value = 0;
+ element.style.opacity = value;
+ return element;
+ }
+
+ function setOpacity_IE(element, value) {
+ if (STANDARD_CSS_OPACITY_SUPPORTED) return setOpacity(element, value);
+
+ element = hasLayout_IE($(element));
+ var filter = Element.getStyle(element, 'filter'),
+ style = element.style;
+
+ if (value == 1 || value === '') {
+ filter = stripAlphaFromFilter_IE(filter);
+ if (filter) style.filter = filter;
+ else style.removeAttribute('filter');
+ return element;
+ }
+
+ if (value < 0.00001) value = 0;
+
+ style.filter =
+ stripAlphaFromFilter_IE(filter) + ' alpha(opacity=' + value * 100 + ')';
+
+ return element;
+ }
+
+ function getOpacity(element) {
+ return Element.getStyle(element, 'opacity');
+ }
+
+ function getOpacity_IE(element) {
+ if (STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity(element);
+
+ var filter = Element.getStyle(element, 'filter');
+ if (filter.length === 0) return 1.0;
+ var match = (filter || '').match(/alpha\(opacity=(.*)\)/i);
+ if (match && match[1]) return parseFloat(match[1]) / 100;
+ return 1.0;
+ }
+
+ Object.extend(methods, {
+ setStyle: setStyle,
+ getStyle: getStyle,
+ setOpacity: setOpacity,
+ getOpacity: getOpacity
+ });
+
+ if ('styleFloat' in DIV.style) {
+ methods.getStyle = getStyle_IE;
+ methods.setOpacity = setOpacity_IE;
+ methods.getOpacity = getOpacity_IE;
+ }
+
+ var UID = 0;
+
+ GLOBAL.Element.Storage = { UID: 1 };
+
+ function getUniqueElementID(element) {
+ if (element === window) return 0;
+
+ if (typeof element._prototypeUID === 'undefined')
+ element._prototypeUID = Element.Storage.UID++;
+ return element._prototypeUID;
+ }
+
+ function getUniqueElementID_IE(element) {
+ if (element === window) return 0;
+ if (element == document) return 1;
+ return element.uniqueID;
+ }
+
+ var HAS_UNIQUE_ID_PROPERTY = 'uniqueID' in DIV;
+ if (HAS_UNIQUE_ID_PROPERTY) getUniqueElementID = getUniqueElementID_IE;
+
+ function getStorage(element) {
+ if (!(element = $(element))) return;
+
+ var uid = getUniqueElementID(element);
+
+ if (!Element.Storage[uid]) Element.Storage[uid] = $H();
+
+ return Element.Storage[uid];
+ }
+
+ function store(element, key, value) {
+ if (!(element = $(element))) return;
+ var storage = getStorage(element);
+ if (arguments.length === 2) {
+ storage.update(key);
+ } else {
+ storage.set(key, value);
+ }
+ return element;
+ }
+
+ function retrieve(element, key, defaultValue) {
+ if (!(element = $(element))) return;
+ var storage = getStorage(element),
+ value = storage.get(key);
+
+ if (Object.isUndefined(value)) {
+ storage.set(key, defaultValue);
+ value = defaultValue;
+ }
+
+ return value;
+ }
+
+ Object.extend(methods, {
+ getStorage: getStorage,
+ store: store,
+ retrieve: retrieve
+ });
+
+ var Methods = {},
+ ByTag = Element.Methods.ByTag,
+ F = Prototype.BrowserFeatures;
+
+ if (!F.ElementExtensions && '__proto__' in DIV) {
+ GLOBAL.HTMLElement = {};
+ GLOBAL.HTMLElement.prototype = DIV['__proto__'];
+ F.ElementExtensions = true;
+ }
+
+ function checkElementPrototypeDeficiency(tagName) {
+ if (typeof window.Element === 'undefined') return false;
+ if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false;
+ var proto = window.Element.prototype;
+ if (proto) {
+ var id = '_' + (Math.random() + '').slice(2),
+ el = document.createElement(tagName);
+ proto[id] = 'x';
+ var isBuggy = el[id] !== 'x';
+ delete proto[id];
+ el = null;
+ return isBuggy;
+ }
+
+ return false;
+ }
+
+ var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY =
+ checkElementPrototypeDeficiency('object');
+
+ function extendElementWith(element, methods) {
+ for (var property in methods) {
+ var value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+ }
+
+ var EXTENDED = {};
+ function elementIsExtended(element) {
+ var uid = getUniqueElementID(element);
+ return uid in EXTENDED;
+ }
+
+ function extend(element) {
+ if (!element || elementIsExtended(element)) return element;
+ if (element.nodeType !== Node.ELEMENT_NODE || element == window)
+ return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName.toUpperCase();
+
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ extendElementWith(element, methods);
+ EXTENDED[getUniqueElementID(element)] = true;
+ return element;
+ }
+
+ function extend_IE8(element) {
+ if (!element || elementIsExtended(element)) return element;
+
+ var t = element.tagName;
+ if (t && /^(?:object|applet|embed)$/i.test(t)) {
+ extendElementWith(element, Element.Methods);
+ extendElementWith(element, Element.Methods.Simulated);
+ extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
+ }
+
+ return element;
+ }
+
+ if (F.SpecificElementExtensions) {
+ extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K;
+ }
+
+ function addMethodsToTagName(tagName, methods) {
+ tagName = tagName.toUpperCase();
+ if (!ByTag[tagName]) ByTag[tagName] = {};
+ Object.extend(ByTag[tagName], methods);
+ }
+
+ function mergeMethods(destination, methods, onlyIfAbsent) {
+ if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ 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];
+
+ var element = document.createElement(tagName),
+ proto = element['__proto__'] || element.constructor.prototype;
+
+ element = null;
+ return proto;
+ }
+
+ function addMethods(methods) {
+ if (arguments.length === 0) addFormMethods();
+
+ if (arguments.length === 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) {
+ Object.extend(Element.Methods, methods || {});
+ } else {
+ if (Object.isArray(tagName)) {
+ for (var i = 0, tag; (tag = tagName[i]); i++)
+ addMethodsToTagName(tag, methods);
+ } else {
+ addMethodsToTagName(tagName, methods);
+ }
+ }
+
+ var ELEMENT_PROTOTYPE = window.HTMLElement
+ ? HTMLElement.prototype
+ : Element.prototype;
+
+ if (F.ElementExtensions) {
+ mergeMethods(ELEMENT_PROTOTYPE, Element.Methods);
+ mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ mergeMethods(klass.prototype, ByTag[tag]);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ Object.extend(Element, Element.Methods.Simulated);
+ delete Element.ByTag;
+ delete Element.Simulated;
+
+ Element.extend.refresh();
+
+ ELEMENT_CACHE = {};
+ }
+
+ Object.extend(GLOBAL.Element, {
+ extend: extend,
+ addMethods: addMethods
+ });
+
+ if (extend === Prototype.K) {
+ GLOBAL.Element.extend.refresh = Prototype.emptyFunction;
+ } else {
+ GLOBAL.Element.extend.refresh = function () {
+ if (Prototype.BrowserFeatures.ElementExtensions) return;
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+
+ EXTENDED = {};
+ };
+ }
+
+ function addFormMethods() {
+ Object.extend(Form, Form.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),
+ BUTTON: Object.clone(Form.Element.Methods)
+ });
+ }
+
+ Element.addMethods(methods);
+
+ function destroyCache_IE() {
+ DIV = null;
+ ELEMENT_CACHE = null;
+ }
+
+ if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE);
+})(this);
+(function () {
+ function toDecimal(pctString) {
+ var match = pctString.match(/^(\d+)%?$/i);
+ if (!match) return null;
+ return Number(match[1]) / 100;
+ }
+
+ function getRawStyle(element, style) {
+ element = $(element);
+
+ var value = element.style[style];
+ if (!value || value === 'auto') {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+
+ if (style === 'opacity') return value ? parseFloat(value) : 1.0;
+ return value === 'auto' ? null : value;
+ }
+
+ function getRawStyle_IE(element, style) {
+ var value = element.style[style];
+ if (!value && element.currentStyle) {
+ value = element.currentStyle[style];
+ }
+ return value;
+ }
+
+ function getContentWidth(element, context) {
+ var boxWidth = element.offsetWidth;
+
+ var bl = getPixelValue(element, 'borderLeftWidth', context) || 0;
+ var br = getPixelValue(element, 'borderRightWidth', context) || 0;
+ var pl = getPixelValue(element, 'paddingLeft', context) || 0;
+ var pr = getPixelValue(element, 'paddingRight', context) || 0;
+
+ return boxWidth - bl - br - pl - pr;
+ }
+
+ if (
+ !Object.isUndefined(document.documentElement.currentStyle) &&
+ !Prototype.Browser.Opera
+ ) {
+ getRawStyle = getRawStyle_IE;
+ }
+
+ function getPixelValue(value, property, context) {
+ var element = null;
+ if (Object.isElement(value)) {
+ element = value;
+ value = getRawStyle(element, property);
+ }
+
+ if (value === null || Object.isUndefined(value)) {
+ return null;
+ }
+
+ if (/^(?:-)?\d+(\.\d+)?(px)?$/i.test(value)) {
+ return window.parseFloat(value);
+ }
+
+ var isPercentage = value.include('%'),
+ isViewport = context === document.viewport;
+
+ if (
+ /\d/.test(value) &&
+ element &&
+ element.runtimeStyle &&
+ !(isPercentage && isViewport)
+ ) {
+ var style = element.style.left,
+ rStyle = element.runtimeStyle.left;
+ element.runtimeStyle.left = element.currentStyle.left;
+ element.style.left = value || 0;
+ value = element.style.pixelLeft;
+ element.style.left = style;
+ element.runtimeStyle.left = rStyle;
+
+ return value;
+ }
+
+ if (element && isPercentage) {
+ context = context || element.parentNode;
+ var decimal = toDecimal(value),
+ whole = null;
+
+ var isHorizontal =
+ property.include('left') ||
+ property.include('right') ||
+ property.include('width');
+
+ var isVertical =
+ property.include('top') ||
+ property.include('bottom') ||
+ property.include('height');
+
+ if (context === document.viewport) {
+ if (isHorizontal) {
+ whole = document.viewport.getWidth();
+ } else if (isVertical) {
+ whole = document.viewport.getHeight();
+ }
+ } else {
+ if (isHorizontal) {
+ whole = $(context).measure('width');
+ } else if (isVertical) {
+ whole = $(context).measure('height');
+ }
+ }
+
+ return whole === null ? 0 : whole * decimal;
+ }
+
+ return 0;
+ }
+
+ function toCSSPixels(number) {
+ if (Object.isString(number) && number.endsWith('px')) return number;
+ return number + 'px';
+ }
+
+ function isDisplayed(element) {
+ while (element && element.parentNode) {
+ var display = element.getStyle('display');
+ if (display === 'none') {
+ return false;
+ }
+ element = $(element.parentNode);
+ }
+ return true;
+ }
+
+ var hasLayout = Prototype.K;
+ if ('currentStyle' in document.documentElement) {
+ hasLayout = function (element) {
+ if (!element.currentStyle.hasLayout) {
+ element.style.zoom = 1;
+ }
+ return element;
+ };
+ }
+
+ function cssNameFor(key) {
+ if (key.include('border')) key = key + '-width';
+ return key.camelize();
+ }
+
+ Element.Layout = Class.create(Hash, {
+ initialize: function ($super, element, preCompute) {
+ $super();
+ this.element = $(element);
+
+ Element.Layout.PROPERTIES.each(function (property) {
+ this._set(property, null);
+ }, this);
+
+ if (preCompute) {
+ this._preComputing = true;
+ this._begin();
+ Element.Layout.PROPERTIES.each(this._compute, this);
+ this._end();
+ this._preComputing = false;
+ }
+ },
+
+ _set: function (property, value) {
+ return Hash.prototype.set.call(this, property, value);
+ },
+
+ set: function (property, value) {
+ throw 'Properties of Element.Layout are read-only.';
+ },
+
+ get: function ($super, property) {
+ var value = $super(property);
+ return value === null ? this._compute(property) : value;
+ },
+
+ _begin: function () {
+ if (this._isPrepared()) return;
+
+ var element = this.element;
+ if (isDisplayed(element)) {
+ this._setPrepared(true);
+ return;
+ }
+
+ var originalStyles = {
+ position: element.style.position || '',
+ width: element.style.width || '',
+ visibility: element.style.visibility || '',
+ display: element.style.display || ''
+ };
+
+ element.store('prototype_original_styles', originalStyles);
+
+ var position = getRawStyle(element, 'position'),
+ width = element.offsetWidth;
+
+ if (width === 0 || width === null) {
+ element.style.display = 'block';
+ width = element.offsetWidth;
+ }
+
+ var context =
+ position === 'fixed' ? document.viewport : element.parentNode;
+
+ var tempStyles = {
+ visibility: 'hidden',
+ display: 'block'
+ };
+
+ if (position !== 'fixed') tempStyles.position = 'absolute';
+
+ element.setStyle(tempStyles);
+
+ var positionedWidth = element.offsetWidth,
+ newWidth;
+ if (width && positionedWidth === width) {
+ newWidth = getContentWidth(element, context);
+ } else if (position === 'absolute' || position === 'fixed') {
+ newWidth = getContentWidth(element, context);
+ } else {
+ var parent = element.parentNode,
+ pLayout = $(parent).getLayout();
+
+ newWidth =
+ pLayout.get('width') -
+ this.get('margin-left') -
+ this.get('border-left') -
+ this.get('padding-left') -
+ this.get('padding-right') -
+ this.get('border-right') -
+ this.get('margin-right');
+ }
+
+ element.setStyle({ width: newWidth + 'px' });
+
+ this._setPrepared(true);
+ },
+
+ _end: function () {
+ var element = this.element;
+ var originalStyles = element.retrieve('prototype_original_styles');
+ element.store('prototype_original_styles', null);
+ element.setStyle(originalStyles);
+ this._setPrepared(false);
+ },
+
+ _compute: function (property) {
+ var COMPUTATIONS = Element.Layout.COMPUTATIONS;
+ if (!(property in COMPUTATIONS)) {
+ throw 'Property not found.';
+ }
+
+ return this._set(
+ property,
+ COMPUTATIONS[property].call(this, this.element)
+ );
+ },
+
+ _isPrepared: function () {
+ return this.element.retrieve('prototype_element_layout_prepared', false);
+ },
+
+ _setPrepared: function (bool) {
+ return this.element.store('prototype_element_layout_prepared', bool);
+ },
+
+ toObject: function () {
+ var args = $A(arguments);
+ var keys =
+ args.length === 0
+ ? Element.Layout.PROPERTIES
+ : args.join(' ').split(' ');
+ var obj = {};
+ keys.each(function (key) {
+ if (!Element.Layout.PROPERTIES.include(key)) return;
+ var value = this.get(key);
+ if (value != null) obj[key] = value;
+ }, this);
+ return obj;
+ },
+
+ toHash: function () {
+ var obj = this.toObject.apply(this, arguments);
+ return new Hash(obj);
+ },
+
+ toCSS: function () {
+ var args = $A(arguments);
+ var keys =
+ args.length === 0
+ ? Element.Layout.PROPERTIES
+ : args.join(' ').split(' ');
+ var css = {};
+
+ keys.each(function (key) {
+ if (!Element.Layout.PROPERTIES.include(key)) return;
+ if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
+
+ var value = this.get(key);
+ if (value != null) css[cssNameFor(key)] = value + 'px';
+ }, this);
+ return css;
+ },
+
+ inspect: function () {
+ return '#';
+ }
+ });
+
+ Object.extend(Element.Layout, {
+ PROPERTIES: $w(
+ 'height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'
+ ),
+
+ COMPOSITE_PROPERTIES: $w(
+ 'padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'
+ ),
+
+ COMPUTATIONS: {
+ height: function (element) {
+ if (!this._preComputing) this._begin();
+
+ var bHeight = this.get('border-box-height');
+ if (bHeight <= 0) {
+ if (!this._preComputing) this._end();
+ return 0;
+ }
+
+ var bTop = this.get('border-top'),
+ bBottom = this.get('border-bottom');
+
+ var pTop = this.get('padding-top'),
+ pBottom = this.get('padding-bottom');
+
+ if (!this._preComputing) this._end();
+
+ return bHeight - bTop - bBottom - pTop - pBottom;
+ },
+
+ width: function (element) {
+ if (!this._preComputing) this._begin();
+
+ var bWidth = this.get('border-box-width');
+ if (bWidth <= 0) {
+ if (!this._preComputing) this._end();
+ return 0;
+ }
+
+ var bLeft = this.get('border-left'),
+ bRight = this.get('border-right');
+
+ var pLeft = this.get('padding-left'),
+ pRight = this.get('padding-right');
+
+ if (!this._preComputing) this._end();
+ return bWidth - bLeft - bRight - pLeft - pRight;
+ },
+
+ 'padding-box-height': function (element) {
+ var height = this.get('height'),
+ pTop = this.get('padding-top'),
+ pBottom = this.get('padding-bottom');
+
+ return height + pTop + pBottom;
+ },
+
+ 'padding-box-width': function (element) {
+ var width = this.get('width'),
+ pLeft = this.get('padding-left'),
+ pRight = this.get('padding-right');
+
+ return width + pLeft + pRight;
+ },
+
+ 'border-box-height': function (element) {
+ if (!this._preComputing) this._begin();
+ var height = element.offsetHeight;
+ if (!this._preComputing) this._end();
+ return height;
+ },
+
+ 'border-box-width': function (element) {
+ if (!this._preComputing) this._begin();
+ var width = element.offsetWidth;
+ if (!this._preComputing) this._end();
+ return width;
+ },
+
+ 'margin-box-height': function (element) {
+ var bHeight = this.get('border-box-height'),
+ mTop = this.get('margin-top'),
+ mBottom = this.get('margin-bottom');
+
+ if (bHeight <= 0) return 0;
+
+ return bHeight + mTop + mBottom;
+ },
+
+ 'margin-box-width': function (element) {
+ var bWidth = this.get('border-box-width'),
+ mLeft = this.get('margin-left'),
+ mRight = this.get('margin-right');
+
+ if (bWidth <= 0) return 0;
+
+ return bWidth + mLeft + mRight;
+ },
+
+ top: function (element) {
+ var offset = element.positionedOffset();
+ return offset.top;
+ },
+
+ bottom: function (element) {
+ var offset = element.positionedOffset(),
+ parent = element.getOffsetParent(),
+ pHeight = parent.measure('height');
+
+ var mHeight = this.get('border-box-height');
+
+ return pHeight - mHeight - offset.top;
+ },
+
+ left: function (element) {
+ var offset = element.positionedOffset();
+ return offset.left;
+ },
+
+ right: function (element) {
+ var offset = element.positionedOffset(),
+ parent = element.getOffsetParent(),
+ pWidth = parent.measure('width');
+
+ var mWidth = this.get('border-box-width');
+
+ return pWidth - mWidth - offset.left;
+ },
+
+ 'padding-top': function (element) {
+ return getPixelValue(element, 'paddingTop');
+ },
+
+ 'padding-bottom': function (element) {
+ return getPixelValue(element, 'paddingBottom');
+ },
+
+ 'padding-left': function (element) {
+ return getPixelValue(element, 'paddingLeft');
+ },
+
+ 'padding-right': function (element) {
+ return getPixelValue(element, 'paddingRight');
+ },
+
+ 'border-top': function (element) {
+ return getPixelValue(element, 'borderTopWidth');
+ },
+
+ 'border-bottom': function (element) {
+ return getPixelValue(element, 'borderBottomWidth');
+ },
+
+ 'border-left': function (element) {
+ return getPixelValue(element, 'borderLeftWidth');
+ },
+
+ 'border-right': function (element) {
+ return getPixelValue(element, 'borderRightWidth');
+ },
+
+ 'margin-top': function (element) {
+ return getPixelValue(element, 'marginTop');
+ },
+
+ 'margin-bottom': function (element) {
+ return getPixelValue(element, 'marginBottom');
+ },
+
+ 'margin-left': function (element) {
+ return getPixelValue(element, 'marginLeft');
+ },
+
+ 'margin-right': function (element) {
+ return getPixelValue(element, 'marginRight');
+ }
+ }
+ });
+
+ if ('getBoundingClientRect' in document.documentElement) {
+ Object.extend(Element.Layout.COMPUTATIONS, {
+ right: function (element) {
+ var parent = hasLayout(element.getOffsetParent());
+ var rect = element.getBoundingClientRect(),
+ pRect = parent.getBoundingClientRect();
+
+ return (pRect.right - rect.right).round();
+ },
+
+ bottom: function (element) {
+ var parent = hasLayout(element.getOffsetParent());
+ var rect = element.getBoundingClientRect(),
+ pRect = parent.getBoundingClientRect();
+
+ return (pRect.bottom - rect.bottom).round();
+ }
+ });
+ }
+
+ Element.Offset = Class.create({
+ initialize: function (left, top) {
+ this.left = left.round();
+ this.top = top.round();
+
+ this[0] = this.left;
+ this[1] = this.top;
+ },
+
+ relativeTo: function (offset) {
+ return new Element.Offset(this.left - offset.left, this.top - offset.top);
+ },
+
+ inspect: function () {
+ return '#'.interpolate(this);
+ },
+
+ toString: function () {
+ return '[#{left}, #{top}]'.interpolate(this);
+ },
+
+ toArray: function () {
+ return [this.left, this.top];
+ }
+ });
+
+ function getLayout(element, preCompute) {
+ return new Element.Layout(element, preCompute);
+ }
+
+ function measure(element, property) {
+ return $(element).getLayout().get(property);
+ }
+
+ function getHeight(element) {
+ return Element.getDimensions(element).height;
+ }
+
+ function getWidth(element) {
+ return Element.getDimensions(element).width;
+ }
+
+ function getDimensions(element) {
+ element = $(element);
+ var display = Element.getStyle(element, 'display');
+
+ if (display && display !== 'none') {
+ return { width: element.offsetWidth, height: element.offsetHeight };
+ }
+
+ var style = element.style;
+ var originalStyles = {
+ visibility: style.visibility,
+ position: style.position,
+ display: style.display
+ };
+
+ var newStyles = {
+ visibility: 'hidden',
+ display: 'block'
+ };
+
+ if (originalStyles.position !== 'fixed') newStyles.position = 'absolute';
+
+ Element.setStyle(element, newStyles);
+
+ var dimensions = {
+ width: element.offsetWidth,
+ height: element.offsetHeight
+ };
+
+ Element.setStyle(element, originalStyles);
+
+ return dimensions;
+ }
+
+ function getOffsetParent(element) {
+ element = $(element);
+
+ function selfOrBody(element) {
+ return isHtml(element) ? $(document.body) : $(element);
+ }
+
+ if (
+ isDocument(element) ||
+ isDetached(element) ||
+ isBody(element) ||
+ isHtml(element)
+ )
+ return $(document.body);
+
+ var isInline = Element.getStyle(element, 'display') === 'inline';
+ if (!isInline && element.offsetParent)
+ return selfOrBody(element.offsetParent);
+
+ while ((element = element.parentNode) && element !== document.body) {
+ if (Element.getStyle(element, 'position') !== 'static') {
+ return selfOrBody(element);
+ }
+ }
+
+ return $(document.body);
+ }
+
+ function cumulativeOffset(element) {
+ element = $(element);
+ var valueT = 0,
+ valueL = 0;
+ if (element.parentNode) {
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ }
+ return new Element.Offset(valueL, valueT);
+ }
+
+ function positionedOffset(element) {
+ element = $(element);
+
+ var layout = element.getLayout();
+
+ var valueT = 0,
+ valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (isBody(element)) break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+
+ valueL -= layout.get('margin-left');
+ valueT -= layout.get('margin-top');
+
+ return new Element.Offset(valueL, valueT);
+ }
+
+ function cumulativeScrollOffset(element) {
+ var valueT = 0,
+ valueL = 0;
+ do {
+ if (element === document.body) {
+ var bodyScrollNode =
+ document.documentElement || document.body.parentNode || document.body;
+ valueT += !Object.isUndefined(window.pageYOffset)
+ ? window.pageYOffset
+ : bodyScrollNode.scrollTop || 0;
+ valueL += !Object.isUndefined(window.pageXOffset)
+ ? window.pageXOffset
+ : bodyScrollNode.scrollLeft || 0;
+ break;
+ } else {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ }
+ } while (element);
+ return new Element.Offset(valueL, valueT);
+ }
+
+ function viewportOffset(forElement) {
+ var valueT = 0,
+ valueL = 0,
+ docBody = document.body;
+
+ forElement = $(forElement);
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (
+ element.offsetParent == docBody &&
+ Element.getStyle(element, 'position') == 'absolute'
+ )
+ break;
+ } while ((element = element.offsetParent));
+
+ element = forElement;
+ do {
+ if (element != docBody) {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while ((element = element.parentNode));
+ return new Element.Offset(valueL, valueT);
+ }
+
+ function absolutize(element) {
+ element = $(element);
+
+ if (Element.getStyle(element, 'position') === 'absolute') {
+ return element;
+ }
+
+ var offsetParent = getOffsetParent(element);
+ var eOffset = element.viewportOffset(),
+ pOffset = offsetParent.viewportOffset();
+
+ var offset = eOffset.relativeTo(pOffset);
+ var layout = element.getLayout();
+
+ element.store('prototype_absolutize_original_styles', {
+ position: element.getStyle('position'),
+ left: element.getStyle('left'),
+ top: element.getStyle('top'),
+ width: element.getStyle('width'),
+ height: element.getStyle('height')
+ });
+
+ element.setStyle({
+ position: 'absolute',
+ top: offset.top + 'px',
+ left: offset.left + 'px',
+ width: layout.get('width') + 'px',
+ height: layout.get('height') + 'px'
+ });
+
+ return element;
+ }
+
+ function relativize(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'position') === 'relative') {
+ return element;
+ }
+
+ var originalStyles = element.retrieve(
+ 'prototype_absolutize_original_styles'
+ );
+
+ if (originalStyles) element.setStyle(originalStyles);
+ return element;
+ }
+
+ function scrollTo(element) {
+ element = $(element);
+ var pos = Element.cumulativeOffset(element);
+ window.scrollTo(pos.left, pos.top);
+ return element;
+ }
+
+ function makePositioned(element) {
+ element = $(element);
+ var position = Element.getStyle(element, 'position'),
+ styles = {};
+ if (position === 'static' || !position) {
+ styles.position = 'relative';
+ if (Prototype.Browser.Opera) {
+ styles.top = 0;
+ styles.left = 0;
+ }
+ Element.setStyle(element, styles);
+ Element.store(element, 'prototype_made_positioned', true);
+ }
+ return element;
+ }
+
+ function undoPositioned(element) {
+ element = $(element);
+ var storage = Element.getStorage(element),
+ madePositioned = storage.get('prototype_made_positioned');
+
+ if (madePositioned) {
+ storage.unset('prototype_made_positioned');
+ Element.setStyle(element, {
+ position: '',
+ top: '',
+ bottom: '',
+ left: '',
+ right: ''
+ });
+ }
+ return element;
+ }
+
+ function makeClipping(element) {
+ element = $(element);
+
+ var storage = Element.getStorage(element),
+ madeClipping = storage.get('prototype_made_clipping');
+
+ if (Object.isUndefined(madeClipping)) {
+ var overflow = Element.getStyle(element, 'overflow');
+ storage.set('prototype_made_clipping', overflow);
+ if (overflow !== 'hidden') element.style.overflow = 'hidden';
+ }
+
+ return element;
+ }
+
+ function undoClipping(element) {
+ element = $(element);
+ var storage = Element.getStorage(element),
+ overflow = storage.get('prototype_made_clipping');
+
+ if (!Object.isUndefined(overflow)) {
+ storage.unset('prototype_made_clipping');
+ element.style.overflow = overflow || '';
+ }
+
+ return element;
+ }
+
+ function clonePosition(element, source, options) {
+ options = Object.extend(
+ {
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ },
+ options || {}
+ );
+
+ var docEl = document.documentElement;
+
+ source = $(source);
+ element = $(element);
+ var p,
+ delta,
+ layout,
+ styles = {};
+
+ if (options.setLeft || options.setTop) {
+ p = Element.viewportOffset(source);
+ delta = [0, 0];
+ if (Element.getStyle(element, 'position') === 'absolute') {
+ var parent = Element.getOffsetParent(element);
+ if (parent !== document.body) delta = Element.viewportOffset(parent);
+ }
+ }
+
+ function pageScrollXY() {
+ var x = 0,
+ y = 0;
+ if (Object.isNumber(window.pageXOffset)) {
+ x = window.pageXOffset;
+ y = window.pageYOffset;
+ } else if (
+ document.body &&
+ (document.body.scrollLeft || document.body.scrollTop)
+ ) {
+ x = document.body.scrollLeft;
+ y = document.body.scrollTop;
+ } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) {
+ x = docEl.scrollLeft;
+ y = docEl.scrollTop;
+ }
+ return { x: x, y: y };
+ }
+
+ var pageXY = pageScrollXY();
+
+ if (options.setWidth || options.setHeight) {
+ layout = Element.getLayout(source);
+ }
+
+ if (options.setLeft)
+ styles.left = p[0] + pageXY.x - delta[0] + options.offsetLeft + 'px';
+ if (options.setTop)
+ styles.top = p[1] + pageXY.y - delta[1] + options.offsetTop + 'px';
+
+ var currentLayout = element.getLayout();
+
+ if (options.setWidth) {
+ styles.width = layout.get('width') + 'px';
+ }
+ if (options.setHeight) {
+ styles.height = layout.get('height') + 'px';
+ }
+
+ return Element.setStyle(element, styles);
+ }
+
+ if (Prototype.Browser.IE) {
+ getOffsetParent = getOffsetParent.wrap(function (proceed, element) {
+ element = $(element);
+
+ if (
+ isDocument(element) ||
+ isDetached(element) ||
+ isBody(element) ||
+ isHtml(element)
+ )
+ return $(document.body);
+
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ });
+
+ positionedOffset = positionedOffset.wrap(function (proceed, element) {
+ element = $(element);
+ if (!element.parentNode) return new Element.Offset(0, 0);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ hasLayout(offsetParent);
+
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ });
+ } else if (Prototype.Browser.Webkit) {
+ cumulativeOffset = function (element) {
+ element = $(element);
+ var valueT = 0,
+ valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body) {
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+ }
+
+ element = element.offsetParent;
+ } while (element);
+
+ return new Element.Offset(valueL, valueT);
+ };
+ }
+
+ Element.addMethods({
+ getLayout: getLayout,
+ measure: measure,
+ getWidth: getWidth,
+ getHeight: getHeight,
+ getDimensions: getDimensions,
+ getOffsetParent: getOffsetParent,
+ cumulativeOffset: cumulativeOffset,
+ positionedOffset: positionedOffset,
+ cumulativeScrollOffset: cumulativeScrollOffset,
+ viewportOffset: viewportOffset,
+ absolutize: absolutize,
+ relativize: relativize,
+ scrollTo: scrollTo,
+ makePositioned: makePositioned,
+ undoPositioned: undoPositioned,
+ makeClipping: makeClipping,
+ undoClipping: undoClipping,
+ clonePosition: clonePosition
+ });
+
+ function isBody(element) {
+ return element.nodeName.toUpperCase() === 'BODY';
+ }
+
+ function isHtml(element) {
+ return element.nodeName.toUpperCase() === 'HTML';
+ }
+
+ function isDocument(element) {
+ return element.nodeType === Node.DOCUMENT_NODE;
+ }
+
+ function isDetached(element) {
+ return (
+ element !== document.body && !Element.descendantOf(element, document.body)
+ );
+ }
+
+ if ('getBoundingClientRect' in document.documentElement) {
+ Element.addMethods({
+ viewportOffset: function (element) {
+ element = $(element);
+ if (isDetached(element)) return new Element.Offset(0, 0);
+
+ var rect = element.getBoundingClientRect(),
+ docEl = document.documentElement;
+ return new Element.Offset(
+ rect.left - docEl.clientLeft,
+ rect.top - docEl.clientTop
+ );
+ }
+ });
+ }
+})();
+
+(function () {
+ var IS_OLD_OPERA =
+ Prototype.Browser.Opera && window.parseFloat(window.opera.version()) < 9.5;
+ var ROOT = null;
+ function getRootElement() {
+ if (ROOT) return ROOT;
+ ROOT = IS_OLD_OPERA ? document.body : document.documentElement;
+ return ROOT;
+ }
+
+ function getDimensions() {
+ return { width: this.getWidth(), height: this.getHeight() };
+ }
+
+ function getWidth() {
+ return getRootElement().clientWidth;
+ }
+
+ function getHeight() {
+ return getRootElement().clientHeight;
+ }
+
+ function getScrollOffsets() {
+ var x =
+ window.pageXOffset ||
+ document.documentElement.scrollLeft ||
+ document.body.scrollLeft;
+ var y =
+ window.pageYOffset ||
+ document.documentElement.scrollTop ||
+ document.body.scrollTop;
+
+ return new Element.Offset(x, y);
+ }
+
+ document.viewport = {
+ getDimensions: getDimensions,
+ getWidth: getWidth,
+ getHeight: getHeight,
+ getScrollOffsets: getScrollOffsets
+ };
+})();
+window.$$ = function () {
+ var expression = $A(arguments).join(', ');
+ return Prototype.Selector.select(expression, document);
+};
+
+Prototype.Selector = (function () {
+ function select() {
+ throw new Error('Method "Prototype.Selector.select" must be defined.');
+ }
+
+ function match() {
+ throw new Error('Method "Prototype.Selector.match" must be defined.');
+ }
+
+ function find(elements, expression, index) {
+ index = index || 0;
+ var match = Prototype.Selector.match,
+ length = elements.length,
+ matchIndex = 0,
+ i;
+
+ for (i = 0; i < length; i++) {
+ if (match(elements[i], expression) && index == matchIndex++) {
+ return Element.extend(elements[i]);
+ }
+ }
+ }
+
+ function extendElements(elements) {
+ for (var i = 0, length = elements.length; i < length; i++) {
+ Element.extend(elements[i]);
+ }
+ return elements;
+ }
+
+ var K = Prototype.K;
+
+ return {
+ select: select,
+ match: match,
+ find: find,
+ extendElements: Element.extend === K ? K : extendElements,
+ extendElement: Element.extend
+ };
+})();
+Prototype._original_property = window.Sizzle;
+
+(function () {
+ function fakeDefine(fn) {
+ Prototype._actual_sizzle = fn();
+ }
+ fakeDefine.amd = true;
+
+ if (typeof define !== 'undefined' && define.amd) {
+ Prototype._original_define = define;
+ Prototype._actual_sizzle = null;
+ window.define = fakeDefine;
+ }
+})();
+
+/*!
+ * Sizzle CSS Selector Engine v1.10.18
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-02-05
+ */
+(function (window) {
+ var i,
+ support,
+ Expr,
+ getText,
+ isXML,
+ compile,
+ select,
+ outermostContext,
+ sortInput,
+ hasDuplicate,
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+ expando = 'sizzle' + -new Date(),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ sortOrder = function (a, b) {
+ if (a === b) {
+ hasDuplicate = true;
+ }
+ return 0;
+ },
+ strundefined = typeof undefined,
+ MAX_NEGATIVE = 1 << 31,
+ hasOwn = {}.hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ indexOf =
+ arr.indexOf ||
+ function (elem) {
+ var i = 0,
+ len = this.length;
+ for (; i < len; i++) {
+ if (this[i] === elem) {
+ return i;
+ }
+ }
+ return -1;
+ },
+ booleans =
+ 'checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped',
+ whitespace = '[\\x20\\t\\r\\n\\f]',
+ characterEncoding = '(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+',
+ identifier = characterEncoding.replace('w', 'w#'),
+ attributes =
+ '\\[' +
+ whitespace +
+ '*(' +
+ characterEncoding +
+ ')' +
+ whitespace +
+ '*(?:([*^$|!~]?=)' +
+ whitespace +
+ '*(?:([\'"])((?:\\\\.|[^\\\\])*?)\\3|(' +
+ identifier +
+ ')|)|)' +
+ whitespace +
+ '*\\]',
+ pseudos =
+ ':(' +
+ characterEncoding +
+ ')(?:\\((([\'"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|' +
+ attributes.replace(3, 8) +
+ ')*)|.*)\\)|)',
+ rtrim = new RegExp(
+ '^' + whitespace + '+|((?:^|[^\\\\])(?:\\\\.)*)' + whitespace + '+$',
+ 'g'
+ ),
+ rcomma = new RegExp('^' + whitespace + '*,' + whitespace + '*'),
+ rcombinators = new RegExp(
+ '^' + whitespace + '*([>+~]|' + whitespace + ')' + whitespace + '*'
+ ),
+ rattributeQuotes = new RegExp(
+ '=' + whitespace + '*([^\\]\'"]*?)' + whitespace + '*\\]',
+ 'g'
+ ),
+ rpseudo = new RegExp(pseudos),
+ ridentifier = new RegExp('^' + identifier + '$'),
+ matchExpr = {
+ ID: new RegExp('^#(' + characterEncoding + ')'),
+ CLASS: new RegExp('^\\.(' + characterEncoding + ')'),
+ TAG: new RegExp('^(' + characterEncoding.replace('w', 'w*') + ')'),
+ ATTR: new RegExp('^' + attributes),
+ PSEUDO: new RegExp('^' + pseudos),
+ CHILD: new RegExp(
+ '^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(' +
+ whitespace +
+ '*(even|odd|(([+-]|)(\\d*)n|)' +
+ whitespace +
+ '*(?:([+-]|)' +
+ whitespace +
+ '*(\\d+)|))' +
+ whitespace +
+ '*\\)|)',
+ 'i'
+ ),
+ bool: new RegExp('^(?:' + booleans + ')$', 'i'),
+ needsContext: new RegExp(
+ '^' +
+ whitespace +
+ '*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(' +
+ whitespace +
+ '*((?:-\\d)?\\d*)' +
+ whitespace +
+ '*\\)|)(?=[^-]|$)',
+ 'i'
+ )
+ },
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+ rnative = /^[^{]+\{\s*\[native \w/,
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+ rsibling = /[+~]/,
+ rescape = /'|\\/g,
+ runescape = new RegExp(
+ '\\\\([\\da-f]{1,6}' + whitespace + '?|(' + whitespace + ')|.)',
+ 'ig'
+ ),
+ funescape = function (_, escaped, escapedWhitespace) {
+ var high = '0x' + escaped - 0x10000;
+ return high !== high || escapedWhitespace
+ ? escaped
+ : high < 0
+ ? String.fromCharCode(high + 0x10000)
+ : String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
+ };
+
+ try {
+ push.apply(
+ (arr = slice.call(preferredDoc.childNodes)),
+ preferredDoc.childNodes
+ );
+ arr[preferredDoc.childNodes.length].nodeType;
+ } catch (e) {
+ push = {
+ apply: arr.length
+ ? function (target, els) {
+ push_native.apply(target, slice.call(els));
+ }
+ : function (target, els) {
+ var j = target.length,
+ i = 0;
+ while ((target[j++] = els[i++])) {}
+ target.length = j - 1;
+ }
+ };
+ }
+
+ function Sizzle(selector, context, results, seed) {
+ var match, elem, m, nodeType, i, groups, old, nid, newContext, newSelector;
+
+ if (
+ (context ? context.ownerDocument || context : preferredDoc) !== document
+ ) {
+ setDocument(context);
+ }
+
+ context = context || document;
+ results = results || [];
+
+ if (!selector || typeof selector !== 'string') {
+ return results;
+ }
+
+ if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
+ return [];
+ }
+
+ if (documentIsHTML && !seed) {
+ if ((match = rquickExpr.exec(selector))) {
+ if ((m = match[1])) {
+ if (nodeType === 9) {
+ elem = context.getElementById(m);
+ if (elem && elem.parentNode) {
+ if (elem.id === m) {
+ results.push(elem);
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ if (
+ context.ownerDocument &&
+ (elem = context.ownerDocument.getElementById(m)) &&
+ contains(context, elem) &&
+ elem.id === m
+ ) {
+ results.push(elem);
+ return results;
+ }
+ }
+ } else if (match[2]) {
+ push.apply(results, context.getElementsByTagName(selector));
+ return results;
+ } else if (
+ (m = match[3]) &&
+ support.getElementsByClassName &&
+ context.getElementsByClassName
+ ) {
+ push.apply(results, context.getElementsByClassName(m));
+ return results;
+ }
+ }
+
+ if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType === 9 && selector;
+
+ if (nodeType === 1 && context.nodeName.toLowerCase() !== 'object') {
+ groups = tokenize(selector);
+
+ if ((old = context.getAttribute('id'))) {
+ nid = old.replace(rescape, '\\$&');
+ } else {
+ context.setAttribute('id', nid);
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while (i--) {
+ groups[i] = nid + toSelector(groups[i]);
+ }
+ newContext =
+ (rsibling.test(selector) && testContext(context.parentNode)) ||
+ context;
+ newSelector = groups.join(',');
+ }
+
+ if (newSelector) {
+ try {
+ push.apply(results, newContext.querySelectorAll(newSelector));
+ return results;
+ } catch (qsaError) {
+ } finally {
+ if (!old) {
+ context.removeAttribute('id');
+ }
+ }
+ }
+ }
+ }
+
+ return select(selector.replace(rtrim, '$1'), context, results, seed);
+ }
+
+ /**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+ function createCache() {
+ var keys = [];
+
+ function cache(key, value) {
+ if (keys.push(key + ' ') > Expr.cacheLength) {
+ delete cache[keys.shift()];
+ }
+ return (cache[key + ' '] = value);
+ }
+ return cache;
+ }
+
+ /**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+ function markFunction(fn) {
+ fn[expando] = true;
+ return fn;
+ }
+
+ /**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+ function assert(fn) {
+ var div = document.createElement('div');
+
+ try {
+ return !!fn(div);
+ } catch (e) {
+ return false;
+ } finally {
+ if (div.parentNode) {
+ div.parentNode.removeChild(div);
+ }
+ div = null;
+ }
+ }
+
+ /**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+ function addHandle(attrs, handler) {
+ var arr = attrs.split('|'),
+ i = attrs.length;
+
+ while (i--) {
+ Expr.attrHandle[arr[i]] = handler;
+ }
+ }
+
+ /**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+ function siblingCheck(a, b) {
+ var cur = b && a,
+ diff =
+ cur &&
+ a.nodeType === 1 &&
+ b.nodeType === 1 &&
+ (~b.sourceIndex || MAX_NEGATIVE) - (~a.sourceIndex || MAX_NEGATIVE);
+
+ if (diff) {
+ return diff;
+ }
+
+ if (cur) {
+ while ((cur = cur.nextSibling)) {
+ if (cur === b) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+ }
+
+ /**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+ function createInputPseudo(type) {
+ return function (elem) {
+ var name = elem.nodeName.toLowerCase();
+ return name === 'input' && elem.type === type;
+ };
+ }
+
+ /**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+ function createButtonPseudo(type) {
+ return function (elem) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === 'input' || name === 'button') && elem.type === type;
+ };
+ }
+
+ /**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+ function createPositionalPseudo(fn) {
+ return markFunction(function (argument) {
+ argument = +argument;
+ return markFunction(function (seed, matches) {
+ var j,
+ matchIndexes = fn([], seed.length, argument),
+ i = matchIndexes.length;
+
+ while (i--) {
+ if (seed[(j = matchIndexes[i])]) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+ }
+
+ /**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+ function testContext(context) {
+ return (
+ context && typeof context.getElementsByTagName !== strundefined && context
+ );
+ }
+
+ support = Sizzle.support = {};
+
+ /**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+ isXML = Sizzle.isXML = function (elem) {
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== 'HTML' : false;
+ };
+
+ /**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+ setDocument = Sizzle.setDocument = function (node) {
+ var hasCompare,
+ doc = node ? node.ownerDocument || node : preferredDoc,
+ parent = doc.defaultView;
+
+ if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {
+ return document;
+ }
+
+ document = doc;
+ docElem = doc.documentElement;
+
+ documentIsHTML = !isXML(doc);
+
+ if (parent && parent !== parent.top) {
+ if (parent.addEventListener) {
+ parent.addEventListener(
+ 'unload',
+ function () {
+ setDocument();
+ },
+ false
+ );
+ } else if (parent.attachEvent) {
+ parent.attachEvent('onunload', function () {
+ setDocument();
+ });
+ }
+ }
+
+ /* Attributes
+ ---------------------------------------------------------------------- */
+
+ support.attributes = assert(function (div) {
+ div.className = 'i';
+ return !div.getAttribute('className');
+ });
+
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
+
+ support.getElementsByTagName = assert(function (div) {
+ div.appendChild(doc.createComment(''));
+ return !div.getElementsByTagName('*').length;
+ });
+
+ support.getElementsByClassName =
+ rnative.test(doc.getElementsByClassName) &&
+ assert(function (div) {
+ div.innerHTML = "";
+
+ div.firstChild.className = 'i';
+ return div.getElementsByClassName('i').length === 2;
+ });
+
+ support.getById = assert(function (div) {
+ docElem.appendChild(div).id = expando;
+ return !doc.getElementsByName || !doc.getElementsByName(expando).length;
+ });
+
+ if (support.getById) {
+ Expr.find['ID'] = function (id, context) {
+ if (typeof context.getElementById !== strundefined && documentIsHTML) {
+ var m = context.getElementById(id);
+ return m && m.parentNode ? [m] : [];
+ }
+ };
+ Expr.filter['ID'] = function (id) {
+ var attrId = id.replace(runescape, funescape);
+ return function (elem) {
+ return elem.getAttribute('id') === attrId;
+ };
+ };
+ } else {
+ delete Expr.find['ID'];
+
+ Expr.filter['ID'] = function (id) {
+ var attrId = id.replace(runescape, funescape);
+ return function (elem) {
+ var node =
+ typeof elem.getAttributeNode !== strundefined &&
+ elem.getAttributeNode('id');
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ Expr.find['TAG'] = support.getElementsByTagName
+ ? function (tag, context) {
+ if (typeof context.getElementsByTagName !== strundefined) {
+ return context.getElementsByTagName(tag);
+ }
+ }
+ : function (tag, context) {
+ var elem,
+ tmp = [],
+ i = 0,
+ results = context.getElementsByTagName(tag);
+
+ if (tag === '*') {
+ while ((elem = results[i++])) {
+ if (elem.nodeType === 1) {
+ tmp.push(elem);
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ Expr.find['CLASS'] =
+ support.getElementsByClassName &&
+ function (className, context) {
+ if (
+ typeof context.getElementsByClassName !== strundefined &&
+ documentIsHTML
+ ) {
+ return context.getElementsByClassName(className);
+ }
+ };
+
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
+
+ rbuggyMatches = [];
+
+ rbuggyQSA = [];
+
+ if ((support.qsa = rnative.test(doc.querySelectorAll))) {
+ assert(function (div) {
+ div.innerHTML = "";
+
+ if (div.querySelectorAll("[t^='']").length) {
+ rbuggyQSA.push('[*^$]=' + whitespace + '*(?:\'\'|"")');
+ }
+
+ if (!div.querySelectorAll('[selected]').length) {
+ rbuggyQSA.push('\\[' + whitespace + '*(?:value|' + booleans + ')');
+ }
+
+ if (!div.querySelectorAll(':checked').length) {
+ rbuggyQSA.push(':checked');
+ }
+ });
+
+ assert(function (div) {
+ var input = doc.createElement('input');
+ input.setAttribute('type', 'hidden');
+ div.appendChild(input).setAttribute('name', 'D');
+
+ if (div.querySelectorAll('[name=d]').length) {
+ rbuggyQSA.push('name' + whitespace + '*[*^$|!~]?=');
+ }
+
+ if (!div.querySelectorAll(':enabled').length) {
+ rbuggyQSA.push(':enabled', ':disabled');
+ }
+
+ div.querySelectorAll('*,:x');
+ rbuggyQSA.push(',.*:');
+ });
+ }
+
+ if (
+ (support.matchesSelector = rnative.test(
+ (matches =
+ docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector)
+ ))
+ ) {
+ assert(function (div) {
+ support.disconnectedMatch = matches.call(div, 'div');
+
+ matches.call(div, "[s!='']:x");
+ rbuggyMatches.push('!=', pseudos);
+ });
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join('|'));
+ rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join('|'));
+
+ /* Contains
+ ---------------------------------------------------------------------- */
+ hasCompare = rnative.test(docElem.compareDocumentPosition);
+
+ contains =
+ hasCompare || rnative.test(docElem.contains)
+ ? function (a, b) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return (
+ a === bup ||
+ !!(
+ bup &&
+ bup.nodeType === 1 &&
+ (adown.contains
+ ? adown.contains(bup)
+ : a.compareDocumentPosition &&
+ a.compareDocumentPosition(bup) & 16)
+ )
+ );
+ }
+ : function (a, b) {
+ if (b) {
+ while ((b = b.parentNode)) {
+ if (b === a) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ /* Sorting
+ ---------------------------------------------------------------------- */
+
+ sortOrder = hasCompare
+ ? function (a, b) {
+ if (a === b) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+ if (compare) {
+ return compare;
+ }
+
+ compare =
+ (a.ownerDocument || a) === (b.ownerDocument || b)
+ ? a.compareDocumentPosition(b)
+ : 1;
+
+ if (
+ compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition(a) === compare)
+ ) {
+ if (
+ a === doc ||
+ (a.ownerDocument === preferredDoc && contains(preferredDoc, a))
+ ) {
+ return -1;
+ }
+ if (
+ b === doc ||
+ (b.ownerDocument === preferredDoc && contains(preferredDoc, b))
+ ) {
+ return 1;
+ }
+
+ return sortInput
+ ? indexOf.call(sortInput, a) - indexOf.call(sortInput, b)
+ : 0;
+ }
+
+ return compare & 4 ? -1 : 1;
+ }
+ : function (a, b) {
+ if (a === b) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [a],
+ bp = [b];
+
+ if (!aup || !bup) {
+ return a === doc
+ ? -1
+ : b === doc
+ ? 1
+ : aup
+ ? -1
+ : bup
+ ? 1
+ : sortInput
+ ? indexOf.call(sortInput, a) - indexOf.call(sortInput, b)
+ : 0;
+ } else if (aup === bup) {
+ return siblingCheck(a, b);
+ }
+
+ cur = a;
+ while ((cur = cur.parentNode)) {
+ ap.unshift(cur);
+ }
+ cur = b;
+ while ((cur = cur.parentNode)) {
+ bp.unshift(cur);
+ }
+
+ while (ap[i] === bp[i]) {
+ i++;
+ }
+
+ return i
+ ? siblingCheck(ap[i], bp[i])
+ : ap[i] === preferredDoc
+ ? -1
+ : bp[i] === preferredDoc
+ ? 1
+ : 0;
+ };
+
+ return doc;
+ };
+
+ Sizzle.matches = function (expr, elements) {
+ return Sizzle(expr, null, null, elements);
+ };
+
+ Sizzle.matchesSelector = function (elem, expr) {
+ if ((elem.ownerDocument || elem) !== document) {
+ setDocument(elem);
+ }
+
+ expr = expr.replace(rattributeQuotes, "='$1']");
+
+ if (
+ support.matchesSelector &&
+ documentIsHTML &&
+ (!rbuggyMatches || !rbuggyMatches.test(expr)) &&
+ (!rbuggyQSA || !rbuggyQSA.test(expr))
+ ) {
+ try {
+ var ret = matches.call(elem, expr);
+
+ if (
+ ret ||
+ support.disconnectedMatch ||
+ (elem.document && elem.document.nodeType !== 11)
+ ) {
+ return ret;
+ }
+ } catch (e) {}
+ }
+
+ return Sizzle(expr, document, null, [elem]).length > 0;
+ };
+
+ Sizzle.contains = function (context, elem) {
+ if ((context.ownerDocument || context) !== document) {
+ setDocument(context);
+ }
+ return contains(context, elem);
+ };
+
+ Sizzle.attr = function (elem, name) {
+ if ((elem.ownerDocument || elem) !== document) {
+ setDocument(elem);
+ }
+
+ var fn = Expr.attrHandle[name.toLowerCase()],
+ val =
+ fn && hasOwn.call(Expr.attrHandle, name.toLowerCase())
+ ? fn(elem, name, !documentIsHTML)
+ : undefined;
+
+ return val !== undefined
+ ? val
+ : support.attributes || !documentIsHTML
+ ? elem.getAttribute(name)
+ : (val = elem.getAttributeNode(name)) && val.specified
+ ? val.value
+ : null;
+ };
+
+ Sizzle.error = function (msg) {
+ throw new Error('Syntax error, unrecognized expression: ' + msg);
+ };
+
+ /**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+ Sizzle.uniqueSort = function (results) {
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
+
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice(0);
+ results.sort(sortOrder);
+
+ if (hasDuplicate) {
+ while ((elem = results[i++])) {
+ if (elem === results[i]) {
+ j = duplicates.push(i);
+ }
+ }
+ while (j--) {
+ results.splice(duplicates[j], 1);
+ }
+ }
+
+ sortInput = null;
+
+ return results;
+ };
+
+ /**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+ getText = Sizzle.getText = function (elem) {
+ var node,
+ ret = '',
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if (!nodeType) {
+ while ((node = elem[i++])) {
+ ret += getText(node);
+ }
+ } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
+ if (typeof elem.textContent === 'string') {
+ return elem.textContent;
+ } else {
+ for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText(elem);
+ }
+ }
+ } else if (nodeType === 3 || nodeType === 4) {
+ return elem.nodeValue;
+ }
+
+ return ret;
+ };
+
+ Expr = Sizzle.selectors = {
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ '>': { dir: 'parentNode', first: true },
+ ' ': { dir: 'parentNode' },
+ '+': { dir: 'previousSibling', first: true },
+ '~': { dir: 'previousSibling' }
+ },
+
+ preFilter: {
+ ATTR: function (match) {
+ match[1] = match[1].replace(runescape, funescape);
+
+ match[3] = (match[4] || match[5] || '').replace(runescape, funescape);
+
+ if (match[2] === '~=') {
+ match[3] = ' ' + match[3] + ' ';
+ }
+
+ return match.slice(0, 4);
+ },
+
+ CHILD: function (match) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if (match[1].slice(0, 3) === 'nth') {
+ if (!match[3]) {
+ Sizzle.error(match[0]);
+ }
+
+ match[4] = +(match[4]
+ ? match[5] + (match[6] || 1)
+ : 2 * (match[3] === 'even' || match[3] === 'odd'));
+ match[5] = +(match[7] + match[8] || match[3] === 'odd');
+ } else if (match[3]) {
+ Sizzle.error(match[0]);
+ }
+
+ return match;
+ },
+
+ PSEUDO: function (match) {
+ var excess,
+ unquoted = !match[5] && match[2];
+
+ if (matchExpr['CHILD'].test(match[0])) {
+ return null;
+ }
+
+ if (match[3] && match[4] !== undefined) {
+ match[2] = match[4];
+ } else if (
+ unquoted &&
+ rpseudo.test(unquoted) &&
+ (excess = tokenize(unquoted, true)) &&
+ (excess =
+ unquoted.indexOf(')', unquoted.length - excess) - unquoted.length)
+ ) {
+ match[0] = match[0].slice(0, excess);
+ match[2] = unquoted.slice(0, excess);
+ }
+
+ return match.slice(0, 3);
+ }
+ },
+
+ filter: {
+ TAG: function (nodeNameSelector) {
+ var nodeName = nodeNameSelector
+ .replace(runescape, funescape)
+ .toLowerCase();
+ return nodeNameSelector === '*'
+ ? function () {
+ return true;
+ }
+ : function (elem) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ CLASS: function (className) {
+ var pattern = classCache[className + ' '];
+
+ return (
+ pattern ||
+ ((pattern = new RegExp(
+ '(^|' + whitespace + ')' + className + '(' + whitespace + '|$)'
+ )) &&
+ classCache(className, function (elem) {
+ return pattern.test(
+ (typeof elem.className === 'string' && elem.className) ||
+ (typeof elem.getAttribute !== strundefined &&
+ elem.getAttribute('class')) ||
+ ''
+ );
+ }))
+ );
+ },
+
+ ATTR: function (name, operator, check) {
+ return function (elem) {
+ var result = Sizzle.attr(elem, name);
+
+ if (result == null) {
+ return operator === '!=';
+ }
+ if (!operator) {
+ return true;
+ }
+
+ result += '';
+
+ return operator === '='
+ ? result === check
+ : operator === '!='
+ ? result !== check
+ : operator === '^='
+ ? check && result.indexOf(check) === 0
+ : operator === '*='
+ ? check && result.indexOf(check) > -1
+ : operator === '$='
+ ? check && result.slice(-check.length) === check
+ : operator === '~='
+ ? (' ' + result + ' ').indexOf(check) > -1
+ : operator === '|='
+ ? result === check ||
+ result.slice(0, check.length + 1) === check + '-'
+ : false;
+ };
+ },
+
+ CHILD: function (type, what, argument, first, last) {
+ var simple = type.slice(0, 3) !== 'nth',
+ forward = type.slice(-4) !== 'last',
+ ofType = what === 'of-type';
+
+ return first === 1 && last === 0
+ ? function (elem) {
+ return !!elem.parentNode;
+ }
+ : function (elem, context, xml) {
+ var cache,
+ outerCache,
+ node,
+ diff,
+ nodeIndex,
+ start,
+ dir = simple !== forward ? 'nextSibling' : 'previousSibling',
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if (parent) {
+ if (simple) {
+ while (dir) {
+ node = elem;
+ while ((node = node[dir])) {
+ if (
+ ofType
+ ? node.nodeName.toLowerCase() === name
+ : node.nodeType === 1
+ ) {
+ return false;
+ }
+ }
+ start = dir = type === 'only' && !start && 'nextSibling';
+ }
+ return true;
+ }
+
+ start = [forward ? parent.firstChild : parent.lastChild];
+
+ if (forward && useCache) {
+ outerCache = parent[expando] || (parent[expando] = {});
+ cache = outerCache[type] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[nodeIndex];
+
+ while (
+ (node =
+ (++nodeIndex && node && node[dir]) ||
+ (diff = nodeIndex = 0) ||
+ start.pop())
+ ) {
+ if (node.nodeType === 1 && ++diff && node === elem) {
+ outerCache[type] = [dirruns, nodeIndex, diff];
+ break;
+ }
+ }
+ } else if (
+ useCache &&
+ (cache = (elem[expando] || (elem[expando] = {}))[type]) &&
+ cache[0] === dirruns
+ ) {
+ diff = cache[1];
+ } else {
+ while (
+ (node =
+ (++nodeIndex && node && node[dir]) ||
+ (diff = nodeIndex = 0) ||
+ start.pop())
+ ) {
+ if (
+ (ofType
+ ? node.nodeName.toLowerCase() === name
+ : node.nodeType === 1) &&
+ ++diff
+ ) {
+ if (useCache) {
+ (node[expando] || (node[expando] = {}))[type] = [
+ dirruns,
+ diff
+ ];
+ }
+
+ if (node === elem) {
+ break;
+ }
+ }
+ }
+ }
+
+ diff -= last;
+ return (
+ diff === first || (diff % first === 0 && diff / first >= 0)
+ );
+ }
+ };
+ },
+
+ PSEUDO: function (pseudo, argument) {
+ var args,
+ fn =
+ Expr.pseudos[pseudo] ||
+ Expr.setFilters[pseudo.toLowerCase()] ||
+ Sizzle.error('unsupported pseudo: ' + pseudo);
+
+ if (fn[expando]) {
+ return fn(argument);
+ }
+
+ if (fn.length > 1) {
+ args = [pseudo, pseudo, '', argument];
+ return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())
+ ? markFunction(function (seed, matches) {
+ var idx,
+ matched = fn(seed, argument),
+ i = matched.length;
+ while (i--) {
+ idx = indexOf.call(seed, matched[i]);
+ seed[idx] = !(matches[idx] = matched[i]);
+ }
+ })
+ : function (elem) {
+ return fn(elem, 0, args);
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ not: markFunction(function (selector) {
+ var input = [],
+ results = [],
+ matcher = compile(selector.replace(rtrim, '$1'));
+
+ return matcher[expando]
+ ? markFunction(function (seed, matches, context, xml) {
+ var elem,
+ unmatched = matcher(seed, null, xml, []),
+ i = seed.length;
+
+ while (i--) {
+ if ((elem = unmatched[i])) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ })
+ : function (elem, context, xml) {
+ input[0] = elem;
+ matcher(input, null, xml, results);
+ return !results.pop();
+ };
+ }),
+
+ has: markFunction(function (selector) {
+ return function (elem) {
+ return Sizzle(selector, elem).length > 0;
+ };
+ }),
+
+ contains: markFunction(function (text) {
+ return function (elem) {
+ return (
+ (elem.textContent || elem.innerText || getText(elem)).indexOf(
+ text
+ ) > -1
+ );
+ };
+ }),
+
+ lang: markFunction(function (lang) {
+ if (!ridentifier.test(lang || '')) {
+ Sizzle.error('unsupported lang: ' + lang);
+ }
+ lang = lang.replace(runescape, funescape).toLowerCase();
+ return function (elem) {
+ var elemLang;
+ do {
+ if (
+ (elemLang = documentIsHTML
+ ? elem.lang
+ : elem.getAttribute('xml:lang') || elem.getAttribute('lang'))
+ ) {
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf(lang + '-') === 0;
+ }
+ } while ((elem = elem.parentNode) && elem.nodeType === 1);
+ return false;
+ };
+ }),
+
+ target: function (elem) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice(1) === elem.id;
+ },
+
+ root: function (elem) {
+ return elem === docElem;
+ },
+
+ focus: function (elem) {
+ return (
+ elem === document.activeElement &&
+ (!document.hasFocus || document.hasFocus()) &&
+ !!(elem.type || elem.href || ~elem.tabIndex)
+ );
+ },
+
+ enabled: function (elem) {
+ return elem.disabled === false;
+ },
+
+ disabled: function (elem) {
+ return elem.disabled === true;
+ },
+
+ checked: function (elem) {
+ var nodeName = elem.nodeName.toLowerCase();
+ return (
+ (nodeName === 'input' && !!elem.checked) ||
+ (nodeName === 'option' && !!elem.selected)
+ );
+ },
+
+ selected: function (elem) {
+ if (elem.parentNode) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ empty: function (elem) {
+ for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ if (elem.nodeType < 6) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ parent: function (elem) {
+ return !Expr.pseudos['empty'](elem);
+ },
+
+ header: function (elem) {
+ return rheader.test(elem.nodeName);
+ },
+
+ input: function (elem) {
+ return rinputs.test(elem.nodeName);
+ },
+
+ button: function (elem) {
+ var name = elem.nodeName.toLowerCase();
+ return (
+ (name === 'input' && elem.type === 'button') || name === 'button'
+ );
+ },
+
+ text: function (elem) {
+ var attr;
+ return (
+ elem.nodeName.toLowerCase() === 'input' &&
+ elem.type === 'text' &&
+ ((attr = elem.getAttribute('type')) == null ||
+ attr.toLowerCase() === 'text')
+ );
+ },
+
+ first: createPositionalPseudo(function () {
+ return [0];
+ }),
+
+ last: createPositionalPseudo(function (matchIndexes, length) {
+ return [length - 1];
+ }),
+
+ eq: createPositionalPseudo(function (matchIndexes, length, argument) {
+ return [argument < 0 ? argument + length : argument];
+ }),
+
+ even: createPositionalPseudo(function (matchIndexes, length) {
+ var i = 0;
+ for (; i < length; i += 2) {
+ matchIndexes.push(i);
+ }
+ return matchIndexes;
+ }),
+
+ odd: createPositionalPseudo(function (matchIndexes, length) {
+ var i = 1;
+ for (; i < length; i += 2) {
+ matchIndexes.push(i);
+ }
+ return matchIndexes;
+ }),
+
+ lt: createPositionalPseudo(function (matchIndexes, length, argument) {
+ var i = argument < 0 ? argument + length : argument;
+ for (; --i >= 0; ) {
+ matchIndexes.push(i);
+ }
+ return matchIndexes;
+ }),
+
+ gt: createPositionalPseudo(function (matchIndexes, length, argument) {
+ var i = argument < 0 ? argument + length : argument;
+ for (; ++i < length; ) {
+ matchIndexes.push(i);
+ }
+ return matchIndexes;
+ })
+ }
+ };
+
+ Expr.pseudos['nth'] = Expr.pseudos['eq'];
+
+ for (i in {
+ radio: true,
+ checkbox: true,
+ file: true,
+ password: true,
+ image: true
+ }) {
+ Expr.pseudos[i] = createInputPseudo(i);
+ }
+ for (i in { submit: true, reset: true }) {
+ Expr.pseudos[i] = createButtonPseudo(i);
+ }
+
+ function setFilters() {}
+ setFilters.prototype = Expr.filters = Expr.pseudos;
+ Expr.setFilters = new setFilters();
+
+ function tokenize(selector, parseOnly) {
+ var matched,
+ match,
+ tokens,
+ type,
+ soFar,
+ groups,
+ preFilters,
+ cached = tokenCache[selector + ' '];
+
+ if (cached) {
+ return parseOnly ? 0 : cached.slice(0);
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while (soFar) {
+ if (!matched || (match = rcomma.exec(soFar))) {
+ if (match) {
+ soFar = soFar.slice(match[0].length) || soFar;
+ }
+ groups.push((tokens = []));
+ }
+
+ matched = false;
+
+ if ((match = rcombinators.exec(soFar))) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: match[0].replace(rtrim, ' ')
+ });
+ soFar = soFar.slice(matched.length);
+ }
+
+ for (type in Expr.filter) {
+ if (
+ (match = matchExpr[type].exec(soFar)) &&
+ (!preFilters[type] || (match = preFilters[type](match)))
+ ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice(matched.length);
+ }
+ }
+
+ if (!matched) {
+ break;
+ }
+ }
+
+ return parseOnly
+ ? soFar.length
+ : soFar
+ ? Sizzle.error(selector)
+ : tokenCache(selector, groups).slice(0);
+ }
+
+ function toSelector(tokens) {
+ var i = 0,
+ len = tokens.length,
+ selector = '';
+ for (; i < len; i++) {
+ selector += tokens[i].value;
+ }
+ return selector;
+ }
+
+ function addCombinator(matcher, combinator, base) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === 'parentNode',
+ doneName = done++;
+
+ return combinator.first
+ ? function (elem, context, xml) {
+ while ((elem = elem[dir])) {
+ if (elem.nodeType === 1 || checkNonElements) {
+ return matcher(elem, context, xml);
+ }
+ }
+ }
+ : function (elem, context, xml) {
+ var oldCache,
+ outerCache,
+ newCache = [dirruns, doneName];
+
+ if (xml) {
+ while ((elem = elem[dir])) {
+ if (elem.nodeType === 1 || checkNonElements) {
+ if (matcher(elem, context, xml)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ((elem = elem[dir])) {
+ if (elem.nodeType === 1 || checkNonElements) {
+ outerCache = elem[expando] || (elem[expando] = {});
+ if (
+ (oldCache = outerCache[dir]) &&
+ oldCache[0] === dirruns &&
+ oldCache[1] === doneName
+ ) {
+ return (newCache[2] = oldCache[2]);
+ } else {
+ outerCache[dir] = newCache;
+
+ if ((newCache[2] = matcher(elem, context, xml))) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+ }
+
+ function elementMatcher(matchers) {
+ return matchers.length > 1
+ ? function (elem, context, xml) {
+ var i = matchers.length;
+ while (i--) {
+ if (!matchers[i](elem, context, xml)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ : matchers[0];
+ }
+
+ function multipleContexts(selector, contexts, results) {
+ var i = 0,
+ len = contexts.length;
+ for (; i < len; i++) {
+ Sizzle(selector, contexts[i], results);
+ }
+ return results;
+ }
+
+ function condense(unmatched, map, filter, context, xml) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for (; i < len; i++) {
+ if ((elem = unmatched[i])) {
+ if (!filter || filter(elem, context, xml)) {
+ newUnmatched.push(elem);
+ if (mapped) {
+ map.push(i);
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+ }
+
+ function setMatcher(
+ preFilter,
+ selector,
+ matcher,
+ postFilter,
+ postFinder,
+ postSelector
+ ) {
+ if (postFilter && !postFilter[expando]) {
+ postFilter = setMatcher(postFilter);
+ }
+ if (postFinder && !postFinder[expando]) {
+ postFinder = setMatcher(postFinder, postSelector);
+ }
+ return markFunction(function (seed, results, context, xml) {
+ var temp,
+ i,
+ elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+ elems =
+ seed ||
+ multipleContexts(
+ selector || '*',
+ context.nodeType ? [context] : context,
+ []
+ ),
+ matcherIn =
+ preFilter && (seed || !selector)
+ ? condense(elems, preMap, preFilter, context, xml)
+ : elems,
+ matcherOut = matcher
+ ? postFinder || (seed ? preFilter : preexisting || postFilter)
+ ? []
+ : results
+ : matcherIn;
+
+ if (matcher) {
+ matcher(matcherIn, matcherOut, context, xml);
+ }
+
+ if (postFilter) {
+ temp = condense(matcherOut, postMap);
+ postFilter(temp, [], context, xml);
+
+ i = temp.length;
+ while (i--) {
+ if ((elem = temp[i])) {
+ matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
+ }
+ }
+ }
+
+ if (seed) {
+ if (postFinder || preFilter) {
+ if (postFinder) {
+ temp = [];
+ i = matcherOut.length;
+ while (i--) {
+ if ((elem = matcherOut[i])) {
+ temp.push((matcherIn[i] = elem));
+ }
+ }
+ postFinder(null, (matcherOut = []), temp, xml);
+ }
+
+ i = matcherOut.length;
+ while (i--) {
+ if (
+ (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1
+ ) {
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+ } else {
+ matcherOut = condense(
+ matcherOut === results
+ ? matcherOut.splice(preexisting, matcherOut.length)
+ : matcherOut
+ );
+ if (postFinder) {
+ postFinder(null, results, matcherOut, xml);
+ } else {
+ push.apply(results, matcherOut);
+ }
+ }
+ });
+ }
+
+ function matcherFromTokens(tokens) {
+ var checkContext,
+ matcher,
+ j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[tokens[0].type],
+ implicitRelative = leadingRelative || Expr.relative[' '],
+ i = leadingRelative ? 1 : 0,
+ matchContext = addCombinator(
+ function (elem) {
+ return elem === checkContext;
+ },
+ implicitRelative,
+ true
+ ),
+ matchAnyContext = addCombinator(
+ function (elem) {
+ return indexOf.call(checkContext, elem) > -1;
+ },
+ implicitRelative,
+ true
+ ),
+ matchers = [
+ function (elem, context, xml) {
+ return (
+ (!leadingRelative && (xml || context !== outermostContext)) ||
+ ((checkContext = context).nodeType
+ ? matchContext(elem, context, xml)
+ : matchAnyContext(elem, context, xml))
+ );
+ }
+ ];
+
+ for (; i < len; i++) {
+ if ((matcher = Expr.relative[tokens[i].type])) {
+ matchers = [addCombinator(elementMatcher(matchers), matcher)];
+ } else {
+ matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);
+
+ if (matcher[expando]) {
+ j = ++i;
+ for (; j < len; j++) {
+ if (Expr.relative[tokens[j].type]) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher(matchers),
+ i > 1 &&
+ toSelector(
+ tokens
+ .slice(0, i - 1)
+ .concat({ value: tokens[i - 2].type === ' ' ? '*' : '' })
+ ).replace(rtrim, '$1'),
+ matcher,
+ i < j && matcherFromTokens(tokens.slice(i, j)),
+ j < len && matcherFromTokens((tokens = tokens.slice(j))),
+ j < len && toSelector(tokens)
+ );
+ }
+ matchers.push(matcher);
+ }
+ }
+
+ return elementMatcher(matchers);
+ }
+
+ function matcherFromGroupMatchers(elementMatchers, setMatchers) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function (seed, context, xml, results, outermost) {
+ var elem,
+ j,
+ matcher,
+ matchedCount = 0,
+ i = '0',
+ unmatched = seed && [],
+ setMatched = [],
+ contextBackup = outermostContext,
+ elems = seed || (byElement && Expr.find['TAG']('*', outermost)),
+ dirrunsUnique = (dirruns +=
+ contextBackup == null ? 1 : Math.random() || 0.1),
+ len = elems.length;
+
+ if (outermost) {
+ outermostContext = context !== document && context;
+ }
+
+ for (; i !== len && (elem = elems[i]) != null; i++) {
+ if (byElement && elem) {
+ j = 0;
+ while ((matcher = elementMatchers[j++])) {
+ if (matcher(elem, context, xml)) {
+ results.push(elem);
+ break;
+ }
+ }
+ if (outermost) {
+ dirruns = dirrunsUnique;
+ }
+ }
+
+ if (bySet) {
+ if ((elem = !matcher && elem)) {
+ matchedCount--;
+ }
+
+ if (seed) {
+ unmatched.push(elem);
+ }
+ }
+ }
+
+ matchedCount += i;
+ if (bySet && i !== matchedCount) {
+ j = 0;
+ while ((matcher = setMatchers[j++])) {
+ matcher(unmatched, setMatched, context, xml);
+ }
+
+ if (seed) {
+ if (matchedCount > 0) {
+ while (i--) {
+ if (!(unmatched[i] || setMatched[i])) {
+ setMatched[i] = pop.call(results);
+ }
+ }
+ }
+
+ setMatched = condense(setMatched);
+ }
+
+ push.apply(results, setMatched);
+
+ if (
+ outermost &&
+ !seed &&
+ setMatched.length > 0 &&
+ matchedCount + setMatchers.length > 1
+ ) {
+ Sizzle.uniqueSort(results);
+ }
+ }
+
+ if (outermost) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ? markFunction(superMatcher) : superMatcher;
+ }
+
+ compile = Sizzle.compile = function (
+ selector,
+ match /* Internal Use Only */
+ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[selector + ' '];
+
+ if (!cached) {
+ if (!match) {
+ match = tokenize(selector);
+ }
+ i = match.length;
+ while (i--) {
+ cached = matcherFromTokens(match[i]);
+ if (cached[expando]) {
+ setMatchers.push(cached);
+ } else {
+ elementMatchers.push(cached);
+ }
+ }
+
+ cached = compilerCache(
+ selector,
+ matcherFromGroupMatchers(elementMatchers, setMatchers)
+ );
+
+ cached.selector = selector;
+ }
+ return cached;
+ };
+
+ /**
+ * A low-level selection function that works with Sizzle's compiled
+ * selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ * selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+ select = Sizzle.select = function (selector, context, results, seed) {
+ var i,
+ tokens,
+ token,
+ type,
+ find,
+ compiled = typeof selector === 'function' && selector,
+ match = !seed && tokenize((selector = compiled.selector || selector));
+
+ results = results || [];
+
+ if (match.length === 1) {
+ tokens = match[0] = match[0].slice(0);
+ if (
+ tokens.length > 2 &&
+ (token = tokens[0]).type === 'ID' &&
+ support.getById &&
+ context.nodeType === 9 &&
+ documentIsHTML &&
+ Expr.relative[tokens[1].type]
+ ) {
+ context = (Expr.find['ID'](
+ token.matches[0].replace(runescape, funescape),
+ context
+ ) || [])[0];
+ if (!context) {
+ return results;
+ } else if (compiled) {
+ context = context.parentNode;
+ }
+
+ selector = selector.slice(tokens.shift().value.length);
+ }
+
+ i = matchExpr['needsContext'].test(selector) ? 0 : tokens.length;
+ while (i--) {
+ token = tokens[i];
+
+ if (Expr.relative[(type = token.type)]) {
+ break;
+ }
+ if ((find = Expr.find[type])) {
+ if (
+ (seed = find(
+ token.matches[0].replace(runescape, funescape),
+ (rsibling.test(tokens[0].type) &&
+ testContext(context.parentNode)) ||
+ context
+ ))
+ ) {
+ tokens.splice(i, 1);
+ selector = seed.length && toSelector(tokens);
+ if (!selector) {
+ push.apply(results, seed);
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ (compiled || compile(selector, match))(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ (rsibling.test(selector) && testContext(context.parentNode)) || context
+ );
+ return results;
+ };
+
+ support.sortStable = expando.split('').sort(sortOrder).join('') === expando;
+
+ support.detectDuplicates = !!hasDuplicate;
+
+ setDocument();
+
+ support.sortDetached = assert(function (div1) {
+ return div1.compareDocumentPosition(document.createElement('div')) & 1;
+ });
+
+ if (
+ !assert(function (div) {
+ div.innerHTML = "";
+ return div.firstChild.getAttribute('href') === '#';
+ })
+ ) {
+ addHandle('type|href|height|width', function (elem, name, isXML) {
+ if (!isXML) {
+ return elem.getAttribute(name, name.toLowerCase() === 'type' ? 1 : 2);
+ }
+ });
+ }
+
+ if (
+ !support.attributes ||
+ !assert(function (div) {
+ div.innerHTML = '';
+ div.firstChild.setAttribute('value', '');
+ return div.firstChild.getAttribute('value') === '';
+ })
+ ) {
+ addHandle('value', function (elem, name, isXML) {
+ if (!isXML && elem.nodeName.toLowerCase() === 'input') {
+ return elem.defaultValue;
+ }
+ });
+ }
+
+ if (
+ !assert(function (div) {
+ return div.getAttribute('disabled') == null;
+ })
+ ) {
+ addHandle(booleans, function (elem, name, isXML) {
+ var val;
+ if (!isXML) {
+ return elem[name] === true
+ ? name.toLowerCase()
+ : (val = elem.getAttributeNode(name)) && val.specified
+ ? val.value
+ : null;
+ }
+ });
+ }
+
+ if (typeof define === 'function' && define.amd) {
+ define(function () {
+ return Sizzle;
+ });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Sizzle;
+ } else {
+ window.Sizzle = Sizzle;
+ }
+})(window);
+
+(function () {
+ if (typeof Sizzle !== 'undefined') {
+ return;
+ }
+
+ if (typeof define !== 'undefined' && define.amd) {
+ window.Sizzle = Prototype._actual_sizzle;
+ window.define = Prototype._original_define;
+ delete Prototype._actual_sizzle;
+ delete Prototype._original_define;
+ } else if (typeof module !== 'undefined' && module.exports) {
+ window.Sizzle = module.exports;
+ module.exports = {};
+ }
+})();
+
+(function (engine) {
+ var extendElements = Prototype.Selector.extendElements;
+
+ function select(selector, scope) {
+ return extendElements(engine(selector, scope || document));
+ }
+
+ function match(element, selector) {
+ return engine.matches(selector, [element]).length == 1;
+ }
+
+ Prototype.Selector.engine = engine;
+ Prototype.Selector.select = select;
+ Prototype.Selector.match = match;
+})(Sizzle);
+
+window.Sizzle = Prototype._original_property;
+delete Prototype._original_property;
+
+var Form = {
+ reset: function (form) {
+ form = $(form);
+ form.reset();
+ return form;
+ },
+
+ serializeElements: function (elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key,
+ value,
+ submitted = false,
+ submit = options.submit,
+ accumulator,
+ initial;
+
+ if (options.hash) {
+ initial = {};
+ accumulator = function (result, key, value) {
+ if (key in result) {
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key] = result[key].concat(value);
+ } else result[key] = value;
+ return result;
+ };
+ } else {
+ initial = '';
+ accumulator = function (result, key, values) {
+ if (!Object.isArray(values)) {
+ values = [values];
+ }
+ if (!values.length) {
+ return result;
+ }
+ var encodedKey = encodeURIComponent(key).gsub(/%20/, '+');
+ return (
+ result +
+ (result ? '&' : '') +
+ values
+ .map(function (value) {
+ value = value.gsub(/(\r)?\n/, '\r\n');
+ value = encodeURIComponent(value);
+ value = value.gsub(/%20/, '+');
+ return encodedKey + '=' + value;
+ })
+ .join('&')
+ );
+ };
+ }
+
+ return elements.inject(initial, function (result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name;
+ value = $(element).getValue();
+ if (
+ value != null &&
+ element.type != 'file' &&
+ (element.type != 'submit' ||
+ (!submitted &&
+ submit !== false &&
+ (!submit || key == submit) &&
+ (submitted = true)))
+ ) {
+ result = accumulator(result, key, value);
+ }
+ }
+ return result;
+ });
+ }
+};
+
+Form.Methods = {
+ serialize: function (form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function (form) {
+ var elements = $(form).getElementsByTagName('*');
+ var element,
+ results = [],
+ serializers = Form.Element.Serializers;
+
+ for (var i = 0; (element = elements[i]); i++) {
+ if (serializers[element.tagName.toLowerCase()])
+ results.push(Element.extend(element));
+ }
+ return results;
+ },
+
+ getInputs: function (form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (
+ var i = 0, matchingInputs = [], length = inputs.length;
+ i < length;
+ i++
+ ) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function (form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function (form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function (form) {
+ var elements = $(form)
+ .getElements()
+ .findAll(function (element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements
+ .findAll(function (element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ })
+ .sortBy(function (element) {
+ return element.tabIndex;
+ })
+ .first();
+
+ return firstByIndex
+ ? firstByIndex
+ : elements.find(function (element) {
+ return /^(?:input|select|textarea)$/i.test(element.tagName);
+ });
+ },
+
+ focusFirstElement: function (form) {
+ form = $(form);
+ var element = form.findFirstElement();
+ if (element) element.activate();
+ return form;
+ },
+
+ request: function (form, options) {
+ (form = $(form)), (options = Object.clone(options || {}));
+
+ var params = options.parameters,
+ action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function (element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function (element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function (element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = {};
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function (element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function (element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function (element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function (element) {
+ return $(element).value != '';
+ },
+
+ activate: function (element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (
+ element.select &&
+ (element.tagName.toLowerCase() != 'input' ||
+ !/^(?:button|reset|submit)$/i.test(element.type))
+ )
+ element.select();
+ } catch (e) {}
+ return element;
+ },
+
+ disable: function (element) {
+ element = $(element);
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function (element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = (function () {
+ function input(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return inputSelector(element, value);
+ default:
+ return valueSelector(element, value);
+ }
+ }
+
+ function inputSelector(element, value) {
+ if (Object.isUndefined(value))
+ return element.checked ? element.value : null;
+ else element.checked = !!value;
+ }
+
+ function valueSelector(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ }
+
+ function select(element, value) {
+ if (Object.isUndefined(value))
+ return (element.type === 'select-one' ? selectOne : selectMany)(element);
+
+ var opt,
+ currentValue,
+ single = !Object.isArray(value);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ currentValue = this.optionValue(opt);
+ if (single) {
+ if (currentValue == value) {
+ opt.selected = true;
+ return;
+ }
+ } else opt.selected = value.include(currentValue);
+ }
+ }
+
+ function selectOne(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? optionValue(element.options[index]) : null;
+ }
+
+ function selectMany(element) {
+ var values,
+ length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(optionValue(opt));
+ }
+ return values;
+ }
+
+ function optionValue(opt) {
+ return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
+ }
+
+ return {
+ input: input,
+ inputSelector: inputSelector,
+ textarea: valueSelector,
+ select: select,
+ selectOne: selectOne,
+ selectMany: selectMany,
+ optionValue: optionValue,
+ button: valueSelector
+ };
+})();
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function ($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function () {
+ var value = this.getValue();
+ if (
+ Object.isString(this.lastValue) && Object.isString(value)
+ ? this.lastValue != value
+ : String(this.lastValue) != String(value)
+ ) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function () {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function () {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function (element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else this.registerCallback(this.element);
+ },
+
+ onElementEvent: function () {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function () {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function (element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function () {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function () {
+ return Form.serialize(this.element);
+ }
+});
+(function (GLOBAL) {
+ var DIV = document.createElement('div');
+ var docEl = document.documentElement;
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED =
+ 'onmouseenter' in docEl && 'onmouseleave' in docEl;
+
+ var Event = {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45
+ };
+
+ var isIELegacyEvent = function (event) {
+ return false;
+ };
+
+ if (window.attachEvent) {
+ if (window.addEventListener) {
+ isIELegacyEvent = function (event) {
+ return !(event instanceof window.Event);
+ };
+ } else {
+ isIELegacyEvent = function (event) {
+ return true;
+ };
+ }
+ }
+
+ var _isButton;
+
+ function _isButtonForDOMEvents(event, code) {
+ return event.which ? event.which === code + 1 : event.button === code;
+ }
+
+ var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
+ function _isButtonForLegacyEvents(event, code) {
+ return event.button === legacyButtonMap[code];
+ }
+
+ function _isButtonForWebKit(event, code) {
+ switch (code) {
+ case 0:
+ return event.which == 1 && !event.metaKey;
+ case 1:
+ return event.which == 2 || (event.which == 1 && event.metaKey);
+ case 2:
+ return event.which == 3;
+ default:
+ return false;
+ }
+ }
+
+ if (window.attachEvent) {
+ if (!window.addEventListener) {
+ _isButton = _isButtonForLegacyEvents;
+ } else {
+ _isButton = function (event, code) {
+ return isIELegacyEvent(event)
+ ? _isButtonForLegacyEvents(event, code)
+ : _isButtonForDOMEvents(event, code);
+ };
+ }
+ } else if (Prototype.Browser.WebKit) {
+ _isButton = _isButtonForWebKit;
+ } else {
+ _isButton = _isButtonForDOMEvents;
+ }
+
+ function isLeftClick(event) {
+ return _isButton(event, 0);
+ }
+
+ function isMiddleClick(event) {
+ return _isButton(event, 1);
+ }
+
+ function isRightClick(event) {
+ return _isButton(event, 2);
+ }
+
+ function element(event) {
+ return Element.extend(_element(event));
+ }
+
+ function _element(event) {
+ event = Event.extend(event);
+
+ var node = event.target,
+ type = event.type,
+ currentTarget = event.currentTarget;
+
+ if (currentTarget && currentTarget.tagName) {
+ if (
+ type === 'load' ||
+ type === 'error' ||
+ (type === 'click' &&
+ currentTarget.tagName.toLowerCase() === 'input' &&
+ currentTarget.type === 'radio')
+ )
+ node = currentTarget;
+ }
+
+ return node.nodeType == Node.TEXT_NODE ? node.parentNode : node;
+ }
+
+ function findElement(event, expression) {
+ var element = _element(event),
+ selector = Prototype.Selector;
+ if (!expression) return Element.extend(element);
+ while (element) {
+ if (Object.isElement(element) && selector.match(element, expression))
+ return Element.extend(element);
+ element = element.parentNode;
+ }
+ }
+
+ function pointer(event) {
+ return { x: pointerX(event), y: pointerY(event) };
+ }
+
+ function pointerX(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollLeft: 0 };
+
+ return (
+ event.pageX ||
+ event.clientX +
+ (docElement.scrollLeft || body.scrollLeft) -
+ (docElement.clientLeft || 0)
+ );
+ }
+
+ function pointerY(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollTop: 0 };
+
+ return (
+ event.pageY ||
+ event.clientY +
+ (docElement.scrollTop || body.scrollTop) -
+ (docElement.clientTop || 0)
+ );
+ }
+
+ function stop(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+
+ event.stopped = true;
+ }
+
+ Event.Methods = {
+ isLeftClick: isLeftClick,
+ isMiddleClick: isMiddleClick,
+ isRightClick: isRightClick,
+
+ element: element,
+ findElement: findElement,
+
+ pointer: pointer,
+ pointerX: pointerX,
+ pointerY: pointerY,
+
+ stop: stop
+ };
+
+ var methods = Object.keys(Event.Methods).inject({}, function (m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (window.attachEvent) {
+ function _relatedTarget(event) {
+ var element;
+ switch (event.type) {
+ case 'mouseover':
+ case 'mouseenter':
+ element = event.fromElement;
+ break;
+ case 'mouseout':
+ case 'mouseleave':
+ element = event.toElement;
+ break;
+ default:
+ return null;
+ }
+ return Element.extend(element);
+ }
+
+ var additionalMethods = {
+ stopPropagation: function () {
+ this.cancelBubble = true;
+ },
+ preventDefault: function () {
+ this.returnValue = false;
+ },
+ inspect: function () {
+ return '[object Event]';
+ }
+ };
+
+ Event.extend = function (event, element) {
+ if (!event) return false;
+
+ if (!isIELegacyEvent(event)) return event;
+
+ if (event._extendedByPrototype) return event;
+ event._extendedByPrototype = Prototype.emptyFunction;
+
+ var pointer = Event.pointer(event);
+
+ Object.extend(event, {
+ target: event.srcElement || element,
+ relatedTarget: _relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+
+ Object.extend(event, methods);
+ Object.extend(event, additionalMethods);
+
+ return event;
+ };
+ } else {
+ Event.extend = Prototype.K;
+ }
+
+ if (window.addEventListener) {
+ Event.prototype =
+ window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
+ Object.extend(Event.prototype, methods);
+ }
+
+ var EVENT_TRANSLATIONS = {
+ mouseenter: 'mouseover',
+ mouseleave: 'mouseout'
+ };
+
+ function getDOMEventName(eventName) {
+ return EVENT_TRANSLATIONS[eventName] || eventName;
+ }
+
+ if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) getDOMEventName = Prototype.K;
+
+ function getUniqueElementID(element) {
+ if (element === window) return 0;
+
+ if (typeof element._prototypeUID === 'undefined')
+ element._prototypeUID = Element.Storage.UID++;
+ return element._prototypeUID;
+ }
+
+ function getUniqueElementID_IE(element) {
+ if (element === window) return 0;
+ if (element == document) return 1;
+ return element.uniqueID;
+ }
+
+ if ('uniqueID' in DIV) getUniqueElementID = getUniqueElementID_IE;
+
+ function isCustomEvent(eventName) {
+ return eventName.include(':');
+ }
+
+ Event._isCustomEvent = isCustomEvent;
+
+ function getOrCreateRegistryFor(element, uid) {
+ var CACHE = GLOBAL.Event.cache;
+ if (Object.isUndefined(uid)) uid = getUniqueElementID(element);
+ if (!CACHE[uid]) CACHE[uid] = { element: element };
+ return CACHE[uid];
+ }
+
+ function destroyRegistryForElement(element, uid) {
+ if (Object.isUndefined(uid)) uid = getUniqueElementID(element);
+ delete GLOBAL.Event.cache[uid];
+ }
+
+ function register(element, eventName, handler) {
+ var registry = getOrCreateRegistryFor(element);
+ if (!registry[eventName]) registry[eventName] = [];
+ var entries = registry[eventName];
+
+ var i = entries.length;
+ while (i--) if (entries[i].handler === handler) return null;
+
+ var uid = getUniqueElementID(element);
+ var responder = GLOBAL.Event._createResponder(uid, eventName, handler);
+ var entry = {
+ responder: responder,
+ handler: handler
+ };
+
+ entries.push(entry);
+ return entry;
+ }
+
+ function unregister(element, eventName, handler) {
+ var registry = getOrCreateRegistryFor(element);
+ var entries = registry[eventName] || [];
+
+ var i = entries.length,
+ entry;
+ while (i--) {
+ if (entries[i].handler === handler) {
+ entry = entries[i];
+ break;
+ }
+ }
+
+ if (entry) {
+ var index = entries.indexOf(entry);
+ entries.splice(index, 1);
+ }
+
+ if (entries.length === 0) {
+ delete registry[eventName];
+ if (Object.keys(registry).length === 1 && 'element' in registry)
+ destroyRegistryForElement(element);
+ }
+
+ return entry;
+ }
+
+ function observe(element, eventName, handler) {
+ element = $(element);
+ var entry = register(element, eventName, handler);
+
+ if (entry === null) return element;
+
+ var responder = entry.responder;
+ if (isCustomEvent(eventName))
+ observeCustomEvent(element, eventName, responder);
+ else observeStandardEvent(element, eventName, responder);
+
+ return element;
+ }
+
+ function observeStandardEvent(element, eventName, responder) {
+ var actualEventName = getDOMEventName(eventName);
+ if (element.addEventListener) {
+ element.addEventListener(actualEventName, responder, false);
+ } else {
+ element.attachEvent('on' + actualEventName, responder);
+ }
+ }
+
+ function observeCustomEvent(element, eventName, responder) {
+ if (element.addEventListener) {
+ element.addEventListener('dataavailable', responder, false);
+ } else {
+ element.attachEvent('ondataavailable', responder);
+ element.attachEvent('onlosecapture', responder);
+ }
+ }
+
+ function stopObserving(element, eventName, handler) {
+ element = $(element);
+ var handlerGiven = !Object.isUndefined(handler),
+ eventNameGiven = !Object.isUndefined(eventName);
+
+ if (!eventNameGiven && !handlerGiven) {
+ stopObservingElement(element);
+ return element;
+ }
+
+ if (!handlerGiven) {
+ stopObservingEventName(element, eventName);
+ return element;
+ }
+
+ var entry = unregister(element, eventName, handler);
+
+ if (!entry) return element;
+ removeEvent(element, eventName, entry.responder);
+ return element;
+ }
+
+ function stopObservingStandardEvent(element, eventName, responder) {
+ var actualEventName = getDOMEventName(eventName);
+ if (element.removeEventListener) {
+ element.removeEventListener(actualEventName, responder, false);
+ } else {
+ element.detachEvent('on' + actualEventName, responder);
+ }
+ }
+
+ function stopObservingCustomEvent(element, eventName, responder) {
+ if (element.removeEventListener) {
+ element.removeEventListener('dataavailable', responder, false);
+ } else {
+ element.detachEvent('ondataavailable', responder);
+ element.detachEvent('onlosecapture', responder);
+ }
+ }
+
+ function stopObservingElement(element) {
+ var uid = getUniqueElementID(element),
+ registry = GLOBAL.Event.cache[uid];
+ if (!registry) return;
+
+ destroyRegistryForElement(element, uid);
+
+ var entries, i;
+ for (var eventName in registry) {
+ if (eventName === 'element') continue;
+
+ entries = registry[eventName];
+ i = entries.length;
+ while (i--) removeEvent(element, eventName, entries[i].responder);
+ }
+ }
+
+ function stopObservingEventName(element, eventName) {
+ var registry = getOrCreateRegistryFor(element);
+ var entries = registry[eventName];
+ if (entries) {
+ delete registry[eventName];
+ }
+
+ entries = entries || [];
+
+ var i = entries.length;
+ while (i--) removeEvent(element, eventName, entries[i].responder);
+
+ for (var name in registry) {
+ if (name === 'element') continue;
+ return; // There is another registered event
+ }
+
+ destroyRegistryForElement(element);
+ }
+
+ function removeEvent(element, eventName, handler) {
+ if (isCustomEvent(eventName))
+ stopObservingCustomEvent(element, eventName, handler);
+ else stopObservingStandardEvent(element, eventName, handler);
+ }
+
+ function getFireTarget(element) {
+ if (element !== document) return element;
+ if (document.createEvent && !element.dispatchEvent)
+ return document.documentElement;
+ return element;
+ }
+
+ function fire(element, eventName, memo, bubble) {
+ element = getFireTarget($(element));
+ if (Object.isUndefined(bubble)) bubble = true;
+ memo = memo || {};
+
+ var event = fireEvent(element, eventName, memo, bubble);
+ return Event.extend(event);
+ }
+
+ function fireEvent_DOM(element, eventName, memo, bubble) {
+ var event = document.createEvent('HTMLEvents');
+ event.initEvent('dataavailable', bubble, true);
+
+ event.eventName = eventName;
+ event.memo = memo;
+
+ element.dispatchEvent(event);
+ return event;
+ }
+
+ function fireEvent_IE(element, eventName, memo, bubble) {
+ var event = document.createEventObject();
+ event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
+
+ event.eventName = eventName;
+ event.memo = memo;
+
+ element.fireEvent(event.eventType, event);
+ return event;
+ }
+
+ var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE;
+
+ Event.Handler = Class.create({
+ initialize: function (element, eventName, selector, callback) {
+ this.element = $(element);
+ this.eventName = eventName;
+ this.selector = selector;
+ this.callback = callback;
+ this.handler = this.handleEvent.bind(this);
+ },
+
+ start: function () {
+ Event.observe(this.element, this.eventName, this.handler);
+ return this;
+ },
+
+ stop: function () {
+ Event.stopObserving(this.element, this.eventName, this.handler);
+ return this;
+ },
+
+ handleEvent: function (event) {
+ var element = Event.findElement(event, this.selector);
+ if (element) this.callback.call(this.element, event, element);
+ }
+ });
+
+ function on(element, eventName, selector, callback) {
+ element = $(element);
+ if (Object.isFunction(selector) && Object.isUndefined(callback)) {
+ (callback = selector), (selector = null);
+ }
+
+ return new Event.Handler(element, eventName, selector, callback).start();
+ }
+
+ Object.extend(Event, Event.Methods);
+
+ Object.extend(Event, {
+ fire: fire,
+ observe: observe,
+ stopObserving: stopObserving,
+ on: on
+ });
+
+ Element.addMethods({
+ fire: fire,
+
+ observe: observe,
+
+ stopObserving: stopObserving,
+
+ on: on
+ });
+
+ Object.extend(document, {
+ fire: fire.methodize(),
+
+ observe: observe.methodize(),
+
+ stopObserving: stopObserving.methodize(),
+
+ on: on.methodize(),
+
+ loaded: false
+ });
+
+ if (GLOBAL.Event) Object.extend(window.Event, Event);
+ else GLOBAL.Event = Event;
+
+ GLOBAL.Event.cache = {};
+
+ function destroyCache_IE() {
+ GLOBAL.Event.cache = null;
+ }
+
+ if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE);
+
+ DIV = null;
+ docEl = null;
+})(this);
+
+(function (GLOBAL) {
+ /* Code for creating leak-free event responders is based on work by
+ John-David Dalton. */
+
+ var docEl = document.documentElement;
+ var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED =
+ 'onmouseenter' in docEl && 'onmouseleave' in docEl;
+
+ function isSimulatedMouseEnterLeaveEvent(eventName) {
+ return (
+ !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
+ (eventName === 'mouseenter' || eventName === 'mouseleave')
+ );
+ }
+
+ function createResponder(uid, eventName, handler) {
+ if (Event._isCustomEvent(eventName))
+ return createResponderForCustomEvent(uid, eventName, handler);
+ if (isSimulatedMouseEnterLeaveEvent(eventName))
+ return createMouseEnterLeaveResponder(uid, eventName, handler);
+
+ return function (event) {
+ if (!Event.cache) return;
+
+ var element = Event.cache[uid].element;
+ Event.extend(event, element);
+ handler.call(element, event);
+ };
+ }
+
+ function createResponderForCustomEvent(uid, eventName, handler) {
+ return function (event) {
+ var cache = Event.cache[uid];
+ var element = cache && cache.element;
+
+ if (Object.isUndefined(event.eventName)) return false;
+
+ if (event.eventName !== eventName) return false;
+
+ Event.extend(event, element);
+ handler.call(element, event);
+ };
+ }
+
+ function createMouseEnterLeaveResponder(uid, eventName, handler) {
+ return function (event) {
+ var element = Event.cache[uid].element;
+
+ Event.extend(event, element);
+ var parent = event.relatedTarget;
+
+ while (parent && parent !== element) {
+ try {
+ parent = parent.parentNode;
+ } catch (e) {
+ parent = element;
+ }
+ }
+
+ if (parent === element) return;
+ handler.call(element, event);
+ };
+ }
+
+ GLOBAL.Event._createResponder = createResponder;
+ docEl = null;
+})(this);
+
+(function (GLOBAL) {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
+
+ var TIMER;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (TIMER) window.clearTimeout(TIMER);
+ document.loaded = true;
+ document.fire('dom:loaded');
+ }
+
+ function checkReadyState() {
+ if (document.readyState === 'complete') {
+ document.detachEvent('onreadystatechange', checkReadyState);
+ fireContentLoadedEvent();
+ }
+ }
+
+ function pollDoScroll() {
+ try {
+ document.documentElement.doScroll('left');
+ } catch (e) {
+ TIMER = pollDoScroll.defer();
+ return;
+ }
+
+ fireContentLoadedEvent();
+ }
+
+ if (document.readyState === 'complete') {
+ fireContentLoadedEvent();
+ return;
+ }
+
+ if (document.addEventListener) {
+ document.addEventListener(
+ 'DOMContentLoaded',
+ fireContentLoadedEvent,
+ false
+ );
+ } else {
+ document.attachEvent('onreadystatechange', checkReadyState);
+ if (window == top) TIMER = pollDoScroll.defer();
+ }
+
+ Event.observe(window, 'load', fireContentLoadedEvent);
+})(this);
+
+Element.addMethods();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.addMethods({
+ childOf: Element.Methods.descendantOf
+});
+
+var Insertion = {
+ Before: function (element, content) {
+ return Element.insert(element, { before: content });
+ },
+
+ Top: function (element, content) {
+ return Element.insert(element, { top: content });
+ },
+
+ Bottom: function (element, content) {
+ return Element.insert(element, { bottom: content });
+ },
+
+ After: function (element, content) {
+ return Element.insert(element, { after: content });
+ }
+};
+
+var $continue = new Error(
+ '"throw $continue" is deprecated, use "return" instead'
+);
+
+var Position = {
+ includeScrollOffsets: false,
+
+ prepare: function () {
+ this.deltaX =
+ window.pageXOffset ||
+ document.documentElement.scrollLeft ||
+ document.body.scrollLeft ||
+ 0;
+ this.deltaY =
+ window.pageYOffset ||
+ document.documentElement.scrollTop ||
+ document.body.scrollTop ||
+ 0;
+ },
+
+ within: function (element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (
+ y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth
+ );
+ },
+
+ withinIncludingScrolloffsets: function (element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (
+ this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth
+ );
+ },
+
+ overlap: function (mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return (
+ (this.offset[1] + element.offsetHeight - this.ycomp) /
+ element.offsetHeight
+ );
+ if (mode == 'horizontal')
+ return (
+ (this.offset[0] + element.offsetWidth - this.xcomp) /
+ element.offsetWidth
+ );
+ },
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function (element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function (element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function (source, target, options) {
+ options = options || {};
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName)
+ document.getElementsByClassName = (function (instanceMethods) {
+ function iter(name) {
+ return name.blank()
+ ? null
+ : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath
+ ? function (element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className)
+ ? $w(className).map(iter).join('')
+ : iter(className);
+ return cond
+ ? document._getElementsByXPath('.//*' + cond, element)
+ : [];
+ }
+ : function (element, className) {
+ className = className.toString().strip();
+ var elements = [],
+ classNames = /\s/.test(className) ? $w(className) : null;
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; (child = nodes[i]); i++) {
+ if (
+ child.className &&
+ (cn = ' ' + child.className + ' ') &&
+ (cn.include(className) ||
+ (classNames &&
+ classNames.all(function (name) {
+ return (
+ !name.toString().blank() && cn.include(' ' + name + ' ')
+ );
+ })))
+ )
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function (className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(
+ className
+ );
+ };
+ })(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function (element) {
+ this.element = $(element);
+ },
+
+ _each: function (iterator, context) {
+ this.element.className
+ .split(/\s+/)
+ .select(function (name) {
+ return name.length > 0;
+ })
+ ._each(iterator, context);
+ },
+
+ set: function (className) {
+ this.element.className = className;
+ },
+
+ add: function (classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function (classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function () {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+(function () {
+ window.Selector = Class.create({
+ initialize: function (expression) {
+ this.expression = expression.strip();
+ },
+
+ findElements: function (rootElement) {
+ return Prototype.Selector.select(this.expression, rootElement);
+ },
+
+ match: function (element) {
+ return Prototype.Selector.match(element, this.expression);
+ },
+
+ toString: function () {
+ return this.expression;
+ },
+
+ inspect: function () {
+ return '#';
+ }
+ });
+
+ Object.extend(Selector, {
+ matchElements: function (elements, expression) {
+ var match = Prototype.Selector.match,
+ results = [];
+
+ for (var i = 0, length = elements.length; i < length; i++) {
+ var element = elements[i];
+ if (match(element, expression)) {
+ results.push(Element.extend(element));
+ }
+ }
+ return results;
+ },
+
+ findElement: function (elements, expression, index) {
+ index = index || 0;
+ var matchIndex = 0,
+ element;
+ for (var i = 0, length = elements.length; i < length; i++) {
+ element = elements[i];
+ if (
+ Prototype.Selector.match(element, expression) &&
+ index === matchIndex++
+ ) {
+ return Element.extend(element);
+ }
+ }
+ },
+
+ findChildElements: function (element, expressions) {
+ var selector = expressions.toArray().join(', ');
+ return Prototype.Selector.select(selector, element || document);
+ }
+ });
+})();
diff --git a/test/integration/full/contrast/prototype.html b/test/integration/full/contrast/prototype.html
new file mode 100644
index 000000000..f7f915d6b
--- /dev/null
+++ b/test/integration/full/contrast/prototype.html
@@ -0,0 +1,32 @@
+
+
+
+ Test Page
+
+
+
+
+
+
+
+
+
+
+
+
+