jade_node.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. package jade
  2. import (
  3. "bytes"
  4. "fmt"
  5. "go/parser"
  6. "html"
  7. "io"
  8. "log"
  9. "regexp"
  10. "strings"
  11. )
  12. type tagNode struct {
  13. nodeType
  14. pos
  15. tr *tree
  16. Nodes []node
  17. AttrName []string
  18. AttrCode []string
  19. AttrUesc []bool
  20. TagName string
  21. tagType itemType
  22. }
  23. func (t *tree) newTag(pos pos, name string, tagType itemType) *tagNode {
  24. return &tagNode{tr: t, nodeType: nodeTag, pos: pos, TagName: name, tagType: tagType}
  25. }
  26. func (l *tagNode) append(n node) {
  27. l.Nodes = append(l.Nodes, n)
  28. }
  29. func (l *tagNode) tree() *tree {
  30. return l.tr
  31. }
  32. func (l *tagNode) attr(a, b string, c bool) {
  33. for k, v := range l.AttrName {
  34. // add to existing attribute
  35. if v == a {
  36. l.AttrCode[k] = fmt.Sprintf(tag__arg_add, l.AttrCode[k], b)
  37. return
  38. }
  39. }
  40. l.AttrName = append(l.AttrName, a)
  41. l.AttrCode = append(l.AttrCode, b)
  42. l.AttrUesc = append(l.AttrUesc, c)
  43. }
  44. func (l *tagNode) ifAttrArgBollean() {
  45. for k, v := range l.AttrCode {
  46. if v == "true" {
  47. l.AttrCode[k] = `"` + l.AttrName[k] + `"`
  48. } else if v == "false" {
  49. l.AttrName = append(l.AttrName[:k], l.AttrName[k+1:]...)
  50. l.AttrCode = append(l.AttrCode[:k], l.AttrCode[k+1:]...)
  51. l.AttrUesc = append(l.AttrUesc[:k], l.AttrUesc[k+1:]...)
  52. }
  53. }
  54. }
  55. // `"aaa'a" + 'b\"bb"b' + 'c'` >>> `"aaa'a" + "b\"bb\"b" + "c"`
  56. func filterString(in string) string {
  57. var (
  58. rs = []rune(in)
  59. flag, prev rune
  60. psn int
  61. )
  62. for k, r := range rs {
  63. // fmt.Println(string(r), " ", r)
  64. switch r {
  65. case '"':
  66. if flag == '\'' && prev != '\\' {
  67. rs[k] = 0 // bookmark for replace
  68. }
  69. if flag == 0 {
  70. flag = '"'
  71. psn = k
  72. } else if r == flag && prev != '\\' {
  73. flag = 0
  74. }
  75. case '\'':
  76. if flag == 0 {
  77. flag = '\''
  78. psn = k
  79. } else if r == flag && prev != '\\' {
  80. // if k-(psn+1) != 1 {
  81. rs[psn] = '"'
  82. rs[k] = '"'
  83. // }
  84. flag = 0
  85. }
  86. case '`':
  87. if flag == 0 {
  88. flag = '`'
  89. psn = k
  90. } else if r == flag {
  91. flag = 0
  92. }
  93. }
  94. prev = r
  95. }
  96. filterPlus(rs)
  97. filterJsEsc(rs)
  98. out := strings.Replace(string(rs), string(rune(0)), `\"`, -1)
  99. out = strings.Replace(out, string(rune(1)), ``, -1)
  100. out = strings.Replace(out, string([]rune{2, 2}), "`+", -1)
  101. out = strings.Replace(out, string(rune(3)), "+`", -1)
  102. return out
  103. }
  104. // "aaa" + "bbb" >>> "aaabbb"
  105. func filterPlus(rs []rune) {
  106. var (
  107. flag, prev rune
  108. psn int
  109. )
  110. for k, r := range rs {
  111. switch r {
  112. case '"':
  113. if flag == 0 {
  114. flag = '"'
  115. if psn > 0 {
  116. for i := psn; i < k+1; i++ {
  117. // fmt.Println(string(rs[i]), rs[i])
  118. rs[i] = 1
  119. }
  120. }
  121. } else if r == flag && prev != '\\' {
  122. psn = k
  123. flag = 0
  124. }
  125. case '`':
  126. if flag == 0 {
  127. flag = '`'
  128. } else if r == flag {
  129. flag = 0
  130. }
  131. case ' ', '+':
  132. default:
  133. psn = 0
  134. }
  135. prev = r
  136. }
  137. }
  138. // `aaa ${bbb} ccc` >>> `aaa `+bbb+` ccc`
  139. func filterJsEsc(rs []rune) {
  140. var (
  141. flag, prev rune
  142. code bool
  143. )
  144. for k, r := range rs {
  145. switch r {
  146. case '`':
  147. if flag == 0 {
  148. flag = '`'
  149. } else if r == flag {
  150. flag = 0
  151. }
  152. case '{':
  153. if flag == '`' && prev == '$' {
  154. rs[k-1] = 2
  155. rs[k] = 2
  156. code = true
  157. }
  158. case '}':
  159. if flag == '`' && code {
  160. rs[k] = 3
  161. }
  162. }
  163. prev = r
  164. }
  165. }
  166. func ifAttrArgString(a string, unesc bool) (string, bool) {
  167. var (
  168. str = []rune(a)
  169. lng = len(str)
  170. first = str[0]
  171. last = str[lng-1]
  172. )
  173. switch first {
  174. case '"', '\'':
  175. if first == last {
  176. for k, v := range str[1 : lng-1] {
  177. if v == first && str[k] != '\\' {
  178. return "", false
  179. }
  180. }
  181. if unesc {
  182. return string(str[1 : lng-1]), true
  183. }
  184. return html.EscapeString(string(str[1 : lng-1])), true
  185. }
  186. case '`':
  187. if first == last {
  188. if !strings.ContainsAny(string(str[1:lng-1]), "`") {
  189. if unesc {
  190. return string(str[1 : lng-1]), true
  191. }
  192. return html.EscapeString(string(str[1 : lng-1])), true
  193. }
  194. }
  195. }
  196. return "", false
  197. }
  198. func ternary(a string) (string, bool) {
  199. var (
  200. re = regexp.MustCompile(`^(.+)\?(.+):(.+)$`)
  201. match = re.FindStringSubmatch(a)
  202. )
  203. if len(match) == 4 {
  204. for _, v := range match[1:4] {
  205. if _, err := parser.ParseExpr(v); err != nil {
  206. return "", false
  207. }
  208. }
  209. return "ternary(" + match[1] + ", " + match[2] + ", " + match[3] + ")", true
  210. }
  211. return "", false
  212. }
  213. func (l *tagNode) String() string {
  214. var b = new(bytes.Buffer)
  215. l.WriteIn(b)
  216. return b.String()
  217. }
  218. func (l *tagNode) WriteIn(b io.Writer) {
  219. var (
  220. attr = new(bytes.Buffer)
  221. )
  222. l.ifAttrArgBollean()
  223. if len(l.AttrName) > 0 {
  224. fmt.Fprint(attr, tag__arg_bgn)
  225. for k, name := range l.AttrName {
  226. attrStr := filterString(l.AttrCode[k])
  227. if arg, ok := ifAttrArgString(attrStr, l.AttrUesc[k]); ok {
  228. fmt.Fprintf(attr, tag__arg_str, name, arg)
  229. } else if !golang_mode {
  230. fmt.Fprintf(attr, tag__arg_esc, name, attrStr)
  231. } else if _, err := parser.ParseExpr(attrStr); err == nil {
  232. if l.AttrUesc[k] {
  233. fmt.Fprintf(attr, tag__arg_une, name, l.pos, attrStr)
  234. } else {
  235. fmt.Fprintf(attr, tag__arg_esc, name, l.pos, attrStr)
  236. }
  237. } else if arg, ok := ternary(attrStr); ok {
  238. if l.AttrUesc[k] {
  239. fmt.Fprintf(attr, tag__arg_une, name, l.pos, arg)
  240. } else {
  241. fmt.Fprintf(attr, tag__arg_esc, name, l.pos, arg)
  242. }
  243. } else {
  244. log.Fatalln("Error tag attribute value ==> ", attrStr)
  245. }
  246. }
  247. fmt.Fprint(attr, tag__arg_end)
  248. }
  249. switch l.tagType {
  250. case itemTagVoid:
  251. fmt.Fprintf(b, tag__void, l.TagName, attr)
  252. case itemTagVoidInline:
  253. fmt.Fprintf(b, tag__void, l.TagName, attr)
  254. default:
  255. fmt.Fprintf(b, tag__bgn, l.TagName, attr)
  256. for _, inner := range l.Nodes {
  257. inner.WriteIn(b)
  258. }
  259. fmt.Fprintf(b, tag__end, l.TagName)
  260. }
  261. }
  262. func (l *tagNode) CopyTag() *tagNode {
  263. if l == nil {
  264. return l
  265. }
  266. n := l.tr.newTag(l.pos, l.TagName, l.tagType)
  267. n.AttrCode = l.AttrCode
  268. n.AttrName = l.AttrName
  269. n.AttrUesc = l.AttrUesc
  270. for _, elem := range l.Nodes {
  271. n.append(elem.Copy())
  272. }
  273. return n
  274. }
  275. func (l *tagNode) Copy() node {
  276. return l.CopyTag()
  277. }
  278. //
  279. //
  280. type condNode struct {
  281. nodeType
  282. pos
  283. tr *tree
  284. Nodes []node
  285. cond string
  286. condType itemType
  287. }
  288. func (t *tree) newCond(pos pos, cond string, condType itemType) *condNode {
  289. return &condNode{tr: t, nodeType: nodeCond, pos: pos, cond: cond, condType: condType}
  290. }
  291. func (l *condNode) append(n node) {
  292. l.Nodes = append(l.Nodes, n)
  293. }
  294. func (l *condNode) tree() *tree {
  295. return l.tr
  296. }
  297. func (l *condNode) String() string {
  298. var b = new(bytes.Buffer)
  299. l.WriteIn(b)
  300. return b.String()
  301. }
  302. func (l *condNode) WriteIn(b io.Writer) {
  303. switch l.condType {
  304. case itemIf:
  305. fmt.Fprintf(b, cond__if, l.cond)
  306. case itemUnless:
  307. fmt.Fprintf(b, cond__unless, l.cond)
  308. case itemCase:
  309. fmt.Fprintf(b, cond__case, l.cond)
  310. case itemWhile:
  311. fmt.Fprintf(b, cond__while, l.cond)
  312. case itemFor, itemEach:
  313. if k, v, name, ok := l.parseForArgs(); ok {
  314. fmt.Fprintf(b, cond__for, k, v, name)
  315. } else {
  316. fmt.Fprintf(b, "\n{{ Error malformed each: %s }}", l.cond)
  317. }
  318. case itemForIfNotContain:
  319. if k, v, name, ok := l.parseForArgs(); ok {
  320. fmt.Fprintf(b, cond__for_if, name, k, v, name)
  321. } else {
  322. fmt.Fprintf(b, "\n{{ Error malformed each: %s }}", l.cond)
  323. }
  324. default:
  325. fmt.Fprintf(b, "{{ Error Cond %s }}", l.cond)
  326. }
  327. for _, n := range l.Nodes {
  328. n.WriteIn(b)
  329. }
  330. fmt.Fprint(b, cond__end)
  331. }
  332. func (l *condNode) parseForArgs() (k, v, name string, ok bool) {
  333. sp := strings.SplitN(l.cond, " in ", 2)
  334. if len(sp) != 2 {
  335. return
  336. }
  337. name = strings.Trim(sp[1], " ")
  338. re := regexp.MustCompile(`^(\w+)\s*,\s*(\w+)$`)
  339. kv := re.FindAllStringSubmatch(strings.Trim(sp[0], " "), -1)
  340. if len(kv) == 1 && len(kv[0]) == 3 {
  341. k = kv[0][2]
  342. v = kv[0][1]
  343. ok = true
  344. return
  345. }
  346. r2 := regexp.MustCompile(`^\w+$`)
  347. kv2 := r2.FindAllString(strings.Trim(sp[0], " "), -1)
  348. if len(kv2) == 1 {
  349. k = "_"
  350. v = kv2[0]
  351. ok = true
  352. return
  353. }
  354. return
  355. }
  356. func (l *condNode) CopyCond() *condNode {
  357. if l == nil {
  358. return l
  359. }
  360. n := l.tr.newCond(l.pos, l.cond, l.condType)
  361. for _, elem := range l.Nodes {
  362. n.append(elem.Copy())
  363. }
  364. return n
  365. }
  366. func (l *condNode) Copy() node {
  367. return l.CopyCond()
  368. }
  369. //
  370. //
  371. type codeNode struct {
  372. nodeType
  373. pos
  374. tr *tree
  375. codeType itemType
  376. Code []byte // The text; may span newlines.
  377. }
  378. func (t *tree) newCode(pos pos, text string, codeType itemType) *codeNode {
  379. return &codeNode{tr: t, nodeType: nodeCode, pos: pos, Code: []byte(text), codeType: codeType}
  380. }
  381. func (t *codeNode) String() string {
  382. var b = new(bytes.Buffer)
  383. t.WriteIn(b)
  384. return b.String()
  385. }
  386. func (t *codeNode) WriteIn(b io.Writer) {
  387. switch t.codeType {
  388. case itemCodeBuffered:
  389. if !golang_mode {
  390. fmt.Fprintf(b, code__buffered, filterString(string(t.Code)))
  391. return
  392. }
  393. if code, ok := ifAttrArgString(string(t.Code), false); ok {
  394. fmt.Fprintf(b, code__buffered, t.pos, `"`+code+`"`)
  395. } else {
  396. fmt.Fprintf(b, code__buffered, t.pos, filterString(string(t.Code)))
  397. }
  398. case itemCodeUnescaped:
  399. if !golang_mode {
  400. fmt.Fprintf(b, code__unescaped, filterString(string(t.Code)))
  401. return
  402. }
  403. fmt.Fprintf(b, code__unescaped, t.pos, filterString(string(t.Code)))
  404. case itemCode:
  405. fmt.Fprintf(b, code__longcode, filterString(string(t.Code)))
  406. case itemElse:
  407. fmt.Fprintf(b, code__else)
  408. case itemElseIf:
  409. fmt.Fprintf(b, code__else_if, filterString(string(t.Code)))
  410. case itemForElse:
  411. fmt.Fprintf(b, code__for_else)
  412. case itemCaseWhen:
  413. fmt.Fprintf(b, code__case_when, filterString(string(t.Code)))
  414. case itemCaseDefault:
  415. fmt.Fprintf(b, code__case_def)
  416. case itemMixinBlock:
  417. fmt.Fprintf(b, code__mix_block)
  418. default:
  419. fmt.Fprintf(b, "{{ Error Code %s }}", t.Code)
  420. }
  421. }
  422. func (t *codeNode) tree() *tree {
  423. return t.tr
  424. }
  425. func (t *codeNode) Copy() node {
  426. return &codeNode{tr: t.tr, nodeType: nodeCode, pos: t.pos, codeType: t.codeType, Code: append([]byte{}, t.Code...)}
  427. }
  428. //
  429. //
  430. type blockNode struct {
  431. nodeType
  432. pos
  433. tr *tree
  434. blockType itemType
  435. Name string
  436. }
  437. func (t *tree) newBlock(pos pos, name string, textType itemType) *blockNode {
  438. return &blockNode{tr: t, nodeType: nodeBlock, pos: pos, Name: name, blockType: textType}
  439. }
  440. func (bn *blockNode) String() string {
  441. var b = new(bytes.Buffer)
  442. bn.WriteIn(b)
  443. return b.String()
  444. }
  445. func (bn *blockNode) WriteIn(b io.Writer) {
  446. var (
  447. out_blk = bn.tr.block[bn.Name]
  448. out_pre, ok_pre = bn.tr.block[bn.Name+"_prepend"]
  449. out_app, ok_app = bn.tr.block[bn.Name+"_append"]
  450. )
  451. if ok_pre {
  452. out_pre.WriteIn(b)
  453. }
  454. out_blk.WriteIn(b)
  455. if ok_app {
  456. out_app.WriteIn(b)
  457. }
  458. }
  459. func (bn *blockNode) tree() *tree {
  460. return bn.tr
  461. }
  462. func (bn *blockNode) Copy() node {
  463. return &blockNode{tr: bn.tr, nodeType: nodeBlock, pos: bn.pos, blockType: bn.blockType, Name: bn.Name}
  464. }
  465. //
  466. //
  467. type textNode struct {
  468. nodeType
  469. pos
  470. tr *tree
  471. textType itemType
  472. Text []byte // The text; may span newlines.
  473. }
  474. func (t *tree) newText(pos pos, text []byte, textType itemType) *textNode {
  475. return &textNode{tr: t, nodeType: nodeText, pos: pos, Text: text, textType: textType}
  476. }
  477. func (t *textNode) String() string {
  478. var b = new(bytes.Buffer)
  479. t.WriteIn(b)
  480. return b.String()
  481. }
  482. func (t *textNode) WriteIn(b io.Writer) {
  483. switch t.textType {
  484. case itemComment:
  485. fmt.Fprintf(b, text__comment, t.Text)
  486. default:
  487. if !golang_mode {
  488. fmt.Fprintf(b, text__str, t.Text)
  489. } else {
  490. fmt.Fprintf(b, text__str, bytes.Replace(t.Text, []byte("`"), []byte("`+\"`\"+`"), -1))
  491. }
  492. }
  493. }
  494. func (t *textNode) tree() *tree {
  495. return t.tr
  496. }
  497. func (t *textNode) Copy() node {
  498. return &textNode{tr: t.tr, nodeType: nodeText, pos: t.pos, textType: t.textType, Text: append([]byte{}, t.Text...)}
  499. }
  500. //
  501. //
  502. type mixinNode struct {
  503. nodeType
  504. pos
  505. tr *tree
  506. Nodes []node
  507. AttrName []string
  508. AttrCode []string
  509. AttrRest []string
  510. MixinName string
  511. block []node
  512. tagType itemType
  513. }
  514. func (t *tree) newMixin(pos pos) *mixinNode {
  515. return &mixinNode{tr: t, nodeType: nodeMixin, pos: pos}
  516. }
  517. func (l *mixinNode) append(n node) {
  518. l.Nodes = append(l.Nodes, n)
  519. }
  520. func (l *mixinNode) appendToBlock(n node) {
  521. l.block = append(l.block, n)
  522. }
  523. func (l *mixinNode) attr(a, b string, c bool) {
  524. l.AttrName = append(l.AttrName, a)
  525. l.AttrCode = append(l.AttrCode, b)
  526. }
  527. func (l *mixinNode) tree() *tree {
  528. return l.tr
  529. }
  530. func (l *mixinNode) String() string {
  531. var b = new(bytes.Buffer)
  532. l.WriteIn(b)
  533. return b.String()
  534. }
  535. func (l *mixinNode) WriteIn(b io.Writer) {
  536. var (
  537. attr = new(bytes.Buffer)
  538. an = len(l.AttrName)
  539. rest = len(l.AttrRest)
  540. )
  541. if an > 0 {
  542. fmt.Fprintf(attr, mixin__var_bgn)
  543. if rest > 0 {
  544. // TODO
  545. // fmt.Println("-------- ", mixin__var_rest, l.AttrName[an-1], l.AttrRest)
  546. fmt.Fprintf(attr, mixin__var_rest, strings.TrimLeft(l.AttrName[an-1], "."), l.AttrRest)
  547. l.AttrName = l.AttrName[:an-1]
  548. }
  549. for k, name := range l.AttrName {
  550. fmt.Fprintf(attr, mixin__var, name, filterString(l.AttrCode[k]))
  551. }
  552. fmt.Fprintf(attr, mixin__var_end)
  553. }
  554. fmt.Fprintf(b, mixin__bgn, attr)
  555. if len(l.block) > 0 {
  556. b.Write([]byte(mixin__var_block_bgn))
  557. for _, n := range l.block {
  558. n.WriteIn(b)
  559. }
  560. b.Write([]byte(mixin__var_block_end))
  561. } else {
  562. b.Write([]byte(mixin__var_block))
  563. }
  564. for _, n := range l.Nodes {
  565. n.WriteIn(b)
  566. }
  567. fmt.Fprintf(b, mixin__end)
  568. }
  569. func (l *mixinNode) CopyMixin() *mixinNode {
  570. if l == nil {
  571. return l
  572. }
  573. n := l.tr.newMixin(l.pos)
  574. for _, elem := range l.Nodes {
  575. n.append(elem.Copy())
  576. }
  577. return n
  578. }
  579. func (l *mixinNode) Copy() node {
  580. return l.CopyMixin()
  581. }
  582. //
  583. //
  584. type doctypeNode struct {
  585. nodeType
  586. pos
  587. tr *tree
  588. doctype string
  589. }
  590. func (t *tree) newDoctype(pos pos, text string) *doctypeNode {
  591. doc := ""
  592. txt := strings.Trim(text, " ")
  593. if len(txt) > 0 {
  594. sls := strings.SplitN(txt, " ", 2)
  595. switch sls[0] {
  596. case "5", "html":
  597. doc = `<!DOCTYPE html%s>`
  598. case "xml":
  599. doc = `<?xml version="1.0" encoding="utf-8"%s ?>`
  600. case "1.1", "xhtml":
  601. doc = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"%s>`
  602. case "basic":
  603. doc = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd"%s>`
  604. case "strict":
  605. doc = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"%s>`
  606. case "frameset":
  607. doc = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"%s>`
  608. case "transitional":
  609. doc = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"%s>`
  610. case "mobile":
  611. doc = `<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd"%s>`
  612. case "4", "4strict":
  613. doc = `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"%s>`
  614. case "4frameset":
  615. doc = `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"%s>`
  616. case "4transitional":
  617. doc = `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"%s>`
  618. }
  619. if doc == "" {
  620. doc = fmt.Sprintf("<!DOCTYPE %s>", txt)
  621. } else if doc != "" && len(sls) == 2 {
  622. doc = fmt.Sprintf(doc, " "+sls[1])
  623. } else {
  624. doc = fmt.Sprintf(doc, "")
  625. }
  626. } else {
  627. doc = `<!DOCTYPE html>`
  628. }
  629. return &doctypeNode{tr: t, nodeType: nodeDoctype, pos: pos, doctype: doc}
  630. }
  631. func (d *doctypeNode) String() string {
  632. return fmt.Sprintf(text__str, d.doctype)
  633. }
  634. func (d *doctypeNode) WriteIn(b io.Writer) {
  635. fmt.Fprintf(b, text__str, d.doctype)
  636. // b.Write([]byte(d.doctype))
  637. }
  638. func (d *doctypeNode) tree() *tree {
  639. return d.tr
  640. }
  641. func (d *doctypeNode) Copy() node {
  642. return &doctypeNode{tr: d.tr, nodeType: nodeDoctype, pos: d.pos, doctype: d.doctype}
  643. }