hunk.code_window:           return this;
           }
           if (typeof value != 'object') {
             value = [value];
           }
           this.find('option').removeAttr('selected');
  
hunk.labels: keep keep replace keep keep keep
hunk.after_edit:         if (typeof value !== 'object') {
  
hunk.file_path: lib/api/attributes.js
hunk.type: replace
file: /**
   * Methods for getting and modifying attributes.
   *
   * @module cheerio/attributes
   */
  
  var text = require('../static').text;
  var utils = require('../utils');
  var isTag = utils.isTag;
  var domEach = utils.domEach;
  var hasOwn = Object.prototype.hasOwnProperty;
  var camelCase = utils.camelCase;
  var cssCase = utils.cssCase;
  var rspace = /\s+/;
  var dataAttrPrefix = 'data-';
  // Lookup table for coercing string data-* attributes to their corresponding
  // JavaScript primitives
  var primitives = {
    null: null,
    true: true,
    false: false,
  };
  // Attributes that are booleans
  var rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i;
  // Matches strings that look like JSON objects or arrays
  var rbrace = /^(?:{[\w\W]*}|\[[\w\W]*])$/;
  
  /**
   * Gets a node's attribute. For boolean attributes, it will return the value's
   * name should it be set.
   *
   * Also supports getting the `value` of several form elements.
   *
   * @private
   * @param {Element} elem - Elenent to get the attribute of.
   * @param {string} name - Name of the attribute.
   * @returns {object | string | undefined} The attribute's value.
   */
  function getAttr(elem, name) {
    if (!elem || !isTag(elem)) return;
  
    if (!elem.attribs) {
      elem.attribs = {};
    }
  
    // Return the entire attribs object if no attribute specified
    if (!name) {
      return elem.attribs;
    }
  
    if (hasOwn.call(elem.attribs, name)) {
      // Get the (decoded) attribute
      return rboolean.test(name) ? name : elem.attribs[name];
    }
  
    // Mimic the DOM and return text content as value for `option's`
    if (elem.name === 'option' && name === 'value') {
      return text(elem.children);
    }
  
    // Mimic DOM with default value for radios/checkboxes
    if (
      elem.name === 'input' &&
      (elem.attribs.type === 'radio' || elem.attribs.type === 'checkbox') &&
      name === 'value'
    ) {
      return 'on';
    }
  }
  
  /**
   * Sets the value of an attribute. The attribute will be deleted if the value is `null`.
   *
   * @private
   * @param {Element} el - The element to set the attribute on.
   * @param {string} name - The attribute's name.
   * @param {string | null} value - The attribute's value.
   */
  function setAttr(el, name, value) {
    if (value === null) {
      removeAttribute(el, name);
    } else {
      el.attribs[name] = value + '';
    }
  }
  
  /**
   * Method for getting and setting attributes. Gets the attribute value for only
   * the first element in the matched set. If you set an attribute's value to
   * `null`, you remove that attribute. You may also pass a `map` and `function`
   * like jQuery.
   *
   * @example
   *   $('ul').attr('id');
   *   //=> fruits
   *
   *   $('.apple').attr('id', 'favorite').html();
   *   //=> <li class="apple" id="favorite">Apple</li>
   *
   * @param {string} name - Name of the attribute.
   * @param {string | Function} [value] - If specified sets the value of the attribute.
   * @see {@link https://api.jquery.com/attr/}
   *
   * @returns {string | Cheerio} If `value` is specified the instance itself,
   *     otherwise the attribute's value.
   */
  exports.attr = function (name, value) {
    // Set the value (with attr map support)
    if (typeof name === 'object' || arguments.length > 1) {
      if (typeof value === 'function') {
        return domEach(this, function (i, el) {
          setAttr(el, name, value.call(el, i, el.attribs[name]));
        });
      }
      return domEach(this, function (i, el) {
        if (!isTag(el)) return;
  
        if (typeof name === 'object') {
          Object.keys(name).forEach(function (objName) {
            var objValue = name[objName];
            setAttr(el, objName, objValue);
          });
        } else {
          setAttr(el, name, value);
        }
      });
    }
  
    return getAttr(this[0], name);
  };
  
  /**
   * Gets a node's prop.
   *
   * @private
   * @param {Node} el - Elenent to get the prop of.
   * @param {string} name - Name of the prop.
   * @returns {string | undefined} The prop's value.
   */
  function getProp(el, name) {
    if (!el || !isTag(el)) return;
  
    return name in el
      ? el[name]
      : rboolean.test(name)
      ? getAttr(el, name) !== undefined
      : getAttr(el, name);
  }
  
  /**
   * Sets the value of a prop.
   *
   * @private
   * @param {Element} el - The element to set the prop on.
   * @param {string} name - The prop's name.
   * @param {string | null} value - The prop's value.
   */
  function setProp(el, name, value) {
    if (name in el) {
      el[name] = value;
    } else {
      setAttr(el, name, rboolean.test(name) ? (value ? '' : null) : value);
    }
  }
  
  /**
   * Method for getting and setting properties. Gets the property value for only
   * the first element in the matched set.
   *
   * @example
   *   $('input[type="checkbox"]').prop('checked');
   *   //=> false
   *
   *   $('input[type="checkbox"]').prop('checked', true).val();
   *   //=> ok
   *
   * @param {string} name - Name of the property.
   * @param {any} [value] - If specified set the property to this.
   * @see {@link https://api.jquery.com/prop/}
   *
   * @returns {string | Cheerio} If `value` is specified the instance itself,
   *     otherwise the prop's value.
   */
  exports.prop = function (name, value) {
    if (typeof name === 'string' && value === undefined) {
      switch (name) {
        case 'style': {
          var property = this.css();
          var keys = Object.keys(property);
          keys.forEach(function (p, i) {
            property[i] = p;
          });
  
          property.length = keys.length;
  
          return property;
        }
        case 'tagName':
        case 'nodeName':
          return this[0].name.toUpperCase();
  
        case 'outerHTML':
          return this.clone().wrap('<container />').parent().html();
  
        case 'innerHTML':
          return this.html();
  
        default:
          return getProp(this[0], name);
      }
    }
  
    if (typeof name === 'object' || value !== undefined) {
      if (typeof value === 'function') {
        return domEach(this, function (j, el) {
          setProp(el, name, value.call(el, j, getProp(el, name)));
        });
      }
  
      return domEach(this, function (__, el) {
        if (!isTag(el)) return;
  
        if (typeof name === 'object') {
          Object.keys(name).forEach(function (key) {
            var val = name[key];
            setProp(el, key, val);
          });
        } else {
          setProp(el, name, value);
        }
      });
    }
  };
  
  /**
   * Sets the value of a data attribute.
   *
   * @private
   * @param {Element} el - The element to set the data attribute on.
   * @param {string | object} name - The data attribute's name.
   * @param {string | null} value - The data attribute's value.
   */
  function setData(el, name, value) {
    if (!el.data) {
      el.data = {};
    }
  
    if (typeof name === 'object') Object.assign(el.data, name);
    else if (typeof name === 'string' && value !== undefined) {
      el.data[name] = value;
    }
  }
  
  /**
   * Read the specified attribute from the equivalent HTML5 `data-*` attribute,
   * and (if present) cache the value in the node's internal data store. If no
   * attribute name is specified, read *all* HTML5 `data-*` attributes in this manner.
   *
   * @private
   * @param {Element} el - Elenent to get the data attribute of.
   * @param {string} [name] - Name of the data attribute.
   * @returns {any} The data attribute's value, or a map with all of the data attribute.
   */
  function readData(el, name) {
    var readAll = arguments.length === 1;
    var domNames;
    var jsNames;
  
    if (readAll) {
      domNames = Object.keys(el.attribs).filter(function (attrName) {
        return attrName.slice(0, dataAttrPrefix.length) === dataAttrPrefix;
      });
      jsNames = domNames.map(function (_domName) {
        return camelCase(_domName.slice(dataAttrPrefix.length));
      });
    } else {
      domNames = [dataAttrPrefix + cssCase(name)];
      jsNames = [name];
    }
  
    for (var idx = 0; idx < domNames.length; ++idx) {
      var domName = domNames[idx];
      var jsName = jsNames[idx];
      var value;
      if (hasOwn.call(el.attribs, domName) && !hasOwn.call(el.data, jsName)) {
        value = el.attribs[domName];
  
        if (hasOwn.call(primitives, value)) {
          value = primitives[value];
        } else if (value === String(Number(value))) {
          value = Number(value);
        } else if (rbrace.test(value)) {
          try {
            value = JSON.parse(value);
          } catch (e) {
            /* ignore */
          }
        }
  
        el.data[jsName] = value;
      }
    }
  
    return readAll ? el.data : value;
  }
  
  /**
   * Method for getting and setting data attributes. Gets or sets the data
   * attribute value for only the first element in the matched set.
   *
   * @example
   *   $('<div data-apple-color="red"></div>').data();
   *   //=> { appleColor: 'red' }
   *
   *   $('<div data-apple-color="red"></div>').data('apple-color');
   *   //=> 'red'
   *
   *   const apple = $('.apple').data('kind', 'mac');
   *   apple.data('kind');
   *   //=> 'mac'
   *
   * @param {string} name - Name of the attribute.
   * @param {any} [value] - If specified new value.
   * @see {@link https://api.jquery.com/data/}
   *
   * @returns {string | Cheerio | undefined} If `value` is specified the instance itself,
   *     otherwise the data attribute's value.
   */
  exports.data = function (name, value) {
    var elem = this[0];
  
    if (!elem || !isTag(elem)) return;
  
    if (!elem.data) {
      elem.data = {};
    }
  
    // Return the entire data object if no data specified
    if (!name) {
      return readData(elem);
    }
  
    // Set the value (with attr map support)
    if (typeof name === 'object' || value !== undefined) {
      domEach(this, function (i, el) {
        setData(el, name, value);
      });
      return this;
    }
    if (hasOwn.call(elem.data, name)) {
      return elem.data[name];
    }
  
    return readData(elem, name);
  };
  
  /**
   * Method for getting and setting the value of input, select, and textarea.
   * Note: Support for `map`, and `function` has not been added yet.
   *
   * @example
   *   $('input[type="text"]').val();
   *   //=> input_text
   *
   *   $('input[type="text"]').val('test').html();
   *   //=> <input type="text" value="test"/>
   *
   * @param {string | string[]} [value] - If specified new value.
   * @see {@link https://api.jquery.com/val/}
   * @returns {string | Cheerio | undefined} If a new `value` is specified the instance
   *     itself, otherwise the value.
   */
  exports.val = function (value) {
    var querying = arguments.length === 0;
    var element = this[0];
  
    if (!element) return;
  
    switch (element.name) {
      case 'textarea':
        return this.text(value);
      case 'select': {
        var option = this.find('option:selected');
        if (!option) return;
        if (!querying) {
          if (this.attr('multiple') == null && typeof value == 'object') {
            return this;
          }
          if (typeof value != 'object') {
            value = [value];
          }
          this.find('option').removeAttr('selected');
          for (var i = 0; i < value.length; i++) {
            this.find('option[value="' + value[i] + '"]').attr('selected', '');
          }
          return this;
        }
  
        return this.attr('multiple')
          ? option.toArray().map(function (el) {
              return getAttr(el, 'value');
            })
          : option.attr('value');
      }
      case 'input':
      case 'option':
        return querying ? this.attr('value') : this.attr('value', value);
    }
  };
  
  /**
   * Remove an attribute.
   *
   * @private
   * @param {Element} elem - Node to remove attribute from.
   * @param {string} name - Name of the attribute to remove.
   */
  function removeAttribute(elem, name) {
    if (!elem.attribs || !hasOwn.call(elem.attribs, name)) return;
  
    delete elem.attribs[name];
  }
  
  /**
   * Splits a space-separated list of names to individual names.
   *
   * @param {string} names - Names to split.
   * @returns {string[]} - Split names.
   */
  function splitNames(names) {
    return names ? names.trim().split(rspace) : [];
  }
  
  /**
   * Method for removing attributes by `name`.
   *
   * @example
   *   $('.pear').removeAttr('class').html();
   *   //=> <li>Pear</li>
   *
   *   $('.apple').attr('id', 'favorite');
   *   $('.apple').removeAttr('id class').html();
   *   //=> <li>Apple</li>
   *
   * @param {string} name - Name of the attribute.
   * @see {@link https://api.jquery.com/removeAttr/}
   *
   * @returns {Cheerio} The instance itself.
   */
  exports.removeAttr = function (name) {
    var attrNames = splitNames(name);
  
    for (var i = 0; i < attrNames.length; i++) {
      domEach(this, function (_, elem) {
        removeAttribute(elem, attrNames[i]);
      });
    }
  
    return this;
  };
  
  /**
   * Check to see if *any* of the matched elements have the given `className`.
   *
   * @example
   *   $('.pear').hasClass('pear');
   *   //=> true
   *
   *   $('apple').hasClass('fruit');
   *   //=> false
   *
   *   $('li').hasClass('pear');
   *   //=> true
   *
   * @param {string} className - Name of the class.
   * @see {@link https://api.jquery.com/hasClass/}
   *
   * @returns {boolean} Indicates if an element has the given `className`.
   */
  exports.hasClass = function (className) {
    return this.toArray().some(function (elem) {
      var clazz = elem.attribs && elem.attribs['class'];
      var idx = -1;
  
      if (clazz && className.length) {
        while ((idx = clazz.indexOf(className, idx + 1)) > -1) {
          var end = idx + className.length;
  
          if (
            (idx === 0 || rspace.test(clazz[idx - 1])) &&
            (end === clazz.length || rspace.test(clazz[end]))
          ) {
            return true;
          }
        }
      }
  
      return false;
    });
  };
  
  /**
   * Adds class(es) to all of the matched elements. Also accepts a `function` like jQuery.
   *
   * @example
   *   $('.pear').addClass('fruit').html();
   *   //=> <li class="pear fruit">Pear</li>
   *
   *   $('.apple').addClass('fruit red').html();
   *   //=> <li class="apple fruit red">Apple</li>
   *
   * @param {string | Function} value - Name of new class.
   * @see {@link https://api.jquery.com/addClass/}
   * @returns {Cheerio} The instance itself.
   */
  exports.addClass = function (value) {
    // Support functions
    if (typeof value === 'function') {
      return domEach(this, function (i, el) {
        var className = el.attribs['class'] || '';
        exports.addClass.call([el], value.call(el, i, className));
      });
    }
  
    // Return if no value or not a string or function
    if (!value || typeof value !== 'string') return this;
  
    var classNames = value.split(rspace);
    var numElements = this.length;
  
    for (var i = 0; i < numElements; i++) {
      // If selected element isn't a tag, move on
      if (!isTag(this[i])) continue;
  
      // If we don't already have classes
      var className = getAttr(this[i], 'class');
  
      if (!className) {
        setAttr(this[i], 'class', classNames.join(' ').trim());
      } else {
        var setClass = ' ' + className + ' ';
  
        // Check if class already exists
        for (var j = 0; j < classNames.length; j++) {
          var appendClass = classNames[j] + ' ';
          if (setClass.indexOf(' ' + appendClass) < 0) setClass += appendClass;
        }
  
        setAttr(this[i], 'class', setClass.trim());
      }
    }
  
    return this;
  };
  
  /**
   * Removes one or more space-separated classes from the selected elements. If no
   * `className` is defined, all classes will be removed. Also accepts a
   * `function` like jQuery.
   *
   * @example
   *   $('.pear').removeClass('pear').html();
   *   //=> <li class="">Pear</li>
   *
   *   $('.apple').addClass('red').removeClass().html();
   *   //=> <li class="">Apple</li>
   *
   * @param {string | Function} value - Name of the class.
   * @see {@link https://api.jquery.com/removeClass/}
   * @returns {Cheerio} The instance itself.
   */
  exports.removeClass = function (value) {
    // Handle if value is a function
    if (typeof value === 'function') {
      return domEach(this, function (i, el) {
        exports.removeClass.call(
          [el],
          value.call(el, i, el.attribs['class'] || '')
        );
      });
    }
  
    var classes = splitNames(value);
    var numClasses = classes.length;
    var removeAll = arguments.length === 0;
  
    return domEach(this, function (_, el) {
      if (!isTag(el)) return;
  
      if (removeAll) {
        // Short circuit the remove all case as this is the nice one
        el.attribs.class = '';
      } else {
        var elClasses = splitNames(el.attribs.class);
        var changed = false;
  
        for (var j = 0; j < numClasses; j++) {
          var index = elClasses.indexOf(classes[j]);
  
          if (index >= 0) {
            elClasses.splice(index, 1);
            changed = true;
  
            // We have to do another pass to ensure that there are not duplicate
            // classes listed
            j--;
          }
        }
        if (changed) {
          el.attribs.class = elClasses.join(' ');
        }
      }
    });
  };
  
  /**
   * Add or remove class(es) from the matched elements, depending on either the
   * class's presence or the value of the switch argument. Also accepts a
   * `function` like jQuery.
   *
   * @example
   *   $('.apple.green').toggleClass('fruit green red').html();
   *   //=> <li class="apple fruit red">Apple</li>
   *
   *   $('.apple.green').toggleClass('fruit green red', true).html();
   *   //=> <li class="apple green fruit red">Apple</li>
   *
   * @param {string | Function} value - Name of the class. Can also be a function.
   * @param {boolean} [stateVal] - If specified the state of the class.
   * @see {@link https://api.jquery.com/toggleClass/}
   *
   * @returns {Cheerio} The instance itself.
   */
  exports.toggleClass = function (value, stateVal) {
    // Support functions
    if (typeof value === 'function') {
      return domEach(this, function (i, el) {
        exports.toggleClass.call(
          [el],
          value.call(el, i, el.attribs['class'] || '', stateVal),
          stateVal
        );
      });
    }
  
    // Return if no value or not a string or function
    if (!value || typeof value !== 'string') return this;
  
    var classNames = value.split(rspace);
    var numClasses = classNames.length;
    var state = typeof stateVal === 'boolean' ? (stateVal ? 1 : -1) : 0;
    var numElements = this.length;
  
    for (var i = 0; i < numElements; i++) {
      // If selected element isn't a tag, move on
      if (!isTag(this[i])) continue;
  
      var elementClasses = splitNames(this[i].attribs.class);
  
      // Check if class already exists
      for (var j = 0; j < numClasses; j++) {
        // Check if the class name is currently defined
        var index = elementClasses.indexOf(classNames[j]);
  
        // Add if stateValue === true or we are toggling and there is no value
        if (state >= 0 && index < 0) {
          elementClasses.push(classNames[j]);
        } else if (state <= 0 && index >= 0) {
          // Otherwise remove but only if the item exists
          elementClasses.splice(index, 1);
        }
      }
  
      this[i].attribs.class = elementClasses.join(' ');
    }
  
    return this;
  };
  
  /**
   * Checks the current list of elements and returns `true` if _any_ of the
   * elements match the selector. If using an element or Cheerio selection,
   * returns `true` if _any_ of the elements match. If using a predicate
   * function, the function is executed in the context of the selected element,
   * so `this` refers to the current element.
   *
   * @param {string | Function | Cheerio | Node} selector - Selector for the selection.
   * @see {@link https://api.jquery.com/is/}
   *
   * @returns {boolean} Whether or not the selector matches an element of the instance.
   */
  exports.is = function (selector) {
    if (selector) {
      return this.filter(selector).length > 0;
    }
    return false;
  };
  