Calcular la especificidad de un selector CSS

8

Hoy, su tarea es escribir un programa (o una función) que acepte una cadena y genere (o devuelva) cuatro enteros.


Entrada

La cadena de entrada es un selector CSS3 y puede contener básicamente cualquier carácter Unicode.


Salida

La salida representa la especificidad CSS de este selector.

  • El primer número siempre es 0 (porque se usa para estilos en línea, y este ejercicio no se aplica a los estilos en línea)

  • El segundo número es el número de identificadores ( #foo) presentes en el selector.

  • El tercer número es el número de clases ( .foo), atributos ( [bar]) y pseudo-clases presentes en el selector.

  • El cuarto número es el número de elementos ( biz) y pseudoelementos presentes en el selector.


Notas:

  • El selector universal (*) no se cuenta en ninguna parte

  • Los pseudo-elementos ::beforey ::aftertambién se pueden escribir con un solo ":" (notación heredada)

  • La entrada puede usar la :not(selector)pseudoclase. El selector interno no cuenta, incluso si contiene identificadores, clases, elementos, ...)

  • Los "ladrillos" del selector están separados por combinadores (espacios / pestañas, +, >, ~, ex: body > div+a ~*), pero también pueden acumularse (ex: div#foo.bar[baz]:hover::before)

  • También debe manejar secuencias de escape CSS ( \seguidas de 1 a 6 números hexadecimales, seguidos de un espacio) y caracteres especiales escapados ( \seguidos de cualquiera de estos !"#$%&'()*+,-./:;<=>?@[\]^`{|}~:) correctamente. Esos escapes pueden ser parte de cualquier ladrillo del selector (id, clase, etc.).

  • No necesita hacer nada en particular si recibe un selector no válido o un selector CSS4. No te molestes en implementar un validador selector CSS3.

  • Aquí hay algunos enlaces para aprender más sobre CSS específicamente:


Ejemplos

// Universal

* => 0,0,0,0

// CARNÉ DE IDENTIDAD

#id => 0,1,0,0

// Clase

.class => 0,0,1,0

// Atributos

[foo] => 0,0,1,0
[foo = "bar"] => 0,0,1,0
[foo ~ = "bar"] => 0,0,1,0
[foo ^ = "bar"] => 0,0,1,0
[foo $ = "bar"] => 0,0,1,0
[foo * = "bar"] => 0,0,1,0
[foo | = "bar"] => 0,0,1,0
[foo = bar] => 0,0,1,0
[foo = 'bar'] => 0,0,1,0

(Nota: los corchetes [] pueden contener cualquier cosa excepto un "]" sin escape)

// Pseudo-clases

: raíz => 0,0,1,0
: enésimo hijo (n) => 0,0,1,0
: nth-last-child (n) => 0,0,1,0
: enésimo tipo (n) => 0,0,1,0
: enésimo último tipo (n) => 0,0,1,0
: primer hijo => 0,0,1,0
: último hijo => 0,0,1,0
: primero de tipo => 0,0,1,0
: último tipo => 0,0,1,0
: only-child => 0,0,1,0
: solo de tipo => 0,0,1,0
: vacío => 0,0,1,0
: enlace => 0,0,1,0
: visitado => 0,0,1,0
: activo => 0,0,1,0
: hover => 0,0,1,0
: foco => 0,0,1,0
: objetivo => 0,0,1,0
: lang (fr) => 0,0,1,0
: habilitado => 0,0,1,0
: deshabilitado => 0,0,1,0
: verificado => 0,0,1,0
: no (selector) => 0,0,1,0

(Nota: la palabra clave después de ":" puede ser cualquier cosa menos un pseudo-elemento)


// Elementos

cuerpo => 0,0,0,1

// Pseudoelementos

: antes => 0,0,0,1
: después => 0,0,0,1
:: antes => 0,0,0,1
:: después => 0,0,0,1
:: primera línea => 0,0,0,1
:: primera letra => 0,0,0,1

(Nota: el paréntesis () puede contener cualquier cosa excepto un ")" sin escape)

(continuará)


Si tiene preguntas o necesita ejemplos o datos de prueba, pregunte en los comentarios.

El código más corto (en bytes) gana.

¡Buena suerte!

xem
fuente
3
Agregue ejemplos (idealmente cubriendo todas las peculiaridades en las notas).
Martin Ender
2
La lista de pruebas se ve muy bien, pero algunos ejemplos que van más allá de una sola 1sería genial. (Por cierto, creo que este es realmente un desafío bastante bueno, pero una lista exhaustiva de casos de prueba parece vital para que funcione bien.)
Martin Ender

Respuestas:

1

Javascript ES6 453 430 bytes

Aquí va mi tiro!

a=>(s){var n=z=[],c=[0,0,0,0],e=s.split(/[\s\+\>\~]+/);return e.forEach((s)=>{n=n.concat(s.replace(/\((.*)\)/,"").split(/(?=\[|::|\.)/))}),n.forEach((s)=>{if(0==s.indexOf("::"))return z.push(s);var n=s.split(/(?=:)/);z=z.concat(n[0].split(/(?=[\#])/)),/before|after/.test(n[1])?z.push(":"+n[1]):n[1]&&z.push(n[1])}),z.forEach((s)=>{/^[a-z]+$/gi.test(s)||"::"==s[0]+s[1]?c[3]++:-1!=":.[".indexOf(s[0])?c[2]++:"#"==s[0]&&c[1]++}),c}

@UPDATE mejoró el código, ahora maneja: no y: antes /: después de los selectores mejor, sin embargo, no he probado secuencias de escape CSS.

josegomezr
fuente