Register a custom subclass of Element to be instantiatable by the DOM.

This is necessary to allow the construction of any custom elements.

The class being registered must either subclass HtmlElement or SvgElement. If they subclass these directly then they can be used as:

class FooElement extends HtmlElement{
   void created() {
     print('FooElement created!');
   }
}

main() {
  document.registerElement('x-foo', FooElement);
  var myFoo = new Element.tag('x-foo');
  // prints 'FooElement created!' to the console.
}

The custom element can also be instantiated via HTML using the syntax <x-foo></x-foo>

Other elements can be subclassed as well:

class BarElement extends InputElement{
   void created() {
     print('BarElement created!');
   }
}

main() {
  document.registerElement('x-bar', BarElement);
  var myBar = new Element.tag('input', 'x-bar');
  // prints 'BarElement created!' to the console.
}

This custom element can also be instantiated via HTML using the syntax <input is="x-bar"></input>

Source

@Experimental()
/**
 * Register a custom subclass of Element to be instantiatable by the DOM.
 *
 * This is necessary to allow the construction of any custom elements.
 *
 * The class being registered must either subclass HtmlElement or SvgElement.
 * If they subclass these directly then they can be used as:
 *
 *     class FooElement extends HtmlElement{
 *        void created() {
 *          print('FooElement created!');
 *        }
 *     }
 *
 *     main() {
 *       document.registerElement('x-foo', FooElement);
 *       var myFoo = new Element.tag('x-foo');
 *       // prints 'FooElement created!' to the console.
 *     }
 *
 * The custom element can also be instantiated via HTML using the syntax
 * `<x-foo></x-foo>`
 *
 * Other elements can be subclassed as well:
 *
 *     class BarElement extends InputElement{
 *        void created() {
 *          print('BarElement created!');
 *        }
 *     }
 *
 *     main() {
 *       document.registerElement('x-bar', BarElement);
 *       var myBar = new Element.tag('input', 'x-bar');
 *       // prints 'BarElement created!' to the console.
 *     }
 *
 * This custom element can also be instantiated via HTML using the syntax
 * `<input is="x-bar"></input>`
 *
 */
void registerElement(String tag, Type customElementClass,
    {String extendsTag}) {
  // TODO(terry): Need to handle the extendsTag.

  // Figure out which DOM class is being extended from the user's Dart class.
  var classMirror = reflectClass(customElementClass);
  var jsClassName = _getJSClassName(classMirror);
  if (jsClassName == null) {
    // Only components derived from HTML* can be extended.
    throw new DomException.jsInterop("HierarchyRequestError: Only HTML elements can be customized.");
  }

  if (_hasCreatedConstructor(classMirror)) {
    // Start the hookup the JS way create an <x-foo> element that extends the
    // <x-base> custom element. Inherit its prototype and signal what tag is
    // inherited:
    //
    //     var myProto = Object.create(HTMLElement.prototype);
    //     var myElement = document.registerElement('x-foo', {prototype: myProto});
    var baseElement = js.context[jsClassName];
    if (baseElement == null) {
      // Couldn't find the HTML element so use a generic one.
      baseElement = js.context['HTMLElement'];
    }
    var elemProto = js.context['Object'].callMethod("create", [baseElement['prototype']]);

    // Remember for any upgrading done in wrap_jso.
    _knownCustomeElements[tag] = customElementClass;

    // TODO(terry): Hack to stop recursion re-creating custom element when the
    //              created() constructor of the custom element does e.g.,
    //
    //                  MyElement.created() : super.created() {
    //                    this.innerHtml = "<b>I'm an x-foo-with-markup!</b>";
    //                  }
    //
    //              sanitizing causes custom element to created recursively
    //              until stack overflow.
    //
    //              See https://github.com/dart-lang/sdk/issues/23666
    int creating = 0;
    elemProto['createdCallback'] = new js.JsFunction.withThis(($this) {
      if (_getJSClassName(reflectClass(customElementClass).superclass) != null && creating < 2) {
        creating++;

        var dartClass;
        try {
          dartClass = _blink.Blink_Utils.constructElement(customElementClass, $this);
        } catch (e) {
          dartClass = HtmlElement.internalCreateHtmlElement();
          throw e;
        } finally {
          // Need to remember the Dart class that was created for this custom so
          // return it and setup the blink_jsObject to the $this that we'll be working
          // with as we talk to blink.
          $this['dart_class'] = dartClass;

          creating--;
        }
      }
    });
    elemProto['attributeChangedCallback'] = new js.JsFunction.withThis(($this, attrName, oldVal, newVal) {
      if ($this["dart_class"] != null && $this['dart_class'].attributeChanged != null) {
        $this['dart_class'].attributeChanged(attrName, oldVal, newVal);
      }
    });
    elemProto['attachedCallback'] = new js.JsFunction.withThis(($this) {
      if ($this["dart_class"] != null && $this['dart_class'].attached != null) {
        $this['dart_class'].attached();
      }
    });
    elemProto['detachedCallback'] = new js.JsFunction.withThis(($this) {
      if ($this["dart_class"] != null && $this['dart_class'].detached != null) {
        $this['dart_class'].detached();
      }
    });
    // document.registerElement('x-foo', {prototype: elemProto, extends: extendsTag});
    var jsMap = new js.JsObject.jsify({'prototype': elemProto, 'extends': extendsTag});
    js.context['document'].callMethod('registerElement', [tag, jsMap]);
  }
}