From e8f4351c957ddbcab6322542cb610fdf6624f4ec Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Mon, 18 May 2026 08:28:08 -0600 Subject: [PATCH 1/3] record `language-families`, `build-platforms`, and `umbrella` These are new fields in "info.rkt" at the package level. --- src/pkg-index/update.rkt | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/pkg-index/update.rkt b/src/pkg-index/update.rkt index 2cd00d4..b8b3b6b 100644 --- a/src/pkg-index/update.rkt +++ b/src/pkg-index/update.rkt @@ -159,7 +159,8 @@ (define (update-from-content i) (log! "\tgetting package content for ~v" (hash-ref i 'name)) (match-define-values - (checksum module-paths (list deps rt-deps license implies collection)) + (checksum module-paths (list deps rt-deps license implies collection + umbrella language-families build-platforms)) (pkg:get-pkg-content (pkg:pkg-desc (hash-ref i 'source) #f @@ -173,9 +174,14 @@ (pkg:extract-pkg-dependencies get-info #:build-deps? #f) (get-info 'license (λ () missing)) (get-info 'implies (λ () empty)) - (get-info 'collection (λ () #f))) + (get-info 'collection (λ () #f)) + (get-info 'umbrella (λ () #f)) + (get-info 'language-families (λ () #f)) + (get-info 'build-platforms (λ () #f))) (list empty empty missing empty #f))))) + (define (guard v pred alt-v) (if (pred v) v alt-v)) + (package-begin (define* i (hash-set i 'modules module-paths)) (define* i (hash-set i 'dependencies deps)) @@ -184,9 +190,25 @@ (cond [(eq? license missing) #f] [else (format "~s" license)]))) - (define* i (hash-set i 'implies implies)) + (define* i (hash-set i 'implies (guard implies + (lambda (v) (or (string? v) (eq? v 'core))) + empty))) ;; avoid conflation of symbols and strings in JSON (define* i (hash-set i 'collection (if (eq? collection 'multi) (list 'multi) collection))) + (define* i (hash-set i 'umbrella (guard umbrella + string? + #f))) + (define* i (hash-set i 'language-families (guard language-families + (lambda (l) (and (list? l) + (andmap string? l))) + '("Racket")))) + (define* i (hash-set i 'build-platforms (guard build-platforms + (lambda (l) + (or (not l) + (and (list? l) + (andmap (lambda (i) (or (string? i) (symbol? i))) + l)))) + #f))) i)) (define (do-update! pkgs) From d4289fcaf2d5aa67de88b113a2d11a74dff8d98a Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Mon, 18 May 2026 08:28:41 -0600 Subject: [PATCH 2/3] organize package list by umbrellas A package "P" is under umbrella package "Q" if: * "P" is "Q-lib", "Q-doc", "Q-test", or "Q-exe" * "P" declares "Q" as its `umbrella` via "info.rkt" Note that it would also make sense for "Q" to be an umbrella for "P" if "Q" `implies` "P" via "info.rkt". However, that would let "Q" adopt` "P" without necessarily having the author of "P" agree, so that's why `implies` does not create umbrellas. On the main package listing, only an umbrella package is shown by default. A turn-down arrow in the umbrella package has the contained packages listed next to it and exploses the details for all packages under the umbrella. Umbrella organization is based on the packages actually listed, which might be limited in a search result. However, "this package needs documentation" is now also sensitive to umbrellas (a package is considered documented if anything in its umbrella is documented), and that's independent of the listed packages. Umbrellas are not meant to be nested, and the display mechanism doesn't surrently support nesting beyond a single layer. --- src/bootstrap.rkt | 19 ++++++- src/site.rkt | 120 ++++++++++++++++++++++++++++++++++++----- static/package-list.js | 29 ++++++++++ static/site.js | 1 + static/style.css | 30 +++++++++++ 5 files changed, 186 insertions(+), 13 deletions(-) diff --git a/src/bootstrap.rkt b/src/bootstrap.rkt index 68dc8e4..068be45 100644 --- a/src/bootstrap.rkt +++ b/src/bootstrap.rkt @@ -24,6 +24,7 @@ (require racket/match) (require racket/string) +(require racket/runtime-path) (require web-server/servlet) (require "html-utils.rkt") (require "xexpr-utils.rkt") @@ -42,8 +43,24 @@ (define bootstrap-inline-js (make-parameter #f)) (define bootstrap-head-extra (make-parameter '())) +(define-runtime-path static-content-dir "../static") + +;; Append a cache-busting query string derived from the asset's +;; modification time, so browsers (Firefox in particular, which +;; caches more aggressively when no explicit Cache-Control is sent) +;; refetch when the file changes instead of serving a stale copy. +(define (cache-busting-suffix str) + (define rel (regexp-replace #rx"^/" str "")) + (define path + (and (not (string=? rel "")) + (with-handlers ([exn:fail? (lambda (_) #f)]) + (apply build-path static-content-dir (regexp-split #rx"/" rel))))) + (if (and path (file-exists? path)) + (format "?v=~a" (file-or-directory-modify-seconds path)) + "")) + (define (static str) - (string-append (bootstrap-static-urlprefix) str)) + (string-append (bootstrap-static-urlprefix) str (cache-busting-suffix str))) (define (dynamic str) (string-append (bootstrap-dynamic-urlprefix) str)) diff --git a/src/site.rkt b/src/site.rkt index 4c34628..85d7c01 100644 --- a/src/site.rkt +++ b/src/site.rkt @@ -647,11 +647,64 @@ (string-append (date->string (seconds->date utc #f) #t) " (UTC)") "N/A")) -(define (get-implied-docs pkg) - (define implied-names (map string->symbol - (dependencies->package-names - (package-implies pkg)))) - (append-map package-docs (package-batch-detail implied-names))) +(define (get-all-pkg-docs pkg inferred-umbrellas) + (define-values (docs seen) + (let loop ([pkgs (list pkg)] [docs null] [seen #hash()]) + (cond + [(null? pkgs) (values (set->list (list->set docs)) + seen)] + [(hash-ref seen (package-name (car pkgs)) #f) + (loop (cdr pkgs) docs seen)] + [else + (define pkg (car pkgs)) + (let ([docs (append (package-docs pkg) docs)] + [seen (hash-set seen (package-name pkg) #t)]) + (define implied-names (dependencies->package-names + (package-implies pkg))) + (define umbrella-name (or (package-umbrella pkg) + (hash-ref inferred-umbrellas (package-name pkg) #f))) + (define next-names (map string->symbol + (append (if umbrella-name + (list umbrella-name) + null) + implied-names))) + (cond + [(null? next-names) (loop (cdr pkgs) docs seen)] + [else + (loop (append (package-batch-detail next-names) + (cdr pkgs)) + docs + seen)]))]))) + docs) + +(define (infer-umbrellas) + (define names (for/hash ([name-sym (in-list (all-package-names))]) + (values (symbol->string name-sym) #t))) + (for/fold ([inferred-umbrellas #hash()]) + ([name (in-hash-keys names)]) + (define m (regexp-match #rx"^(.*)-(?:lib|exe|doc|test)$" name)) + (define potential-umbrella-name (and m (cadr m))) + (if (and potential-umbrella-name + (hash-ref names potential-umbrella-name #f)) + (hash-set inferred-umbrellas name potential-umbrella-name) + inferred-umbrellas))) + +;; determines umbrella relationshops, but only among `pkgs`, +;; while `inferrd-umbrellas` may have information on additional packages +(define (umbrellas-and-members pkgs inferred-umbrellas) + (define names (for/hash ([pkg (in-list pkgs)]) (values (package-name pkg) #t))) + (define umbrellas + (for/hash ([pkg (in-list pkgs)] + #:do [(define umbrella-name + (or (package-umbrella pkg) + (hash-ref inferred-umbrellas (package-name pkg) #f)))] + #:when (hash-ref names umbrella-name #f)) + (values (package-name pkg) umbrella-name))) + (define umbrella-members + (for/fold ([umbrella-members #hash()]) ([(sub super) (in-hash umbrellas)]) + (hash-set umbrella-members super (cons sub (hash-ref umbrella-members super null))))) + (values umbrellas + umbrella-members)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Package hashtable getters. @@ -693,6 +746,9 @@ (define (package-dependencies pkg) (or (@ pkg dependencies) '())) (define (package-implies pkg) (or (@ pkg implies) '())) (define (package-modules pkg) (or (@ pkg modules) '())) +(define (package-language-families pkg) (or (@ pkg language-families) '("Racket"))) +(define (package-build-platforms pkg) (or (@ pkg build-platforms) #f)) +(define (package-umbrella pkg) (or (@ pkg umbrella) #f)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -746,10 +802,26 @@ ;; representing packages with outstanding build errors or ;; failing tests, or which are missing docs, license metadata, or tags. (define now (/ (current-inexact-milliseconds) 1000)) + (define inferred-umbrellas (infer-umbrellas)) + (define pkgs (package-batch-detail package-names)) + (define-values (umbrellas umbrella-members) (umbrellas-and-members pkgs inferred-umbrellas)) (define-values (pkg-rows num-todos) (for/fold ([pkg-rows null] [num-todos 0]) - ([pkg (package-batch-detail package-names)]) - (define pkg-docs (append (package-docs pkg) (get-implied-docs pkg))) + ([pkg (sort pkgs + (lambda (a b) + (define a-name (package-name a)) + (define b-name (package-name b)) + (define a-parent (or (hash-ref umbrellas a-name #f) a-name)) + (define b-parent (or (hash-ref umbrellas b-name #f) b-name)) + (cond + [(equal? a-parent b-parent) + (cond + [(equal? a-name a-parent) #true] + [(equal? b-name a-parent) #false] + [else (string-cistring todokey))) - (td (span ((class "last-updated-negated") (style "display: none")) + ((data-todokey ,(number->string todokey)) + ,@(if umbrella-name + `([class "umbrella-content in-closed-umbrella"] + [data-umbrella ,umbrella-name]) + null)) + (td ([class "package-left"]) + (span ((class "last-updated-negated") (style "display: none")) ,(~a (- (package-last-updated pkg)))) ,@(maybe-splice (and (not (package-checksum-error pkg)) @@ -784,13 +863,23 @@ "label-danger") "Todo"))) ,@(maybe-splice bulk-operations-enabled? - `(td (p "Ring " ,(~a (package-ring pkg))) + `(td ([class "package-left"]) + (p "Ring " ,(~a (package-ring pkg))) ,(checkbox-input "selected-packages" (package-name pkg) #:id #f #:extra-classes `("selected-packages")))) - (td (h2 ,(package-link (package-name pkg))) - ,(authors-list (package-authors pkg))) + (td ([class "package-left package-desc"]) + (h2 ,(package-link (package-name pkg))) + ,(authors-list (package-authors pkg)) + ,@(if (null? umbrella-member-names) + null + `((div + (div ([class "umbrella-arrow umbrella-closed"] + [data-umbrella ,(package-name pkg)]) + "") + ,@(for/list ([sub (in-list umbrella-member-names)]) + `(span () ,(package-link sub) " ")))))) (td (p ,(if (string=? "" (package-description pkg)) `(span ((class "label label-warning")) "This package needs a description") (package-description pkg))) @@ -1114,6 +1203,8 @@ (td ,(tag-links (package-tags pkg)))) (tr (th "License") (td ,(license-links pkg-license))) + (tr (th "Language families") + (td ,(string-join (package-language-families pkg) ", "))) (tr (th "Last updated") (td ,(utc->string (package-last-updated pkg)))) (tr (th "Ring") @@ -1129,6 +1220,11 @@ (td ,(package-links (dependencies->package-names (package-dependencies pkg))))) + (tr (th "Build platforms") + (td ,(let ([platforms (package-build-platforms pkg)]) + (if platforms + (string-join (map ~a platforms) ", ") + '(i "any platform"))))) (tr (th "Most recent build results") (td (ul ((class "build-results")) ,@(maybe-splice diff --git a/static/package-list.js b/static/package-list.js index 4adfcd4..87f614c 100644 --- a/static/package-list.js +++ b/static/package-list.js @@ -57,4 +57,33 @@ $(function() { $("#todo-msg").hide(); } + document.querySelectorAll('.umbrella-arrow').forEach(arrow => { + arrow.addEventListener('click', () => { + const isOpening = arrow.classList.contains("umbrella-closed"); + + if (isOpening) { + arrow.classList.remove('umbrella-closed'); + arrow.classList.add('umbrella-open'); + document.querySelectorAll('.umbrella-content').forEach( + function (c) { + if (c.dataset.umbrella == arrow.dataset.umbrella) { + c.classList.remove('in-closed-umbrella') + c.classList.add('in-open-umbrella') + } + } + ); + } else { + arrow.classList.remove('umbrella-open'); + arrow.classList.add('umbrella-closed'); + document.querySelectorAll('.umbrella-content').forEach( + function (c) { + if (c.dataset.umbrella == arrow.dataset.umbrella) { + c.classList.remove('in-open-umbrella') + c.classList.add('in-closed-umbrella') + } + } + ); + } + }); + }); }); /* document.ready */ diff --git a/static/site.js b/static/site.js index 3bf8a89..49490b4 100644 --- a/static/site.js +++ b/static/site.js @@ -56,4 +56,5 @@ $(document).ready(function () { completions); }); } + }); diff --git a/static/style.css b/static/style.css index 15ae688..aebeed8 100644 --- a/static/style.css +++ b/static/style.css @@ -166,3 +166,33 @@ th.headerSortDown::after { content: " ▲"; } display: inline-block; padding: 0 0.15em; } + +.umbrella-arrow { + display: inline-block; + position: relative; + left: -2em; + width: 0; +} + +.umbrella-closed::after { + content: "▶"; +} + +.umbrella-open::after { + content: "▼"; +} + +.umbrella-content { +} + +.umbrella-content .package-left { + transform: translateX(1em); + border-top: 0; +} + +.in-closed-umbrella { + display: none; +} + +.in-open-umbrella { +} From eec57881e976353226c537c432c00f0525225867 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 19 May 2026 07:40:25 -0600 Subject: [PATCH 3/3] add language-family column and sorting support The top of the package-list page now shows the language families found for packages. Selecting a language family changes the default sort to have matching packages first, and non-matching packages get a gray background. A `langfam=` query parameter can initialize the family choice. This commit also repairs umbrella groups when sorting by table columns. That involves using a newer fork of tablesorter that provides direct support for child rows. --- src/bootstrap.rkt | 2 +- src/site.rkt | 86 +++++++++++++----- static/jquery.tablesorter.combined.min.js | 3 + static/package-list.js | 101 +++++++++++++++++++++- static/site.js | 7 +- static/style.css | 12 ++- 6 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 static/jquery.tablesorter.combined.min.js diff --git a/src/bootstrap.rkt b/src/bootstrap.rkt index 068be45..dd47611 100644 --- a/src/bootstrap.rkt +++ b/src/bootstrap.rkt @@ -119,7 +119,7 @@ (script ,@(cond [(bootstrap-inline-js) => list] [else '()])) (script ((type "text/javascript") (src ,(static "/jquery.min.js")))) - (script ((type "text/javascript") (src ,(static "/jquery.tablesorter.min.js")))) + (script ((type "text/javascript") (src ,(static "/jquery.tablesorter.combined.min.js")))) (script ((type "text/javascript") (src ,(static "/jquery-ui.min.js")))) (script ((type "text/javascript") (src ,(static "/bootstrap/js/bootstrap.min.js")))) (script ((type "text/javascript") (src ,(static "/site.js")))) diff --git a/src/site.rkt b/src/site.rkt index 85d7c01..9ddf6cd 100644 --- a/src/site.rkt +++ b/src/site.rkt @@ -761,7 +761,9 @@ (action ,(named-url bulk-operation-page)) (method "post")) (table - ((class "packages sortable") (data-todokey ,(number->string num-todos))) + ([class "packages sortable"] + [id "package-table"] + [data-todokey ,(number->string num-todos)]) (thead ,@(maybe-splice bulk-operations-enabled? @@ -787,6 +789,7 @@ ,@(maybe-splice bulk-operations-enabled? `(th 'nbsp)) (th "Package") (th "Description") + (th "Family") (th "Build") (th ((style "display: none")) 'nbsp))) ;; todokey (tbody @@ -805,28 +808,34 @@ (define inferred-umbrellas (infer-umbrellas)) (define pkgs (package-batch-detail package-names)) (define-values (umbrellas umbrella-members) (umbrellas-and-members pkgs inferred-umbrellas)) + (define sorted-pkgs (sort pkgs + (lambda (a b) + (define a-name (package-name a)) + (define b-name (package-name b)) + (define a-parent (or (hash-ref umbrellas a-name #f) a-name)) + (define b-parent (or (hash-ref umbrellas b-name #f) b-name)) + (cond + [(equal? a-parent b-parent) + (cond + [(equal? a-name a-parent) #true] + [(equal? b-name a-parent) #false] + [else (string-cistring todokey)) + ([data-todokey ,(number->string todokey)] + [data-families ,(string-join pkg-sort-families ",")] + [data-sortpos ,(number->string pkg-pos)] ,@(if umbrella-name - `([class "umbrella-content in-closed-umbrella"] + `([class "umbrella-content in-closed-umbrella tablesorter-childRow"] [data-umbrella ,umbrella-name]) null)) (td ([class "package-left"]) @@ -902,8 +917,14 @@ "This package needs license metadata" #:extra-attributes missing-license-tooltip-attributes) `(div - (span ((class "doctags-label")) "License: ") + (span ((class "license-label")) "License: ") ,(license-links pkg-license)))) + (td ,@(apply + append + (for/list ([family (in-list pkg-families)] + [i (in-naturals)]) + (list (if (zero? i) "" ", ") + (family-link family))))) ,(build-status-td pkg) (td ((style "display: none")) ,(number->string todokey)))) (values (cons row-xexp pkg-rows) @@ -945,6 +966,30 @@ (match-define (list u p l) e) (if u `(span ,p ,(buildhost-link u l)) `(span))))) +(define (build-package-language-family-list package-names) + (define pkgs (package-batch-detail package-names)) + (define families + (sort (set->list + (for*/set ([pkg (in-list pkgs)] + [family (in-list (package-language-families pkg))]) + family)) + (lambda (a b) + (if (equal? a "Racket") + #t + (string-ci thead th, > thead td",selectorSort:"th, td",selectorRemove:".remove-me",debug:!1,headerList:[],empties:{},strings:{},parsers:[],globalize:0,imgAttr:0},css:{table:"tablesorter",cssHasChild:"tablesorter-hasChildRow",childRow:"tablesorter-childRow",colgroup:"tablesorter-colgroup",header:"tablesorter-header",headerRow:"tablesorter-headerRow",headerIn:"tablesorter-header-inner",icon:"tablesorter-icon",processing:"tablesorter-processing",sortAsc:"tablesorter-headerAsc",sortDesc:"tablesorter-headerDesc",sortNone:"tablesorter-headerUnSorted"},language:{sortAsc:"Ascending sort applied, ",sortDesc:"Descending sort applied, ",sortNone:"No sort applied, ",sortDisabled:"sorting is disabled",nextAsc:"activate to apply an ascending sort",nextDesc:"activate to apply a descending sort",nextNone:"activate to remove the sort"},regex:{templateContent:/\{content\}/g,templateIcon:/\{icon\}/g,templateName:/\{name\}/i,spaces:/\s+/g,nonWord:/\W/g,formElements:/(input|select|button|textarea)/i,chunk:/(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,chunks:/(^\\0|\\0$)/,hex:/^0x[0-9a-f]+$/i,comma:/,/g,digitNonUS:/[\s|\.]/g,digitNegativeTest:/^\s*\([.\d]+\)/,digitNegativeReplace:/^\s*\(([.\d]+)\)/,digitTest:/^[\-+(]?\d+[)]?$/,digitReplace:/[,.'"\s]/g},string:{max:1,min:-1,emptymin:1,emptymax:-1,zero:0,none:0,null:0,top:!0,bottom:!1},keyCodes:{enter:13},dates:{},instanceMethods:{},setup:function(t,r){var e,a,s,i;t&&t.tHead&&0!==t.tBodies.length&&!0!==t.hasInitialized?(e="",a=R(t),s=R.metadata,t.hasInitialized=!1,t.isProcessing=!0,t.config=r,R.data(t,"tablesorter",r),T.debug(r,"core")&&(console[console.group?"group":"log"]("Initializing tablesorter v"+T.version),R.data(t,"startoveralltimer",new Date)),r.supportsDataObject=((i=R.fn.jquery.split("."))[0]=parseInt(i[0],10),1':"",n.$headers=R(R.map(n.$table.find(n.selectorHeaders),function(e,t){var r,a,s,i,o=R(e);if(!T.getClosest(o,"tr").hasClass(n.cssIgnoreRow))return/(th|td)/i.test(e.nodeName)||(i=T.getClosest(o,"th, td"),o.attr("data-column",i.attr("data-column"))),r=T.getColumnData(n.table,n.headers,t,!0),n.headerContent[t]=o.html(),""===n.headerTemplate||o.find("."+T.css.headerIn).length||(s=n.headerTemplate.replace(T.regex.templateContent,o.html()).replace(T.regex.templateIcon,o.find("."+T.css.icon).length?"":l),n.onRenderTemplate&&(a=n.onRenderTemplate.apply(o,[t,s]))&&"string"==typeof a&&(s=a),o.html('
'+s+"
")),n.onRenderHeader&&n.onRenderHeader.apply(o,[t,n,n.$table]),a=parseInt(o.attr("data-column"),10),e.column=a,i=T.getOrder(T.getData(o,r,"sortInitialOrder")||n.sortInitialOrder),n.sortVars[a]={count:-1,order:i?n.sortReset?[1,0,2]:[1,0]:n.sortReset?[0,1,2]:[0,1],lockedOrder:!1,sortedBy:""},void 0!==(i=T.getData(o,r,"lockedOrder")||!1)&&!1!==i&&(n.sortVars[a].lockedOrder=!0,n.sortVars[a].order=T.getOrder(i)?[1,1]:[0,0]),n.headerList[t]=e,o.addClass(T.css.header+" "+n.cssHeader),T.getClosest(o,"tr").addClass(T.css.headerRow+" "+n.cssHeaderRow).attr("role","row"),n.tabIndex&&o.attr("tabindex",0),e})),n.$headerIndexed=[],r=0;r'),t=e.$table.width(),s=(a=e.$tbodies.find("tr:first").children(":visible")).length,i=0;i").css("width",r));e.$table.prepend(o)}},getData:function(e,t,r){var a,s,i="",e=R(e);return e.length?(a=!!R.metadata&&e.metadata(),s=" "+(e.attr("class")||""),void 0!==e.data(r)||void 0!==e.data(r.toLowerCase())?i+=e.data(r)||e.data(r.toLowerCase()):a&&void 0!==a[r]?i+=a[r]:t&&void 0!==t[r]?i+=t[r]:" "!==s&&s.match(" "+r+"-")&&(i=s.match(new RegExp("\\s"+r+"-([\\w-]+)"))[1]||""),R.trim(i)):""},getColumnData:function(e,t,r,a,s){if("object"!=typeof t||null===t)return t;var i,e=(e=R(e)[0]).config,s=s||e.$headers,o=e.$headerIndexed&&e.$headerIndexed[r]||s.find('[data-column="'+r+'"]:last');if(void 0!==t[r])return a?t[r]:t[s.index(o)];for(i in t)if("string"==typeof i&&o.filter(i).add(o.find(i)).length)return t[i]},isProcessing:function(e,t,r){var a=(e=R(e))[0].config,s=r||e.find("."+T.css.header);t?(void 0!==r&&0'),R.fn.detach?t.detach():t.remove();r=R(e).find("colgroup.tablesorter-savemyplace");t.insertAfter(r),r.remove(),e.isProcessing=!1},clearTableBody:function(e){R(e)[0].config.$tbodies.children().detach()},characterEquivalents:{a:"áàâãäąå",A:"ÁÀÂÃÄĄÅ",c:"çćč",C:"ÇĆČ",e:"éèêëěę",E:"ÉÈÊËĚĘ",i:"íìİîïı",I:"ÍÌİÎÏ",o:"óòôõöō",O:"ÓÒÔÕÖŌ",ss:"ß",SS:"ẞ",u:"úùûüů",U:"ÚÙÛÜŮ"},replaceAccents:function(e){var t,r="[",a=T.characterEquivalents;if(!T.characterRegex){for(t in T.characterRegexArray={},a)"string"==typeof t&&(r+=a[t],T.characterRegexArray[t]=new RegExp("["+a[t]+"]","g"));T.characterRegex=new RegExp(r+"]")}if(T.characterRegex.test(e))for(t in a)"string"==typeof t&&(e=e.replace(T.characterRegexArray[t],t));return e},validateOptions:function(e){var t,r,a,s,i="headers sortForce sortList sortAppend widgets".split(" "),o=e.originalSettings;if(o){for(t in T.debug(e,"core")&&(s=new Date),o)if("undefined"===(a=typeof T.defaults[t]))console.warn('Tablesorter Warning! "table.config.'+t+'" option not recognized');else if("object"===a)for(r in o[t])a=T.defaults[t]&&typeof T.defaults[t][r],R.inArray(t,i)<0&&"undefined"===a&&console.warn('Tablesorter Warning! "table.config.'+t+"."+r+'" option not recognized');T.debug(e,"core")&&console.log("validate options time:"+T.benchmark(s))}},restoreHeaders:function(e){for(var t,r=R(e)[0].config,a=r.$table.find(r.selectorHeaders),s=a.length,i=0;i tr").children("th, td"),!1===t&&0<=R.inArray("uitheme",s.widgets)&&(a.triggerHandler("applyWidgetId",["uitheme"]),a.triggerHandler("applyWidgetId",["zebra"])),i.find("tr").not(o).remove(),i="sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets removeWidget destroy mouseup mouseleave "+"keypress sortBegin sortEnd resetToLoadState ".split(" ").join(s.namespace+" "),a.removeData("tablesorter").unbind(i.replace(T.regex.spaces," ")),s.$headers.add(n).removeClass([T.css.header,s.cssHeader,s.cssAsc,s.cssDesc,T.css.sortAsc,T.css.sortDesc,T.css.sortNone].join(" ")).removeAttr("data-column").removeAttr("aria-label").attr("aria-disabled","true"),o.find(s.selectorSort).unbind("mousedown mouseup keypress ".split(" ").join(s.namespace+" ").replace(T.regex.spaces," ")),T.restoreHeaders(e),a.toggleClass(T.css.table+" "+s.tableClass+" tablesorter-"+s.theme,!1===t),a.removeClass(s.namespace.slice(1)),e.hasInitialized=!1,delete e.config.cache,"function"==typeof r&&r(e),T.debug(s,"core"))&&console.log("tablesorter has been removed")}};R.fn.tablesorter=function(t){return this.each(function(){var e=R.extend(!0,{},T.defaults,t,T.instanceMethods);e.originalSettings=t,!this.hasInitialized&&T.buildTable&&"TABLE"!==this.nodeName?T.buildTable(this,e):T.setup(this,e)})},window.console&&window.console.log||(T.logs=[],(console={}).log=console.warn=console.error=console.table=function(){var e=1> Using",s?c:"cookies"),u.parseJSON&&(i=s?u.parseJSON(g[c][t]||"null")||{}:(a=p.cookie.split(/[;\s|=]/),0!==(n=u.inArray(t,a)+1)&&u.parseJSON(a[n]||"null")||{})),void 0===r||!g.JSON||!JSON.hasOwnProperty("stringify"))return i&&i[f]?i[f][e]:"";i[f]||(i[f]={}),i[f][e]=r,s?g[c][t]=JSON.stringify(i):((o=new Date).setTime(o.getTime()+31536e6),p.cookie=t+"="+JSON.stringify(i).replace(/\"/g,'"')+"; expires="+o.toGMTString()+"; path=/")}}(e,window,document),function(_){"use strict";var $=_.tablesorter||{};$.themes={bootstrap:{table:"table table-bordered table-striped",caption:"caption",header:"bootstrap-header",sortNone:"",sortAsc:"",sortDesc:"",active:"",hover:"",icons:"",iconSortNone:"bootstrap-icon-unsorted",iconSortAsc:"glyphicon glyphicon-chevron-up",iconSortDesc:"glyphicon glyphicon-chevron-down",filterRow:"",footerRow:"",footerCells:"",even:"",odd:""},jui:{table:"ui-widget ui-widget-content ui-corner-all",caption:"ui-widget-content",header:"ui-widget-header ui-corner-all ui-state-default",sortNone:"",sortAsc:"",sortDesc:"",active:"ui-state-active",hover:"ui-state-hover",icons:"ui-icon",iconSortNone:"ui-icon-carat-2-n-s ui-icon-caret-2-n-s",iconSortAsc:"ui-icon-carat-1-n ui-icon-caret-1-n",iconSortDesc:"ui-icon-carat-1-s ui-icon-caret-1-s",filterRow:"",footerRow:"",footerCells:"",even:"ui-widget-content",odd:"ui-state-default"}},_.extend($.css,{wrapper:"tablesorter-wrapper"}),$.addWidget({id:"uitheme",priority:10,format:function(e,t,r){var a,s,i,o,n,l,c,d,f,u,g,p,h=$.themes,m=t.$table.add(_(t.namespace+"_extra_table")),b=t.$headers.add(_(t.namespace+"_extra_headers")),y=t.theme||"jui",w=h[y]||{},v=_.trim([w.sortNone,w.sortDesc,w.sortAsc,w.active].join(" ")),x=_.trim([w.iconSortNone,w.iconSortDesc,w.iconSortAsc].join(" ")),C=$.debug(t,"uitheme");for(C&&(n=new Date),m.hasClass("tablesorter-"+y)&&t.theme===t.appliedTheme&&r.uitheme_applied||(r.uitheme_applied=!0,u=h[t.appliedTheme]||{},h=(p=!_.isEmptyObject(u))?[u.sortNone,u.sortDesc,u.sortAsc,u.active].join(" "):"",g=p?[u.iconSortNone,u.iconSortDesc,u.iconSortAsc].join(" "):"",p&&(r.zebra[0]=_.trim(" "+r.zebra[0].replace(" "+u.even,"")),r.zebra[1]=_.trim(" "+r.zebra[1].replace(" "+u.odd,"")),t.$tbodies.children().removeClass([u.even,u.odd].join(" "))),w.even&&(r.zebra[0]+=" "+w.even),w.odd&&(r.zebra[1]+=" "+w.odd),m.children("caption").removeClass(u.caption||"").addClass(w.caption),d=m.removeClass((t.appliedTheme?"tablesorter-"+(t.appliedTheme||""):"")+" "+(u.table||"")).addClass("tablesorter-"+y+" "+(w.table||"")).children("tfoot"),t.appliedTheme=t.theme,d.length&&d.children("tr").removeClass(u.footerRow||"").addClass(w.footerRow).children("th, td").removeClass(u.footerCells||"").addClass(w.footerCells),b.removeClass((p?[u.header,u.hover,h].join(" "):"")||"").addClass(w.header).not(".sorter-false").unbind("mouseenter.tsuitheme mouseleave.tsuitheme").bind("mouseenter.tsuitheme mouseleave.tsuitheme",function(e){_(this)["mouseenter"===e.type?"addClass":"removeClass"](w.hover||"")}),b.each(function(){var e=_(this);e.find("."+$.css.wrapper).length||e.wrapInner('
')}),t.cssIcon&&b.find("."+$.css.icon).removeClass(p?[u.icons,g].join(" "):"").addClass(w.icons||""),$.hasWidget(t.table,"filter")&&(s=function(){m.children("thead").children("."+$.css.filterRow).removeClass(p&&u.filterRow||"").addClass(w.filterRow||"")},r.filter_initialized?s():m.one("filterInit",function(){s()}))),a=0;a> Applied "+y+" theme"+$.benchmark(n))},remove:function(e,t,r,a){var s,i,o,n,l;r.uitheme_applied&&(s=t.$table,t=t.appliedTheme||"jui",i=$.themes[t]||$.themes.jui,o=s.children("thead").children(),n=i.sortNone+" "+i.sortDesc+" "+i.sortAsc,l=i.iconSortNone+" "+i.iconSortDesc+" "+i.iconSortAsc,s.removeClass("tablesorter-"+t+" "+i.table),r.uitheme_applied=!1,a||(s.find($.css.header).removeClass(i.header),o.unbind("mouseenter.tsuitheme mouseleave.tsuitheme").removeClass(i.hover+" "+n+" "+i.active).filter("."+$.css.filterRow).removeClass(i.filterRow),o.find("."+$.css.icon).removeClass(i.icons+" "+l)))}})}(e),function(m){"use strict";var b=m.tablesorter||{};b.addWidget({id:"columns",priority:65,options:{columns:["primary","secondary","tertiary"]},format:function(e,t,r){for(var a,s,i,o,n,l=t.$table,c=t.$tbodies,d=t.sortList,f=d.length,u=r&&r.columns||["primary","secondary","tertiary"],g=u.length-1,p=u.join(" "),h=0;h=]/g,query:"(q|query)",wild01:/\?/g,wild0More:/\*/g,quote:/\"/g,isNeg1:/(>=?\s*-\d)/,isNeg2:/(<=?\s*\d)/},types:{or:function(e,t,r){if(!H.orTest.test(t.iFilter)&&!H.orSplit.test(t.filter)||H.regex.test(t.filter))return null;for(var a,s,i=A.extend({},t),o=t.filter.split(H.orSplit),n=t.iFilter.split(H.orSplit),l=o.length,c=0;c]=?/,gtTest:/>/,gteTest:/>=/,ltTest:/'+(i.data("placeholder")||i.attr("data-placeholder")||f.filter_placeholder.select||"")+"":"",0<=(s=n=a).indexOf(f.filter_selectSourceSeparator)&&(s=(n=a.split(f.filter_selectSourceSeparator))[1],n=n[0]),t+="");d.$table.find("thead").find("select."+h.filter+'[data-column="'+o+'"]').append(t),("function"==typeof(s=f.filter_selectSource)||N.getColumnData(r,s,o))&&D.buildSelect(d.table,o,"",!0,i.hasClass(f.filter_onlyAvail))}D.buildDefault(r,!0),D.bindSearch(r,d.$table.find("."+h.filter),!0),f.filter_external&&D.bindSearch(r,f.filter_external),f.filter_hideFilters&&D.hideFilters(d),d.showProcessing&&(s="filterStart filterEnd ".split(" ").join(d.namespace+"filter-sp "),d.$table.unbind(s.replace(N.regex.spaces," ")).bind(s,function(e,t){i=t?d.$table.find("."+h.header).filter("[data-column]").filter(function(){return""!==t[A(this).data("column")]}):"",N.isProcessing(r,"filterStart"===e.type,t?i:"")})),d.filteredRows=d.totalRows,s="tablesorter-initialized pagerBeforeInitialized ".split(" ").join(d.namespace+"filter "),d.$table.unbind(s.replace(N.regex.spaces," ")).bind(s,function(){D.completeInit(this)}),d.pager&&d.pager.initialized&&!f.filter_initialized?(d.$table.triggerHandler("filterFomatterUpdate"),setTimeout(function(){D.filterInitComplete(d)},100)):f.filter_initialized||D.completeInit(r)},completeInit:function(e){var t=e.config,r=t.widgetOptions,a=D.setDefaults(e,t,r)||[];!a.length||t.delayInit&&""===a.join("")||N.setFilters(e,a,!0),t.$table.triggerHandler("filterFomatterUpdate"),setTimeout(function(){r.filter_initialized||D.filterInitComplete(t)},100)},formatterUpdated:function(e,t){e=e&&e.closest("table"),e=e.length&&e[0].config,e=e&&e.widgetOptions;e&&!e.filter_initialized&&(e.filter_formatterInit[t]=1)},filterInitComplete:function(e){function t(){s.filter_initialized=!0,e.lastSearch=e.$table.data("lastSearch"),e.$table.triggerHandler("filterInit",e),D.findRows(e.table,e.lastSearch||[]),N.debug(e,"filter")&&console.log("Filter >> Widget initialized")}var r,a,s=e.widgetOptions,i=0;if(A.isEmptyObject(s.filter_formatter))t();else{for(a=s.filter_formatterInit.length,r=0;r',p=0;p");for(t.$filters=A(g+="").appendTo(t.$table.children("thead").eq(0)).children("td"),p=0;p").appendTo(a):((l=N.getColumnData(e,r.filter_formatter,p))?(r.filter_formatterCount++,(g=(g=l(a,p))&&0===g.length?a.children("input"):g)&&(0===g.parent().length||g.parent().length&&g.parent()[0]!==a[0])&&a.append(g)):g=A('').appendTo(a),g&&(c=s.data("placeholder")||s.attr("data-placeholder")||r.filter_placeholder.search||"",g.attr("placeholder",c))),g)&&(n=(A.isArray(r.filter_cssFilter)?void 0!==r.filter_cssFilter[p]&&r.filter_cssFilter[p]||"":r.filter_cssFilter)||"",g.addClass(h.filter+" "+n),c=(n=r.filter_filterLabel).match(/{{([^}]+?)}}/g),A.each(c=c||["{{label}}"],function(e,t){var r=new RegExp(t,"g"),t=s.attr("data-"+t.replace(/{{|}}/g,"")),t=void 0===t?s.text():t;n=n.replace(r,A.trim(t))}),g.attr({"data-column":a.attr("data-column"),"aria-label":n}),o)&&(g.attr("placeholder","").addClass(h.filterDisabled)[0].disabled=!0)},bindSearch:function(s,e,t){var r,i,o,a,n;s=A(s)[0],(e=A(e)).length&&(i=s.config,o=i.widgetOptions,a=i.namespace+"filter",n=o.filter_$externalFilters,!0!==t&&(r=o.filter_anyColumnSelector+","+o.filter_multipleColumnSelector,o.filter_$anyMatch=e.filter(r),n&&n.length?o.filter_$externalFilters=o.filter_$externalFilters.add(e):o.filter_$externalFilters=e,N.setFilters(s,i.$table.data("lastSearch")||[],!1===t)),r="keypress keyup keydown search change input ".split(" ").join(a+" "),e.attr("data-lastSearchTime",(new Date).getTime()).unbind(r.replace(N.regex.spaces," ")).bind("keydown"+a,function(e){if(e.which===l.escape&&!s.config.widgetOptions.filter_resetOnEsc)return!1}).bind("keyup"+a,function(e){o=s.config.widgetOptions;var t=parseInt(A(this).attr("data-column"),10),r="boolean"==typeof o.filter_liveSearch?o.filter_liveSearch:N.getColumnData(s,o.filter_liveSearch,t);if(void 0===r&&(r=o.filter_liveSearch.fallback||!1),A(this).attr("data-lastSearchTime",(new Date).getTime()),e.which===l.escape)this.value=o.filter_resetOnEsc?"":i.lastSearch[t];else{if(""!==this.value&&("number"==typeof r&&this.value.length=l.left&&e.which<=l.down)))return;if(!1===r&&""!==this.value&&e.which!==l.enter)return}D.searching(s,!0,!0,t)}).bind("search change keypress input blur ".split(" ").join(a+" "),function(e){var t=parseInt(A(this).attr("data-column"),10),r=e.type,a="boolean"==typeof o.filter_liveSearch?o.filter_liveSearch:N.getColumnData(s,o.filter_liveSearch,t);!s.config.widgetOptions.filter_initialized||e.which!==l.enter&&"search"!==r&&"blur"!==r&&("change"!==r&&"input"!==r||!0!==a&&(!0===a||"INPUT"===e.target.nodeName)||this.value===i.lastSearch[t])||(e.preventDefault(),A(this).attr("data-lastSearchTime",(new Date).getTime()),D.searching(s,"keypress"!==r||e.which===l.enter,!0,t))}))},searching:function(e,t,r,a){var s,i=e.config.widgetOptions;void 0===a?s=!1:void 0===(s="boolean"==typeof i.filter_liveSearch?i.filter_liveSearch:N.getColumnData(e,i.filter_liveSearch,a))&&(s=i.filter_liveSearch.fallback||!1),clearTimeout(i.filter_searchTimer),void 0===t||!0===t?i.filter_searchTimer=setTimeout(function(){D.checkFilters(e,t,r)},s?i.filter_searchDelay:10):D.checkFilters(e,t,r)},equalFilters:function(e,t,r){var a,s=[],i=[],o=e.columns+1;for(t=A.isArray(t)?t:[],r=A.isArray(r)?r:[],a=0;a=e.columns&&(o=e.columns-1);i<=o;i++)f[f.length]=i;t=t.replace(a[c],"")}if(!r&&/,/.test(t))for(d=(n=t.split(/\s*,\s*/)).length,l=0;l> Starting filter widget search",r),m=new Date),F.filteredRows=0,t=z||[],c=F.totalRows=0;c> Searching through "+(v&&w> Completed search"+N.benchmark(m)),R.filter_initialized&&(F.$table.triggerHandler("filterBeforeEnd",F),F.$table.triggerHandler("filterEnd",F)),setTimeout(function(){N.applyWidget(F.table)},0)}},getOptionSource:function(e,t,r){var a=(e=A(e)[0]).config,s=!1,i=a.widgetOptions.filter_selectSource,a=a.$table.data("lastSearch")||[],o="function"==typeof i||N.getColumnData(e,i,t);if(r&&""!==a[t]&&(r=!1),!0===o)s=i(e,t,r);else{if(o instanceof A||"string"===A.type(o)&&0<=o.indexOf(""))return o;if(A.isArray(o))s=o;else if("object"===A.type(i)&&o&&null===(s=o(e,t,r)))return null}return!1===s&&(s=D.getOptions(e,t,r)),D.processOptions(e,t,s)},processOptions:function(a,s,r){if(!A.isArray(r))return!1;var i,e,t,o,n,l=(a=A(a)[0]).config,c=null!=s&&0<=s&&s'+(u.data("placeholder")||u.attr("data-placeholder")||f.filter_placeholder.select||"")+"",u=d.$table.find("thead").find("select."+h.filter+'[data-column="'+t+'"]').val();if(void 0!==r&&""!==r||null!==(r=D.getOptionSource(e,t,s))){if(A.isArray(r)){for(i=0;i"}else""+c!="[object Object]"&&(0<=(o=n=c=(""+c).replace(H.quote,""")).indexOf(f.filter_selectSourceSeparator)&&(o=(l=n.split(f.filter_selectSourceSeparator))[0],n=l[1]),g+=""!==c?"":"");r=[]}e=(d.$filters||d.$table.children("thead")).find("."+h.filter),(s=(e=f.filter_$externalFilters?e&&e.length?e.add(f.filter_$externalFilters):f.filter_$externalFilters:e).filter('select[data-column="'+t+'"]')).length&&(s[a?"html":"append"](g),A.isArray(r)||s.append(r).val(u),s.val(u))}}},buildDefault:function(e,t){for(var r,a,s=e.config,i=s.widgetOptions,o=s.columns,n=0;n'),y=b.parent().addClass(z.css.stickyHide).css({position:d.length?"absolute":"fixed",padding:parseInt(b.parent().parent().css("padding-left"),10),top:p+m,left:0,visibility:"hidden",zIndex:l.stickyHeaders_zIndex||2}),p=b.children("thead:first"),w="",v=function(e,t){for(var r,a,s,i=e.filter(":visible"),o=i.length,n=0;ns.top&&a thead:gt(0), tr.sticky-false").hide(),b.find("> tbody, > tfoot").remove(),b.find("caption").toggle(l.stickyHeaders_includeCaption),i=p.children().children(),b.css({height:0,width:0,margin:0}),i.find("."+z.css.resizer).remove(),c.addClass("hasStickyHeaders").bind("pagerComplete"+o,function(){C()}),z.bindEvents(e,p.children().children("."+z.css.header)),l.stickyHeaders_appendTo?$(l.stickyHeaders_appendTo).append(y):c.after(y),t.onRenderHeader)for(a=(s=p.children("tr").children()).length,r=0;r";c("head").append(e)}),d.resizable={init:function(e,t){if(!e.$table.hasClass("hasResizable")){e.$table.addClass("hasResizable");var r,a,s,i=e.$table,o=i.parent(),n=parseInt(i.css("margin-top"),10),l=t.resizable_vars={useStorage:d.storage&&!1!==t.resizable,$wrap:o,mouseXPosition:0,$target:null,$next:null,overflow:"auto"===o.css("overflow")||"scroll"===o.css("overflow")||"auto"===o.css("overflow-x")||"scroll"===o.css("overflow-x"),storedSizes:[]};for(d.resizableReset(e.table,!0),l.tableWidth=i.width(),l.fullWidth=Math.abs(o.width()-l.tableWidth)<20,l.useStorage&&l.overflow&&(d.storage(e.table,"tablesorter-table-original-css-width",l.tableWidth),s=d.storage(e.table,"tablesorter-table-resized-width")||"auto",d.resizable.setWidth(i,s,!0)),t.resizable_vars.storedSizes=o=(l.useStorage?d.storage(e.table,d.css.resizableStorage):[])||[],d.resizable.setWidths(e,t,o),d.resizable.updateStoredSizes(e,t),t.$resizable_container=c('
').css({top:n}).insertBefore(i),a=0;a').appendTo(t.$resizable_container).attr({"data-column":a,unselectable:"on"}).data("header",r).bind("selectstart",!1);d.resizable.bindings(e,t)}},updateStoredSizes:function(e,t){var r,a,s=e.columns,i=t.resizable_vars;for(i.storedSizes=[],r=0;r> Saving last sort: "+e.sortList+l.benchmark(s)):(i.addClass("hasSaveSort"),o="",l.storage&&(o=c(e),n&&console.log('saveSort >> Last sort loaded: "'+o+'"'+l.benchmark(s)),i.bind("saveSortReset",function(e){e.stopPropagation(),l.storage(t,"tablesorter-savesort","")})),a&&o&&0 { + language_families.push(elem.dataset.family) + language_family_elems.push(elem) + }) + } + const i = language_families.indexOf(language_family); + if (i > 0) { + language_families.unshift(language_families.splice(i, 1)[0]); + language_family_elems.unshift(language_family_elems.splice(i, 1)[0]); + const dest = document.querySelector("#language-family-list"); + const elems = [] + for (const fam of language_family_elems) { + if (elems.length > 0) { + const span = document.createElement("span"); + span.textContent = ", "; + elems.push(span); + } + elems.push(fam); + } + dest.replaceChildren(...elems); + } + + document.querySelectorAll('[data-families]').forEach(row => { + const families = row.dataset.families.split(","); + if (families.includes(language_family)) { + row.style.backgroundColor = ""; + } else { + row.style.backgroundColor = "whitesmoke"; + } + }) + + $("#package-table").trigger("sortReset"); + const tbody = document.querySelector("#package-table tbody"); + const rows = [...tbody.querySelectorAll("tr")]; + + rows.sort((a, b) => { + const a_fams = a.dataset.families.split(","); + const b_fams = b.dataset.families.split(","); + + function best_match(fams) { + best = language_families.length + for (const fam of fams) { + const i = language_families.indexOf(fam) + if (i > -1 && i < best) { + best = i; + } + } + return best; + } + + const a_pos = best_match(a_fams); + const b_pos = best_match(b_fams); + + if (a_pos != b_pos) + return a_pos - b_pos; + return parseInt(a.dataset.sortpos) - parseInt(b.dataset.sortpos); + }); + + rows.forEach(row => tbody.appendChild(row)); + + document.querySelectorAll('.family-select').forEach(elem => { + if (elem.dataset.family == language_family) { + const span = document.createElement("span"); + span.textContent = language_family; + elem.replaceChildren(span); + } else { + const link = document.createElement("a"); + link.href = "#"; // makes it look/behave like a link + link.textContent = elem.dataset.family; + link.addEventListener("click", (e) => { + e.preventDefault(); // stop the # from scrolling/navigating + language_family = elem.dataset.family; + on_language_family(); + }); + elem.replaceChildren(link); + } + }) +} + $(function() { "use strict"; + var query_family = (new URLSearchParams(window.location.search)).get("langfam"); + if (query_family) { + language_family = query_family; + } + function applyFilter() { $("table.packages > tbody > tr").each(function() { var row = this; @@ -36,7 +128,7 @@ $(function() { $("#todo-msg").show(); $("#todo-msg").html( todoTotal + " todos. " + - " Click here to see them." + " See package with todos." ); var filterIsApplied = false; @@ -45,11 +137,11 @@ $(function() { var filterLink = $(this); if (!filterIsApplied) { applyFilter(); - filterLink.text("Click to see all packages."); + filterLink.text("See all packages."); filterIsApplied = true; } else { removeFilter(); - filterLink.text("Click here to see them."); + filterLink.text("See packages with todos."); filterIsApplied = false; } }); @@ -86,4 +178,7 @@ $(function() { } }); }); + + on_language_family(); + }); /* document.ready */ diff --git a/static/site.js b/static/site.js index 49490b4..eb4e8a4 100644 --- a/static/site.js +++ b/static/site.js @@ -43,7 +43,12 @@ PkgSite = (function () { })(); $(document).ready(function () { - $("table.sortable").tablesorter(); + $("table.sortable").tablesorter({ + widgets: ['children'], + widgetOptions: { + children_attach: true // keeps child rows with their parent during sort + } + }) if ($("#tags").length) { PkgSite.dynamicJSON((document.body.className === "package-form") diff --git a/static/style.css b/static/style.css index aebeed8..5c22b41 100644 --- a/static/style.css +++ b/static/style.css @@ -35,7 +35,7 @@ body { .build_yellow { background-color: #ffffcc; } .build_red { background-color: #ffcccc; } -.doctags-label { font-weight: bold; } +.doctags-label, .license-label { font-weight: bold; } table.packages, table.package-details, table.package-versions { width: 100%; @@ -73,7 +73,8 @@ ul.module-list { .search-results { margin-top: 3em; } .main-page .package-count, -.search-results .package-count { +.search-results .package-count, +.language-families { font-size: 100%; padding: 0.5em; background: #eee; @@ -116,6 +117,10 @@ input#new_version { margin: 2em; } +.language-families { + margin-bottom: 1em; +} + .package-count { font-size: 120%; } @@ -196,3 +201,6 @@ th.headerSortDown::after { content: " ▲"; } .in-open-umbrella { } + +thead th.tablesorter-headerAsc .tablesorter-header-inner::after { content: " ▲"; } +thead th.tablesorter-headerDesc .tablesorter-header-inner::after { content: " ▼"; }