: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (node.getAttribute('aria-label')) {
this.label = node.getAttribute('aria-label').trim();
this.isExpandable = false;
var elem = node.firstElementChild;
if (elem.tagName.toLowerCase() == 'ul') {
elem.setAttribute('role', 'group');
this.isExpandable = true;
elem = elem.nextElementSibling;
this.keyCode = Object.freeze({
TreeitemLink.prototype.init = function () {
this.domNode.tabIndex = -1;
if (!this.domNode.getAttribute('role')) {
this.domNode.setAttribute('role', 'treeitem');
this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
this.domNode.addEventListener('click', this.handleClick.bind(this));
this.domNode.addEventListener('focus', this.handleFocus.bind(this));
this.domNode.addEventListener('blur', this.handleBlur.bind(this));
this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this));
this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this));
this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this));
this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this));
TreeitemLink.prototype.isExpanded = function () {
return this.domNode.getAttribute('aria-expanded') === 'true';
TreeitemLink.prototype.handleKeydown = function (event) {
var tgt = event.currentTarget,
function isPrintableCharacter(str) {
return str.length === 1 && str.match(/\S/);
function printableCharacter(item) {
item.tree.expandAllSiblingItems(item);
if (isPrintableCharacter(_char)) {
item.tree.setFocusByFirstCharacter(item, _char);
this.stopDefaultClick = false;
if (event.altKey || event.ctrlKey || event.metaKey) {
if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) {
this.stopDefaultClick = true;
if (isPrintableCharacter(_char)) {
printableCharacter(this);
case this.keyCode.RETURN:
this.tree.collapseTreeitem(this);
this.tree.expandTreeitem(this);
this.stopDefaultClick = true;
this.tree.setFocusToPreviousItem(this);
this.tree.setFocusToNextItem(this);
this.tree.setFocusToNextItem(this);
this.tree.expandTreeitem(this);
if (this.isExpandable && this.isExpanded()) {
this.tree.collapseTreeitem(this);
this.tree.setFocusToParentItem(this);
this.tree.setFocusToFirstItem();
this.tree.setFocusToLastItem();
if (isPrintableCharacter(_char)) {
printableCharacter(this);
TreeitemLink.prototype.handleClick = function (event) {
// Only process click events that directly happened on this treeitem.
if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) {
this.tree.collapseTreeitem(this);
this.tree.expandTreeitem(this);
TreeitemLink.prototype.handleFocus = function (event) {
node = node.firstElementChild;
node.classList.add('focus');
TreeitemLink.prototype.handleBlur = function (event) {
node = node.firstElementChild;
node.classList.remove('focus');
TreeitemLink.prototype.handleMouseOver = function (event) {
event.currentTarget.classList.add('hover');
TreeitemLink.prototype.handleMouseOut = function (event) {
event.currentTarget.classList.remove('hover');
* Creates a new TreeLinks.
* @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2b.html|W3C Treeview Example}
TreeLinks = (function () {
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
* Desc: Tree widget that implements ARIA Authoring Practices
* for a tree being used as a file viewer
* Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt
* Tree item object for representing the state and user interactions for a
* An element with the role=tree attribute
var TreeLinks = function (node) {
// Check whether node is a DOM element.
if (typeof node !== 'object') {
this.firstTreeitem = null;
this.lastTreeitem = null;
TreeLinks.prototype.init = function () {
function findTreeitems(node, tree, group) {
var elem = node.firstElementChild;
if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') {
ti = new TreeitemLink(elem, tree, group);
tree.firstChars.push(ti.label.substring(0, 1).toLowerCase());
if (elem.firstElementChild) {
findTreeitems(elem, tree, ti);
elem = elem.nextElementSibling;
// Initialize pop up menus.
if (!this.domNode.getAttribute('role')) {
this.domNode.setAttribute('role', 'tree');
findTreeitems(this.domNode, this, false);
this.updateVisibleTreeitems();
this.firstTreeitem.domNode.tabIndex = 0;
TreeLinks.prototype.setFocusToItem = function (treeitem) {
for (var i = 0; i < this.treeitems.length; i++) {
var ti = this.treeitems[i];
ti.domNode.tabIndex = -1;
TreeLinks.prototype.setFocusToNextItem = function (currentItem) {
for (var i = (this.treeitems.length - 1); i >= 0; i--) {
var ti = this.treeitems[i];
if (ti === currentItem) {
this.setFocusToItem(nextItem);
TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) {
for (var i = 0; i < this.treeitems.length; i++) {
var ti = this.treeitems[i];
if (ti === currentItem) {
this.setFocusToItem(prevItem);
TreeLinks.prototype.setFocusToParentItem = function (currentItem) {
if (currentItem.groupTreeitem) {
this.setFocusToItem(currentItem.groupTreeitem);
TreeLinks.prototype.setFocusToFirstItem = function () {
this.setFocusToItem(this.firstTreeitem);
TreeLinks.prototype.setFocusToLastItem = function () {
this.setFocusToItem(this.lastTreeitem);
TreeLinks.prototype.expandTreeitem = function (currentItem) {
if (currentItem.isExpandable) {
currentItem.domNode.setAttribute('aria-expanded', true);
this.updateVisibleTreeitems();
TreeLinks.prototype.expandAllSiblingItems = function (currentItem) {
for (var i = 0; i < this.treeitems.length; i++) {
var ti = this.treeitems[i];
if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) {
TreeLinks.prototype.collapseTreeitem = function (currentItem) {
var groupTreeitem = false;
if (currentItem.isExpanded()) {
groupTreeitem = currentItem;
groupTreeitem = currentItem.groupTreeitem;
groupTreeitem.domNode.setAttribute('aria-expanded', false);
this.updateVisibleTreeitems();
this.setFocusToItem(groupTreeitem);
TreeLinks.prototype.updateVisibleTreeitems = function () {
this.firstTreeitem = this.treeitems[0];
for (var i = 0; i < this.treeitems.length; i++) {
var ti = this.treeitems[i];
var parent = ti.domNode.parentNode;
while (parent && (parent !== this.domNode)) {
if (parent.getAttribute('aria-expanded') == 'false') {
parent = parent.parentNode;
TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, _char) {
_char = _char.toLowerCase();
// Get start index for search based on position of currentItem.
start = this.treeitems.indexOf(currentItem) + 1;
if (start === this.treeitems.length) {
// Check remaining slots in the menu.
index = this.getIndexFirstChars(start, _char);
// If not found in remaining slots, check from beginning.
index = this.getIndexFirstChars(0, _char);
this.setFocusToItem(this.treeitems[index]);
TreeLinks.prototype.getIndexFirstChars = function (startIndex, _char) {
for (var i = startIndex; i < this.firstChars.length; i++) {
if (this.treeitems[i].isVisible) {
if (_char === this.firstChars[i]) {