function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }
function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct.bind(); } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
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."); }
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 _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
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; }
import { h, render } from 'preact';
import { ErrorTypes, isPromise, parseJson, getElementTag, getPropKey, getElementAttributes, getAsyncComponent } from './shared';
import { parseHtml } from './parse';
import { kebabCaseIt } from 'case-it/kebab';

/* -----------------------------------
 *
 * Define
 *
 * -------------------------------- */

function define(tagName, child) {
  var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  var wrapComponent = options.wrapComponent;
  var preRender = typeof window === 'undefined';
  var elementTag = getElementTag(tagName);
  if (!preRender) {
    customElements.define(elementTag, setupElement(child, options));
    return;
  }
  var content = child();
  if (isPromise(content)) {
    throw new Error("".concat(ErrorTypes.Promise, " : <").concat(tagName, ">"));
  }
  var component = content;
  var attributes = {
    server: true
  };
  if (wrapComponent) {
    component = wrapComponent(content);
  }
  return function (props) {
    return h(elementTag, attributes, [h('script', {
      type: 'application/json',
      dangerouslySetInnerHTML: {
        __html: JSON.stringify(props)
      }
    }), h(component, props)]);
  };
}

/* -----------------------------------
 *
 * Setup
 *
 * -------------------------------- */

function setupElement(component) {
  var _class;
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  var _options$attributes = options.attributes,
    attributes = _options$attributes === void 0 ? [] : _options$attributes;
  if (typeof Reflect !== 'undefined' && Reflect.construct) {
    var CustomElement = function CustomElement() {
      var element = Reflect.construct(HTMLElement, [], CustomElement);
      element.__mounted = false;
      element.__component = component;
      element.__properties = {};
      element.__slots = {};
      element.__children = void 0;
      element.__options = options;
      return element;
    };
    CustomElement.observedAttributes = ['props'].concat(_toConsumableArray(attributes));
    CustomElement.prototype = Object.create(HTMLElement.prototype);
    CustomElement.prototype.constructor = CustomElement;
    CustomElement.prototype.connectedCallback = onConnected;
    CustomElement.prototype.attributeChangedCallback = onAttributeChange;
    CustomElement.prototype.disconnectedCallback = onDisconnected;
    attributes.forEach(function (name) {
      Object.defineProperty(CustomElement.prototype, name, {
        get: function get() {
          return this.__properties[name];
        },
        set: function set(v) {
          if (this.__mounted) {
            this.attributeChangedCallback(name, null, v);
          }
          var type = _typeof(v);
          if (v == null || type === 'string' || type === 'boolean' || type === 'number') {
            this.setAttribute(name, v);
          }
        }
      });
    });

    // This works, but it clobbers pre-existing event handlers made via `addEventListener`
    // events.forEach((name) => {
    //   Object.defineProperty(CustomElement.prototype, name, {
    //     set(v) {
    //       if (this.__mounted) {
    //         this.attributeChangedCallback(name, null, v);
    //       }
    //     },
    //   });
    // });

    return CustomElement;
  }
  return _class = /*#__PURE__*/function (_HTMLElement) {
    _inherits(CustomElement, _HTMLElement);
    var _super = _createSuper(CustomElement);
    function CustomElement() {
      var _this;
      _classCallCheck(this, CustomElement);
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }
      _this = _super.call.apply(_super, [this].concat(args));
      _defineProperty(_assertThisInitialized(_this), "__mounted", false);
      _defineProperty(_assertThisInitialized(_this), "__component", component);
      _defineProperty(_assertThisInitialized(_this), "__properties", {});
      _defineProperty(_assertThisInitialized(_this), "__slots", {});
      _defineProperty(_assertThisInitialized(_this), "__children", void 0);
      _defineProperty(_assertThisInitialized(_this), "__options", options);
      return _this;
    }
    _createClass(CustomElement, [{
      key: "connectedCallback",
      value: function connectedCallback() {
        onConnected.call(this);
      }
    }, {
      key: "attributeChangedCallback",
      value: function attributeChangedCallback() {
        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
          args[_key2] = arguments[_key2];
        }
        onAttributeChange.call.apply(onAttributeChange, [this].concat(args));
      }
    }, {
      key: "disconnectedCallback",
      value: function disconnectedCallback() {
        onDisconnected.call(this);
      }
    }]);
    return CustomElement;
  }( /*#__PURE__*/_wrapNativeSuper(HTMLElement)), _defineProperty(_class, "observedAttributes", ['props'].concat(_toConsumableArray(attributes))), _class;
}

// Using part of Voorhoede's register function to implement custom event handlers
// https://github.com/voorhoede/preact-web-components-demo/blob/main/src/lib/register.js#L158
function proxyEvents(props, eventNames, CustomElement) {
  var callbacks = {};
  (eventNames || []).forEach(function (name) {
    // Convert the event name to a kebab-case format and replace 'on' with 'ds'
    // This prevents the custom events from conflicting with the native events
    var customName = kebabCaseIt(name.replace('on', 'ds'));
    var existingCallback = function existingCallback() {
      return null;
    };

    // Don't know why `existingCallback` is being defined
    // Why would a callback already be defined in props?
    if (props && props[name]) {
      existingCallback = props[name].bind({});
    }

    // The callback created here is passed when the custom element's connectedCallback is called
    // Dispatches a custom event when called
    var customCb = function customCb(event) {
      // TODO: We might need to start customizing this function for certain callbacks because
      // not all of our handler functions use an event as the parameter. We might need to
      // allow the caller of the define function to pass custom callbacks for certain events
      // that return the custom event.
      var customEvent = new CustomEvent(customName, _objectSpread(_objectSpread({}, event), {}, {
        composed: true,
        bubbles: true,
        detail: {
          target: event.target
        }
      }));
      CustomElement.dispatchEvent(customEvent);
      existingCallback();
    };
    callbacks[name] = customCb;
  });
  return callbacks;
}

/* -----------------------------------
 *
 * Connected
 *
 * -------------------------------- */

function onConnected() {
  var _this2 = this;
  var attributes = getElementAttributes.call(this);
  var props = this.getAttribute('props');
  var json = this.querySelector('[type="application/json"]');
  var data = parseJson.call(this, props || (json === null || json === void 0 ? void 0 : json.innerHTML) || '{}');
  var eventHandlers = proxyEvents(this.__properties, this.__options.events, this);
  json === null || json === void 0 ? void 0 : json.remove();
  var children = this.__children;
  if (!this.__mounted && !this.hasAttribute('server')) {
    children = h(parseHtml.call(this), {});
  }
  this.__properties = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, this.__slots), data), attributes), eventHandlers);
  this.__children = children || [];
  this.removeAttribute('server');
  this.innerHTML = '';
  var response = this.__component();
  var renderer = function renderer(result) {
    return finaliseComponent.call(_this2, result);
  };
  if (isPromise(response)) {
    getAsyncComponent(response, this.tagName).then(renderer);
    return;
  }
  renderer(response);
}

/* -----------------------------------
 *
 * Attribute
 *
 * -------------------------------- */

function onAttributeChange(name, original, updated) {
  if (!this.__mounted) {
    return;
  }
  updated = updated == null ? void 0 : updated;
  var props = this.__properties;
  if (name === 'props') {
    props = _objectSpread(_objectSpread({}, props), parseJson.call(this, updated));
  } else {
    props[getPropKey(name)] = updated;
  }
  this.__properties = props;
  render(h(this.__instance, _objectSpread(_objectSpread({}, props), {}, {
    parent: this,
    children: this.__children
  })), this);
}

/* -----------------------------------
 *
 * Disconnected
 *
 * -------------------------------- */

function onDisconnected() {
  render(null, this);
}

/* -----------------------------------
 *
 * Finalise
 *
 * -------------------------------- */

function finaliseComponent(component) {
  var tagName = this.tagName;
  var wrapComponent = this.__options.wrapComponent;
  if (!component) {
    console.error(ErrorTypes.Missing, ": <".concat(tagName.toLowerCase(), ">"));
    return;
  }
  if (wrapComponent) {
    component = wrapComponent(component);
  }
  this.__instance = component;
  this.__mounted = true;
  var props = _objectSpread(_objectSpread({}, this.__properties), {}, {
    parent: this,
    children: this.__children
  });
  render(h(component, props), this);
}

/* -----------------------------------
 *
 * Export
 *
 * -------------------------------- */

export { define };