| Index: js/io-element.js | 
| =================================================================== | 
| --- a/js/io-element.js | 
| +++ b/js/io-element.js | 
| @@ -1,26 +1,94 @@ | 
| -/* globals module, require */ | 
| +/* | 
| + * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| + * Copyright (C) 2006-present eyeo GmbH | 
| + * | 
| + * Adblock Plus is free software: you can redistribute it and/or modify | 
| + * it under the terms of the GNU General Public License version 3 as | 
| + * published by the Free Software Foundation. | 
| + * | 
| + * Adblock Plus is distributed in the hope that it will be useful, | 
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| + * GNU General Public License for more details. | 
| + * | 
| + * You should have received a copy of the GNU General Public License | 
| + * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| + */ | 
|  | 
| "use strict"; | 
|  | 
| // Custom Elements ponyfill (a polyfill triggered on demand) | 
| const customElementsPonyfill = require("document-register-element/pony"); | 
| if (typeof customElements !== "object") | 
| customElementsPonyfill(window); | 
|  | 
| // external dependencies | 
| const {default: HyperHTMLElement} = require("hyperhtml-element/cjs"); | 
|  | 
| +// common DOM utilities exposed as IOElement.utils | 
| +const DOMUtils = { | 
| + | 
| +  // boolean related operations/helpers | 
| +  boolean: { | 
| +    // utils.boolean.attribute(node, name, setAsTrue):void | 
| +    // set a generic node attribute name as "true" | 
| +    // if value is a boolean one or it removes the attribute | 
| +    attribute(node, name, setAsTrue) | 
| +    { | 
| +      // don't use `this.value(value)` with `this` as context | 
| +      // to make destructuring of helpers always work. | 
| +      // @example | 
| +      // const {attribute: setBoolAttr} = IOElement.utils.boolean; | 
| +      // setBoolAttr(node, 'test', true); | 
| +      if (DOMUtils.boolean.value(setAsTrue)) | 
| +      { | 
| +        node.setAttribute(name, "true"); | 
| +      } | 
| +      else | 
| +      { | 
| +        node.removeAttribute(name); | 
| +      } | 
| +    }, | 
| + | 
| +    // utils.boolean.value(any):boolean | 
| +    // it returns either true or false | 
| +    // via truthy or falsy values, but also via strings | 
| +    // representing "true", "false" as well as "0" or "1" | 
| +    value(value) | 
| +    { | 
| +      if (typeof value === "string" && value.length) | 
| +      { | 
| +        try | 
| +        { | 
| +          value = JSON.parse(value); | 
| +        } | 
| +        catch (error) | 
| +        { | 
| +          // Ignore invalid JSON to continue using value as string | 
| +        } | 
| +      } | 
| +      return !!value; | 
| +    } | 
| +  } | 
| +}; | 
| + | 
| // provides a unique-id suffix per each component | 
| let counter = 0; | 
|  | 
| // common Custom Element class to extend | 
| class IOElement extends HyperHTMLElement | 
| { | 
| +  // exposes DOM helpers as read only utils | 
| +  static get utils() | 
| +  { | 
| +    return DOMUtils; | 
| +  } | 
| + | 
| // get a unique ID or, if null, set one and returns it | 
| static getID(element) | 
| { | 
| return element.getAttribute("id") || IOElement.setID(element); | 
| } | 
|  | 
| // set a unique ID to a generic element and returns the ID | 
| static setID(element) | 
| @@ -36,16 +104,33 @@ | 
| return IOElement.getID(this); | 
| } | 
|  | 
| // whenever an element is created, render its content once | 
| created() { this.render(); } | 
|  | 
| // by default, render is a no-op | 
| render() {} | 
| + | 
| +  // usually a template would contain a main element such | 
| +  // input, button, div, section, etc. | 
| +  // having a simple way to retrieve such element can be | 
| +  // both semantic and handy, as opposite of using | 
| +  // this.children[0] each time | 
| +  get child() | 
| +  { | 
| +    let element = this.firstElementChild; | 
| +    // if accessed too early, will render automatically | 
| +    if (!element) | 
| +    { | 
| +      this.render(); | 
| +      element = this.firstElementChild; | 
| +    } | 
| +    return element; | 
| +  } | 
| } | 
|  | 
| // whenever an interpolation with ${{i18n: 'string-id'}} is found | 
| // transform such value into the expected content | 
| // example: | 
| //  render() { | 
| //    return this.html`<div>${{i18n:'about-abp'}}</div>`; | 
| //  } | 
|  |