UNPKG

easy-toggle-state

Version:

A tiny JavaScript library to easily toggle the state of any HTML element in any contexts, and create UI components in no time.

744 lines (613 loc) 28 kB
/** * ------------------------------------------------------------------- * easy-toggle-state * A tiny JavaScript library to easily toggle the state of any HTML element in any contexts, and create UI components in no time. * * @author Matthieu Bué <https://twikito.com> * @version v1.16.0 * @link https://twikito.github.io/easy-toggle-state/ * @license MIT : https://github.com/Twikito/easy-toggle-state/blob/master/LICENSE * ------------------------------------------------------------------- */ (function () { 'use strict'; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } /** * Prefix set to all attributes. */ var PREFIX = document.documentElement.getAttribute("data-easy-toggle-state-custom-prefix") || "toggle"; var getPrefix = function getPrefix() { return PREFIX; }; /** * Retrieve a valid HTML attribute string. * @param {string} key - A string to build a html attribute * @param {string} prefix - The prefix maybe set by user * @returns {string} - A valid html attribute */ var dataset = function dataset(key) { var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : getPrefix(); return ["data", prefix, key].filter(Boolean).join("-"); }; /** * All constants containing HTML attributes string. */ var ARROWS = dataset("arrows"), CHECKED = "aria-checked", CLASS = dataset("class"), CLASS_TARGET = dataset("class-on-target"), CLASS_TRIGGER = dataset("class-on-trigger"), DEFAULT_CLASS = "is-active", ESCAPE = dataset("escape"), EVENT = dataset("event"), EXPANDED = "aria-expanded", GROUP = dataset("group"), HIDDEN = "aria-hidden", IS_ACTIVE = dataset("is-active"), MODAL = dataset("modal"), OUTSIDE = dataset("outside"), OUTSIDE_EVENT = dataset("outside-event"), PRESSED = "aria-pressed", RADIO_GROUP = dataset("radio-group"), SELECTED = "aria-selected", TARGET = dataset("target"), TARGET_ALL = dataset("target-all"), TARGET_NEXT = dataset("target-next"), TARGET_PARENT = dataset("target-parent"), TARGET_PREVIOUS = dataset("target-previous"), TARGET_SELF = dataset("target-self"), TARGET_STATE = dataset("state"), TRIGGER_OFF = dataset("trigger-off"); /** * Hooks */ var TOGGLE_AFTER = new Event("toggleAfter"), TOGGLE_BEFORE = new Event("toggleBefore"); /** * Retrieve all trigger elements with a specific attribute, or all nodes in a specific scope. * @param {string} selector - A string that contains a selector * @param {node} node - An element in which to make the selection * @returns {array} - An array of elements */ var $$ = (function (selector, node) { var scope = selector ? "[".concat(selector, "]") : ""; if (node) { return _toConsumableArray(node.querySelectorAll(scope)); } var query = ["[".concat(CLASS, "]").concat(scope), "[".concat(CLASS_TRIGGER, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET_ALL, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET_NEXT, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET_PREVIOUS, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET_PARENT, "]").concat(scope), "[".concat(CLASS_TARGET, "][").concat(TARGET_SELF, "]").concat(scope)].join().trim(); return _toConsumableArray(document.querySelectorAll(query)); }); /** * Dispatch hooks * @param {node} element - An element on which dispatch the hook * @param {string} action - An event to dispatch * @returns {boolean} - True or False */ var dispatchHook = (function (element, action) { return element.dispatchEvent(action); }); /** * Add a namespace for element properties. * @param {string} property - The property aadded on any element * @returns {string} - The property with the namespace */ var namespacedProp = (function (property) { return "easyToggleState_".concat(property); }); /** * Aria attributes toggle manager. * @param {node} element - Current element with aria attributes to manage. * @param {json} [config] - List of aria attributes and value to assign. * @returns {undefined} */ var manageAria = (function (element) { var _ref; var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (_ref = {}, _defineProperty(_ref, CHECKED, element[namespacedProp('isActive')]), _defineProperty(_ref, EXPANDED, element[namespacedProp('isActive')]), _defineProperty(_ref, HIDDEN, !element[namespacedProp('isActive')]), _defineProperty(_ref, PRESSED, element[namespacedProp('isActive')]), _defineProperty(_ref, SELECTED, element[namespacedProp('isActive')]), _ref); return Object.keys(config).forEach(function (key) { return element.hasAttribute(key) && element.setAttribute(key, config[key]); }); }); var warningText = function warningText(classItem, attribute) { var isTarget = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; return "This trigger has the class name '".concat(classItem, "' filled in both attributes '").concat(CLASS, "' and '").concat(attribute, "'. As a result, this class will be toggled ").concat(isTarget && 'on its target(s)', " twice at the same time."); }; /** * Retrieve an array of class names from an attribute value. * @param {node} element - The trigger element on which get the attribute * @param {string} attribute - The attribute on which get class names * @returns {array} - An array of class names */ var classFromAttribute = function classFromAttribute(element, attribute) { return (element.getAttribute(attribute) || '').split(' ').filter(Boolean); }; /** * Retrieve class lists for trigger and target elements. * @param {node} element - The trigger element on which get all class names * @returns {object} - An object with two arrays with trigger and target class lists */ var retrieveClassList = (function (element) { if (element.hasAttribute(CLASS) && element.getAttribute(CLASS) && (element.hasAttribute(CLASS_TRIGGER) || element.hasAttribute(CLASS_TARGET))) { var triggerClassArray = classFromAttribute(element, CLASS_TRIGGER); var targetClassArray = classFromAttribute(element, CLASS_TARGET); /** Warn if there repetition class name between CLASS and CLASS_TRIGGER or CLASS and CLASS_TARGET */ classFromAttribute(element, CLASS).forEach(function (classItem) { if (triggerClassArray.includes(classItem)) { console.warn(warningText(classItem, CLASS_TRIGGER), element); } if (targetClassArray.includes(classItem)) { console.warn(warningText(classItem, CLASS_TARGET, true), element); } }); } /** Get class list for trigger and targets from attributes */ var lists = [CLASS, CLASS_TRIGGER, CLASS_TARGET].reduce(function (acc, val) { var _acc$trigger, _acc$target; var list = classFromAttribute(element, val); (val === CLASS || val === CLASS_TRIGGER) && (_acc$trigger = acc.trigger).push.apply(_acc$trigger, _toConsumableArray(list)); (val === CLASS || val === CLASS_TARGET) && (_acc$target = acc.target).push.apply(_acc$target, _toConsumableArray(list)); return acc; }, { trigger: [], target: [] }); !lists.trigger.length && (element.hasAttribute(CLASS) || element.hasAttribute(CLASS_TRIGGER)) && lists.trigger.push(DEFAULT_CLASS); !lists.target.length && (element.hasAttribute(CLASS) || element.hasAttribute(CLASS_TARGET)) && lists.target.push(DEFAULT_CLASS); return lists; }); /** * Retrieve all active elements of a group. * @param {node} element - An element of a group * @returns {array} - An array of active elements of a group */ var retrieveGroupActiveElement = (function (element) { var type = element.hasAttribute(GROUP) ? GROUP : RADIO_GROUP; return $$("".concat(type, "=\"").concat(element.getAttribute(type), "\"")).filter(function (groupElement) { return groupElement[namespacedProp('isActive')]; }); }); /** * Test a selector. * @param {string} selector - The selector corresponding to the targets list * @param {string} attribute - The selector scope, set by the user * @returns {undefined} */ var testSelector = function testSelector(selector, attribute) { if (!selector) { console.warn("You should fill the attribute '".concat(attribute, "' with a selector")); } }; /** * Test a targets list. * @param {string} selector - The selector corresponding to the targets list * @param {nodeList} targetList - A target elements list * @returns {nodeList} - The targets list */ var testTargets = function testTargets(selector, targetList) { /** Test if there's no match for a selector */ if (targetList.length === 0) { console.warn("There's no match with the selector '".concat(selector, "' for this trigger")); return []; } /** Test if there's more than one match for an ID selector */ var matches = selector.match(/#\w+/gi); if (matches) { matches.forEach(function (match) { var result = _toConsumableArray(targetList).filter(function (target) { return target.id === match.slice(1); }); if (result.length > 1) { console.warn("There's ".concat(result.length, " matches with the selector '").concat(match, "' for this trigger")); } }); } return _toConsumableArray(targetList); }; /** * Retrieve all targets of a trigger element, depending of its target attribute. * @param {node} element - A trigger element * @returns {nodeList} - All targets of a trigger element */ var retrieveTargets = (function (element) { if (element.hasAttribute(TARGET) || element.hasAttribute(TARGET_ALL)) { var selector = element.getAttribute(TARGET) || element.getAttribute(TARGET_ALL); testSelector(selector, element.hasAttribute(TARGET) ? TARGET : TARGET_ALL); return testTargets(selector, document.querySelectorAll(selector)); } if (element.hasAttribute(TARGET_PARENT)) { var _selector = element.getAttribute(TARGET_PARENT); testSelector(_selector, TARGET_PARENT); return testTargets(_selector, element.parentElement.querySelectorAll(_selector)); } if (element.hasAttribute(TARGET_SELF)) { var _selector2 = element.getAttribute(TARGET_SELF); testSelector(_selector2, TARGET_SELF); return testTargets(_selector2, element.querySelectorAll(_selector2)); } if (element.hasAttribute(TARGET_PREVIOUS)) { return testTargets("previous", [element.previousElementSibling].filter(Boolean)); } if (element.hasAttribute(TARGET_NEXT)) { return testTargets("next", [element.nextElementSibling].filter(Boolean)); } return []; }); /** * Toggle each class in list on the element. * @param {node} element - An element on which toggle each class * @param {array} list - An array of classlist to toggle * @returns {undefined} */ var toggleClassList = (function (element, list) { return list.forEach(function (listItem) { element.classList.toggle(listItem); }); }); /** Need to use a map for some event handler to ensure to have the same signature */ var HANDLER_MAP = {}; /** * Manage event listener on document * @param {element} element - The element on which test if there event type specified * @returns {undefined} */ var addEventListenerOnDocument = function addEventListenerOnDocument(element) { return document.addEventListener(element.getAttribute(OUTSIDE_EVENT) || element.getAttribute(EVENT) || "click", documentEventHandler, false); }; /** * Toggle off all elements width 'data-toggle-outside' attribute * when reproducing specified or click event outside itself or its targets. * @param {event} event - Event triggered on document * @returns {undefined} */ var documentEventHandler = function documentEventHandler(event) { var eTarget = event.target, eType = event.type; var insideTarget = false; $$(OUTSIDE).filter(function (element) { return element.getAttribute(OUTSIDE_EVENT) === eType || element.getAttribute(EVENT) === eType && !element.hasAttribute(OUTSIDE_EVENT) || eType === "click" && !element.hasAttribute(EVENT) && !element.hasAttribute(OUTSIDE_EVENT); }).forEach(function (element) { var e = eTarget.closest("[".concat(TARGET_STATE, "=\"true\"]")); if (e && e[namespacedProp('trigger')] === element) { insideTarget = true; } if (!insideTarget && element !== eTarget && !element.contains(eTarget) && element[namespacedProp('isActive')]) { (element.hasAttribute(GROUP) || element.hasAttribute(RADIO_GROUP) ? manageGroup : manageToggle)(element); } }); if (!insideTarget) { document.removeEventListener(eType, documentEventHandler, false); } // eTarget may be an element inside a trigger var newTarget = eTarget.closest("[".concat(CLASS, "][").concat(OUTSIDE, "],[").concat(CLASS_TRIGGER, "][").concat(OUTSIDE, "],[").concat(CLASS_TARGET, "][").concat(OUTSIDE, "]")); if (newTarget && newTarget[namespacedProp('isActive')]) { addEventListenerOnDocument(eTarget); } }; /** * Manage click on elements with 'data-trigger-off' attribute. * @param {event} event - Event triggered on element with 'trigger-off' attribute * @returns {undefined} */ var triggerOffHandler = function triggerOffHandler(event) { return manageToggle(event.currentTarget[namespacedProp('target')]); }; /** * Manage event ouside trigger or target elements. * @param {node} element - The element to toggle when 'click' or custom event is triggered on document * @returns {undefined} */ var manageTriggerOutside = function manageTriggerOutside(element) { if (!element.hasAttribute(OUTSIDE)) { return; } if (element.hasAttribute(RADIO_GROUP)) { return console.warn("You can't use '".concat(OUTSIDE, "' on a radio grouped trigger")); } if (element[namespacedProp('isActive')]) { return addEventListenerOnDocument(element); } }; /** * Manage elements inside a target element which have 'data-toggle-trigger-off' attribute. * @param {node} targetElement - An element targeted by the trigger element * @param {node} triggerElement - The trigger element * @returns {undefined} */ var manageTriggerOff = function manageTriggerOff(targetElement, triggerElement) { var triggerOffList = $$(TRIGGER_OFF, targetElement).filter(function (triggerOff) { return !triggerOff.getAttribute(TRIGGER_OFF) || targetElement.matches(triggerOff.getAttribute(TRIGGER_OFF)); }); if (triggerOffList.length === 0) { return; } if (triggerElement[namespacedProp('isActive')]) { return triggerOffList.forEach(function (triggerOff) { // Do not override if another reference is already set if (!triggerOff[namespacedProp('target')]) { triggerOff[namespacedProp('target')] = triggerElement; triggerOff.addEventListener("click", triggerOffHandler, false); } }); } triggerOffList.forEach(function (triggerOff) { // Remove reference only if it equals current trigger if (triggerOff[namespacedProp('target')] === triggerElement) { triggerOff[namespacedProp('target')] = null; triggerOff.removeEventListener("click", triggerOffHandler, false); } }); return triggerElement.focus(); }; /** * Manage focus trap inside a target element: * When Tab key is pressed, if focus is outside of the container, give focus on first item ; * when Tab key is pressed, if focus is on last item, give focus on first one ; * when Shift + Tab keys are pressed, if focus is on first item, give focus on last one. * @param {node} targetElement - The focus trap container * @returns {undefined} */ var focusTrapHandler = function focusTrapHandler(targetElement) { return function (event) { var focusablesList = _toConsumableArray(targetElement.querySelectorAll("a[href], area[href], input:not([type='hidden']):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]")); if (!focusablesList.length || event.key !== "Tab") { return; } var currentItem = event.target, firstItem = focusablesList[0], lastItem = focusablesList[focusablesList.length - 1]; // Outside focus trap container: focus on first if (focusablesList.indexOf(currentItem) === -1) { event.preventDefault(); return firstItem.focus(); } if (event.shiftKey && currentItem === firstItem) { event.preventDefault(); return lastItem.focus(); } if (!event.shiftKey && currentItem === lastItem) { event.preventDefault(); return firstItem.focus(); } }; }; /** * Manage attributes and events of targets elements. * @param {node} triggerElement - The trigger element * @param {array} classListForTarget - The class list to toggle * @param {boolean} onLoadActive - A flag for active by default * @returns {undefined} */ var manageTargets = function manageTargets(triggerElement, classListForTarget, onLoadActive) { return retrieveTargets(triggerElement).forEach(function (targetElement) { dispatchHook(targetElement, TOGGLE_BEFORE); targetElement[namespacedProp('isActive')] = !targetElement[namespacedProp('isActive')]; manageAria(targetElement); if (onLoadActive) { var _targetElement$classL; (_targetElement$classL = targetElement.classList).add.apply(_targetElement$classL, _toConsumableArray(classListForTarget)); } else { toggleClassList(targetElement, classListForTarget); } if (triggerElement.hasAttribute(OUTSIDE)) { targetElement.setAttribute(TARGET_STATE, triggerElement[namespacedProp('isActive')]); targetElement[namespacedProp('trigger')] = triggerElement; } if (triggerElement.hasAttribute(MODAL)) { if (targetElement[namespacedProp('isActive')]) { HANDLER_MAP[targetElement] = focusTrapHandler(targetElement); document.addEventListener("keydown", HANDLER_MAP[targetElement], false); } else { document.removeEventListener("keydown", HANDLER_MAP[targetElement], false); delete HANDLER_MAP[targetElement]; } } dispatchHook(targetElement, TOGGLE_AFTER); manageTriggerOff(targetElement, triggerElement); }); }; /** * Toggle class and aria on trigger and target elements. * @param {node} element - The element to toggle state and attributes * @returns {undefined} */ var manageToggle = function manageToggle(element) { dispatchHook(element, TOGGLE_BEFORE); var classList = retrieveClassList(element); toggleClassList(element, classList.trigger); element[namespacedProp('isActive')] = !element[namespacedProp('isActive')]; manageAria(element); dispatchHook(element, TOGGLE_AFTER); manageTargets(element, classList.target, false); return manageTriggerOutside(element); }; /** * Toggle elements of a same group. * @param {node} element - The element to test if it's in a group * @returns {undefined} */ var manageGroup = function manageGroup(element) { var groupActiveElements = retrieveGroupActiveElement(element); if (groupActiveElements.length === 0) { return manageToggle(element); } if (groupActiveElements.indexOf(element) === -1) { groupActiveElements.forEach(manageToggle); return manageToggle(element); } if (groupActiveElements.indexOf(element) !== -1 && !element.hasAttribute(RADIO_GROUP)) { return manageToggle(element); } }; /** * Check if a trigger element is active. * @param {node} element - A trigger element * @returns {boolean} - The active state of the trigger element */ var isActive = function isActive(element) { return !!element[namespacedProp('isActive')]; }; /** * Unbind toggling management from an element list. * @param {node} elementList - An element, or element list, on which remove the toggling management * @returns {node} - Same element, or element list */ var unbind = function unbind(elementList) { (elementList[Symbol.iterator] ? _toConsumableArray(elementList) : [elementList]).forEach(function (element) { element[namespacedProp('unbind')] && element[namespacedProp('unbind')](); }); return elementList; }; /** * Unbind toggling management from all initialized elements in the page. * @returns {nodeList} - A list of unbinded triggers */ var unbindAll = function unbindAll() { return unbind($$().filter(function (trigger) { return trigger[namespacedProp('isInitialized')]; })); }; /** * Initialization. * @returns {array} - An array of initialized triggers */ var initialize = (function () { /** * Warn if there some CLASS_TARGET triggers with no specified target. */ _toConsumableArray(document.querySelectorAll("[".concat(CLASS_TARGET, "]:not([").concat(TARGET, "]):not([").concat(TARGET_ALL, "]):not([").concat(TARGET_NEXT, "]):not([").concat(TARGET_PREVIOUS, "]):not([").concat(TARGET_PARENT, "]):not([").concat(TARGET_SELF, "])"))).forEach(function (element) { console.warn("This trigger has the attribute '".concat(CLASS_TARGET, "', but no specified target\n"), element); }); /** * Active by default management. */ $$(IS_ACTIVE).filter(function (trigger) { return !trigger[namespacedProp('isDefaultInitialized')]; }).forEach(function (trigger) { if ((trigger.hasAttribute(GROUP) || trigger.hasAttribute(RADIO_GROUP)) && retrieveGroupActiveElement(trigger).length > 0) { return console.warn("Toggle group '".concat(trigger.getAttribute(GROUP) || trigger.getAttribute(RADIO_GROUP), "' must not have more than one trigger with '").concat(IS_ACTIVE, "'")); } manageToggle(trigger); trigger[namespacedProp('isDefaultInitialized')] = true; }); /** * Set specified or click event on each trigger element. */ var triggerList = $$().filter(function (trigger) { return !trigger[namespacedProp('isInitialized')]; }); triggerList.forEach(function (trigger) { var handler = function handler(event) { event.preventDefault(); (trigger.hasAttribute(GROUP) || trigger.hasAttribute(RADIO_GROUP) ? manageGroup : manageToggle)(trigger); }; var eventName = trigger.getAttribute(EVENT) || "click"; trigger.addEventListener(eventName, handler, false); trigger[namespacedProp('unbind')] = function () { trigger.removeEventListener(eventName, handler, false); trigger[namespacedProp('isInitialized')] = false; }; trigger[namespacedProp('isInitialized')] = true; }); /** * Escape key management. */ if ($$(ESCAPE).length > 0 && !document[namespacedProp('isEscapeKeyInitialized')]) { document.addEventListener("keydown", function (event) { if (!(event.key === "Escape") && !(event.key === "Esc")) { return; } $$(ESCAPE).forEach(function (trigger) { if (!trigger[namespacedProp('isActive')]) { return; } if (trigger.hasAttribute(RADIO_GROUP)) { return console.warn("You can't use '".concat(ESCAPE, "' on a radio grouped trigger")); } return (trigger.hasAttribute(GROUP) ? manageGroup : manageToggle)(trigger); }); }, false); document[namespacedProp('isEscapeKeyInitialized')] = true; } /** * Arrows key management. */ if ($$(ARROWS).length > 0 && !document[namespacedProp('isArrowKeysInitialized')]) { document.addEventListener("keydown", function (event) { var activeElement = document.activeElement; if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"].indexOf(event.key) === -1 || !activeElement.hasAttribute(CLASS) && !activeElement.hasAttribute(CLASS_TRIGGER) && !activeElement.hasAttribute(CLASS_TARGET) || !activeElement.hasAttribute(ARROWS)) { return; } if (!activeElement.hasAttribute(GROUP) && !activeElement.hasAttribute(RADIO_GROUP)) { return console.warn("You can't use '".concat(ARROWS, "' on a trigger without '").concat(GROUP, "' or '").concat(RADIO_GROUP, "'")); } event.preventDefault(); var groupList = activeElement.hasAttribute(GROUP) ? $$("".concat(GROUP, "='").concat(activeElement.getAttribute(GROUP), "'")) : $$("".concat(RADIO_GROUP, "='").concat(activeElement.getAttribute(RADIO_GROUP), "'")); var newElement = activeElement; switch (event.key) { case "ArrowUp": case "ArrowLeft": newElement = groupList.indexOf(activeElement) > 0 ? groupList[groupList.indexOf(activeElement) - 1] : groupList[groupList.length - 1]; break; case "ArrowDown": case "ArrowRight": newElement = groupList.indexOf(activeElement) < groupList.length - 1 ? groupList[groupList.indexOf(activeElement) + 1] : groupList[0]; break; case "Home": newElement = groupList[0]; break; case "End": newElement = groupList[groupList.length - 1]; break; } newElement.focus(); return newElement.dispatchEvent(new Event(newElement.getAttribute(EVENT) || "click")); }, false); document[namespacedProp('isArrowKeysInitialized')] = true; } return triggerList; }); var handler = function handler() { initialize(); document.removeEventListener("DOMContentLoaded", handler); }; document.addEventListener("DOMContentLoaded", handler); window.easyToggleState = Object.assign(initialize, { isActive: isActive, unbind: unbind, unbindAll: unbindAll }); }());