/**
 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { getData } from './node_data';
import { symbols } from './symbols';
import {
  createMap,
  has
} from './util';

/**
 * Namespace reference map for attributes on special non-HTML elements
 * Associates each attribute name with the required namespace
 */
const xlinkNS = 'http://www.w3.org/1999/xlink';
const xmlNS = 'http://www.w3.org/XML/1998/namespace';
const attributeNSMap = {
  'xlink:actuate': xlinkNS,
  'xlink:arcrole': xlinkNS,
  'xlink:href': xlinkNS,
  'xlink:role': xlinkNS,
  'xlink:show': xlinkNS,
  'xlink:title': xlinkNS,
  'xlink:type': xlinkNS,
  'xml:base': xmlNS,
  'xml:lang': xmlNS,
  'xml:space': xmlNS
};

/**
 * Applies an attribute or property to a given Element. If the value is null
 * or undefined, it is removed from the Element. Otherwise, the value is set
 * as an attribute. If the element is not in the HTML namespace (eg SVG),
 * the attribute is set in the appropriate namespace.
 * @param {!Element} el
 * @param {string} name The attribute's name.
 * @param {?(boolean|number|string)=} value The attribute's value.
 */
const applyAttr = function(el, name, value) {
  if (value == null) {
    el.removeAttribute(name);
  } else {
    if (el.namespaceURI !== 'http://www.w3.org/1999/xhtml') {
      const attrNS = attributeNSMap[name] || null;
      el.setAttributeNS(attrNS, name, value);
    } else {
      el.setAttribute(name, value);
    }
  }
};

/**
 * Applies a property to a given Element.
 * @param {!Element} el
 * @param {string} name The property's name.
 * @param {*} value The property's value.
 */
const applyProp = function(el, name, value) {
  el[name] = value;
};


/**
 * Applies a style to an Element. No vendor prefix expansion is done for
 * property names/values.
 * @param {!Element} el
 * @param {string} name The attribute's name.
 * @param {*} style The style to set. Either a string of css or an object
 *     containing property-value pairs.
 */
const applyStyle = function(el, name, style) {
  if (typeof style === 'string') {
    el.style.cssText = style;
  } else {
    el.style.cssText = '';
    const elStyle = el.style;
    const obj = /** @type {!Object<string,string>} */(style);

    for (const prop in obj) {
      if (has(obj, prop)) {
        elStyle[prop] = obj[prop];
      }
    }
  }
};


/**
 * Updates a single attribute on an Element.
 * @param {!Element} el
 * @param {string} name The attribute's name.
 * @param {*} value The attribute's value. If the value is an object or
 *     function it is set on the Element, otherwise, it is set as an HTML
 *     attribute.
 */
const applyAttributeTyped = function(el, name, value) {
  const type = typeof value;

  if (type === 'object' || type === 'function') {
    applyProp(el, name, value);
  } else {
    applyAttr(el, name, /** @type {?(boolean|number|string)} */(value));
  }
};


/**
 * Calls the appropriate attribute mutator for this attribute.
 * @param {!Element} el
 * @param {string} name The attribute's name.
 * @param {*} value The attribute's value.
 */
const updateAttribute = function(el, name, value) {
  const data = getData(el);
  const attrs = data.attrs;

  if (attrs[name] === value) {
    return;
  }

  const mutator = attributes[name] || attributes[symbols.default];
  mutator(el, name, value);

  attrs[name] = value;
};


/**
 * A publicly mutable object to provide custom mutators for attributes.
 * @const {!Object<string, function(!Element, string, *)>}
 */
const attributes = createMap();

// Special generic mutator that's called for any attribute that does not
// have a specific mutator.
attributes[symbols.default] = applyAttributeTyped;

attributes[symbols.placeholder] = function() {};

attributes['style'] = applyStyle;


/** */
export {
  updateAttribute,
  applyProp,
  applyAttr,
  attributes
};

@@ -762,19 +762,9 @@
 %0A * 
-Namespace r
+R
 efer
@@ -780,109 +780,33 @@
 for 
-attributes on special non-HTML elements%0A * Associates each attribute name with the required namespace
+SVG namespaced attributes
 %0A */
@@ -1363,116 +1363,8 @@
 ute.
- If the element is not in the HTML namespace (eg SVG),%0A * the attribute is set in the appropriate namespace.
 %0A * 
@@ -1619,76 +1619,10 @@
     
-if (el.namespaceURI !== 'http://www.w3.org/1999/xhtml') %7B%0A      cons
+le
 t at
@@ -1652,17 +1652,27 @@
 ame%5D
- %7C%7C null;
+;%0A    if (attrNS) %7B
 %0A   

Remove unneeded check for HTML namespace Use setAttribute instead of setAttributeNS for all attributes that don't require a namespace