apex4x
Version:
The Comprehensive ARIA Development Suite
547 lines (527 loc) • 18.2 kB
JavaScript
/*@license
ARIA Tree Module 3.0 for Apex 4X
Author: Bryan Garaventa (https://www.linkedin.com/in/bgaraventa)
Home: WhatSock.com : Download: https://github.com/whatsock/apex
License: MIT (https://opensource.org/licenses/MIT)
Required dependencies: RovingTabIndex.js
*/
(function () {
if (!("setTree" in $A)) {
$A.addWidgetProfile("Tree", {
configure: function (dc) {
return {
toggleHide: true,
preload: true,
preloadImages: true,
preloadCSS: true,
className: "tree",
escToClose: true,
click: function (ev, dc) {
ev.stopPropagation();
},
storeData: true,
};
},
afterRender: function (dc) {
$A.setAttr(dc.triggerNode, "aria-expanded", "true");
$A.data(dc.triggerNode, "expanded", true);
$A.loop(
dc.RTI.nodes,
function (i, o) {
dc.getState(
o,
$A.getAttr(o, "aria-checked"),
$A.hasAttr(o, "aria-checked"),
false,
dc.RTI.nodes,
);
},
"array",
);
},
beforeRemove: function (dc) {
$A.loop(
dc.RTI.children,
function (i, o) {
o.DC.remove();
},
"map",
);
},
afterRemove: function (dc) {
$A.setAttr(dc.triggerNode, "aria-expanded", "false");
$A.data(dc.triggerNode, "expanded", false);
},
});
var isIE = $A.isIE();
$A.extend({
setTree: function (o, config) {
if (this._4X) {
config = o;
o = this._X;
}
if ($A.isPlainObject(o)) {
config = o;
o = config.content || null;
}
config = config || {};
var main = null,
multiselect = config.multiselect === true,
tag = $A.extend(
true,
{
parent: "ul",
child: "a",
parse: function (ref) {
if (isIE) {
var mItems = [];
$A.query(ref.children, function (i, o) {
var c = $A.first(o, function (e) {
if (e.nodeName.toLowerCase() === tag.child) return true;
});
if ($A.isNode(c)) mItems.push(c);
});
return mItems;
} else return ref.querySelectorAll(":scope > * > " + tag.child);
},
},
config.tag || {},
),
getState = function (o, attributeValue, hasAttribute, write, nodes) {
if (hasAttribute) {
var c = 0;
if (attributeValue === "true") c = 1;
else if (attributeValue === "mixed") c = 2;
$A.data(o, "check", c);
if (write) {
$A.setAttr(o, "aria-checked", attributeValue);
}
return c;
} else {
var s = $A.data(o, "check");
if ($A.isNum(s)) return s;
}
return false;
},
genTree = function (o, p, list, top, level, triggerIndex) {
var ref =
list ||
($A.isNode(o) &&
($A.getAttr(o, "data-controls") ||
$A.next(o, function (e) {
if (e.nodeName.toLowerCase() === tag.parent) return true;
})));
if (ref) ref = $A.morph(ref);
if (!$A.isNode(ref)) return;
if (o) {
$A.setAttr(o, "aria-expanded", "false");
$A.data(o, "expanded", false);
$A(o).owns(ref);
}
var mItems = tag.parse(ref);
$A.setAttr(ref, "role", top ? "tree" : "group");
$A.svgFix(ref);
var DC = $A.toDC(
$A.extend(
{
trigger: o,
triggerIndex: triggerIndex,
content: ref,
isTop: top,
on: "opentree",
widgetType: "Tree",
toggleHide: true,
getState: getState,
},
config,
),
);
if (p)
DC.map({
parent: p.DC,
});
DC.RTI = new $A.RovingTabIndex(
$A.extend(
{
DC: DC,
parent: p,
trigger: o,
nodes: mItems,
startIndex: 0,
orientation: 2,
autoSwitch: config.autoSwitch || "semi",
autoLoop: false,
isTree: true,
onClick: function (ev, triggerNode, RTI, DC) {
var that = this,
isDisabled = $A.isDisabled(that),
check = getState(triggerNode);
if (!$A.isNum(check) && multiselect)
check =
$A.getAttr(triggerNode, "aria-selected") === "true";
if (!isDisabled && $A.isDC(DC)) DC.toggle();
else if (!isDisabled && $A.isFn(config.onActivate)) {
config.onActivate.apply(that, [
ev,
triggerNode,
RTI,
DC || $A.boundTo(that),
check,
function (attributeValue) {
getState(
triggerNode,
attributeValue,
true,
true,
RTI.nodes,
);
},
]);
}
ev.preventDefault();
},
onSpace: function (ev, triggerNode, RTI, DC) {
RTI.onClick.apply(this, arguments);
ev.preventDefault();
},
onEnter: function (ev, triggerNode, RTI, DC) {
RTI.onClick.apply(this, arguments);
ev.preventDefault();
},
onFocus: function (ev, triggerNode, RTI, DC) {
$A.query('*[tabindex="0"]', main, function (i, o) {
if (o !== triggerNode) $A.setAttr(o, "tabindex", -1);
});
if (!$A.isTouch && !multiselect) {
$A.query(
'*[aria-selected="true"]',
main,
function (i, o) {
if (o !== triggerNode)
$A.setAttr(o, "aria-selected", "false");
},
);
$A.setAttr(triggerNode, "aria-selected", "true");
}
ev.stopPropagation();
},
onRight: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
var that = this,
isDisabled = $A.isDisabled(that);
if (!isDisabled && $A.isDC(DC)) {
if (DC.loaded) {
if (DC.RTI.nodes.length) {
DC.RTI.focus(0);
}
} else DC.render();
}
ev.preventDefault();
},
onUp: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
var get = function (RTI, trigger) {
var r = RTI.children.get(trigger) || {};
if (
$A.isDC(r.DC) &&
r.DC.loaded &&
r.nodes.length &&
r.children.has(r.nodes[r.nodes.length - 1]) &&
r.children.get(r.nodes[r.nodes.length - 1]).DC.loaded
)
return get(r, r.nodes[r.nodes.length - 1]);
else if ($A.isDC(r.DC) && r.DC.loaded && r.nodes.length)
return r;
else return null;
},
r = get(RTI, RTI.nodes[RTI.index - 1]);
if (r && r.nodes.length) {
r.focus(r.nodes.length - 1);
return false;
} else if (isTop && RTI.parent) {
RTI.parent.focus(RTI.trigger);
return false;
}
},
onLeft: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
if ($A.isDC(DC) && DC.loaded) DC.remove();
else if (RTI.parent) RTI.parent.focus(RTI.trigger);
ev.preventDefault();
},
onCtrlLeft: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
if (RTI.parent) {
RTI.DC.remove();
RTI.parent.focus(RTI.parent.index);
} else {
$A.loop(
RTI.children,
function (i, c) {
if ($A.isDC(c.DC)) c.DC.remove();
},
"map",
);
}
ev.preventDefault();
},
onCtrlRight: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
var get = function (r) {
$A.loop(
r.children,
function (i, c) {
if ($A.isDC(c.DC))
c.DC.render(function () {
get(c);
});
},
"map",
);
};
get(RTI);
ev.preventDefault();
},
onDown: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
if ($A.isDC(DC) && DC.loaded) {
DC.RTI.focus(0);
return false;
} else if (isBottom) {
var r = RTI,
get = function (p) {
if (!p.parent) {
r = null;
return -1;
}
if (
p.DC.triggerIndex + 1 <=
p.parent.nodes.length - 1
) {
r = p.parent;
return p.DC.triggerIndex + 1;
}
return get(p.parent);
},
i = get(RTI);
if (r && i !== -1) r.focus(i);
return false;
}
},
onEsc: function (ev, triggerNode, RTI, DC) {
if (RTI.parent) {
RTI.DC.remove();
RTI.parent.focus(RTI.parent.index);
} else {
$A.loop(
RTI.children,
function (i, c) {
if ($A.isDC(c.DC)) c.DC.remove();
},
"map",
);
}
ev.preventDefault();
},
onHome: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
RTI.top.focus(0);
return false;
},
onEnd: function (
ev,
triggerNode,
RTI,
DC,
arrowKey,
isTop,
isBottom,
) {
var get = function (RTI, trigger) {
var r = RTI.children.get(trigger) || {};
if (
$A.isDC(r.DC) &&
r.DC.loaded &&
r.nodes.length &&
r.children.has(r.nodes[r.nodes.length - 1]) &&
r.children.get(r.nodes[r.nodes.length - 1]).DC.loaded
)
return get(r, r.nodes[r.nodes.length - 1]);
else if ($A.isDC(r.DC) && r.DC.loaded && r.nodes.length)
return r;
else return RTI;
},
r = get(RTI.top, RTI.top.nodes[RTI.top.nodes.length - 1]);
if (r && r.nodes.length) {
r.focus(r.nodes.length - 1);
return false;
}
return false;
},
},
config.extendRTI || {},
),
);
$A.loop(
mItems,
function (i, o) {
genTree(o, DC.RTI, null, false, level + 1, i);
var check = getState(
o,
$A.getAttr(o, "data-check"),
$A.hasAttr(o, "data-check"),
),
n =
($A.isFn(o.querySelector) && o.querySelector("input")) ||
false;
if ($A.isNode(n)) {
$A.bindObjects(n, o);
if (n.checked) check = 1;
}
$A.setAttr(o, {
role: "treeitem",
"aria-level": level,
});
if ($A.isNum(check)) {
var c = "false";
if (check === 1) c = "true";
else if (check === 2) c = "mixed";
$A.setAttr(o, {
"aria-checked": c,
});
$A(o).on(
"attributeChange",
function (
MutationObject,
o,
attributeName,
attributeValue,
attributePriorValue,
DC,
SavedData,
) {
if ($A.isNode(n)) {
var check = getState(o, attributeValue, true);
n.checked = check ? true : false;
}
},
{
attributeFilter: ["aria-checked"],
},
);
}
$A.closest(o, function (o) {
if (o === ref) return true;
$A.setAttr(o, "role", "presentation");
});
},
"array",
);
$A.updateDisabled(mItems);
if (top) {
if (
!multiselect &&
$A.getAttr(ref, "aria-multiselectable") === "true"
)
multiselect = true;
else if (multiselect)
$A.setAttr(ref, "aria-multiselectable", "true");
else {
var a = $A.inArray(
ref.querySelector('*[aria-selected="true"]'),
mItems,
);
if (a >= 0) DC.RTI.activate(a);
}
}
return DC;
};
var DC = null;
o = $A.morph(o);
main = o;
var gen = function (l, i) {
DC = genTree(null, null, l, true, 1, i);
$A(main)
.setAttr("tabindex", "0")
.on("focus click", function (ev) {
if (DC.RTI.nodes.length) {
DC.RTI.focus();
$A.setAttr(main, "tabindex", "-1");
}
});
};
var p = config.fetch && config.fetch.url,
s =
(config.fetch && config.fetch.data && config.fetch.data.selector) ||
$A.getSelectorFromURI(p),
isP = s && $A.isPath(p) ? true : false;
config.fetch = null;
if (isP) {
$A.load(
p,
config.root,
{
selector: s,
},
function (c) {
main = c;
gen(main);
},
);
} else {
gen(main);
}
return $A._XR.call(this, DC);
},
});
}
})();