Yu-Gi-Oh! Deck Building and Card Inventory Management web interface written in Common Lisp, utilizing HTMX.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
4.6KB

  1. ;;;; src/toolkit/toolkit.lisp
  2. (in-package #:cl-deck-builder2.toolkit.utils)
  3. ;; https://stackoverflow.com/a/11965885
  4. ;; def grouped(l, n):
  5. ;; for i in xrange(0, len(l), n):
  6. ;; yield l[i:i+n]
  7. (defun grouped (seq &optional (n 1000))
  8. "Group elements in a list by some number of elements.
  9. ARGUMENTS
  10. SEQ The sequence to be grouped.
  11. N The number of elements per grouping."
  12. (loop :for i :upto (1- (length seq)) :by n
  13. :collect (subseq seq i (min (length seq)
  14. (+ i n)))))
  15. ;; Not sure how I arrived at this gist.
  16. ;;
  17. ;; <https://gist.github.com/html/4085786>
  18. ;;
  19. ;; My code is modified slightly.
  20. (defun normalize-newlines (string)
  21. "Remove or replace #\Return(#\Newline)? sequences with just #\Newline.
  22. The \#Newline following #\Return is optional, so just #\Return also gets turned into #\Newline.
  23. ARGUMENTS
  24. STRING The target string."
  25. (ppcre:regex-replace-all
  26. (format nil "~C(\\n)?" #\Return)
  27. string
  28. (format nil "~C" #\Newline)))
  29. ;; Is this necessary? I uploaded a file with a BOM and it exploded.
  30. (defun strip-bom (string)
  31. "If STRING is UTF-8 and contains a UTF-8 BOM it will be removed.
  32. ARGUMENTS
  33. STRING The string to check."
  34. (when string
  35. (case (type-of (char string 0))
  36. (extended-char (subseq string 1))
  37. (t string))))
  38. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  39. (defun query-param (name parsed)
  40. "Parse query param values. Get the value of the specified element NAME in the query PARSED.
  41. ARGUMENTS
  42. NAME The query to search for in the parameter list.
  43. PARSED The parameter list."
  44. ;; TODO STRING=? STRING-EQUAL?? Maybe I should add a &KEY (test #'STRING=)?
  45. (assoc-utils:aget parsed name))
  46. ;; (defun generate-pages ()
  47. ;; "Generate pagination"
  48. ;; (let ((pages nil))
  49. ;; (dotimes (i 10)
  50. ;; (push (list :id (+ 1 i) :start (* 10 i)) pages))
  51. ;; (reverse pages)))
  52. ;; TODO Passing weird floats to this. How can I put the weird math into here?
  53. ;;
  54. ;; I made it into a helper function for now.
  55. ;; No idea how to even explain this. It works!
  56. (defun generate-pages-helper (start &optional (max 10) (multiplier 10))
  57. "Generate pagination up to MAX pages, with MULTIPLIER items per page."
  58. ;; We always want 10 pages. If we start at page 0, subtract five,
  59. ;; then take the nearest lowest non-negative integer (0), this gets
  60. ;; us a comfy left bound.
  61. (let ((start- (- start 5)))
  62. ;; If START- is less than zero, make it zero. This gives us a safe
  63. ;; left bound.
  64. (when (< start- 0)
  65. (setf start- 0))
  66. (loop for i from 0 upto (1- (min (min max 8) (- max start -8))) collect
  67. `(:id ,(+ start- i) :offset ,(* multiplier (+ start- i))))))
  68. (defun generate-pages (length offset &optional (limit 10))
  69. "Generate pagination for LENGTH number of pages, with LIMIT number of items per page, offset into the list by OFFSET number of pages.
  70. ARGUMENTS
  71. LENGTH The length of the array we're generating pages for.
  72. OFFSET The starting index.
  73. LIMIT The number of entries per page. Default is 10."
  74. (generate-pages-helper (floor (/ offset limit))
  75. (ceiling (/ length limit))
  76. limit))
  77. (defun get-opposite-direction (direction)
  78. "Get the opposite direction"
  79. (if (string= direction "asc")
  80. "desc"
  81. "asc"))
  82. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  83. ;; Idea for RLE from PCL by Paul Graham
  84. ;; This implementation based on <https://gist.github.com/trhura/5820848>
  85. ;;
  86. ;; Nope it's not I wrote my own implementation!
  87. (defun rle-encode (lst &key (key #'identity) (test #'eq))
  88. (loop for line in (mapcar key (remove-duplicates lst :key key :test test))
  89. collect (cons (count line lst :key key :test test) line)))
  90. ;; (sort * ;#'> :key #'car))
  91. (defun rle-decode (lst)
  92. (loop for line in lst
  93. nconc (make-list 3 :initial-element (second line))))
  94. ;; XXX Where does this go?
  95. (defun rle-encode-plist (plist &key (key #'identity) (test #'eq))
  96. (rle-encode
  97. (mapcar (lambda (plist)
  98. (setf (getf plist :name)
  99. (princ-to-string (getf plist :name)))
  100. plist)
  101. plist)
  102. :key key :test test))
  103. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  104. ;; For The Label Maker
  105. ;; https://tex.stackexchange.com/a/119383
  106. (defun latex-escape (s)
  107. (cl-ppcre:regex-replace-all
  108. "(\\\\backslash)"
  109. (cl-ppcre:regex-replace-all
  110. "~"
  111. (cl-ppcre:regex-replace-all
  112. "(\\^)"
  113. (cl-ppcre:regex-replace-all
  114. "([\$\#&%_{}])"
  115. (cl-ppcre:regex-replace-all "\\" s "\\backslash")
  116. "\\\\\\1")
  117. "\\\\\\1{}")
  118. "\\\\texttt{\\~{}}")
  119. "$\\1$"))