Calcular a especificidade de um seletor CSS

8

Hoje, sua tarefa é escrever um programa (ou uma função) que aceite uma string e produza (ou retorne) quatro números inteiros.


Entrada

A sequência de entrada é um seletor CSS3 e pode conter basicamente qualquer caractere Unicode.


Resultado

A saída representa a especificidade do CSS desse seletor.

  • O primeiro número é sempre 0 (porque é usado para estilos embutidos e este exercício não se aplica a estilos embutidos)

  • O segundo número é o número de ids ( #foo) presentes no seletor.

  • O terceiro número é o número de classes ( .foo), atributos ( [bar]) e pseudo-classes presentes no seletor.

  • O quarto número é o número de elementos ( biz) e pseudoelementos presentes no seletor.


Notas:

  • O seletor universal (*) não é contado em nenhum lugar

  • Os pseudo-elementos ::beforee ::aftertambém podem ser escritos com um único ":" (notação herdada)

  • A entrada pode usar a :not(selector)pseudo-classe. O seletor interno não conta, mesmo que contenha IDs, classes, elementos, ...)

  • Os "tijolos" do selector são separados por combinadores (espaços / abas, +, >, ~, ex: body > div+a ~*), mas eles também podem ser acumulados (ex: div#foo.bar[baz]:hover::before)

  • Você também precisa lidar com seqüências de escape de CSS ( \seguidas de 1 a 6 números hexadecimais, seguidos de um espaço) e caracteres especiais de escape ( \seguidos por qualquer um destes !"#$%&'()*+,-./:;<=>?@[\]^`{|}~:) corretamente. Essas fugas podem fazer parte de qualquer bloco do seletor (ID, classe, etc.).

  • Você não precisa fazer nada em particular se receber um seletor inválido ou um CSS4. Não se preocupe em implementar um validador de seletor CSS3.

  • Aqui estão alguns links para aprender mais sobre CSS:


Exemplos

// Universal

* => 0,0,0,0

// EU IRIA

#id => 0,1,0,0

// Classe

.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

(NB: colchetes [] podem conter qualquer coisa, exceto um "]" sem escape)

// Pseudo-classes

: raiz => 0,0,1,0
: enésimo filho (n) => 0,0,1,0
: enésimo último filho (n) => 0,0,1,0
: enésimo tipo (n) => 0,0,1,0
: enésimo último do tipo (n) => 0,0,1,0
: primeiro filho => 0,0,1,0
: último filho => 0,0,1,0
: primeiro-tipo => 0,0,1,0
: último tipo => 0,0,1,0
: filho único => 0,0,1,0
: apenas do tipo => 0,0,1,0
: vazio => 0,0,1,0
: link => 0,0,1,0
: visitado => 0,0,1,0
: ativo => 0,0,1,0
: pairar => 0,0,1,0
: foco => 0,0,1,0
: target => 0,0,1,0
: lang (fr) => 0,0,1,0
: ativado => 0,0,1,0
: desativado => 0,0,1,0
: verificado => 0,0,1,0
: not (seletor) => 0,0,1,0

(NB: a palavra-chave após ":" pode ser qualquer coisa, exceto um pseudo-elemento)


// Elementos

body => 0,0,0,1

// Pseudoelementos

: antes => 0,0,0,1
: depois => 0,0,0,1
:: antes => 0,0,0,1
:: depois => 0,0,0,1
:: primeira linha => 0,0,0,1
:: primeira letra => 0,0,0,1

(NB: parênteses () pode conter qualquer coisa, exceto um ") sem escape")

(continua)


Se você tiver dúvidas ou precisar de exemplos ou dados de teste, pergunte nos comentários.

O código mais curto (em bytes) vence.

Boa sorte!

xem
fonte
3
Por favor, adicione exemplos (cobrindo idealmente todas as peculiaridades das notas).
Martin Ender
2
A lista de testes parece realmente boa, mas alguns exemplos que vão além de um único 1seriam ótimos. (Btw, eu acho que este é realmente um bom desafio, mas uma lista exaustiva de casos de teste parece vital para que ele funcione bem.)
Martin Ender

Respostas:

1

Javascript ES6 453 430 bytes

Aqui vai a minha chance!

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}

O @UPDATE melhorou o código, agora lida com os seletores: not e: before /: after, mas ainda não testou as seqüências de escape de CSS.

josegomezr
fonte