Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ctrly

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ctrly - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

dist/ctrly-es2015.js

263

dist/ctrly.js
/*!
* ctrly v0.1.0 (2018-08-21)
* ctrly v0.2.0
* Copyright (c) 2018 Jan Sorgalla

@@ -32,16 +32,9 @@ * License: MIT

function scrollPositionRestorer(element) {
var positions = parents(element).map(function (element) {
return {
element: element,
top: element.scrollTop,
left: element.scrollLeft
};
var positions = parents(element).map(function (parent) {
return [parent, parent.scrollTop, parent.scrollLeft];
});
return function () {
positions.forEach(function (_ref) {
var element = _ref.element,
top = _ref.top,
left = _ref.left;
element.scrollTop = top;
element.scrollLeft = left;
positions.forEach(function (cache) {
cache[0].scrollTop = cache[1];
cache[0].scrollLeft = cache[2];
});

@@ -236,2 +229,60 @@ };

var selector = ['a[href]',
'area[href]', 'input', 'select', 'textarea', 'button', 'iframe', 'object', 'audio[controls]', 'video[controls]', '[contenteditable]', '[tabindex]'].join(',');
var inputNodeNameRegexp = /^(input|select|textarea|button|object)$/;
function focusableFilter(element) {
var nodeName = element.nodeName.toLowerCase();
if (nodeName === 'area') {
return isValidArea(element);
}
if (element.disabled) {
return false;
}
if (inputNodeNameRegexp.test(nodeName)) {
var fieldset = closest(element, 'fieldset');
if (fieldset && fieldset.disabled) {
return false;
}
}
return visible(element);
}
function tabbableFilter(element) {
var index = tabIndex(element);
return focusableFilter(element) && index >= 0;
}
function compare(a, b) {
var aIndex = tabIndex(a, true);
var bIndex = tabIndex(b, true);
if (aIndex === bIndex) {
return a.compareDocumentPosition(b) & 2 ? 1 : -1;
}
return aIndex - bIndex;
}
function isValidArea(element) {
var map = element.parentNode;
var mapName = map.name;
if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
return false;
}
var images = find("img[usemap=\"#".concat(mapName, "\"]"));
return images.length > 0 && visible(images[0]);
}
function visible(element) {
var style = getComputedStyle(element);
return (
style.visibility !== 'hidden' && style.visibility !== 'collapse' && style.display !== 'none' && parents(element).every(function (el) {
return getComputedStyle(el).display !== 'none';
})
);
}
function tabIndex(element) {
var positiveOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var index = parseInt(element.getAttribute('tabindex'), 10);
return isNaN(index) ? 0 : positiveOnly && index < 0 ? 0 : index;
}
function tabbable(element) {
return find(selector, arguments.length > 0 ? element : document).filter(tabbableFilter).sort(compare);
}
var defaultOptions = {

@@ -241,13 +292,10 @@ selector: '[data-ctrly]',

focusTarget: true,
closeOnBlur: true,
closeOnEsc: true,
closeOnOutsideClick: true,
closeOnScroll: false,
constrainFocus: false,
trapFocus: false,
allowMultiple: false,
on: null
};
var focusableElementsSelector = 'a[href],area[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),iframe,object,embed,[contenteditable],[tabindex]:not([tabindex^="-"])';
var passiveEventOptions = {
passive: true
};
function settings(opts) {

@@ -268,9 +316,13 @@ var extended = {};

}
function findControls(target) {
return find("[aria-controls=\"".concat(target.id, "\"]"));
}
function findTarget(control) {
var targetId = control.getAttribute('aria-controls');
return {
targetId: targetId,
target: targetId ? document.getElementById(targetId) : null
};
return document.getElementById(control.getAttribute('aria-controls') || control.getAttribute('data-ctrly'));
}
function resetControl(control) {
control.removeAttribute('aria-controls');
control.removeAttribute('aria-expanded');
}
var idCounter = 0;
function ctrly() {

@@ -281,3 +333,3 @@ var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

var eventListener = options.on || {};
var removers = {};
var instances = {};
function context(control) {

@@ -300,22 +352,14 @@ if (!options.context) {

}
function close(control) {
var returnFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var _findTarget = findTarget(control),
targetId = _findTarget.targetId,
target = _findTarget.target;
if (!targetId) {
control.setAttribute('aria-expanded', 'false');
return false;
}
function reset() {
if (removers[targetId]) {
removers[targetId]();
delete removers[targetId];
function findParentTarget(control) {
var element = control;
while (element) {
if (element.id && instances[element.id]) {
return element;
}
find("[aria-controls=\"".concat(targetId, "\"]")).forEach(function (c) {
c.setAttribute('aria-expanded', 'false');
});
element = element.parentElement;
}
}
function close(target) {
var returnFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
if (!target) {
reset();
return false;

@@ -329,4 +373,13 @@ }

}
reset();
var currentActiveElement = activeElement();
var _ref = instances[target.id] || {},
lastActiveElement = _ref.lastActiveElement,
destroy = _ref.destroy;
delete instances[target.id];
if (destroy) {
destroy();
}
findControls(target).forEach(function (c) {
c.setAttribute('aria-expanded', 'false');
});
target.removeAttribute('data-ctrly-opened');

@@ -336,4 +389,4 @@ target.setAttribute('aria-hidden', 'true');

target.blur();
if (returnFocus && target.contains(currentActiveElement)) {
focus(control, {
if (returnFocus && lastActiveElement && target.contains(currentActiveElement)) {
focus(lastActiveElement, {
restoreScrollPosition: true

@@ -345,5 +398,6 @@ });

}
function closeOthers(control) {
find(controlSelector, context(control)).forEach(function (other) {
if (other !== control) {
function closeOthers(target) {
find(controlSelector, context(target)).forEach(function (control) {
var other = findTarget(control);
if (other && other.id !== target.id) {
close(other, false);

@@ -363,10 +417,28 @@ }

if (options.closeOnOutsideClick || options.closeOnScroll) {
removeFuncs.push(on(target, 'mouseenter', activate, passiveEventOptions));
removeFuncs.push(on(target, 'mouseleave', deactivate, passiveEventOptions));
removeFuncs.push(on(target, 'touchstart', activate, passiveEventOptions));
removeFuncs.push(on(target, 'touchend', deactivate, passiveEventOptions));
removeFuncs.push(on(target, 'mouseenter', activate, {
passive: true
}));
removeFuncs.push(on(target, 'mouseleave', deactivate, {
passive: true
}));
removeFuncs.push(on(target, 'touchstart', activate, {
passive: true
}));
removeFuncs.push(on(target, 'touchend', deactivate, {
passive: true
}));
}
if (options.closeOnBlur && !options.trapFocus) {
removeFuncs.push(on(target, 'focusout', function (e) {
if (!e.relatedTarget || !target.contains(e.relatedTarget)) {
close(target, false);
}
}, {
capture: true,
passive: true
}));
}
if (options.closeOnEsc) {
removeFuncs.push(on(document, 'keydown', function (e) {
if (keyCode(e) === 27 && close(control)) {
if (keyCode(e) === 27 && close(target)) {
e.preventDefault();

@@ -379,5 +451,7 @@ }

if (!active && keyCode(e) === 1 && !closest(e.target, controlSelector)) {
close(control);
close(target);
}
}, passiveEventOptions));
}, {
passive: true
}));
}

@@ -387,7 +461,9 @@ if (options.closeOnScroll) {

if (!active) {
close(control);
close(target);
}
}, passiveEventOptions));
}, {
passive: true
}));
}
if (options.constrainFocus) {
if (options.trapFocus) {
removeFuncs.push(on(document, 'keydown', function (e) {

@@ -397,4 +473,4 @@ if (keyCode(e) !== 9) {

}
var focusableElements = find(focusableElementsSelector, target);
if (!focusableElements[0]) {
var tabbableElements = tabbable(target);
if (!tabbableElements[0]) {
e.preventDefault();

@@ -405,4 +481,4 @@ focus(target);

var active = activeElement();
var firstTabStop = focusableElements[0];
var lastTabStop = focusableElements[focusableElements.length - 1];
var firstTabStop = tabbableElements[0];
var lastTabStop = tabbableElements[tabbableElements.length - 1];
if (e.shiftKey && active === firstTabStop) {

@@ -426,7 +502,5 @@ e.preventDefault();

function open(control) {
var _findTarget2 = findTarget(control),
targetId = _findTarget2.targetId,
target = _findTarget2.target;
var target = findTarget(control);
if (!target) {
control.setAttribute('aria-expanded', 'false');
resetControl(control);
return false;

@@ -440,4 +514,7 @@ }

}
removers[targetId] = addEventListeners(control, target);
find("[aria-controls=\"".concat(targetId, "\"]")).forEach(function (c) {
instances[target.id] = {
lastActiveElement: activeElement(),
destroy: addEventListeners(control, target)
};
findControls(target).forEach(function (c) {
c.setAttribute('aria-expanded', 'true');

@@ -458,4 +535,11 @@ });

}
var target = findTarget(control);
if (!target) {
if (close(findParentTarget(control))) {
e.preventDefault();
}
return;
}
if (control.getAttribute('aria-expanded') === 'true') {
if (close(control)) {
if (close(target)) {
e.preventDefault();

@@ -466,9 +550,9 @@ }

if (!options.allowMultiple) {
closeOthers(control);
closeOthers(target);
}
var target = open(control);
open(control);
if (target) {
e.preventDefault();
if (options.focusTarget) {
focus(find(focusableElementsSelector, target)[0] || target);
focus(tabbable(target)[0] || target);
}

@@ -482,3 +566,19 @@ target.scrollTop = 0;

find(controlSelector).forEach(function (control) {
if (control.getAttribute('aria-expanded') === 'true') {
var target = findTarget(control);
if (!target) {
resetControl(control);
return;
}
control.setAttribute('aria-controls', target.id);
var labelledBy = findControls(target).map(function (control) {
if (!control.id) {
control.setAttribute('id', 'ctrly-control-' + ++idCounter);
}
return control.id;
});
var newLabelledBy = (target.getAttribute('aria-labelledby') || '').split(' ').concat(labelledBy).filter(function (id, pos, arr) {
return id !== '' && arr.indexOf(id) === pos;
});
target.setAttribute('aria-labelledby', newLabelledBy.join(' '));
if (control.getAttribute('aria-expanded') === 'true' || control.hasAttribute('data-ctrly-open')) {
open(control);

@@ -488,7 +588,4 @@ return;

control.setAttribute('aria-expanded', 'false');
var _findTarget3 = findTarget(control),
target = _findTarget3.target;
if (target) {
target.setAttribute('aria-hidden', 'true');
}
target.setAttribute('aria-hidden', 'true');
target.removeAttribute('tabindex');
});

@@ -503,7 +600,11 @@ });

find(controlSelector).forEach(function (control) {
close(control, false);
var target = findTarget(control);
if (target) {
close(target, false);
}
});
for (var id in removers) {
if (Object.prototype.hasOwnProperty.call(removers, id)) {
removers[id].call();
for (var id in instances) {
if (Object.prototype.hasOwnProperty.call(instances, id)) {
instances[id].destroy();
delete instances[id];
}

@@ -510,0 +611,0 @@ }

/*!
* ctrly v0.1.0 (2018-08-21)
* ctrly v0.2.0
* Copyright (c) 2018 Jan Sorgalla
* License: MIT
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.ctrly=t()}(this,function(){"use strict";function h(){try{var e=document.activeElement;return e&&e.nodeName?e:document.body}catch(e){return document.body}}function b(e){var t,n;(1<arguments.length&&void 0!==arguments[1]?arguments[1]:{}).restoreScrollPosition&&(n=function(e){for(var t=[];e&&e.parentNode&&1===e.parentNode.nodeType;)e=e.parentNode,t.push(e);return t}(e).map(function(e){return{element:e,top:e.scrollTop,left:e.scrollLeft}}),t=function(){n.forEach(function(e){var t=e.element,n=e.top,r=e.left;t.scrollTop=n,t.scrollLeft=r})});try{e.focus()}catch(e){}t&&t()}function m(e,t){var n=1<arguments.length?t:document;return n&&"function"==typeof n.querySelectorAll?[].slice.call(n.querySelectorAll(e)):[]}function n(e,t){if(!e)return!1;var n=e.matches||e.webkitMatchesSelector||e.msMatchesSelector;return"function"==typeof n&&n.call(e,t)}function y(e,t){if(!e)return null;if("function"==typeof e.closest)return e.closest(t);do{if(n(e,t))return e;e=e.parentNode}while(e&&1===e.nodeType);return null}var t;function a(){if(t)return t;t={capture:!1,once:!1,passive:!1};var e={get capture(){return!(t.capture=!0)},get once(){return!(t.once=!0)},get passive(){return!(t.passive=!0)}};return window.addEventListener("test",e,e),window.removeEventListener("test",e,e),t}function i(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},t=a(),n=t.once,r=t.passive,o=t.capture;return n||r||o?(n||delete e.once,r||delete e.passive,o||delete e.capture,e):Boolean(e.capture)}function g(t,e,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:{capture:!1};if(!t||"function"!=typeof t.addEventListener)return function(){};var o=n,c=function(){!function(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:{capture:!1};e&&"function"==typeof e.removeEventListener&&e.removeEventListener(t,n,i(r))}(t,e,o,r)};return r.once&&!a().once&&(o=function(e){c(),n.call(t,e)}),t.addEventListener(e,o,i(r)),c}var u={selector:"[data-ctrly]",context:null,focusTarget:!0,closeOnEsc:!0,closeOnOutsideClick:!0,closeOnScroll:!1,constrainFocus:!1,allowMultiple:!1,on:null},E='a[href],area[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),iframe,object,embed,[contenteditable],[tabindex]:not([tabindex^="-"])',A={passive:!0};function w(e){return"which"in e?e.which:e.keyCode}function x(e){var t=e.getAttribute("aria-controls");return{targetId:t,target:t?document.getElementById(t):null}}return function(){var n,r,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},s=(n={},[u,e].forEach(function(e){for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=e[t])}),n),d=s.selector,o=s.on||{},f={};function p(e,t){return("function"!=typeof o[t]||!1!==o[t](e))&&!1!==function(e,t){var n,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};if(!e||"function"!=typeof e.dispatchEvent)return!0;r.bubbles=r.bubbles||!1,r.cancelable=r.cancelable||!1,r.composed=r.composed||!1,r.detail=r.detail||null;try{n=new CustomEvent(t,r)}catch(e){(n=document.createEvent("CustomEvent")).initCustomEvent(t,r.bubbles,r.cancelable,r.detail)}return e.dispatchEvent(n)}(e,"ctrly:".concat(t),{bubbles:!0,cancelable:!0})}function v(e){var t=!(1<arguments.length&&void 0!==arguments[1])||arguments[1],n=x(e),r=n.targetId,o=n.target;if(!r)return e.setAttribute("aria-expanded","false"),!1;function c(){f[r]&&(f[r](),delete f[r]),m('[aria-controls="'.concat(r,'"]')).forEach(function(e){e.setAttribute("aria-expanded","false")})}if(!o)return c(),!1;if(!o.hasAttribute("data-ctrly-opened"))return!1;if(!p(o,"close"))return!1;c();var a=h();return o.removeAttribute("data-ctrly-opened"),o.setAttribute("aria-hidden","true"),o.removeAttribute("tabindex"),o.blur(),t&&o.contains(a)&&b(e,{restoreScrollPosition:!0}),p(o,"closed"),o}function c(t){var e;m(d,(e=t,s.context?y(e,s.context):document)).forEach(function(e){e!==t&&v(e,!1)})}function a(e){var t,c,n,r,o,a,i=x(e),u=i.targetId,l=i.target;return l?!l.hasAttribute("data-ctrly-opened")&&!!p(l,"open")&&(f[u]=(t=e,c=l,r=!(n=[]),o=function(){r=!0},a=function(){r=!1},(s.closeOnOutsideClick||s.closeOnScroll)&&(n.push(g(c,"mouseenter",o,A)),n.push(g(c,"mouseleave",a,A)),n.push(g(c,"touchstart",o,A)),n.push(g(c,"touchend",a,A))),s.closeOnEsc&&n.push(g(document,"keydown",function(e){27===w(e)&&v(t)&&e.preventDefault()})),s.closeOnOutsideClick&&n.push(g(document,"click",function(e){r||1!==w(e)||y(e.target,d)||v(t)},A)),s.closeOnScroll&&n.push(g(window,"scroll",function(){r||v(t)},A)),s.constrainFocus&&n.push(g(document,"keydown",function(e){if(9===w(e)){var t=m(E,c);if(!t[0])return e.preventDefault(),void b(c);var n=h(),r=t[0],o=t[t.length-1];if(e.shiftKey&&n===r)return e.preventDefault(),void b(o);e.shiftKey||n!==o||(b(r),e.preventDefault())}})),function(){for(;n.length;)n.shift().call()}),m('[aria-controls="'.concat(u,'"]')).forEach(function(e){e.setAttribute("aria-expanded","true")}),l.setAttribute("data-ctrly-opened",""),l.setAttribute("aria-hidden","false"),l.setAttribute("tabindex","-1"),p(l,"opened"),l):(e.setAttribute("aria-expanded","false"),!1)}function t(){var e,t;r||(r=function(e,t,n,r){var o=4<arguments.length&&void 0!==arguments[4]?arguments[4]:{capture:!1},c=!0===o.once;delete o.once;var a=g(e,t,function(e){var t=y(e.target,n);t&&(c&&a(),r.call(t,e,t))},o);return a}(document,"click",d,function(e,t){if(1===w(e))if("true"!==t.getAttribute("aria-expanded")){s.allowMultiple||c(t);var n=a(t);n&&(e.preventDefault(),s.focusTarget&&b(m(E,n)[0]||n),n.scrollTop=0,n.scrollLeft=0)}else v(t)&&e.preventDefault()})),e=function(){m(d).forEach(function(e){if("true"!==e.getAttribute("aria-expanded")){e.setAttribute("aria-expanded","false");var t=x(e).target;t&&t.setAttribute("aria-hidden","true")}else a(e)})},"complete"!==(t=document.readyState)&&"interactive"!==t?document.addEventListener("DOMContentLoaded",function(){e()},i({capture:!0,once:!0,passive:!0})):setTimeout(e,0)}return t(),{init:t,destroy:function(){for(var e in r&&(r(),r=null),m(d).forEach(function(e){v(e,!1)}),f)Object.prototype.hasOwnProperty.call(f,e)&&f[e].call()}}}});
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.ctrly=t()}(this,function(){"use strict";function f(){try{var e=document.activeElement;return e&&e.nodeName?e:document.body}catch(e){return document.body}}function r(e){for(var t=[];e&&e.parentNode&&1===e.parentNode.nodeType;)e=e.parentNode,t.push(e);return t}function p(e){var t,n;(1<arguments.length&&void 0!==arguments[1]?arguments[1]:{}).restoreScrollPosition&&(n=r(e).map(function(e){return[e,e.scrollTop,e.scrollLeft]}),t=function(){n.forEach(function(e){e[0].scrollTop=e[1],e[0].scrollLeft=e[2]})});try{e.focus()}catch(e){}t&&t()}function v(e,t){var n=1<arguments.length?t:document;return n&&"function"==typeof n.querySelectorAll?[].slice.call(n.querySelectorAll(e)):[]}function n(e,t){if(!e)return!1;var n=e.matches||e.webkitMatchesSelector||e.msMatchesSelector;return"function"==typeof n&&n.call(e,t)}function b(e,t){if(!e)return null;if("function"==typeof e.closest)return e.closest(t);do{if(n(e,t))return e;e=e.parentNode}while(e&&1===e.nodeType);return null}var t;function a(){if(t)return t;t={capture:!1,once:!1,passive:!1};var e={get capture(){return!(t.capture=!0)},get once(){return!(t.once=!0)},get passive(){return!(t.passive=!0)}};return window.addEventListener("test",e,e),window.removeEventListener("test",e,e),t}function h(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},t=a(),n=t.once,r=t.passive,o=t.capture;return n||r||o?(n||delete e.once,r||delete e.passive,o||delete e.capture,e):Boolean(e.capture)}function m(t,e,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:{capture:!1};if(!t||"function"!=typeof t.addEventListener)return function(){};var o=n,i=function(){!function(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:{capture:!1};e&&"function"==typeof e.removeEventListener&&e.removeEventListener(t,n,h(r))}(t,e,o,r)};return r.once&&!a().once&&(o=function(e){i(),n.call(t,e)}),t.addEventListener(e,o,h(r)),i}var o=["a[href]","area[href]","input","select","textarea","button","iframe","object","audio[controls]","video[controls]","[contenteditable]","[tabindex]"].join(","),i=/^(input|select|textarea|button|object)$/;function c(e){var t=e.nodeName.toLowerCase();if("area"===t)return function(e){var t=e.parentNode,n=t.name;if(!e.href||!n||"map"!==t.nodeName.toLowerCase())return!1;var r=v('img[usemap="#'.concat(n,'"]'));return 0<r.length&&s(r[0])}(e);if(e.disabled)return!1;if(i.test(t)){var n=b(e,"fieldset");if(n&&n.disabled)return!1}return s(e)}function u(e){var t=d(e);return c(e)&&0<=t}function l(e,t){var n=d(e,!0),r=d(t,!0);return n===r?2&e.compareDocumentPosition(t)?1:-1:n-r}function s(e){var t=getComputedStyle(e);return"hidden"!==t.visibility&&"collapse"!==t.visibility&&"none"!==t.display&&r(e).every(function(e){return"none"!==getComputedStyle(e).display})}function d(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],n=parseInt(e.getAttribute("tabindex"),10);return isNaN(n)?0:t&&n<0?0:n}function y(e){return v(o,0<arguments.length?e:document).filter(u).sort(l)}var g={selector:"[data-ctrly]",context:null,focusTarget:!0,closeOnBlur:!0,closeOnEsc:!0,closeOnOutsideClick:!0,closeOnScroll:!1,trapFocus:!1,allowMultiple:!1,on:null};function A(e){return"which"in e?e.which:e.keyCode}function E(e){return v('[aria-controls="'.concat(e.id,'"]'))}function w(e){return document.getElementById(e.getAttribute("aria-controls")||e.getAttribute("data-ctrly"))}function x(e){e.removeAttribute("aria-controls"),e.removeAttribute("aria-expanded")}var O=0;return function(){var n,r,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},c=(n={},[g,e].forEach(function(e){for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[t]=e[t])}),n),u=c.selector,o=c.on||{},l={};function s(e,t){return("function"!=typeof o[t]||!1!==o[t](e))&&!1!==function(e,t){var n,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};if(!e||"function"!=typeof e.dispatchEvent)return!0;r.bubbles=r.bubbles||!1,r.cancelable=r.cancelable||!1,r.composed=r.composed||!1,r.detail=r.detail||null;try{n=new CustomEvent(t,r)}catch(e){(n=document.createEvent("CustomEvent")).initCustomEvent(t,r.bubbles,r.cancelable,r.detail)}return e.dispatchEvent(n)}(e,"ctrly:".concat(t),{bubbles:!0,cancelable:!0})}function d(e){var t=!(1<arguments.length&&void 0!==arguments[1])||arguments[1];if(!e)return!1;if(!e.hasAttribute("data-ctrly-opened"))return!1;if(!s(e,"close"))return!1;var n=f(),r=l[e.id]||{},o=r.lastActiveElement,i=r.destroy;return delete l[e.id],i&&i(),E(e).forEach(function(e){e.setAttribute("aria-expanded","false")}),e.removeAttribute("data-ctrly-opened"),e.setAttribute("aria-hidden","true"),e.removeAttribute("tabindex"),e.blur(),t&&o&&e.contains(n)&&p(o,{restoreScrollPosition:!0}),s(e,"closed"),e}function i(n){var e;v(u,(e=n,c.context?b(e,c.context):document)).forEach(function(e){var t=w(e);t&&t.id!==n.id&&d(t,!1)})}function a(e){var i,t,n,r,o,a=w(e);return a?!a.hasAttribute("data-ctrly-opened")&&!!s(a,"open")&&(l[a.id]={lastActiveElement:f(),destroy:(i=a,t=[],n=!1,r=function(){n=!0},o=function(){n=!1},(c.closeOnOutsideClick||c.closeOnScroll)&&(t.push(m(i,"mouseenter",r,{passive:!0})),t.push(m(i,"mouseleave",o,{passive:!0})),t.push(m(i,"touchstart",r,{passive:!0})),t.push(m(i,"touchend",o,{passive:!0}))),c.closeOnBlur&&!c.trapFocus&&t.push(m(i,"focusout",function(e){e.relatedTarget&&i.contains(e.relatedTarget)||d(i,!1)},{capture:!0,passive:!0})),c.closeOnEsc&&t.push(m(document,"keydown",function(e){27===A(e)&&d(i)&&e.preventDefault()})),c.closeOnOutsideClick&&t.push(m(document,"click",function(e){n||1!==A(e)||b(e.target,u)||d(i)},{passive:!0})),c.closeOnScroll&&t.push(m(window,"scroll",function(){n||d(i)},{passive:!0})),c.trapFocus&&t.push(m(document,"keydown",function(e){if(9===A(e)){var t=y(i);if(!t[0])return e.preventDefault(),void p(i);var n=f(),r=t[0],o=t[t.length-1];if(e.shiftKey&&n===r)return e.preventDefault(),void p(o);e.shiftKey||n!==o||(p(r),e.preventDefault())}})),function(){for(;t.length;)t.shift().call()})},E(a).forEach(function(e){e.setAttribute("aria-expanded","true")}),a.setAttribute("data-ctrly-opened",""),a.setAttribute("aria-hidden","false"),a.setAttribute("tabindex","-1"),s(a,"opened"),a):(x(e),!1)}function t(){var e,t;r||(r=function(e,t,n,r){var o=4<arguments.length&&void 0!==arguments[4]?arguments[4]:{capture:!1},i=!0===o.once;delete o.once;var a=m(e,t,function(e){var t=b(e.target,n);t&&(i&&a(),r.call(t,e,t))},o);return a}(document,"click",u,function(e,t){if(1===A(e)){var n=w(t);n?"true"!==t.getAttribute("aria-expanded")?(c.allowMultiple||i(n),a(t),n&&(e.preventDefault(),c.focusTarget&&p(y(n)[0]||n),n.scrollTop=0,n.scrollLeft=0)):d(n)&&e.preventDefault():d(function(e){for(var t=e;t;){if(t.id&&l[t.id])return t;t=t.parentElement}}(t))&&e.preventDefault()}})),e=function(){v(u).forEach(function(e){var t=w(e);if(t){e.setAttribute("aria-controls",t.id);var n=E(t).map(function(e){return e.id||e.setAttribute("id","ctrly-control-"+ ++O),e.id}),r=(t.getAttribute("aria-labelledby")||"").split(" ").concat(n).filter(function(e,t,n){return""!==e&&n.indexOf(e)===t});t.setAttribute("aria-labelledby",r.join(" ")),"true"===e.getAttribute("aria-expanded")||e.hasAttribute("data-ctrly-open")?a(e):(e.setAttribute("aria-expanded","false"),t.setAttribute("aria-hidden","true"),t.removeAttribute("tabindex"))}else x(e)})},"complete"!==(t=document.readyState)&&"interactive"!==t?document.addEventListener("DOMContentLoaded",function(){e()},h({capture:!0,once:!0,passive:!0})):setTimeout(e,0)}return t(),{init:t,destroy:function(){for(var e in r&&(r(),r=null),v(u).forEach(function(e){var t=w(e);t&&d(t,!1)}),l)Object.prototype.hasOwnProperty.call(l,e)&&(l[e].destroy(),delete l[e])}}}});
{
"name": "ctrly",
"version": "0.1.0",
"version": "0.2.0",
"description": "Lightweight and dependency-free content toggling with a focus on accessibility.",

@@ -8,3 +8,3 @@ "repository": "git@github.com:jsor/ctrly.git",

"name": "Jan Sorgalla",
"url": "http://sorgalla.com",
"url": "https://sorgalla.com",
"email": "jsorgalla@gmail.com"

@@ -23,3 +23,4 @@ },

"browser": "dist/ctrly.min.js",
"module": "src/ctrly.mjs",
"module": "dist/ctrly-module.mjs",
"es2015": "src/ctrly-es2015.js",
"files": [

@@ -30,3 +31,9 @@ "dist",

"browserslist": [
"last 2 versions"
"defaults",
"IE >= 10",
"Chrome >= 60",
"Edge >= 15",
"Firefox >= 54",
"iOS >= 10.3",
"Safari >= 10.1"
],

@@ -54,3 +61,3 @@ "xo": {

"lint": "xo",
"size": "size-limit --limit 2.5KB dist/ctrly.min.js",
"size": "size-limit --limit 2.7KB dist/ctrly.js",
"test": "npm run lint && karma start --single-run",

@@ -61,7 +68,7 @@ "test:browserstack": "npm run build:test && browserstack-runner",

"devDependencies": {
"@babel/core": "^7.0.0-rc.1",
"@babel/preset-env": "^7.0.0-rc.1",
"browserstack-runner": "^0.8.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"browserstack-runner": "^0.9.0",
"chai": "^4.1.2",
"domestique": "^1.4.0",
"domestique": "^1.5.0",
"karma": "^3.0.0",

@@ -71,3 +78,3 @@ "karma-chai": "^0.1.0",

"karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.0.1",
"karma-coverage-istanbul-reporter": "^2.0.3",
"karma-firefox-launcher": "^1.1.0",

@@ -78,14 +85,14 @@ "karma-mocha": "^1.3.0",

"mocha": "^5.2.0",
"rollup": "^0.64.1",
"rollup-plugin-babel": "^4.0.0-beta.8",
"rollup": "^0.65.2",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-clean": "^1.0.0",
"rollup-plugin-cleanup": "^3.0.0",
"rollup-plugin-commonjs": "^9.1.5",
"rollup-plugin-commonjs": "^9.1.6",
"rollup-plugin-istanbul": "^2.0.1",
"rollup-plugin-node-resolve": "^3.3.0",
"rollup-plugin-uglify": "^4.0.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-uglify": "^5.0.2",
"simulant": "^0.2.2",
"size-limit": "^0.19.2",
"xo": "^0.22.0"
"size-limit": "^0.20.0",
"xo": "^0.23.0"
}
}

@@ -20,3 +20,3 @@ ctrly

Minified and gzipped, the total footprint weights about 2kB.
Minified and gzipped, the total footprint weights about 2.7kB.

@@ -54,10 +54,17 @@ Installation

The control must have a `data-ctrly` attribute and link to it's target through a
`aria-controls` attribute which must contains the id of the target.
The control must have a `data-ctrly` which must contain the ID of the target.
```html
<button data-ctrly aria-controls="my-target">Toggle</button>
<button data-ctrly="my-target">Toggle</button>
<section id="my-target">You clicked the toggle to make me visible</section>
```
> The [specification](https://www.w3.org/TR/wai-aria-1.1/#aria-controls) also
allows a
[*ID reference list*](https://www.w3.org/TR/wai-aria-1.1/#valuetype_idref_list)
(a list of multiple, space-separated ID references) as value of the
`arial-controls` attribute.
This is **not** supported by ctrly and only a **single** ID reference is
allowed.
To initialize all controls, the `ctrly()` function must be called once.

@@ -69,2 +76,28 @@

ctrly then adds all required ARIA attributes, the `aria-controls` and
`aria-expanded` attributes to the control and the `aria-hidden` and
`aria-labelledby` to the target.
If the control does not have an `id` attribute, ctrly will add an auto-generated
ID.
The fully generated HTML looks like the following.
```html
<button
data-ctrly="my-target"
id="ctrly-control-1"
aria-controls="my-target"
aria-expanded="false">
Toggle
</button>
<section
id="my-target"
aria-hidden="false"
aria-labelledby="ctrly-control-1"
>
You clicked the toggle to make me visible
</section>
```
**Note:** ctrly does not ship with any default CSS which shows and hides the

@@ -81,5 +114,2 @@ target element as it makes no assumptions on how the visibility is controlled.

}
.target-selector[aria-hidden="false"] {
display: block;
}

@@ -90,27 +120,15 @@ /* Toggle via the visibility property */

}
.target-selector[aria-hidden="false"] {
visibility: visible;
}
```
It is also possible to toggle the visibility via the
[`hidden` attribute](https://developer.mozilla.org/de/docs/Web/HTML/Globale_Attribute/hidden).
This can be implemented by using event callbacks to remove and add the attribute.
It is also good practice to hide the controls if JavaScript is disabled.
```js
ctrly({
on: {
open: target => {
target.removeAttribute('hidden');
},
close: target => {
target.addAttribute('hidden');
}
}
});
This can be done depending on the presence of the `aria-controls` attribute
added by ctrly.
```css
.control-selector:not([aria-controls]) {
display: none;
}
```
More information about the event callbacks can be found in the
[Events section](#events).
API

@@ -169,6 +187,7 @@ ---

* [focusTarget](#focustarget)
* [closeOnBlur](#closeonblur)
* [closeOnEsc](#closeonesc)
* [closeOnOutsideClick](#closeonoutsideclick)
* [closeOnScroll](#closeonscroll)
* [constrainFocus](#constrainfocus)
* [trapFocus](#trapfocus)
* [allowMultiple](#allowmultiple)

@@ -186,3 +205,3 @@ * [on](#on)

```html
<button class="my-control" aria-controls="my-target">Toggle</button>
<button class="my-control" data-ctrly="my-target">Toggle</button>
```

@@ -200,3 +219,3 @@

A selector to group controls together. Can be used in combination with the
A selector to group targets together. Can be used in combination with the
[allowMultiple](#allowmultiple) option to allow or disallow multiple open

@@ -211,6 +230,6 @@ targets inside a context.

<div class="my-context">
<button data-ctrly aria-controls="my-target">Toggle</button>
<button data-ctrly="my-target">Toggle</button>
</div>
<div class="my-context">
<button data-ctrly aria-controls="my-target">Toggle</button>
<button data-ctrly="my-target">Toggle</button>
</div>

@@ -241,2 +260,21 @@ ```

### closeOnBlur
*Default:* `true`
By default, targets are closed when the focus is shifted from an element inside
the target to an element outside the target. Passing `false` as an option
disables this behavior.
> This setting is always `false` if [`trapFocus`](#trapfocus) is set
to `true`.
#### Example
```js
ctrly({
closeOnBlur: false
});
```
### closeOnEsc

@@ -287,8 +325,8 @@

### constrainFocus
### trapFocus
*Default:* `false`
By default, targets are closed when there is a mouse click outside the target.
Passing `false` as an option disables this behavior.
Passing `true` as an option ensures that <kbd>TAB</kbd> and
<kbd>SHIFT</kbd>+<kbd>TAB</kbd> do not move focus outside the target.

@@ -315,2 +353,5 @@ #### Example

> To allow multiple open targets, [`closeOnBlur`](#closeonblur) must be set to
`false`.
#### Example

@@ -317,0 +358,0 @@

@@ -9,3 +9,4 @@ import {

on,
ready
ready,
tabbable
} from 'domestique';

@@ -17,6 +18,7 @@

focusTarget: true,
closeOnBlur: true,
closeOnEsc: true,
closeOnOutsideClick: true,
closeOnScroll: false,
constrainFocus: false,
trapFocus: false,
allowMultiple: false,

@@ -26,8 +28,2 @@ on: null

const focusableElementsSelector = 'a[href],area[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),iframe,object,embed,[contenteditable],[tabindex]:not([tabindex^="-"])';
const passiveEventOptions = {
passive: true
};
function settings(opts) {

@@ -52,8 +48,19 @@ const extended = {};

function findControls(target) {
return find(`[aria-controls="${target.id}"]`);
}
function findTarget(control) {
const targetId = control.getAttribute('aria-controls');
return document.getElementById(
control.getAttribute('aria-controls') || control.getAttribute('data-ctrly')
);
}
return {targetId, target: targetId ? document.getElementById(targetId) : null};
function resetControl(control) {
control.removeAttribute('aria-controls');
control.removeAttribute('aria-expanded');
}
let idCounter = 0;
export default function ctrly(opts = {}) {

@@ -65,3 +72,3 @@ const options = settings(opts);

const removers = {};
const instances = {};

@@ -89,23 +96,16 @@ function context(control) {

function close(control, returnFocus = true) {
const {targetId, target} = findTarget(control);
function findParentTarget(control) {
let element = control;
if (!targetId) {
control.setAttribute('aria-expanded', 'false');
return false;
}
function reset() {
if (removers[targetId]) {
removers[targetId]();
delete removers[targetId];
while (element) {
if (element.id && instances[element.id]) {
return element;
}
find(`[aria-controls="${targetId}"]`).forEach(c => {
c.setAttribute('aria-expanded', 'false');
});
element = element.parentElement;
}
}
function close(target, returnFocus = true) {
if (!target) {
reset();
return false;

@@ -122,7 +122,16 @@ }

reset();
// Store reference before we call target.blur()
const currentActiveElement = activeElement();
const {lastActiveElement, destroy} = instances[target.id] || {};
delete instances[target.id];
if (destroy) {
destroy();
}
findControls(target).forEach(c => {
c.setAttribute('aria-expanded', 'false');
});
target.removeAttribute('data-ctrly-opened');

@@ -136,4 +145,8 @@

// We return focus only if the current focus is inside this target
if (returnFocus && target.contains(currentActiveElement)) {
focus(control, {
if (
returnFocus &&
lastActiveElement &&
target.contains(currentActiveElement)
) {
focus(lastActiveElement, {
restoreScrollPosition: true

@@ -148,5 +161,7 @@ });

function closeOthers(control) {
find(controlSelector, context(control)).forEach(other => {
if (other !== control) {
function closeOthers(target) {
find(controlSelector, context(target)).forEach(control => {
const other = findTarget(control);
if (other && other.id !== target.id) {
close(other, false);

@@ -172,19 +187,32 @@ }

removeFuncs.push(
on(target, 'mouseenter', activate, passiveEventOptions)
on(target, 'mouseenter', activate, {passive: true})
);
removeFuncs.push(
on(target, 'mouseleave', deactivate, passiveEventOptions)
on(target, 'mouseleave', deactivate, {passive: true})
);
removeFuncs.push(
on(target, 'touchstart', activate, passiveEventOptions)
on(target, 'touchstart', activate, {passive: true})
);
removeFuncs.push(
on(target, 'touchend', deactivate, passiveEventOptions)
on(target, 'touchend', deactivate, {passive: true})
);
}
if (options.closeOnBlur && !options.trapFocus) {
removeFuncs.push(
on(target, 'focusout', e => {
if (
!e.relatedTarget ||
!target.contains(e.relatedTarget)
) {
close(target, false);
}
}, {capture: true, passive: true})
);
}
if (options.closeOnEsc) {
removeFuncs.push(
on(document, 'keydown', e => {
if (keyCode(e) === 27 && close(control)) {
if (keyCode(e) === 27 && close(target)) {
e.preventDefault();

@@ -204,5 +232,5 @@ }

if (!active && keyCode(e) === 1 && !closest(e.target, controlSelector)) {
close(control);
close(target);
}
}, passiveEventOptions)
}, {passive: true})
);

@@ -215,9 +243,9 @@ }

if (!active) {
close(control);
close(target);
}
}, passiveEventOptions)
}, {passive: true})
);
}
if (options.constrainFocus) {
if (options.trapFocus) {
removeFuncs.push(

@@ -229,5 +257,5 @@ on(document, 'keydown', e => {

const focusableElements = find(focusableElementsSelector, target);
const tabbableElements = tabbable(target);
if (!focusableElements[0]) {
if (!tabbableElements[0]) {
e.preventDefault();

@@ -239,4 +267,4 @@ focus(target);

const active = activeElement();
const firstTabStop = focusableElements[0];
const lastTabStop = focusableElements[focusableElements.length - 1];
const firstTabStop = tabbableElements[0];
const lastTabStop = tabbableElements[tabbableElements.length - 1];

@@ -265,6 +293,6 @@ if (e.shiftKey && active === firstTabStop) {

function open(control) {
const {targetId, target} = findTarget(control);
const target = findTarget(control);
if (!target) {
control.setAttribute('aria-expanded', 'false');
resetControl(control);
return false;

@@ -281,5 +309,8 @@ }

removers[targetId] = addEventListeners(control, target);
instances[target.id] = {
lastActiveElement: activeElement(),
destroy: addEventListeners(control, target)
};
find(`[aria-controls="${targetId}"]`).forEach(c => {
findControls(target).forEach(c => {
c.setAttribute('aria-expanded', 'true');

@@ -307,4 +338,16 @@ });

const target = findTarget(control);
if (!target) {
// Allow controls without a value for data-ctrly
// to close a target if it's a child element
if (close(findParentTarget(control))) {
e.preventDefault();
}
return;
}
if (control.getAttribute('aria-expanded') === 'true') {
if (close(control)) {
if (close(target)) {
e.preventDefault();

@@ -317,6 +360,6 @@ }

if (!options.allowMultiple) {
closeOthers(control);
closeOthers(target);
}
const target = open(control);
open(control);

@@ -328,3 +371,3 @@ if (target) {

focus(
find(focusableElementsSelector, target)[0] || target
tabbable(target)[0] || target
);

@@ -342,14 +385,39 @@ }

find(controlSelector).forEach(control => {
if (control.getAttribute('aria-expanded') === 'true') {
open(control);
const target = findTarget(control);
if (!target) {
resetControl(control);
return;
}
control.setAttribute('aria-expanded', 'false');
control.setAttribute('aria-controls', target.id);
const {target} = findTarget(control);
const labelledBy = findControls(target).map(control => {
if (!control.id) {
control.setAttribute('id', 'ctrly-control-' + ++idCounter);
}
if (target) {
target.setAttribute('aria-hidden', 'true');
return control.id;
});
const newLabelledBy = (target.getAttribute('aria-labelledby') || '')
.split(' ')
.concat(labelledBy)
.filter((id, pos, arr) => {
return id !== '' && arr.indexOf(id) === pos;
});
target.setAttribute('aria-labelledby', newLabelledBy.join(' '));
if (
control.getAttribute('aria-expanded') === 'true' ||
control.hasAttribute('data-ctrly-open')
) {
open(control);
return;
}
control.setAttribute('aria-expanded', 'false');
target.setAttribute('aria-hidden', 'true');
target.removeAttribute('tabindex');
});

@@ -366,9 +434,14 @@ });

find(controlSelector).forEach(control => {
close(control, false);
const target = findTarget(control);
if (target) {
close(target, false);
}
});
// Iterate leftover removers
for (const id in removers) {
if (Object.prototype.hasOwnProperty.call(removers, id)) {
removers[id].call();
// Iterate leftover instances
for (const id in instances) {
if (Object.prototype.hasOwnProperty.call(instances, id)) {
instances[id].destroy();
delete instances[id];
}

@@ -375,0 +448,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc