common.rkt
(module common racket
  (require scribble/core scribble/base scribble/html-properties scribble/decode scriblib/render-cond)
  (require setup/dirs)
  (require mzlib/etc)

;; ---------------------------------------------------------------------------------------------------
  (define css-dir (build-path (find-system-path 'home-dir) ".scribble-styles"))
  (provide (contract-out
                                        ; Set the path to the folder containing the .css files
            [bystro-set-css-dir (-> path? any)]))
  (define (bystro-set-css-dir x) (set! css-dir x))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out [bystro-inject-style (-> string? element?)]))
  (define (bystro-inject-style css-file-name)
    (let ((style (make-style #f
			     (list (make-css-addition (build-path css-dir (string->path css-file-name)
						       ))))))
      (make-element style '())))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
                                        ; Provides relative path for e.g.:
                                        ; @(hyperlink (bystro-path-to-link "../document.pdf") "here")
            [bystro-path-to-link (-> path-string? string?)]))
  (define bystro-path-to-link
    (lambda (relpath)
      (string-append "file://" (path->string (path->complete-path (expand-user-path relpath))))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
                                        ; javascript injection
   [bystro-js (->* () () #:rest (listof string?) element?)]))
  (define (bystro-js . body)
    (make-element 
     (make-style #f (list (make-script-property "text/javascript" body)))
     '()
     ))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
                                        ; javascript from URL
   [bystro-js-url (-> string? element?)]))
  (define (bystro-js-url url)
    (bystro-js "document.write(\"<script src='"  url  "'/><\\/script>\");"))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
                                        ; This is a very universal style selector for element
   [bystro-elemstyle 
    (->* ((or/c #f string?)) () #:rest (listof any/c) style?)]))
  (define (bystro-elemstyle s . otherprops)
    (make-style 
     #f  
     (if s
         (cons 
                                        ; Used as a style property
                                        ; to add arbitrary attributes to an HTML tag:
          (make-attributes (list (cons 'style s)))
          otherprops)
         otherprops
         )))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out  [larger-2 (->* () () #:rest (listof pre-content?) element?)]))
  (define larger-2 (compose larger larger))
  (provide (contract-out  [larger-3 (->* () () #:rest (listof pre-content?) element?)]))
  (define larger-3 (compose larger larger larger))
  (provide (contract-out  [larger-4 (->* () () #:rest (listof pre-content?) element?)]))
  (define larger-4 (compose larger larger larger larger))
  (provide (contract-out  [smaller-2 (->* () () #:rest (listof pre-content?) element?)]))
  (define smaller-2 (compose smaller smaller))
  (provide (contract-out  [smaller-3 (->* () () #:rest (listof pre-content?) element?)]))
  (define smaller-3 (compose smaller smaller smaller))
  (provide (contract-out  [smaller-4 (->* () () #:rest (listof pre-content?) element?)]))
  (define smaller-4 (compose smaller smaller smaller smaller))
;; ---------------------------------------------------------------------------------------------------
  (provide bystro-rectangular-table?)
  (define (bystro-rectangular-table? a)
    (and (list? a) 
         (for/and ([y a]) (list? y))
         (let ([ly (length a)])
           (and ((length a) . > . 0)
                (let ([lx (length (car a))])
                  (and (lx . > . 0)
                       (for/and ([z (cdr a)]) 
                                (= (length z) lx))))))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
                                        ; My table
            [bystro-table (->* (bystro-rectangular-table?) (#:orient (or/c 'hor 'vert)) table?)]))
  (define (bystro-table listofrows #:orient [dirn #f])
    (let* (
           [cell-style-suffix (if dirn
                                  (if (equal? dirn 'hor) 
                                      "-hor"
                                      "-vert")
                                  "")]
           [generic-cell-style
            (make-style (string-append "amktablecell" cell-style-suffix) '())]
           [topleft-cell-style
            (make-style (string-append "amktabletopleftcell" cell-style-suffix) '())]
           [left-cell-style
            (make-style (string-append "amktableleftcell" cell-style-suffix) '())]
           [top-cell-style
            (make-style (string-append "amktabletopcell" cell-style-suffix) '())]
           [style-def-first-row 
            (cons topleft-cell-style
                  (map (lambda (x) top-cell-style) (cdr (car listofrows)))
                  )]
           [style-def-generic-row
            (cons left-cell-style
                  (map (lambda (x) generic-cell-style) (cdr (car listofrows)))
                  )]
           [style-def
            (cons style-def-first-row
                  (map (lambda (x) style-def-generic-row) (cdr listofrows))
                  )]
           )
      (make-table (make-style #f (list (make-table-cells style-def)))
                  (map (lambda (x) 
                         (map (lambda (y) 
                                (if (block? y)
                                    y
                                    (make-paragraph plain y)))
                              x) 
                         )
                       listofrows))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out  
                                        ; table filling 100% of width
   [bystro-longtable (->* ((listof (listof block?))
                           #:styless (listof 
                                      (listof 
                                       (listof 
                                        (or/c 'left 'right 'center 'top 'baseline 'bottom 'vcenter)
                                        ))))
                          (#:width (integer-in 1 100))
                          nested-flow? )]))
  (define (bystro-longtable bss #:styless ass #:width [w 100])
    (nested
     (make-table
      (make-style #f
                  (list
                   (make-attributes (list (cons 'style (string-append 
                                                        "width:" 
                                                        (number->string w) 
                                                        "%;"))))
                   (make-table-cells
                    (map 
                     (lambda (x) 
                       (map 
                        (lambda (y) (make-style #f y))
                        x))
                     ass))))
      bss)))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out 
                                        ; My modified verbatim environment, to avoid line breaks
            [bystro-verb 
             (->* ((or/c string? #f)) 
                  (#:style style? 
                   #:indent exact-nonnegative-integer?) 
                  #:rest (listof string?) block?)]))
  (define (nolinebreaks p #:style [st #f])
    (make-table
     (if st st (make-style #f '()))
     (map (lambda (x)
            (list
             (make-paragraph (make-style #f (list 'div)) 
                             (paragraph-content (car x)))))
          (table-blockss p))))
  (define (bystro-verb #:style [st #f] #:indent [i 0] . x) 
    (nolinebreaks #:style st (apply verbatim #:indent i x)))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out 
                                        ; colored text
   [bystro-clr (->* 
                ((or/c string? (list/c byte? byte? byte?))) 
                () 
                #:rest (listof pre-content?) 
                element?)])) 
  (define (bystro-clr clr-name . txt)
    (element (style #f (list (color-property clr-name))) 
      txt))
;; ---------------------------------------------------------------------------------------------------
  (define (bystro-is-scrbl? p #:exclude-same-name [x #t]) 
    (let-values ([(base name mustbedir) (split-path p)])
      (if (symbol? name) 
          #f
          (let* (
                 [ps (path->string name)]
                 [n (string-length ps)]
                 )
            (and 
             (equal? ".scrbl" (substring ps (max 0 (- n 6))))
             (not 
              (and 
               x
               (equal? 
                (let-values 
                    ([(x scribble-file-name y) 
                      (split-path 
                       (string->path 
                        (vector-ref (current-command-line-arguments) 0)))])
                  scribble-file-name)
                name))))))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out [bystro-dir-contains-scrbl? (-> path? boolean?)]))
  (define (bystro-dir-contains-scrbl? p)
    (if (directory-exists? p)
        (pair? (find-files bystro-is-scrbl? p))
        #f))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
            [bystro-list-scrbls
             (->* (path-string?) (#:exclude-same-name boolean?) (listof path?))]))
  (define (bystro-list-scrbls p #:exclude-same-name [x #t])
    (let ([fs (directory-list p)])
      (filter 
       (λ (u) (bystro-is-scrbl? #:exclude-same-name x u))
       fs
      )))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out
            [bystro-list-scrbls-in-dir
             (->* (path-string?) (#:background-color (listof integer?))  element?)]))
  (define (bystro-list-scrbls-in-dir s #:background-color [clr '(251 206 177)])
    (apply 
     elem #:style (bystro-elemstyle #f (make-background-color-property clr))
     (flatten
      (map (lambda (u) 
             (let* ([x (path->string u)]
                    [n (string-length x)]
                    [bare (substring x 0 (- n 6))]
                    [h (string-append bare ".html")]
                    )
               (list 
                (hyperlink 
                 #:style (make-style 
                          "scrbllink" 
                          (list (make-css-addition (build-path 
                                                    css-dir
                                                    (string->path "misc.css")
                                                    ))))
                 (bystro-path-to-link (string-append s "/" h))  
                 bare) 
                " ")))
           (bystro-list-scrbls s #:exclude-same-name #f)))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out [boldred (->* () #:rest (listof pre-content?) element?)]))
  (define (boldred . x) 
    (bystro-clr "red" (apply bold x)))
;; ---------------------------------------------------------------------------------------------------
                                        ; www section
                                        ; collection of functions useful for putting scribbles
                                        ; as static content on a website
;; ---------------------------------------------------------------------------------------------------
  (define www-url "http://localhost/")
  (provide (contract-out
            ; set the address of the website
            [bystro-set-url (-> string? any)]))
  (define (bystro-set-url x) (set! www-url x)) 
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out 
                                        ; a nice ribbon with local scribblings
            [bystro-www-ribbon (->* () () table?)]))
  (define (bystro-www-ribbon)
     (apply (compose bystro-table list list elem)
            (append 
             (list)
             (flatten
              (map (lambda (u) 
                     (let* ([s (path->string u)]
                            [n (string-length s)]
                            [bare (substring s 0 (- n 6))]
                            [h (string-append bare ".html")]
                            )
                       (list 
                        (hyperlink 
                         #:style (make-style 
                                  "scrbllink" 
                                  (list (make-css-addition (build-path 
                                                            css-dir
                                                            (string->path "misc.css")
                                                            ))))
                         (string-append www-url (path->string (expand-user-path h)))
                         bare)  
                        " ")))
                   (bystro-list-scrbls ".")
                   )))))
;; ---------------------------------------------------------------------------------------------------
  (provide (contract-out 
                                        ; prepends the URS of my website
            [bystro-www-prepend-root (-> string? string?)]))
  (define (bystro-www-prepend-root x)
    (string-append www-url x))
;; ---------------------------------------------------------------------------------------------------
  )