define(['../core', '../core/access', './support'], function(
  jQuery,
  access,
  support
) {
  var rfocusable = /^(?:input|select|textarea|button)$/i;

  jQuery.fn.extend({
    prop: function(name, value) {
      return access(this, jQuery.prop, name, value, arguments.length > 1);
    },

    removeProp: function(name) {
      return this.each(function() {
        delete this[jQuery.propFix[name] || name];
      });
    },
  });

  jQuery.extend({
    propFix: {
      for: 'htmlFor',
      class: 'className',
    },

    prop: function(elem, name, value) {
      var ret,
        hooks,
        notxml,
        nType = elem.nodeType;

      // Don't get/set properties on text, comment and attribute nodes
      if (!elem || nType === 3 || nType === 8 || nType === 2) {
        return;
      }

      notxml = nType !== 1 || !jQuery.isXMLDoc(elem);

      if (notxml) {
        // Fix name and attach hooks
        name = jQuery.propFix[name] || name;
        hooks = jQuery.propHooks[name];
      }

      if (value !== undefined) {
        return hooks &&
          'set' in hooks &&
          (ret = hooks.set(elem, value, name)) !== undefined
          ? ret
          : (elem[name] = value);
      } else {
        return hooks && 'get' in hooks && (ret = hooks.get(elem, name)) !== null
          ? ret
          : elem[name];
      }
    },

    propHooks: {
      tabIndex: {
        get: function(elem) {
          return elem.hasAttribute('tabindex') ||
            rfocusable.test(elem.nodeName) ||
            elem.href
            ? elem.tabIndex
            : -1;
        },
      },
    },
  });

  if (!support.optSelected) {
    jQuery.propHooks.selected = {
      get: function(elem) {
        var parent = elem.parentNode;
        if (parent && parent.parentNode) {
          parent.parentNode.selectedIndex;
        }
        return null;
      },
    };
  }

  jQuery.each(
    [
      'tabIndex',
      'readOnly',
      'maxLength',
      'cellSpacing',
      'cellPadding',
      'rowSpan',
      'colSpan',
      'useMap',
      'frameBorder',
      'contentEditable',
    ],
    function() {
      jQuery.propFix[this.toLowerCase()] = this;
    }
  );
});
