|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { text } from '../static.js'; |
|
|
import { domEach, camelCase, cssCase } from '../utils.js'; |
|
|
import { isTag, type AnyNode, type Element } from 'domhandler'; |
|
|
import type { Cheerio } from '../cheerio.js'; |
|
|
import { innerText, textContent } from 'domutils'; |
|
|
import { ElementType } from 'htmlparser2'; |
|
|
const hasOwn = |
|
|
|
|
|
(Object.hasOwn as (object: unknown, prop: string) => boolean) ?? |
|
|
((object: unknown, prop: string) => |
|
|
Object.prototype.hasOwnProperty.call(object, prop)); |
|
|
const rspace = /\s+/; |
|
|
const dataAttrPrefix = 'data-'; |
|
|
|
|
|
|
|
|
const rboolean = |
|
|
/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i; |
|
|
|
|
|
const rbrace = /^{[^]*}$|^\[[^]*]$/; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getAttr( |
|
|
elem: AnyNode, |
|
|
name: undefined, |
|
|
xmlMode?: boolean, |
|
|
): Record<string, string> | undefined; |
|
|
function getAttr( |
|
|
elem: AnyNode, |
|
|
name: string, |
|
|
xmlMode?: boolean, |
|
|
): string | undefined; |
|
|
function getAttr( |
|
|
elem: AnyNode, |
|
|
name: string | undefined, |
|
|
xmlMode?: boolean, |
|
|
): Record<string, string> | string | undefined { |
|
|
if (!elem || !isTag(elem)) return undefined; |
|
|
|
|
|
elem.attribs ??= {}; |
|
|
|
|
|
|
|
|
if (!name) { |
|
|
return elem.attribs; |
|
|
} |
|
|
|
|
|
if (hasOwn(elem.attribs, name)) { |
|
|
|
|
|
return !xmlMode && rboolean.test(name) ? name : elem.attribs[name]; |
|
|
} |
|
|
|
|
|
|
|
|
if (elem.name === 'option' && name === 'value') { |
|
|
return text(elem.children); |
|
|
} |
|
|
|
|
|
|
|
|
if ( |
|
|
elem.name === 'input' && |
|
|
(elem.attribs['type'] === 'radio' || elem.attribs['type'] === 'checkbox') && |
|
|
name === 'value' |
|
|
) { |
|
|
return 'on'; |
|
|
} |
|
|
|
|
|
return undefined; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setAttr(el: Element, name: string, value: string | null) { |
|
|
if (value === null) { |
|
|
removeAttribute(el, name); |
|
|
} else { |
|
|
el.attribs[name] = `${value}`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function attr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
): string | undefined; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function attr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
): Record<string, string> | undefined; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function attr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
value?: |
|
|
| string |
|
|
| null |
|
|
| ((this: Element, i: number, attrib: string) => string | null), |
|
|
): Cheerio<T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function attr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
values: Record<string, string | null>, |
|
|
): Cheerio<T>; |
|
|
export function attr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name?: string | Record<string, string | null>, |
|
|
value?: |
|
|
| string |
|
|
| null |
|
|
| ((this: Element, i: number, attrib: string) => string | null), |
|
|
): string | Cheerio<T> | undefined | Record<string, string> { |
|
|
|
|
|
if (typeof name === 'object' || value !== undefined) { |
|
|
if (typeof value === 'function') { |
|
|
if (typeof name !== 'string') { |
|
|
{ |
|
|
throw new Error('Bad combination of arguments.'); |
|
|
} |
|
|
} |
|
|
return domEach(this, (el, i) => { |
|
|
if (isTag(el)) setAttr(el, name, value.call(el, i, el.attribs[name])); |
|
|
}); |
|
|
} |
|
|
return domEach(this, (el) => { |
|
|
if (!isTag(el)) return; |
|
|
|
|
|
if (typeof name === 'object') { |
|
|
for (const objName of Object.keys(name)) { |
|
|
const objValue = name[objName]; |
|
|
setAttr(el, objName, objValue); |
|
|
} |
|
|
} else { |
|
|
setAttr(el, name!, value!); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
return arguments.length > 1 |
|
|
? this |
|
|
: getAttr(this[0], name!, this.options.xmlMode); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getProp( |
|
|
el: Element, |
|
|
name: string, |
|
|
xmlMode?: boolean, |
|
|
): string | undefined | boolean | Element[keyof Element] { |
|
|
return name in el |
|
|
? |
|
|
(el[name] as string | undefined) |
|
|
: !xmlMode && rboolean.test(name) |
|
|
? getAttr(el, name, false) !== undefined |
|
|
: getAttr(el, name, xmlMode); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setProp(el: Element, name: string, value: unknown, xmlMode?: boolean) { |
|
|
if (name in el) { |
|
|
|
|
|
el[name] = value; |
|
|
} else { |
|
|
setAttr( |
|
|
el, |
|
|
name, |
|
|
!xmlMode && rboolean.test(name) |
|
|
? value |
|
|
? '' |
|
|
: null |
|
|
: `${value as string}`, |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
interface StyleProp { |
|
|
length: number; |
|
|
[key: string]: string | number; |
|
|
[index: number]: string; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: 'tagName' | 'nodeName', |
|
|
): string | undefined; |
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: 'innerHTML' | 'outerHTML' | 'innerText' | 'textContent', |
|
|
): string | null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: 'style', |
|
|
): StyleProp | undefined; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: 'href' | 'src', |
|
|
): string | undefined; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode, K extends keyof Element>( |
|
|
this: Cheerio<T>, |
|
|
name: K, |
|
|
): Element[K]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode, K extends keyof Element>( |
|
|
this: Cheerio<T>, |
|
|
name: K, |
|
|
value: |
|
|
| Element[K] |
|
|
| ((this: Element, i: number, prop: K) => Element[keyof Element]), |
|
|
): Cheerio<T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
map: Record<string, string | Element[keyof Element] | boolean>, |
|
|
): Cheerio<T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
value: |
|
|
| string |
|
|
| boolean |
|
|
| null |
|
|
| ((this: Element, i: number, prop: string) => string | boolean), |
|
|
): Cheerio<T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function prop<T extends AnyNode>(this: Cheerio<T>, name: string): string; |
|
|
export function prop<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string | Record<string, string | Element[keyof Element] | boolean>, |
|
|
value?: unknown, |
|
|
): |
|
|
| Cheerio<T> |
|
|
| string |
|
|
| boolean |
|
|
| undefined |
|
|
| null |
|
|
| Element[keyof Element] |
|
|
| StyleProp { |
|
|
if (typeof name === 'string' && value === undefined) { |
|
|
const el = this[0]; |
|
|
|
|
|
if (!el) return undefined; |
|
|
|
|
|
switch (name) { |
|
|
case 'style': { |
|
|
const property = this.css() as StyleProp; |
|
|
const keys = Object.keys(property); |
|
|
for (let i = 0; i < keys.length; i++) { |
|
|
property[i] = keys[i]; |
|
|
} |
|
|
|
|
|
property.length = keys.length; |
|
|
|
|
|
return property; |
|
|
} |
|
|
case 'tagName': |
|
|
case 'nodeName': { |
|
|
if (!isTag(el)) return undefined; |
|
|
return el.name.toUpperCase(); |
|
|
} |
|
|
|
|
|
case 'href': |
|
|
case 'src': { |
|
|
if (!isTag(el)) return undefined; |
|
|
const prop = el.attribs?.[name]; |
|
|
|
|
|
if ( |
|
|
typeof URL !== 'undefined' && |
|
|
((name === 'href' && (el.tagName === 'a' || el.tagName === 'link')) || |
|
|
(name === 'src' && |
|
|
(el.tagName === 'img' || |
|
|
el.tagName === 'iframe' || |
|
|
el.tagName === 'audio' || |
|
|
el.tagName === 'video' || |
|
|
el.tagName === 'source'))) && |
|
|
prop !== undefined && |
|
|
this.options.baseURI |
|
|
) { |
|
|
return new URL(prop, this.options.baseURI).href; |
|
|
} |
|
|
|
|
|
return prop; |
|
|
} |
|
|
|
|
|
case 'innerText': { |
|
|
return innerText(el); |
|
|
} |
|
|
|
|
|
case 'textContent': { |
|
|
return textContent(el); |
|
|
} |
|
|
|
|
|
case 'outerHTML': { |
|
|
if (el.type === ElementType.Root) return this.html(); |
|
|
return this.clone().wrap('<container />').parent().html(); |
|
|
} |
|
|
|
|
|
case 'innerHTML': { |
|
|
return this.html(); |
|
|
} |
|
|
|
|
|
default: { |
|
|
if (!isTag(el)) return undefined; |
|
|
return getProp(el, name, this.options.xmlMode); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (typeof name === 'object' || value !== undefined) { |
|
|
if (typeof value === 'function') { |
|
|
if (typeof name === 'object') { |
|
|
throw new TypeError('Bad combination of arguments.'); |
|
|
} |
|
|
return domEach(this, (el, i) => { |
|
|
if (isTag(el)) { |
|
|
setProp( |
|
|
el, |
|
|
name, |
|
|
value.call(el, i, getProp(el, name, this.options.xmlMode)), |
|
|
this.options.xmlMode, |
|
|
); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
return domEach(this, (el) => { |
|
|
if (!isTag(el)) return; |
|
|
|
|
|
if (typeof name === 'object') { |
|
|
for (const key of Object.keys(name)) { |
|
|
const val = name[key]; |
|
|
setProp(el, key, val, this.options.xmlMode); |
|
|
} |
|
|
} else { |
|
|
setProp(el, name, value, this.options.xmlMode); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
return undefined; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface DataElement extends Element { |
|
|
|
|
|
data?: Record<string, unknown>; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setData( |
|
|
elem: DataElement, |
|
|
name: string | Record<string, unknown>, |
|
|
value?: unknown, |
|
|
) { |
|
|
elem.data ??= {}; |
|
|
|
|
|
if (typeof name === 'object') Object.assign(elem.data, name); |
|
|
else if (typeof name === 'string' && value !== undefined) { |
|
|
elem.data[name] = value; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function readAllData(el: DataElement): unknown { |
|
|
for (const domName of Object.keys(el.attribs)) { |
|
|
if (!domName.startsWith(dataAttrPrefix)) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
const jsName = camelCase(domName.slice(dataAttrPrefix.length)); |
|
|
|
|
|
if (!hasOwn(el.data, jsName)) { |
|
|
el.data![jsName] = parseDataValue(el.attribs[domName]); |
|
|
} |
|
|
} |
|
|
|
|
|
return el.data; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function readData(el: DataElement, name: string): unknown { |
|
|
const domName = dataAttrPrefix + cssCase(name); |
|
|
const data = el.data!; |
|
|
|
|
|
if (hasOwn(data, name)) { |
|
|
return data[name]; |
|
|
} |
|
|
|
|
|
if (hasOwn(el.attribs, domName)) { |
|
|
return (data[name] = parseDataValue(el.attribs[domName])); |
|
|
} |
|
|
|
|
|
return undefined; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function parseDataValue(value: string): unknown { |
|
|
if (value === 'null') return null; |
|
|
if (value === 'true') return true; |
|
|
if (value === 'false') return false; |
|
|
const num = Number(value); |
|
|
if (value === String(num)) return num; |
|
|
if (rbrace.test(value)) { |
|
|
try { |
|
|
return JSON.parse(value); |
|
|
} catch { |
|
|
|
|
|
} |
|
|
} |
|
|
return value; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function data<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
): unknown; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function data<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
): Record<string, unknown>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function data<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
value: unknown, |
|
|
): Cheerio<T>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function data<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
values: Record<string, unknown>, |
|
|
): Cheerio<T>; |
|
|
export function data<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name?: string | Record<string, unknown>, |
|
|
value?: unknown, |
|
|
): unknown { |
|
|
const elem = this[0]; |
|
|
|
|
|
if (!elem || !isTag(elem)) return; |
|
|
|
|
|
const dataEl: DataElement = elem; |
|
|
dataEl.data ??= {}; |
|
|
|
|
|
|
|
|
if (name == null) { |
|
|
return readAllData(dataEl); |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof name === 'object' || value !== undefined) { |
|
|
domEach(this, (el) => { |
|
|
if (isTag(el)) { |
|
|
if (typeof name === 'object') setData(el, name); |
|
|
else setData(el, name, value); |
|
|
} |
|
|
}); |
|
|
return this; |
|
|
} |
|
|
|
|
|
return readData(dataEl, name); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function val<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
): string | undefined | string[]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function val<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
value: string | string[], |
|
|
): Cheerio<T>; |
|
|
export function val<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
value?: string | string[], |
|
|
): string | string[] | Cheerio<T> | undefined { |
|
|
const querying = arguments.length === 0; |
|
|
const element = this[0]; |
|
|
|
|
|
if (!element || !isTag(element)) return querying ? undefined : this; |
|
|
|
|
|
switch (element.name) { |
|
|
case 'textarea': { |
|
|
return this.text(value as string); |
|
|
} |
|
|
case 'select': { |
|
|
const option = this.find('option:selected'); |
|
|
if (!querying) { |
|
|
if (this.attr('multiple') == null && typeof value === 'object') { |
|
|
return this; |
|
|
} |
|
|
|
|
|
this.find('option').removeAttr('selected'); |
|
|
|
|
|
const values = typeof value === 'object' ? value : [value]; |
|
|
for (const val of values) { |
|
|
this.find(`option[value="${val}"]`).attr('selected', ''); |
|
|
} |
|
|
|
|
|
return this; |
|
|
} |
|
|
|
|
|
return this.attr('multiple') |
|
|
? option.toArray().map((el) => text(el.children)) |
|
|
: option.attr('value'); |
|
|
} |
|
|
case 'input': |
|
|
case 'option': { |
|
|
return querying |
|
|
? this.attr('value') |
|
|
: this.attr('value', value as string); |
|
|
} |
|
|
} |
|
|
|
|
|
return undefined; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function removeAttribute(elem: Element, name: string) { |
|
|
if (!elem.attribs || !hasOwn(elem.attribs, name)) return; |
|
|
|
|
|
delete elem.attribs[name]; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function splitNames(names?: string): string[] { |
|
|
return names ? names.trim().split(rspace) : []; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function removeAttr<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
name: string, |
|
|
): Cheerio<T> { |
|
|
const attrNames = splitNames(name); |
|
|
|
|
|
for (const attrName of attrNames) { |
|
|
domEach(this, (elem) => { |
|
|
if (isTag(elem)) removeAttribute(elem, attrName); |
|
|
}); |
|
|
} |
|
|
|
|
|
return this; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function hasClass<T extends AnyNode>( |
|
|
this: Cheerio<T>, |
|
|
className: string, |
|
|
): boolean { |
|
|
return this.toArray().some((elem) => { |
|
|
const clazz = isTag(elem) && elem.attribs['class']; |
|
|
let idx = -1; |
|
|
|
|
|
if (clazz && className.length > 0) { |
|
|
while ((idx = clazz.indexOf(className, idx + 1)) > -1) { |
|
|
const end = idx + className.length; |
|
|
|
|
|
if ( |
|
|
(idx === 0 || rspace.test(clazz[idx - 1])) && |
|
|
(end === clazz.length || rspace.test(clazz[end])) |
|
|
) { |
|
|
return true; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return false; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function addClass<T extends AnyNode, R extends ArrayLike<T>>( |
|
|
this: R, |
|
|
value?: |
|
|
| string |
|
|
| ((this: Element, i: number, className: string) => string | undefined), |
|
|
): R { |
|
|
|
|
|
if (typeof value === 'function') { |
|
|
return domEach(this, (el, i) => { |
|
|
if (isTag(el)) { |
|
|
const className = el.attribs['class'] || ''; |
|
|
addClass.call([el], value.call(el, i, className)); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (!value || typeof value !== 'string') return this; |
|
|
|
|
|
const classNames = value.split(rspace); |
|
|
const numElements = this.length; |
|
|
|
|
|
for (let i = 0; i < numElements; i++) { |
|
|
const el = this[i]; |
|
|
|
|
|
if (!isTag(el)) continue; |
|
|
|
|
|
|
|
|
const className = getAttr(el, 'class', false); |
|
|
|
|
|
if (className) { |
|
|
let setClass = ` ${className} `; |
|
|
|
|
|
|
|
|
for (const cn of classNames) { |
|
|
const appendClass = `${cn} `; |
|
|
if (!setClass.includes(` ${appendClass}`)) setClass += appendClass; |
|
|
} |
|
|
|
|
|
setAttr(el, 'class', setClass.trim()); |
|
|
} else { |
|
|
setAttr(el, 'class', classNames.join(' ').trim()); |
|
|
} |
|
|
} |
|
|
|
|
|
return this; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function removeClass<T extends AnyNode, R extends ArrayLike<T>>( |
|
|
this: R, |
|
|
name?: |
|
|
| string |
|
|
| ((this: Element, i: number, className: string) => string | undefined), |
|
|
): R { |
|
|
|
|
|
if (typeof name === 'function') { |
|
|
return domEach(this, (el, i) => { |
|
|
if (isTag(el)) { |
|
|
removeClass.call([el], name.call(el, i, el.attribs['class'] || '')); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
const classes = splitNames(name); |
|
|
const numClasses = classes.length; |
|
|
const removeAll = arguments.length === 0; |
|
|
|
|
|
return domEach(this, (el) => { |
|
|
if (!isTag(el)) return; |
|
|
|
|
|
if (removeAll) { |
|
|
|
|
|
el.attribs['class'] = ''; |
|
|
} else { |
|
|
const elClasses = splitNames(el.attribs['class']); |
|
|
let changed = false; |
|
|
|
|
|
for (let j = 0; j < numClasses; j++) { |
|
|
const index = elClasses.indexOf(classes[j]); |
|
|
|
|
|
if (index !== -1) { |
|
|
elClasses.splice(index, 1); |
|
|
changed = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
j--; |
|
|
} |
|
|
} |
|
|
if (changed) { |
|
|
el.attribs['class'] = elClasses.join(' '); |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function toggleClass<T extends AnyNode, R extends ArrayLike<T>>( |
|
|
this: R, |
|
|
value?: |
|
|
| string |
|
|
| (( |
|
|
this: Element, |
|
|
i: number, |
|
|
className: string, |
|
|
stateVal?: boolean, |
|
|
) => string), |
|
|
stateVal?: boolean, |
|
|
): R { |
|
|
|
|
|
if (typeof value === 'function') { |
|
|
return domEach(this, (el, i) => { |
|
|
if (isTag(el)) { |
|
|
toggleClass.call( |
|
|
[el], |
|
|
value.call(el, i, el.attribs['class'] || '', stateVal), |
|
|
stateVal, |
|
|
); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (!value || typeof value !== 'string') return this; |
|
|
|
|
|
const classNames = value.split(rspace); |
|
|
const numClasses = classNames.length; |
|
|
const state = typeof stateVal === 'boolean' ? (stateVal ? 1 : -1) : 0; |
|
|
const numElements = this.length; |
|
|
|
|
|
for (let i = 0; i < numElements; i++) { |
|
|
const el = this[i]; |
|
|
|
|
|
if (!isTag(el)) continue; |
|
|
|
|
|
const elementClasses = splitNames(el.attribs['class']); |
|
|
|
|
|
|
|
|
for (let j = 0; j < numClasses; j++) { |
|
|
|
|
|
const index = elementClasses.indexOf(classNames[j]); |
|
|
|
|
|
|
|
|
if (state >= 0 && index === -1) { |
|
|
elementClasses.push(classNames[j]); |
|
|
} else if (state <= 0 && index !== -1) { |
|
|
|
|
|
elementClasses.splice(index, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
el.attribs['class'] = elementClasses.join(' '); |
|
|
} |
|
|
|
|
|
return this; |
|
|
} |
|
|
|