<|start_filename|>test/ubl.js<|end_filename|>
(function(g,d,s){if(!g.ESM){
s=d.documentElement;s=s.insertBefore(d.createElement('script'),s.lastChild);
s.type='text/javascript';s.src=g.Reflect?'./js/index.js':'./js/index.es5.js';
}}(window,document));

<|start_filename|>test/js/first.js<|end_filename|>
import P from './p.js';

export default {
  // should receive style for P too
  style(...args) {
    console.log(args.join(', '));
  },
  extends: 'div',
  includes: {P},
  render() {
    this.html`<P>first</P>`;
  }
};


<|start_filename|>test/todo/js/item.js<|end_filename|>
const _data = new WeakMap;

export default {

  extends: 'li',

  style(selector) {
    return `
    ${selector} {
      list-style: none;
      background-color: white;
      transition: background 200ms ease-out;
    }
    ${selector}.flush {
      background-color: lightgreen;
    }
    ${selector} > label > input:checked + span {
      text-decoration: line-through;
      opacity: .5;
    }
    ${selector} > label {
      display: block;
      cursor: pointer;
    }`;
  },

  get data() { return _data.get(this); },
  set data(data) {
    _data.set(this, data);
    this.render();
  },

  render() {
    const {checked, text} = this.data;
    this.html`
    <label>
      <input type=checkbox checked=${checked} onchange=${this}>
      <span>${text}</span>
    </label>`;
  },

  flush() {
    this.addEventListener('transitionend', this);
    this.classList.add('flush');
  },

  oninit(event) {
    console.log(event);
  },

  onchange() {
    const {data} = this;
    data.checked = !data.checked;
    this.classList.toggle('checked', data.checked);
  },

  ontransitionend() {
    this.classList.remove('flush');
  }
}


<|start_filename|>es.config.js<|end_filename|>
import resolve from 'rollup-plugin-node-resolve';
import includePaths from 'rollup-plugin-includepaths';
import { terser } from 'rollup-plugin-terser';

export default {
  input: 'esm/index.js',
  plugins: [
    includePaths({
      include: {
        "@ungap/create-content": "./node_modules/@ungap/degap/create-content.js",
        "@ungap/weakmap": "./node_modules/@ungap/degap/weakmap.js",
        "@ungap/weakset": "./node_modules/@ungap/degap/weakset.js",
        "@ungap/event": "./node_modules/@ungap/degap/event.js",
        "@ungap/essential-map": "./node_modules/@ungap/degap/essential-map.js",
        "@ungap/import-node": "./node_modules/@ungap/degap/import-node.js",
        "@ungap/trim": "./node_modules/@ungap/degap/trim.js"
      },
    }),
    resolve({module: true}),
    terser()
  ],
  context: 'null',
  moduleContext: 'null',
  output: {
    file: 'es.js',
    format: 'iife',
    name: 'heresy'
  }
};


<|start_filename|>test/js/local.js<|end_filename|>
const {define, render, html} = heresy;

import First from './first.js';
import Second from './second.js';
import Third from './third.js';

const Div = define('Div', {
  // should receive style for First and Second too
  style(Div, First, Second) {
    // since outer component style is injected after
    // it is possible to eventually overwrite nested
    // components through higher priority / specificity
    console.log([Div, First, Second].join(', '));
  },
  extends: 'div',
  includes: {First, Second},
  render() {
    this.html`<First/><Second/>`;
  }
});

define('ElementExample', {
  style(Comp, First, Third) {
    console.log([Comp, First, Third].join(', '));
  },
  extends: 'element',
  includes: {First, Third},
  render() {
    this.html`<First/><Third/>`;
  }
});

// either
render(document.body, html`<Div/><ElementExample/>`);

// or document.body.append(Div.new(), ElementExample.new());


<|start_filename|>esm/utils.js<|end_filename|>
let transpiled = null;
// the angry koala check @WebReflection/status/1133757401482584064
try { transpiled = new {o(){}}.o; } catch($) {}

let extend = Super => class extends Super {};
if (transpiled) {
  const {getPrototypeOf, setPrototypeOf} = Object;
  const {construct} = typeof Reflect === 'object' ? Reflect : {
    construct(Super, args, Target) {
      const a = [null];
      for (let i = 0; i < args.length; i++)
        a.push(args[i]);
      const Parent = Super.bind.apply(Super, a);
      return setPrototypeOf(new Parent, Target.prototype);
    }
  };
  extend = function (Super, cutTheMiddleMan) {
    function Class() {
      return construct(
        cutTheMiddleMan ?
          getPrototypeOf(Super) :
          Super,
        arguments,
        Class
      );
    }
    setPrototypeOf(Class.prototype, Super.prototype);
    return setPrototypeOf(Class, Super);
  };
}

const hash = s => {
  const {length} = s;
  let t = 0;
  let i = 0;
  while (i < length) {
    t = ((t << 5) - t) + s.charCodeAt(i++);
    t = t & t;
  }
  return t.toString(36);
};

const registry = {
  map: {},
  re: null
};

const regExp = keys => new RegExp(
  `<(/)?(${keys.join('|')})([^A-Za-z0-9:._-])`,
  'g'
);

let tmp = null;
const replace = (markup, info) => {
  const {map, re} = (tmp || info);
  return markup.replace(re, (_, close, name, after) => {
    const {tagName, is, element} = map[name];
    return element ?
      (close ? `</${is}>` : `<${is}${after}`) :
      (close ? `</${tagName}>` : `<${tagName} is="${is}"${after}`);
  });
};

const selector = ({tagName, is, element}) =>
                  element ? is : `${tagName}[is="${is}"]`;

const getInfo = () => tmp;
const setInfo = info => { tmp = info; };

export {
  extend,
  hash,
  registry,
  regExp,
  replace,
  selector,
  getInfo,
  setInfo
};


<|start_filename|>test/todo/js/index.js<|end_filename|>
import Todo from './todo.js';

const {define, render, html} = heresy;

// global definition, no other Todo definition is possible
define('Todo', Todo);

render(document.body, html`<Todo items=${[{text: "todo", checked: false}]} />`);


<|start_filename|>test/js/xml.js<|end_filename|>

const {define, render, html} = heresy;

define('Circle<div>', {
  style: comp => `
    ${comp} {
      width: 300px;
      height: 200px;
    }`
  ,
  render() {
    this.html`<svg><circle cx="150" cy="100" r="50" fill="#ff0000"/></svg>`;
  }
});

render(document.body, html`<Circle />`);

// document.body.innerHTML
// <h1 is="welcome-heresy">Hello <u>...</u></h1>


<|start_filename|>test/todo/js/hide.js<|end_filename|>
export default {

  extends: 'label',

  style(selector) {
    return `
    ${selector} {
      background-color: white;
      transition: background 200ms ease-out;
    }
    ${selector}.flush {
      background-color: lightgreen;
    }`;
  },

  flush() {
    this.addEventListener('transitionend', this);
    this.classList.add('flush');
  },

  render() {
    this.html`<input type="checkbox"> hide done`;
  },

  ontransitionend() {
    this.classList.remove('flush');
  }

};


<|start_filename|>test/Component.js<|end_filename|>
const Component = {
  extends: 'element',

  includes: {Other, Tags},

  style: (Component, Other, Tags) => `
    ${Component} {}
    ${Other} {}
    ${Tags} {}
  `,

  oninit() {},
  onconnected(event) {},
  ondisconnected(event) {},

  booleanAttributes: [],
  observedAttributes: [],
  onattributechanged(event = {attributeName, oldValue, newValue}) {},

  mappedAttributes: [],
  on${mappedAttribute}(event = {detail}) {},

  render() {
    this.html`<Other/><Tags/>`;
  }
};


<|start_filename|>test/todo/js/todo.js<|end_filename|>
import Hide from './hide.js';
import Item from './item.js';

const {define, html, ref} = heresy;

define('Hide', Hide);
define('Item', Item);

const _items = new WeakMap;

export default {

  extends: 'div',

  style(selector) {
    return `
    ${selector} > ul {
      max-height: 146px;
      overflow: auto;
      padding: 0;
    }
    ${selector} > label {
      cursor: pointer;
    }
    ${selector}.todo-only > ul > .checked {
      display: none;
    }`;
  },

  get items() { return _items.get(this) || []; },
  set items(items) {
    _items.set(this, items);
    this.render();
  },

  render() {
    this.html`
    <input placeholder="type item" onkeydown=${this}>
    <Hide ref=${ref(this, 'hide')} onchange=${this}/>
    <ul>${this.items.map(data => html`<Item data=${data}/>`)}</ul>`;
  },

  onkeydown(event) {
    if (event.key !== 'Enter')
      return;
    event.preventDefault();
    const {target} = event;
    const text = target.value.trim();
    if (text) {
      const i = this.items.findIndex(item => item.text === text);
      if (i < 0) {
        this.items = this.items.concat({text, checked: false});
        target.value = '';
      }
      else {
        const data = this.items[i];
        if (
          !data.checked ||
          !this.classList.contains('todo-only')
        ) {
          const query = `li[is^=item-]:nth-child(${i + 1})`;
          const item = this.querySelector(query);
          item.scrollIntoView();
          item.flush();
        }
        else {
          this.children[1].flush();
        }
      }
    }
  },

  onchange() {
    this.classList.toggle('todo-only', this.hide.current.checked);
  }

};


<|start_filename|>test/js/p.js<|end_filename|>
export default class extends HTMLParagraphElement {
  static get tagName() { return 'p'; }
  oninit() {
    console.log(this.outerHTML);
  }
};


<|start_filename|>test/js/base.js<|end_filename|>
const {define, render, html} = heresy;

define('Welcome', {
  extends: 'element',
  style: comp => `
    ${comp} {
      font-size: 16px;
      font-family: sans-serif;
      font-weight: bold;
    }
    ${comp} > u {
      font-weight: normal;
    }`
  ,
  render({useState}) {
    const [clicks, setState] = useState(0);
    this.html`Hello <u>${navigator.userAgent}</u>
    <hr/>
    <button onclick=${() => setState(clicks + 1)}>clicks ${clicks}</button>`;
  }
});

render(document.body, html`<Welcome/>`);

// document.body.innerHTML
// <h1 is="welcome-heresy">Hello <u>...</u></h1>
