// Memoized maps of attribute to property names and vice versa.
const attributeToPropertyNames = {};
const propertyNamesToAttributes = {};


/**
 * Sets properties when corresponding attributes change.
 *
 * If your component exposes a setter for a property, it's generally a good
 * idea to let devs using your component be able to set that property in HTML
 * via an element attribute. You can code that yourself by writing an
 * `attributeChangedCallback`, or you can use this mixin to get a degree of
 * automatic support.
 *
 * This mixin implements an `attributeChangedCallback` that will attempt to
 * convert a change in an element attribute into a call to the corresponding
 * property setter. Attributes typically follow hyphenated names ("foo-bar"),
 * whereas properties typically use camelCase names ("fooBar"). This mixin
 * respects that convention, automatically mapping the hyphenated attribute
 * name to the corresponding camelCase property name.
 *
 * Example: You define a component using this mixin:
 *
 *     class MyElement extends AttributeMarshallingMixin(HTMLElement) {
 *       get fooBar() { return this._fooBar; }
 *       set fooBar(value) { this._fooBar = value; }
 *     }
 *     customElements.define('my-element', MyElement);
 *
 * If someone then instantiates your component in HTML:
 *
 *     <my-element foo-bar="Hello"></my-element>
 *
 * Then, after the element has been upgraded, the `fooBar` setter will
 * automatically be invoked with the initial value "Hello".
 *
 * Attributes can only have string values. If you'd like to convert string
 * attributes to other types (numbers, booleans), you must implement parsing
 * yourself.
 *
 * @module AttributeMarshallingMixin
 */
export default function AttributeMarshallingMixin(Base) {

  // The class prototype added by the mixin.
  class AttributeMarshalling extends Base {

    // Handle a change to the attribute with the given name.
    attributeChangedCallback(attributeName, oldValue, newValue) {
      if (super.attributeChangedCallback) { super.attributeChangedCallback(); }
      const propertyName = attributeToPropertyName(attributeName);
      // If the attribute name corresponds to a property name, set the property.
      if (propertyName in this) {
        this[propertyName] = newValue;
      }
    }

    static get observedAttributes() {
      /** @type {any} */
      const elementClass = this;
      return attributesForClass(elementClass);
    }

  }

  return AttributeMarshalling;
}


// Return the custom attributes for the given class.
function attributesForClass(classFn) {

  // We treat the HTMLElement base class as if it has no attributes, since we
  // don't want to receive attributeChangedCallback for it.
  if (classFn === HTMLElement) {
    return [];
  }

  // Get attributes for parent class.
  const baseClass = Object.getPrototypeOf(classFn.prototype).constructor;
  // See if parent class defines observedAttributes manually.
  let baseAttributes = baseClass.observedAttributes;
  if (!baseAttributes) {
    // Calculate parent class attributes ourselves.
    baseAttributes = attributesForClass(baseClass);
  }

  // Get attributes for this class.
  const propertyNames = Object.getOwnPropertyNames(classFn.prototype);
  const setterNames = propertyNames.filter(propertyName => {
    const descriptor = Object.getOwnPropertyDescriptor(classFn.prototype, propertyName);
    return descriptor && typeof descriptor.set === 'function';
  });
  const attributes = setterNames.map(setterName =>
      propertyNameToAttribute(setterName));

  // Merge.
  const diff = attributes.filter(attribute =>
      baseAttributes.indexOf(attribute) < 0);
  const result = baseAttributes.concat(diff);

  // Remove standard `style` property.
  const styleIndex = result.indexOf('style');
  if (styleIndex >= 0) {
    result.splice(styleIndex, 1);
  }
  return result;
}


// Convert hyphenated foo-bar attribute name to camel case fooBar property name.
function attributeToPropertyName(attributeName) {
  let propertyName = attributeToPropertyNames[attributeName];
  if (!propertyName) {
    // Convert and memoize.
    const hyphenRegEx = /-([a-z])/g;
    propertyName = attributeName.replace(hyphenRegEx,
        match => match[1].toUpperCase());
    attributeToPropertyNames[attributeName] = propertyName;
  }
  return propertyName;
}


// Convert a camel case fooBar property name to a hyphenated foo-bar attribute.
function propertyNameToAttribute(propertyName) {
  let attribute = propertyNamesToAttributes[propertyName];
  if (!attribute) {
    // Convert and memoize.
    const uppercaseRegEx = /([A-Z])/g;
    attribute = propertyName.replace(uppercaseRegEx, '-$1').toLowerCase();
  }
  return attribute;
}

@@ -2030,16 +2030,24 @@
 lback) %7B
+%0A       
  super.a
@@ -2070,18 +2070,57 @@
 allback(
-);
+attributeName, oldValue, newValue);%0A     
  %7D%0A     

Add missing parameters to super call.