diff options
author | bptato <nincsnevem662@gmail.com> | 2023-12-01 21:25:09 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-12-01 21:29:29 +0100 |
commit | b7482b23c98ecd9bf60309fe9c06b5539595d492 (patch) | |
tree | 640aeaf8a3a39a9a6fded2a466592134fd839c97 /src | |
parent | 90c74ddcab6713f9bb2dfea53a30951cc2e06f88 (diff) | |
download | chawan-b7482b23c98ecd9bf60309fe9c06b5539595d492.tar.gz |
html: add HTMLElement.dataset (+ some twtstr cleanup)
Diffstat (limited to 'src')
-rw-r--r-- | src/html/dom.nim | 78 | ||||
-rw-r--r-- | src/js/javascript.nim | 20 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 30 |
3 files changed, 96 insertions, 32 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim index 941d3ce7..bd8d735a 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -145,6 +145,9 @@ type element: Element localName: string + DOMStringMap = object + target {.cursor.}: HTMLElement + Node* = ref object of EventTarget nodeType*: NodeType childList*: seq[Node] @@ -242,6 +245,7 @@ type element: Element HTMLElement* = ref object of Element + dataset {.jsget.}: DOMStringMap FormAssociatedElement* = ref object of HTMLElement parserInserted*: bool @@ -417,6 +421,7 @@ jsDestructor(Location) jsDestructor(Document) jsDestructor(DOMImplementation) jsDestructor(DOMTokenList) +jsDestructor(DOMStringMap) jsDestructor(Comment) jsDestructor(CDATASection) jsDestructor(DocumentFragment) @@ -1152,6 +1157,56 @@ func value(tokenList: DOMTokenList): string {.jsfget.} = func getter(tokenList: DOMTokenList, i: int): Option[string] {.jsgetprop.} = return tokenList.item(i) +# DOMStringMap +func validateAttributeName(name: string, isq: static bool = false): + Err[DOMException] = + when isq: + if name.matchNameProduction(): + return ok() + else: + if name.matchQNameProduction(): + return ok() + return errDOMException("Invalid character in attribute name", + "InvalidCharacterError") + +func hasprop(map: ptr DOMStringMap, name: string): bool {.jshasprop.} = + return "data-" & name in map[].target.attrs + +proc delete(map: ptr DOMStringMap, name: string): bool {.jsfunc.} = + let name = "data-" & name.camelToKebabCase() + let res = name in map[].target.attrs + map[].target.attrs.del(name) + return res + +func getter(map: ptr DOMStringMap, name: string): Option[string] + {.jsgetprop.} = + let name = "data-" & name.camelToKebabCase() + map[].target.attrs.withValue(name, p): + return some(p[]) + return none(string) + +proc setter(map: ptr DOMStringMap, name, value: string): Err[DOMException] + {.jssetprop.} = + var washy = false + for c in name: + if not washy or c notin AsciiLowerAlpha: + washy = c == '-' + continue + return errDOMException("Lower case after hyphen is not allowed in dataset", + "InvalidCharacterError") + let name = "data-" & name.camelToKebabCase() + ?name.validateAttributeName() + map.target.attr(name, value) + return ok() + +func names(ctx: JSContext, map: ptr DOMStringMap): JSPropertyEnumList + {.jspropnames.} = + var list = newJSPropertyEnumList(ctx, uint32(map[].target.attrs.len)) + for k, v in map[].target.attrs: + if k.startsWith("data-") and AsciiUpperAlpha notin k: + list.add(k["data-".len .. ^1].kebabToCamelCase()) + return list + # NodeList func length(nodeList: NodeList): uint32 {.jsfget.} = return uint32(nodeList.len) @@ -1712,15 +1767,17 @@ func getElementsByClassName0(node: Node, classNames: string): HTMLCollection = var classes = classNames.split(AsciiWhitespace) let isquirks = node.document.mode == QUIRKS if isquirks: - for i in 0 .. classes.high: - classes[i].mtoLowerAscii() + for class in classes.mitems: + for c in class.mitems: + c = c.toLowerAscii() return newCollection[HTMLCollection](node, func(node: Node): bool = if node.nodeType == ELEMENT_NODE: if isquirks: var cl = Element(node).classList - for i in 0 .. cl.toks.high: - cl.toks[i].mtoLowerAscii() + for tok in cl.toks.mitems: + for c in tok.mitems: + c = c.toLowerAscii() for class in classes: if class notin cl: return false @@ -2157,6 +2214,7 @@ func newHTMLElement*(document: Document, tagType: TagType, result.attributes = NamedNodeMap(element: result) result.classList = DOMTokenList(element: result, localName: "classList") result.index = -1 + result.dataset = DOMStringMap(target: result) {.cast(noSideEffect).}: for k, v in attrs: result.attr(k, v) @@ -2413,17 +2471,6 @@ proc attrulgz(element: Element, name: string, value: uint32) = if value > 0: element.attrul(name, value) -func validateAttributeName(name: string, isq: static bool = false): - Err[DOMException] = - when isq: - if name.matchNameProduction(): - return ok() - else: - if name.matchQNameProduction(): - return ok() - return errDOMException("Invalid character in attribute name", - "InvalidCharacterError") - proc setAttribute(element: Element, qualifiedName, value: string): Err[DOMException] {.jsfunc.} = ?validateAttributeName(qualifiedName) @@ -3483,6 +3530,7 @@ proc addDOMModule*(ctx: JSContext) = ctx.registerType(Document, parent = nodeCID) ctx.registerType(DOMImplementation) ctx.registerType(DOMTokenList) + ctx.registerType(DOMStringMap) let characterDataCID = ctx.registerType(CharacterData, parent = nodeCID) ctx.registerType(Comment, parent = characterDataCID) ctx.registerType(CDATASection, parent = characterDataCID) diff --git a/src/js/javascript.nim b/src/js/javascript.nim index d3de6508..124eab5d 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -873,7 +873,7 @@ proc setupGenerator(fun: NimNode, t: BoundFunctionType, proc makeJSCallAndRet(gen: var JSFuncGenerator, okstmt, errstmt: NimNode) = let jfcl = gen.jsFunCallList let dl = gen.dielabel - gen.jsCallAndRet = if gen.returnType.issome: + gen.jsCallAndRet = if gen.returnType.isSome: quote do: block `dl`: return ctx.toJS(`jfcl`) @@ -962,11 +962,19 @@ macro jssetprop*(fun: typed) = gen.finishFunCallList() let jfcl = gen.jsFunCallList let dl = gen.dielabel - gen.jsCallAndRet = quote do: - block `dl`: - `jfcl` - return cint(1) - return cint(-1) + gen.jsCallAndRet = if gen.returnType.isSome: + quote do: + block `dl`: + let v = toJS(ctx, `jfcl`) + if not JS_IsException(v): + return cint(1) + return cint(-1) + else: + quote do: + block `dl`: + `jfcl` + return cint(1) + return cint(-1) let jsProc = gen.newJSProc(getJSSetPropParams(), false) gen.registerFunction() return newStmtList(fun, jsProc) diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim index 2756f2de..8372e588 100644 --- a/src/utils/twtstr.nim +++ b/src/utils/twtstr.nim @@ -67,17 +67,13 @@ const controlLetterMap = genControlLetterMap() func getControlLetter*(c: char): char = return controlLetterMap[int(c)] -proc mtoLowerAscii*(str: var string) = - for i in 0 ..< str.len: - str[i] = str[i].toLowerAscii() - func toHeaderCase*(str: string): string = result = str var flip = true - for i in 0..str.high: + for c in result.mitems: if flip: - result[i] = result[i].toUpperAscii() - flip = result[i] == '-' + c = c.toUpperAscii() + flip = c == '-' func toScreamingSnakeCase*(str: string): string = # input is camel case if str.len >= 1: result &= str[0].toUpperAscii() @@ -94,10 +90,22 @@ func snakeToKebabCase*(str: string): string = if c == '_': c = '-' -func normalizeLocale*(s: string): string = - for i in 0 ..< s.len: - if cast[uint8](s[i]) > 0x20 and s[i] != '_' and s[i] != '-': - result &= s[i].toLowerAscii() +func kebabToCamelCase*(s: string): string = + result = s + var flip = false + for c in result.mitems: + if flip: + c = c.toUpperAscii() + flip = c == '-' + +func camelToKebabCase*(s: string): string = + result = "" + for c in s: + if c in AsciiUpperAlpha: + result &= '-' + result &= c.toLowerAscii() + else: + result &= c func isAscii*(r: Rune): bool = return cast[uint32](r) < 128 |