import dom import htmlutils.tree import htmlutils.utils import json import streams import tables import strutils import sets from sequtils import map, apply var pkgMap : JsonNode const serverURL {.strdefine.}: string = "http://oggio88.soon.it/jpacrepo/" proc last[T](s : seq[T]) : T = s[s.len - 1] proc formatByteSize(size : BiggestInt) : string = size.float64.formatEng(precision=1, siPrefix=true, unit = "B") type DownloadPanel = ref object badge : Element listgroup : Element pkgs : HashSet[string] footer : Element sizeLabel : Element size : BiggestInt proc newDownloadPanel(parent : Element) : DownloadPanel = let dp = DownloadPanel() dp.pkgs = initSet[string]() htmlTreeAppend(parent): "div": classList = ["panel", "panel-default"] "div": classList = ["panel-heading"] "h3": classList = ["panel-title"] "a": text = "Package list " "span": classList = ["badge", "pull-right"] style {"display" :"none"} cb: dp.badge = elem "ul": classList = ["list-group"] cb: dp.listgroup = elem "div": classList = ["panel-footer"] style = { "text-align" : "right", "display" :"none" } "span": style = { "font-weight" : "bold", "padding-right" : "10px" } cb: dp.sizeLabel = elem "button": classList = ["btn", "btn-primary"] attrs = {"type" : "button"} "span": classList = ["glyphicon", "glyphicon-download"] cb: elem.appendChild(document.createTextNode(" Download")) let clickHandler = proc(e : Event) = let pkglist : seq[string] = sequtils.toSeq(dp.pkgs.items()) let form = cast[Formelement](document.createElement("form")) form.style.display = "none" form.setAttribute("method", "post") form.setAttribute("action", serverURL & "rest/pkg/downloadTar") let tf= document.createElement("input") tf.setAttribute("name", "pkgs") let txt = sequtils.foldl(pkglist, a & " " & b) echo txt tf.value(txt) form.appendChild(tf) document.body.appendChild(form) form.submit() elem.addEventListener("click", clickHandler) cb: dp.footer = elem dp proc updateBadge(dp : DownloadPanel) = let st = if dp.pkgs.len() > 0: "block" else: "none" dp.footer.style.display = st dp.badge.style.display = st dp.badge.textContent = $dp.pkgs.len() proc updateSize(dp : DownloadPanel) = dp.sizeLabel.textContent = "Total: " & dp.size.formatByteSize proc addPkg(dp : DownloadPanel, pkgfile : string) = if not dp.pkgs.contains(pkgfile): dp.pkgs.incl(pkgfile) let req = newXMLHTTPRequest() let load_cb = proc(e : Event) = let sz = parseInt(req.responseText) dp.size += sz dp.updateSize htmlTreeAppend(dp.listgroup): "li": var listElement : Element classList = ["list-group-item"] text = pkgfile "span": classList = ["glyphicon", "glyphicon-remove", "pull-right"] cb: let fn = proc(e : Event) = dp.pkgs.excl(pkgfile) listElement.remove() dp.updateBadge dp.size -= sz dp.updateSize elem.addEventListener("click", fn) cb: listElement = elem req.addEventListener("load", load_cb) req.open("get", serverURL & "rest/pkg/filesize/" & pkgfile) req.setRequestHeader("Accept", "application/json") req.send() dp.updateBadge proc readTableRow(row : Element) : JsonNode = let pkgname = $row.querySelector("td:nth-child(2)").textContent let version = $row.querySelector("td:nth-child(3) button").textContent let arch = $row.querySelector("td:nth-child(4) button").textContent pkgMap[pkgname][version][arch] proc createDropdown(parent : Element, data :seq[string], onchange : proc(value : string)) = htmlTreeAppend(parent): "div": var button : Element classList = ["dropdown"] "button": classList = ["btn", "btn-default", "dropdown-toggle"] attrs = {"data-toggle": "dropdown", "type": "button"} cb: elem.textContent = data.last button = elem "ul": classList = ["dropdown-menu"] cb: for line in data: htmlTreeAppend(elem): "li": "a": text = line cb: let fn = proc(e: Event) = button.textContent = elem.textContent onchange($elem.textContent) elem.addEventListener("click", fn) type PkgTable = ref object addButton : Element proc newPkgTable(parent: Element, searchString : string) : PkgTable = var pkgtable = PkgTable() var fragments = newSeq[string]() for fragment in searchString.splitWhitespace(): fragments.add(fragment) var searchResult = newOrderedTable[string,JsonNode]() for key, value in pkgMap: for fragment in fragments: if fragment in key: searchResult.add(key,value) for table in document.querySelectorAll("table.pkgtable"): table.parentNode.removeChild(table) htmlTreeAppend(parent): "table": classList = ["table", "table-striped","pkgtable"] "thead": "tr": "th": "button": classList = ["btn", "btn-default"] attrs = {"type": "button"} "span": classList = ["glyphicon", "glyphicon-plus"] cb: let txt = document.createTextNode(" Add") elem.appendChild(txt) pkgtable.addButton = elem "th": text = "Name" "th": text = "Version" "th": text = "Arch" "th": text = "Installed size" "tbody": cb: var i = 0 for name, versions in searchResult: closureScope: htmlTreeAppend(elem): "tr": var row : Element var archCell : Element var sizeCell : Element let size_change_callback = proc(newValue : string) = sizeCell.textContent = readTableRow(row)["size"].getNum.formatByteSize "td": "div": classList = ["checkbox"] "label": "input": attrs = {"type" : "checkbox"} "td": text = $name "td": cb: var data = newSeq[string]() for version, arches in versions: data.add(version) let vs = versions let change_callback = proc(newValue : string) = archCell.removeChildren() var newdata = newSeq[string]() for arch, pkgname in vs[newValue]: newdata.add(arch) createDropdown(archCell, newdata, size_change_callback) size_change_callback(newValue) createDropdown(elem, data, change_callback) "td": cb: archCell = elem var data = newSeq[string]() var arches : JsonNode for v, a in versions: arches = a for arch, pkgname in arches: data.add(arch) createDropdown(elem, data, size_change_callback) "td": cb: sizeCell = elem for v, arches in versions: for key, value in arches: elem.textContent = value["size"].getNum.formatByteSize return cb: row = elem pkgtable var dp : DownloadPanel # var pkgTable : PkgTable htmlTreeappend document.body: # "img": # attrs {"src" : "img/background.bpg"} # style { # "width" : "100%", # "position" : "fixed", # "bottom" : "0", # "left" : "0", # "z-index" : "-10" # } "div": var table : Element style = {"background-color" : "rgba(255, 255, 255, 0.25)"} classList = ["container"] "div": style = { "margin-top" : "20px", "background-color" : "rgba(224, 224, 224, 0.5)" } classList = ["jumbotron"] "h1": text = "Jpacrepo" "p": text = "Personal archlinux package repository" "div": "form": classList = ["form-horizontal"] "div": classList = ["form-group"] "label": classList = ["control-label", "col-sm-2"] text = " Search package" "div": classList = ["col-sm-10"] "input": classList = ["form-control"] attrs = {"type" : "text"} cb : proc add2DownloadList(e : Event) = let rows = table.querySelectorAll("tbody tr") for row in rows: let cbox = row.querySelector("td:first-child input") if cbox.checked: dp.addPkg(readTableRow(row)["filename"].getStr) proc oninput(e : Event) = if pkgMap.len == 0 or elem.value.len < 2: return let pkgtable = newPkgTable(table, $elem.value) pkgtable.addButton.addEventListener("click", add2DownloadList) elem.addEventListener("input", oninput) "div": classList = ["row"] "div": classList = ["col-sm-3"] cb: dp = newDownloadPanel(elem) "div": classList = ["col-sm-9"] cb: table = elem let r = newXMLHTTPRequest() let load_cb = proc(e : Event) = pkgMap = parseJson($r.responseText) r.addEventListener("load", load_cb) r.open("get", serverURL & "rest/pkg/map") r.setRequestHeader("Accept", "application/json") r.send()