1package parser
2
3import (
4 "bytes"
5
6 "github.com/gomarkdown/markdown/ast"
7)
8
9// attribute parses a (potential) block attribute and adds it to p.
10func (p *Parser) attribute(data []byte) []byte {
11 if len(data) < 3 {
12 return data
13 }
14 i := 0
15 if data[i] != '{' {
16 return data
17 }
18 i++
19
20 // last character must be a } otherwise it's not an attribute
21 end := skipUntilChar(data, i, '\n')
22 if data[end-1] != '}' {
23 return data
24 }
25
26 i = skipSpace(data, i)
27 b := &ast.Attribute{Attrs: make(map[string][]byte)}
28
29 esc := false
30 quote := false
31 trail := 0
32Loop:
33 for ; i < len(data); i++ {
34 switch data[i] {
35 case ' ', '\t', '\f', '\v':
36 if quote {
37 continue
38 }
39 chunk := data[trail+1 : i]
40 if len(chunk) == 0 {
41 trail = i
42 continue
43 }
44 switch {
45 case chunk[0] == '.':
46 b.Classes = append(b.Classes, chunk[1:])
47 case chunk[0] == '#':
48 b.ID = chunk[1:]
49 default:
50 k, v := keyValue(chunk)
51 if k != nil && v != nil {
52 b.Attrs[string(k)] = v
53 } else {
54 // this is illegal in an attribute
55 return data
56 }
57 }
58 trail = i
59 case '"':
60 if esc {
61 esc = !esc
62 continue
63 }
64 quote = !quote
65 case '\\':
66 esc = !esc
67 case '}':
68 if esc {
69 esc = !esc
70 continue
71 }
72 chunk := data[trail+1 : i]
73 if len(chunk) == 0 {
74 return data
75 }
76 switch {
77 case chunk[0] == '.':
78 b.Classes = append(b.Classes, chunk[1:])
79 case chunk[0] == '#':
80 b.ID = chunk[1:]
81 default:
82 k, v := keyValue(chunk)
83 if k != nil && v != nil {
84 b.Attrs[string(k)] = v
85 } else {
86 return data
87 }
88 }
89 i++
90 break Loop
91 default:
92 esc = false
93 }
94 }
95
96 p.attr = b
97 return data[i:]
98}
99
100// key="value" quotes are mandatory.
101func keyValue(data []byte) ([]byte, []byte) {
102 chunk := bytes.SplitN(data, []byte{'='}, 2)
103 if len(chunk) != 2 {
104 return nil, nil
105 }
106 key := chunk[0]
107 value := chunk[1]
108
109 if len(value) < 3 || len(key) == 0 {
110 return nil, nil
111 }
112 if value[0] != '"' || value[len(value)-1] != '"' {
113 return key, nil
114 }
115 return key, value[1 : len(value)-1]
116}