(function(){

var BOOLEAN_PROPERTIES = [
  "allowfullscreen",
  "allowpaymentrequest",
  "allowusermedia",
  "async",
  "autofocus",
  "autoplay",
  "checked",
  "compact",
  "complete",
  "controls",
  "declare",
  "default",
  "defaultchecked",
  "defaultselected",
  "defer",
  "disabled",
  "ended",
  "formnovalidate",
  "hidden",
  "indeterminate",
  "iscontenteditable",
  "ismap",
  "itemscope",
  "loop",
  "multiple",
  "muted",
  "nohref",
  "nomodule",
  "noresize",
  "noshade",
  "novalidate",
  "nowrap",
  "open",
  "paused",
  "playsinline",
  "pubdate",
  "readonly",
  "required",
  "reversed",
  "scoped",
  "seamless",
  "seeking",
  "selected",
  "truespeed",
  "typemustmatch",
  "willvalidate"
];

var PROPERTY_ALIASES = {
  "class": "className",
  "readonly": "readOnly"
};

function isSelectable(element){
  var tagName = element.tagName.toUpperCase();

  if (tagName == "OPTION"){
    return true;
  }

  if (tagName == "INPUT") {
    var type = element.type.toLowerCase();
    return type == "checkbox" || type == "radio";
  }

  return false;
}

function isSelected(element){
  var propertyName = "selected";
  var type = element.type && element.type.toLowerCase();
  if ("checkbox" == type || "radio" == type) {
    propertyName = "checked";
  }

  return !!element[propertyName];
}

function getAttributeValue(element, name){
  var attr = element.getAttributeNode(name);
  return (attr && attr.specified) ? attr.value : null;
}

return function get(element, attribute){
  var value = null;
  var name = attribute.toLowerCase();

  if ("style" == name) {
    value = element.style;

    if (value && (typeof value != "string")) {
      value = value.cssText;
    }

    return value;
  }

  if (("selected" == name || "checked" == name) &&
      isSelectable(element)) {
    return isSelected(element) ? "true" : null;
  }

  tagName = element.tagName.toUpperCase();

  // The property is consistent. Return that in preference to the attribute for links and images.
  if (((tagName == "IMG") && name == "src") ||
      ((tagName == "A") && name == "href")) {
    value = getAttributeValue(element, name);
    if (value) {
      // We want the full URL if present
      value = element[name];
    }
    return value;
  }

  if ("spellcheck" == name) {
    value = getAttributeValue(element, name);
    if (!(value === null)) {
      if (value.toLowerCase() == "false") {
        return "false";
      } else if (value.toLowerCase() == "true") {
        return "true";
      }
    }
    // coerce the property value to a string
    return element[name] + "";
  }
  var propName = PROPERTY_ALIASES[attribute] || attribute;
  if (BOOLEAN_PROPERTIES.some(function(prop){ prop == name })) {
    value = getAttributeValue(element, name);
    value = !(value === null) || element[propName];
    return value ? "true" : null;
  }
  var property;
  try {
    property = element[propName]
  } catch (e) {
    // Leaves property undefined or null
  }
  // 1- Call getAttribute if getProperty fails,
  // i.e. property is null or undefined.
  // This happens for event handlers in Firefox.
  // For example, calling getProperty for 'onclick' would
  // fail while getAttribute for 'onclick' will succeed and
  // return the JS code of the handler.
  //
  // 2- When property is an object we fall back to the
  // actual attribute instead.
  // See issue http://code.google.com/p/selenium/issues/detail?id=966
  if ((property == null) || (typeof property == "object") || (typeof property == "function")) {
    value = getAttributeValue(element, attribute);
  } else {
    value = property;
  };

  // The empty string is a valid return value.
  return value != null ? value.toString() : null;
};

})()