1 package main 2 3 import ( 4 "bytes" 5 "encoding/xml" 6 "flag" 7 "fmt" 8 "io" 9 "os" 10 "path" 11 "sort" 12 "strconv" 13 "strings" 14 15 "vimagination.zapto.org/javascript" 16 "vimagination.zapto.org/parser" 17 "vimagination.zapto.org/rwcount" 18 ) 19 20 func main() { 21 if err := run(); err != nil { 22 fmt.Fprintln(os.Stderr, err) 23 os.Exit(1) 24 } 25 } 26 27 var ( 28 base string 29 pe, svg, mod bool 30 ) 31 32 func run() error { 33 var ( 34 input, output string 35 f, o *os.File 36 err error 37 ) 38 flag.StringVar(&input, "i", "-", "input file") 39 flag.StringVar(&output, "o", "-", "output file") 40 flag.BoolVar(&pe, "e", false, "process on* attrs as javascript") 41 flag.BoolVar(&svg, "s", false, "svg mode") 42 flag.BoolVar(&mod, "m", false, "add required module import") 43 flag.StringVar(&base, "b", "lib/", "lib base") 44 flag.Parse() 45 if input == "-" { 46 f = os.Stdin 47 } else { 48 f, err = os.Open(input) 49 if err != nil { 50 return err 51 } 52 defer f.Close() 53 } 54 55 d := xml.NewDecoder(f) 56 d.Strict = false 57 d.AutoClose = xml.HTMLAutoClose 58 d.Entity = xml.HTMLEntity 59 60 elements := make([]*element, 1, 1024) 61 62 elements[0] = &element{ 63 name: "null", 64 } 65 tagNames := make(map[string]struct{}) 66 for { 67 t, err := d.Token() 68 if err != nil { 69 if err == io.EOF { 70 break 71 } 72 return err 73 } 74 switch t := t.(type) { 75 case xml.StartElement: 76 switch t.Name.Local { 77 case "var": 78 t.Name.Local = "vare" 79 case "switch": 80 t.Name.Local = "switche" 81 } 82 tagNames[t.Name.Local] = struct{}{} 83 elements = append(elements, &element{ 84 name: t.Name.Local, 85 attrs: make(map[string]string), 86 }) 87 elements[len(elements)-2].children = append(elements[len(elements)-2].children, elements[len(elements)-1]) 88 for _, attr := range t.Attr { 89 elements[len(elements)-1].attrs[attr.Name.Local] = attr.Value 90 } 91 case xml.EndElement: 92 elements = elements[:len(elements)-1] 93 case xml.CharData: 94 tt := bytes.TrimSpace(t) 95 if len(tt) > 0 { 96 elements[len(elements)-1].children = append(elements[len(elements)-1].children, text(tt)) 97 } 98 } 99 } 100 if output == "-" { 101 o = os.Stdout 102 } else { 103 o, err = os.Create(output) 104 if err != nil { 105 return err 106 } 107 } 108 if mod { 109 var m string 110 if svg { 111 m = "svg.js" 112 } else { 113 m = "html.js" 114 } 115 fmt.Fprint(o, "import {") 116 elems := make([]string, 0, len(tagNames)) 117 for e := range tagNames { 118 elems = append(elems, e) 119 } 120 sort.Strings(elems) 121 for n, e := range elems { 122 if n > 0 { 123 fmt.Fprint(o, ", ") 124 } 125 fmt.Fprint(o, e) 126 } 127 b := path.Join(base, m) 128 if b[0] != '/' { 129 b = "./" + b 130 } 131 fmt.Fprintf(o, "} from %q;\n", b) 132 } 133 if len(elements[0].children) == 1 { 134 _, err = elements[0].children[0].WriteTo(o) 135 } else { 136 _, err = elements[0].WriteTo(o) 137 } 138 if err != nil { 139 return err 140 } 141 _, err = o.Write([]byte{';'}) 142 if err != nil { 143 return err 144 } 145 return o.Close() 146 } 147 148 type element struct { 149 name string 150 attrs map[string]string 151 children []io.WriterTo 152 } 153 154 func (e *element) WriteTo(w io.Writer) (int64, error) { 155 sw := &rwcount.Writer{Writer: w} 156 fmt.Fprintf(sw, "%s(", e.name) 157 ip := indentPrinter{sw} 158 first := true 159 if len(e.attrs) == 1 { 160 for k, v := range e.attrs { 161 if first { 162 fmt.Fprint(sw, "{") 163 first = false 164 } else { 165 fmt.Fprint(sw, ", {") 166 } 167 if err := printAttr(sw, k, v); err != nil { 168 return 0, err 169 } 170 fmt.Fprint(sw, "}") 171 } 172 } else if len(e.attrs) > 1 { 173 if first { 174 fmt.Fprint(&ip, "{\n") 175 first = false 176 } else { 177 fmt.Fprint(&ip, ", {\n") 178 } 179 var f bool 180 for k, v := range e.attrs { 181 if f { 182 fmt.Fprint(&ip, ",\n") 183 } else { 184 f = true 185 } 186 if err := printAttr(&ip, k, v); err != nil { 187 return 0, err 188 } 189 } 190 fmt.Fprint(sw, "\n}") 191 } 192 if len(e.children) == 1 { 193 if !first { 194 fmt.Fprint(sw, ", ") 195 } 196 e.children[0].WriteTo(sw) 197 } else if len(e.children) > 1 { 198 if first { 199 fmt.Fprint(&ip, "[\n") 200 } else { 201 fmt.Fprint(&ip, ", [\n") 202 } 203 var f bool 204 for _, c := range e.children { 205 if f { 206 fmt.Fprint(&ip, ",\n") 207 } else { 208 f = true 209 } 210 c.WriteTo(&ip) 211 } 212 fmt.Fprint(sw, "\n]") 213 } 214 fmt.Fprint(sw, ")") 215 return sw.Count, sw.Err 216 } 217 218 func printAttr(w io.Writer, key, value string) error { 219 if pe && strings.HasPrefix(key, "on") { 220 s, err := javascript.ParseScript(parser.NewStringTokeniser("function handler(event){" + value + "}")) 221 if err == nil { 222 fmt.Fprintf(w, "%q: %s", key, s) 223 return nil 224 } 225 } 226 fmt.Fprintf(w, "%q: %q", key, value) 227 return nil 228 } 229 230 type text string 231 232 func (t text) WriteTo(w io.Writer) (int64, error) { 233 n, err := io.WriteString(w, strconv.Quote(string(t))) 234 return int64(n), err 235 } 236 237 type indentPrinter struct { 238 io.Writer 239 } 240 241 var indent = []byte{' '} 242 243 func (i *indentPrinter) Write(p []byte) (int, error) { 244 var ( 245 total int 246 last int 247 ) 248 for n, c := range p { 249 if c == '\n' { 250 m, err := i.Writer.Write(p[last : n+1]) 251 total += m 252 if err != nil { 253 return total, err 254 } 255 _, err = i.Writer.Write(indent) 256 if err != nil { 257 return total, err 258 } 259 last = n + 1 260 } 261 } 262 if last != len(p) { 263 m, err := i.Writer.Write(p[last:]) 264 total += m 265 if err != nil { 266 return total, err 267 } 268 } 269 return total, nil 270 } 271 272 func (i *indentPrinter) WriteString(s string) (int, error) { 273 return i.Write([]byte(s)) 274 } 275