Create archiver and offline code handlers
Somewhat messy. Will clean up gradually in future commits.
This commit is contained in:
parent
b8e5fb8dc5
commit
c7cce5479d
46 changed files with 4274 additions and 407 deletions
3
archiver/archive-info.rkt
Normal file
3
archiver/archive-info.rkt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#lang info
|
||||||
|
|
||||||
|
(define build-deps '("rackunit-lib" "web-server-lib" "http-easy-lib" "html-parsing" "html-writing" "json-pointer" "ini-lib" "memo" "net-cookies-lib" "gui-easy-lib" "sql" "charterm" "cli"))
|
71
archiver/archiver-cli.rkt
Normal file
71
archiver/archiver-cli.rkt
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#lang cli
|
||||||
|
(require charterm
|
||||||
|
"archiver.rkt")
|
||||||
|
|
||||||
|
(help (usage "Downloads a single Fandom wiki in BreezeWiki offline format."
|
||||||
|
""
|
||||||
|
"Downloaded pages go into `archive/` next to the executable."
|
||||||
|
"Database goes into `archiver.db*` next to the executable."
|
||||||
|
"The database is necessary to store your download progress and resume where you left off if the process is interrupted.")
|
||||||
|
(ps ""
|
||||||
|
"Default output style is `progress` in a tty and `lines` otherwise."))
|
||||||
|
|
||||||
|
(flag (output-quiet?)
|
||||||
|
("-q" "--output-quiet" "disable progress output")
|
||||||
|
(output-quiet? #t))
|
||||||
|
|
||||||
|
(flag (output-lines?)
|
||||||
|
("-l" "--output-lines" "output the name of each file downloaded")
|
||||||
|
(output-lines? #t))
|
||||||
|
|
||||||
|
(flag (output-progress?)
|
||||||
|
("-p" "--output-progress" "progress output for terminals")
|
||||||
|
(output-progress? #t))
|
||||||
|
|
||||||
|
(constraint (one-of output-quiet? output-lines? output-progress?))
|
||||||
|
|
||||||
|
(program
|
||||||
|
(start [wikiname "wikiname to download"])
|
||||||
|
;; set up arguments
|
||||||
|
(define width 80)
|
||||||
|
(when (not (or (output-quiet?) (output-lines?) (output-progress?)))
|
||||||
|
(cond [(terminal-port? current-input-port)
|
||||||
|
(output-progress? #t)]
|
||||||
|
[else
|
||||||
|
(output-lines? #t)]))
|
||||||
|
(define (update-width)
|
||||||
|
(when (output-progress?)
|
||||||
|
(with-charterm
|
||||||
|
(call-with-values (λ () (charterm-screen-size))
|
||||||
|
(λ (cols rows) (set! width cols))))))
|
||||||
|
(update-width)
|
||||||
|
;; check
|
||||||
|
(when (or (not wikiname) (equal? wikiname ""))
|
||||||
|
(raise-user-error "Please specify the wikiname to download on the command line."))
|
||||||
|
;; stage 1
|
||||||
|
(cond [(output-lines?) (displayln "Downloading list of pages...")]
|
||||||
|
[(output-progress?) (printf "Downloading list of pages... \r")])
|
||||||
|
(if-necessary-download-list-of-pages
|
||||||
|
wikiname
|
||||||
|
(λ (a b c)
|
||||||
|
(cond [(output-progress?) (printf "Downloading list of pages... [~a/~b]\r" a b)])))
|
||||||
|
;; stage 2
|
||||||
|
(save-each-page
|
||||||
|
wikiname
|
||||||
|
(λ (a b c)
|
||||||
|
(define basename (basename->name-for-query c))
|
||||||
|
(cond
|
||||||
|
[(output-lines?)
|
||||||
|
(displayln basename)]
|
||||||
|
[(output-progress?)
|
||||||
|
(when (eq? (modulo a 20) 0)
|
||||||
|
(thread (λ () (update-width))))
|
||||||
|
(define prefix (format "[~a/~a] " a b))
|
||||||
|
(define rest (- width (string-length prefix)))
|
||||||
|
(define real-width (min (string-length basename) rest))
|
||||||
|
(define spare-width (- rest real-width))
|
||||||
|
(define name-display (substring basename 0 real-width))
|
||||||
|
(define whitespace (make-string spare-width #\ ))
|
||||||
|
(printf "~a~a~a\r" prefix name-display whitespace)]))))
|
||||||
|
|
||||||
|
(run start)
|
47
archiver/archiver-database.rkt
Normal file
47
archiver/archiver-database.rkt
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/list
|
||||||
|
racket/path
|
||||||
|
racket/runtime-path
|
||||||
|
racket/string
|
||||||
|
json
|
||||||
|
json-pointer
|
||||||
|
db
|
||||||
|
"../lib/syntax.rkt")
|
||||||
|
|
||||||
|
(provide
|
||||||
|
slc)
|
||||||
|
|
||||||
|
(define-runtime-path database-file "../storage/archiver.db")
|
||||||
|
|
||||||
|
(define migrations
|
||||||
|
(wrap-sql
|
||||||
|
((query-exec slc "create table page (wikiname TEXT NOT NULL, basename TEXT NOT NULL, progress INTEGER NOT NULL, PRIMARY KEY (wikiname, basename))")
|
||||||
|
(query-exec slc "create table wiki (wikiname TEXT NOT NULL, progress INTEGER, PRIMARY KEY (wikiname))"))
|
||||||
|
((query-exec slc "create table special_page (wikiname TEXT NOT NULL, key TEXT NOT NULL, basename TEXT NOT NULL, PRIMARY KEY (wikiname, key))"))
|
||||||
|
((query-exec slc "update wiki set progress = 2 where wikiname in (select wikiname from wiki inner join page using (wikiname) group by wikiname having min(page.progress) = 1)"))
|
||||||
|
((query-exec slc "create table image (wikiname TEXT NOT NULL, hash TEXT NTO NULL, url TEXT NOT NULL, ext TEXT, source INTEGER NOT NULL, progress INTEGER NOT NULL, PRIMARY KEY (wikiname, hash))"))
|
||||||
|
((query-exec slc "alter table wiki add column sitename TEXT")
|
||||||
|
(query-exec slc "alter table wiki add column basepage TEXT")
|
||||||
|
(query-exec slc "alter table wiki add column license_text TEXT")
|
||||||
|
(query-exec slc "alter table wiki add column license_url TEXT"))))
|
||||||
|
|
||||||
|
(define slc (sqlite3-connect #:database database-file #:mode 'create))
|
||||||
|
(query-exec slc "PRAGMA journal_mode=WAL")
|
||||||
|
(define database-version
|
||||||
|
(with-handlers ([exn:fail:sql?
|
||||||
|
(λ (exn)
|
||||||
|
; need to set up the database
|
||||||
|
(query-exec slc "create table database_version (version integer, primary key (version))")
|
||||||
|
(query-exec slc "insert into database_version values (0)")
|
||||||
|
0)])
|
||||||
|
(query-value slc "select version from database_version")))
|
||||||
|
|
||||||
|
(let do-migrate-step ()
|
||||||
|
(when (database-version . < . (length migrations))
|
||||||
|
(call-with-transaction
|
||||||
|
slc
|
||||||
|
(list-ref migrations database-version))
|
||||||
|
(set! database-version (add1 database-version))
|
||||||
|
(query-exec slc "update database_version set version = $1" database-version)
|
||||||
|
(do-migrate-step)))
|
||||||
|
|
204
archiver/archiver-gui.rkt
Normal file
204
archiver/archiver-gui.rkt
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/class
|
||||||
|
racket/list
|
||||||
|
racket/port
|
||||||
|
racket/set
|
||||||
|
racket/string
|
||||||
|
db
|
||||||
|
net/http-easy
|
||||||
|
racket/gui/easy
|
||||||
|
racket/gui/easy/operator
|
||||||
|
"archiver-database.rkt"
|
||||||
|
"archiver.rkt"
|
||||||
|
"../lib/url-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
|
(define active-threads (mutable-seteq))
|
||||||
|
|
||||||
|
(define/obs @auto-retry #f)
|
||||||
|
(define/obs @wikiname "")
|
||||||
|
(define/obs @state 'waiting)
|
||||||
|
(define/obs @num-pages 1)
|
||||||
|
(define/obs @done-pages 0)
|
||||||
|
(define/obs @just-done "")
|
||||||
|
(define/obs @queue '())
|
||||||
|
(define @title
|
||||||
|
(obs-combine
|
||||||
|
(λ (state queue num-pages done-pages)
|
||||||
|
(define suffix (if (pair? queue)
|
||||||
|
(format " +~a" (length queue))
|
||||||
|
""))
|
||||||
|
(define progress (if (eq? num-pages 0)
|
||||||
|
" 0%"
|
||||||
|
(format " ~a%" (round (inexact->exact (* (/ done-pages num-pages) 100))))))
|
||||||
|
(case state
|
||||||
|
[(waiting stage-0) (format "Fandom Archiver~a" suffix)]
|
||||||
|
[(stage-1) (format "Fandom Archiver 0%~a" suffix)]
|
||||||
|
[(stage-2) (format "Fandom Archiver~a~a" progress suffix)]
|
||||||
|
[(err) "ERROR Fandom Archiver"]
|
||||||
|
[(done) "Fandom Archiver 100%"]))
|
||||||
|
@state @queue @num-pages (obs-throttle @done-pages #:duration 5000)))
|
||||||
|
|
||||||
|
(define-syntax-rule (t body ...)
|
||||||
|
(set-add! active-threads (thread (λ () body ...))))
|
||||||
|
|
||||||
|
(define (do-start-or-queue)
|
||||||
|
(define wikiname (obs-peek @wikiname))
|
||||||
|
(:= @wikiname "")
|
||||||
|
(when (not (equal? (string-trim wikiname) ""))
|
||||||
|
(@queue . <~ . (λ (q) (append q (list wikiname))))
|
||||||
|
(shift-queue-maybe)))
|
||||||
|
|
||||||
|
(define (shift-queue-maybe)
|
||||||
|
(when (memq (obs-peek @state) '(waiting done))
|
||||||
|
(define q (obs-peek @queue))
|
||||||
|
(cond
|
||||||
|
[(pair? q)
|
||||||
|
(define wikiname (car q))
|
||||||
|
(:= @queue (cdr q))
|
||||||
|
(do-start-stage1 wikiname)]
|
||||||
|
[#t (:= @state 'done)])))
|
||||||
|
|
||||||
|
(define (do-start-stage1 wikiname)
|
||||||
|
(:= @just-done "")
|
||||||
|
(:= @done-pages 0)
|
||||||
|
(:= @num-pages 1)
|
||||||
|
(t (with-handlers ([exn:fail? (handle-graphical-exn wikiname)])
|
||||||
|
(:= @state 'stage-0)
|
||||||
|
(if-necessary-download-list-of-pages wikiname (λ (now-done num-pages just-done-name)
|
||||||
|
(:= @num-pages num-pages)
|
||||||
|
(:= @done-pages now-done)
|
||||||
|
(:= @just-done just-done-name)
|
||||||
|
(:= @state 'stage-1)))
|
||||||
|
(do-start-stage2 wikiname))))
|
||||||
|
|
||||||
|
(define (do-start-stage2 wikiname)
|
||||||
|
(:= @just-done "")
|
||||||
|
(:= @num-pages 1)
|
||||||
|
(:= @done-pages 0)
|
||||||
|
(t (with-handlers ([exn:fail? (handle-graphical-exn wikiname)])
|
||||||
|
(save-each-page wikiname (λ (now-done num-pages just-done-path)
|
||||||
|
(:= @num-pages num-pages)
|
||||||
|
(:= @done-pages now-done)
|
||||||
|
(:= @just-done just-done-path)))
|
||||||
|
(:= @state 'waiting)
|
||||||
|
(shift-queue-maybe)))
|
||||||
|
(:= @state 'stage-2))
|
||||||
|
|
||||||
|
(define (exn->string e)
|
||||||
|
(with-output-to-string
|
||||||
|
(λ ()
|
||||||
|
(displayln (exn-message e))
|
||||||
|
(displayln "context:")
|
||||||
|
(for ([item (continuation-mark-set->context (exn-continuation-marks e))])
|
||||||
|
(printf " ~a" (srcloc->string (cdr item)))
|
||||||
|
(when (car item)
|
||||||
|
(printf ": ~a" (car item)))
|
||||||
|
(displayln "")))))
|
||||||
|
|
||||||
|
(define ((handle-graphical-exn wikiname) e)
|
||||||
|
(displayln (exn->string e) (current-error-port))
|
||||||
|
(cond
|
||||||
|
[(obs-peek @auto-retry)
|
||||||
|
(do-retry-end wikiname)]
|
||||||
|
[#t
|
||||||
|
(:= @state 'err)
|
||||||
|
(thread
|
||||||
|
(λ ()
|
||||||
|
(define/obs @visible? #t)
|
||||||
|
(render
|
||||||
|
(dialog #:title "Download Error"
|
||||||
|
#:style '(resize-border)
|
||||||
|
#:mixin (λ (%) (class % (super-new)
|
||||||
|
(obs-observe! @visible? (λ (visible?) (send this show visible?)))))
|
||||||
|
(vpanel #:margin '(15 15)
|
||||||
|
(text "Encountered this error while downloading:")
|
||||||
|
(input #:style '(multiple hscroll)
|
||||||
|
#:min-size '(#f 200)
|
||||||
|
(exn->string e))
|
||||||
|
(button "Retry Now" (λ () (:= @visible? #f) (do-retry-now wikiname)))
|
||||||
|
(button "Retry Round-Robin" (λ () (:= @visible? #f) (do-retry-end wikiname)))
|
||||||
|
(button "Skip Wiki" (λ () (:= @visible? #f) (do-continue)))
|
||||||
|
(button "Use Auto-Retry" (λ ()
|
||||||
|
(:= @auto-retry #t)
|
||||||
|
(:= @visible? #f)
|
||||||
|
(do-retry-end wikiname)))
|
||||||
|
(text "Be careful not to auto-retry an infinite loop!")))
|
||||||
|
main-window)))
|
||||||
|
(sleep)
|
||||||
|
; make sure the old broken threads are all gone
|
||||||
|
(for ([th active-threads]) (kill-thread th))
|
||||||
|
(set-clear! active-threads)]))
|
||||||
|
|
||||||
|
(define (do-retry-now wikiname)
|
||||||
|
(@queue . <~ . (λ (q) (append (list wikiname) q)))
|
||||||
|
(:= @state 'waiting)
|
||||||
|
(shift-queue-maybe))
|
||||||
|
|
||||||
|
(define (do-retry-end wikiname)
|
||||||
|
(@queue . <~ . (λ (q) (append q (list wikiname))))
|
||||||
|
(:= @state 'waiting)
|
||||||
|
(shift-queue-maybe))
|
||||||
|
|
||||||
|
(define (do-continue)
|
||||||
|
(:= @state 'waiting)
|
||||||
|
(shift-queue-maybe))
|
||||||
|
|
||||||
|
(define (display-basename basename)
|
||||||
|
(define limit 40)
|
||||||
|
(cond [(string? basename)
|
||||||
|
(define query (basename->name-for-query basename))
|
||||||
|
(define segments (string-split query "/"))
|
||||||
|
(when (and ((string-length query) . > . limit) ((length segments) . >= . 2))
|
||||||
|
(set! query (string-append ".../" (last segments))))
|
||||||
|
(when ((string-length query) . > . limit)
|
||||||
|
(set! query (string-append (substring query 0 (- limit 3)) "...")))
|
||||||
|
query]
|
||||||
|
[#t "?"]))
|
||||||
|
|
||||||
|
(define main-window
|
||||||
|
(render
|
||||||
|
(window #:title @title
|
||||||
|
#:size '(360 200)
|
||||||
|
#:mixin (λ (%) (class %
|
||||||
|
(super-new)
|
||||||
|
(define/augment (on-close)
|
||||||
|
(for ([th active-threads]) (kill-thread th))
|
||||||
|
(disconnect slc))))
|
||||||
|
;; input box at the top
|
||||||
|
(hpanel (text "https://")
|
||||||
|
(input @wikiname
|
||||||
|
(λ (event data) (cond
|
||||||
|
[(eq? event 'input) (:= @wikiname data)]
|
||||||
|
[(eq? event 'return) (do-start-or-queue)])))
|
||||||
|
(text ".fandom.com"))
|
||||||
|
(button (@queue . ~> . (λ (q) (if (null? q) "Start" "Queue"))) (λ () (do-start-or-queue)))
|
||||||
|
(text (@queue . ~> . (λ (q) (if (null? q) "" (string-join #:before-first "Queue: " q ", ")))))
|
||||||
|
;; show status based on overall application state
|
||||||
|
(case-view
|
||||||
|
@state
|
||||||
|
;; waiting for wikiname entry
|
||||||
|
((waiting) (vpanel
|
||||||
|
(text "Fill in the wikiname and click start.")))
|
||||||
|
((stage-0) (vpanel
|
||||||
|
(text "Checking data...")))
|
||||||
|
((stage-1) (vpanel
|
||||||
|
(text "Gathering list of pages...")
|
||||||
|
(text (@just-done . ~> . display-basename))
|
||||||
|
(text (@done-pages . ~> . (λ (x) (if (eq? x 0)
|
||||||
|
"0/?"
|
||||||
|
(format "~a/~a" x (obs-peek @num-pages))))))))
|
||||||
|
;; downloading contents
|
||||||
|
((stage-2) (vpanel
|
||||||
|
(text "Downloading page text...")
|
||||||
|
(progress @done-pages #:range @num-pages)
|
||||||
|
(text (@done-pages . ~> . (λ (x) (format "~a/~a" x (obs-peek @num-pages)))))
|
||||||
|
(text (@just-done . ~> . display-basename))))
|
||||||
|
((done) (vpanel
|
||||||
|
(text "All wikis downloaded!")))
|
||||||
|
((err) (vpanel
|
||||||
|
(text "Error. Check the popup window.")))
|
||||||
|
(else (text (@state . ~> . (λ (state) (format "invalid state: ~a" state))))))
|
||||||
|
(checkbox #:label "Auto-retry on error? (Dangerous)"
|
||||||
|
#:checked? @auto-retry
|
||||||
|
(λ:= @auto-retry)))))
|
309
archiver/archiver.rkt
Normal file
309
archiver/archiver.rkt
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/file
|
||||||
|
racket/function
|
||||||
|
racket/list
|
||||||
|
racket/runtime-path
|
||||||
|
racket/string
|
||||||
|
net/url
|
||||||
|
net/mime
|
||||||
|
file/sha1
|
||||||
|
net/http-easy
|
||||||
|
db
|
||||||
|
"../lib/html-parsing/main.rkt"
|
||||||
|
json
|
||||||
|
"archiver-database.rkt"
|
||||||
|
"../lib/mime-types.rkt"
|
||||||
|
"../lib/tree-updater.rkt"
|
||||||
|
"../lib/url-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt"
|
||||||
|
"../lib/archive-file-mappings.rkt")
|
||||||
|
|
||||||
|
(define archive-slc slc)
|
||||||
|
|
||||||
|
(provide
|
||||||
|
if-necessary-download-list-of-pages
|
||||||
|
download-list-of-pages
|
||||||
|
save-each-page
|
||||||
|
basename->name-for-query
|
||||||
|
image-url->values
|
||||||
|
hash->save-dir
|
||||||
|
archive-slc)
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require rackunit))
|
||||||
|
|
||||||
|
(define-runtime-path archive-root "../storage/archive")
|
||||||
|
#;(define archive-root "archive")
|
||||||
|
|
||||||
|
(define sources '#hasheq((style . 1) (page . 2)))
|
||||||
|
|
||||||
|
(define (get-origin wikiname)
|
||||||
|
(format "https://~a.fandom.com" wikiname))
|
||||||
|
|
||||||
|
(define (insert-wiki-entry wikiname)
|
||||||
|
(define dest-url
|
||||||
|
(format "https://~a.fandom.com/api.php?~a"
|
||||||
|
wikiname
|
||||||
|
(params->query '(("action" . "query")
|
||||||
|
("meta" . "siteinfo")
|
||||||
|
("siprop" . "general|rightsinfo")
|
||||||
|
("format" . "json")
|
||||||
|
("formatversion" . "2")))))
|
||||||
|
(define data (response-json (get dest-url)))
|
||||||
|
(define exists? (query-maybe-value slc "select progress from wiki where wikiname = ?" wikiname))
|
||||||
|
(if exists?
|
||||||
|
(query-exec slc "update wiki set sitename = ?, basepage = ?, license_text = ?, license_url = ? where wikiname = ?"
|
||||||
|
(jp "/query/general/sitename" data)
|
||||||
|
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
||||||
|
(jp "/query/rightsinfo/text" data)
|
||||||
|
(jp "/query/rightsinfo/url" data)
|
||||||
|
wikiname)
|
||||||
|
(query-exec slc "insert into wiki (wikiname, progress, sitename, basepage, license_text, license_url) values (?, 1, ?, ?, ?, ?)"
|
||||||
|
wikiname
|
||||||
|
(jp "/query/general/sitename" data)
|
||||||
|
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
||||||
|
(jp "/query/rightsinfo/text" data)
|
||||||
|
(jp "/query/rightsinfo/url" data))))
|
||||||
|
|
||||||
|
;; call 1 if not yet done for that wiki
|
||||||
|
(define (if-necessary-download-list-of-pages wikiname callback)
|
||||||
|
(define wiki-progress (query-maybe-value slc "select progress from wiki where wikiname = ?" wikiname))
|
||||||
|
;; done yet?
|
||||||
|
(unless (and (real? wiki-progress) (wiki-progress . >= . 1))
|
||||||
|
;; count total pages
|
||||||
|
(define dest-url
|
||||||
|
(format "https://~a.fandom.com/api.php?~a"
|
||||||
|
wikiname
|
||||||
|
(params->query `(("action" . "query") ("meta" . "siteinfo") ("siprop" . "statistics") ("format" . "json")))))
|
||||||
|
(define num-pages (jp "/query/statistics/articles" (response-json (get dest-url))))
|
||||||
|
(download-list-of-pages wikiname callback 0 num-pages #f)))
|
||||||
|
|
||||||
|
;; 1. Download list of wiki pages and store in database
|
||||||
|
(define (download-list-of-pages wikiname callback total-so-far grand-total path-with-namefrom)
|
||||||
|
(define url (if path-with-namefrom
|
||||||
|
(format "https://~a.fandom.com~a" wikiname path-with-namefrom)
|
||||||
|
(format "https://~a.fandom.com/wiki/Local_Sitemap" wikiname)))
|
||||||
|
(define r (get url))
|
||||||
|
(define page (html->xexp (bytes->string/utf-8 (response-body r))))
|
||||||
|
(define link-namefrom
|
||||||
|
((query-selector (λ (t a c x) (and (eq? t 'a)
|
||||||
|
(pair? x)
|
||||||
|
(string-contains? (car x) "Next page")
|
||||||
|
(let ([href (get-attribute 'href a)] )
|
||||||
|
(and href (string-contains? href "/wiki/Local_Sitemap")))))
|
||||||
|
page #:include-text? #t)))
|
||||||
|
(define row-values
|
||||||
|
(for/list ([link (in-producer
|
||||||
|
(query-selector
|
||||||
|
(λ (t a c) (eq? t 'a))
|
||||||
|
((query-selector (λ (t a c) (has-class? "mw-allpages-chunk" a)) page)))
|
||||||
|
#f)])
|
||||||
|
(list wikiname (local-encoded-url->basename (get-attribute 'href (bits->attributes link))) 0)))
|
||||||
|
(define query-template (string-join (make-list (length row-values) "(?, ?, ?)") ", " #:before-first "insert or ignore into page (wikiname, basename, progress) values "))
|
||||||
|
(apply query-exec slc query-template (flatten row-values))
|
||||||
|
(define new-total (+ (length row-values) total-so-far))
|
||||||
|
(callback new-total grand-total (second (last row-values)))
|
||||||
|
(cond
|
||||||
|
[link-namefrom ; repeat on the next page
|
||||||
|
(download-list-of-pages wikiname callback new-total grand-total (get-attribute 'href (bits->attributes link-namefrom)))]
|
||||||
|
[#t ; all done downloading sitemap
|
||||||
|
(insert-wiki-entry wikiname)]))
|
||||||
|
|
||||||
|
;; 2. Download each page via API and:
|
||||||
|
;; * Save API response to file
|
||||||
|
(define max-page-progress 1)
|
||||||
|
(define (save-each-page wikiname callback)
|
||||||
|
;; prepare destination folder
|
||||||
|
(define save-dir (build-path archive-root wikiname))
|
||||||
|
(make-directory* save-dir)
|
||||||
|
;; gather list of basenames to download (that aren't yet complete)
|
||||||
|
(define basenames (query-list slc "select basename from page where wikiname = ? and progress < ?"
|
||||||
|
wikiname max-page-progress))
|
||||||
|
;; counter of complete/incomplete basenames
|
||||||
|
(define already-done-count
|
||||||
|
(query-value slc "select count(*) from page where wikiname = ? and progress = ?"
|
||||||
|
wikiname max-page-progress))
|
||||||
|
(define not-done-count
|
||||||
|
(query-value slc "select count(*) from page where wikiname = ? and progress < ?"
|
||||||
|
wikiname max-page-progress))
|
||||||
|
;; set initial progress
|
||||||
|
(callback already-done-count (+ already-done-count not-done-count) "")
|
||||||
|
;; loop through basenames and download
|
||||||
|
(for ([basename basenames]
|
||||||
|
[i (in-naturals 1)])
|
||||||
|
(define name-for-query (basename->name-for-query basename))
|
||||||
|
(define dest-url
|
||||||
|
(format "https://~a.fandom.com/api.php?~a"
|
||||||
|
wikiname
|
||||||
|
(params->query `(("action" . "parse")
|
||||||
|
("page" . ,name-for-query)
|
||||||
|
("prop" . "text|headhtml|langlinks")
|
||||||
|
("formatversion" . "2")
|
||||||
|
("format" . "json")))))
|
||||||
|
(define r (get dest-url))
|
||||||
|
(define body (response-body r))
|
||||||
|
(define filename (string-append basename ".json"))
|
||||||
|
(define save-path
|
||||||
|
(cond [((string-length basename) . > . 240)
|
||||||
|
(define key (sha1 (string->bytes/latin-1 basename)))
|
||||||
|
(query-exec slc "insert into special_page (wikiname, key, basename) values (?, ?, ?)"
|
||||||
|
wikiname key basename)
|
||||||
|
(build-path save-dir (string-append key ".json"))]
|
||||||
|
[#t
|
||||||
|
(build-path save-dir (string-append basename ".json"))]))
|
||||||
|
(display-to-file body save-path #:exists 'replace)
|
||||||
|
(query-exec slc "update page set progress = 1 where wikiname = ? and basename = ?"
|
||||||
|
wikiname basename)
|
||||||
|
(callback (+ already-done-count i) (+ already-done-count not-done-count) basename))
|
||||||
|
;; saved all pages, register that fact in the database
|
||||||
|
(query-exec slc "update wiki set progress = 2 where wikiname = ?" wikiname))
|
||||||
|
|
||||||
|
;; 3. Download CSS and:
|
||||||
|
;; * Save CSS to file
|
||||||
|
;; * Record style images to database
|
||||||
|
(define (check-style-for-images wikiname path)
|
||||||
|
(define content (file->string path))
|
||||||
|
(define urls (regexp-match* #rx"url\\(\"?'?([^)]*)'?\"?\\)" content #:match-select cadr))
|
||||||
|
(for/list ([url urls]
|
||||||
|
#:when (not (or (equal? url "")
|
||||||
|
(equal? url "'")
|
||||||
|
(string-contains? url "/resources-ucp/")
|
||||||
|
(string-contains? url "/fonts/")
|
||||||
|
(string-contains? url "/drm_fonts/")
|
||||||
|
(string-contains? url "//db.onlinewebfonts.com/")
|
||||||
|
(string-contains? url "//bits.wikimedia.org/")
|
||||||
|
(string-contains? url "dropbox")
|
||||||
|
(string-contains? url "only=styles")
|
||||||
|
(string-contains? url "https://https://")
|
||||||
|
(regexp-match? #rx"^%20" url)
|
||||||
|
(regexp-match? #rx"^data:" url))))
|
||||||
|
(cond
|
||||||
|
[(string-prefix? url "https://") url]
|
||||||
|
[(string-prefix? url "http://") (regexp-replace #rx"http:" url "https:")]
|
||||||
|
[(string-prefix? url "//") (string-append "https:" url)]
|
||||||
|
[(string-prefix? url "/") (format "https://~a.fandom.com~a" wikiname url)]
|
||||||
|
[else (raise-user-error "While calling check-style-for-images, this URL had an unknown format and couldn't be saved:" url path)])))
|
||||||
|
|
||||||
|
(define (download-styles-for-wiki wikiname)
|
||||||
|
(define save-dir (build-path archive-root wikiname "styles"))
|
||||||
|
(make-directory* save-dir)
|
||||||
|
(define theme (λ (theme-name)
|
||||||
|
(cons (format "https://~a.fandom.com/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" wikiname theme-name)
|
||||||
|
(build-path save-dir (format "themeVariables-~a.css" theme-name)))))
|
||||||
|
;; (Listof (Pair url save-path))
|
||||||
|
(define styles
|
||||||
|
(list
|
||||||
|
(theme "default")
|
||||||
|
(theme "light")
|
||||||
|
(theme "dark")
|
||||||
|
(cons (format "https://~a.fandom.com/load.php?lang=en&modules=skin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles%7Csite.styles&only=styles&skin=fandomdesktop" wikiname)
|
||||||
|
(build-path save-dir "site.css"))))
|
||||||
|
(for ([style styles])
|
||||||
|
(define r (get (car style)))
|
||||||
|
(define body (response-body r))
|
||||||
|
(display-to-file body (cdr style) #:exists 'replace)
|
||||||
|
;; XXX: how the HELL do I deal with @import?? would need some kind of recursion here. how will the page server know where to look up the style file to be able to serve them again? do I add another link-stylesheet tag to the main page? what about the remaining stuck @import url?
|
||||||
|
)
|
||||||
|
styles)
|
||||||
|
|
||||||
|
(define (do-step-3 wikiname)
|
||||||
|
(define wiki-progress (query-maybe-value slc "select progress from wiki where wikiname = ?" wikiname))
|
||||||
|
(unless (and (number? wiki-progress) (wiki-progress . >= . 3))
|
||||||
|
(define styles (download-styles-for-wiki wikiname))
|
||||||
|
(define unique-image-urls
|
||||||
|
(remove-duplicates
|
||||||
|
(map image-url->values
|
||||||
|
(flatten
|
||||||
|
(for/list ([style styles])
|
||||||
|
(check-style-for-images wikiname (cdr style)))))
|
||||||
|
#:key cdr))
|
||||||
|
(println unique-image-urls)
|
||||||
|
(for ([pair unique-image-urls])
|
||||||
|
(query-exec slc "insert or ignore into image (wikiname, url, hash, ext, source, progress) values (?, ?, ?, NULL, 1, 0)" wikiname (car pair) (cdr pair)))
|
||||||
|
(query-exec slc "update wiki set progress = 3 where wikiname = ?" wikiname)))
|
||||||
|
|
||||||
|
;; 4: From downloaded pages, record URLs of image sources and inline style images to database
|
||||||
|
(define (hash->save-dir wikiname hash)
|
||||||
|
(build-path archive-root wikiname "images" (substring hash 0 1) (substring hash 0 2)))
|
||||||
|
|
||||||
|
(define (image-url->values i)
|
||||||
|
;; TODO: handle case where there is multiple cb parameter on minecraft wiki
|
||||||
|
;; TODO: ensure it still "works" with broken & on minecraft wiki
|
||||||
|
(define no-cb (regexp-replace #rx"\\cb=[0-9]+&?" i "")) ; remove cb url parameter which does nothing
|
||||||
|
(define key (regexp-replace #rx"[&?]$" no-cb "")) ; remove extra separator if necessary
|
||||||
|
(define hash (sha1 (string->bytes/utf-8 key)))
|
||||||
|
(cons key hash))
|
||||||
|
|
||||||
|
(define (check-json-for-images wikiname path)
|
||||||
|
(define data (with-input-from-file path (λ () (read-json))))
|
||||||
|
(define page (html->xexp (preprocess-html-wiki (jp "/parse/text" data))))
|
||||||
|
(define tree (update-tree-wiki page wikiname))
|
||||||
|
(remove-duplicates
|
||||||
|
(for/list ([element (in-producer
|
||||||
|
(query-selector
|
||||||
|
(λ (t a c)
|
||||||
|
(and (eq? t 'img)
|
||||||
|
(get-attribute 'src a)))
|
||||||
|
tree)
|
||||||
|
#f)])
|
||||||
|
(image-url->values (get-attribute 'src (bits->attributes element))))))
|
||||||
|
|
||||||
|
;; 5. Download image sources and style images according to database
|
||||||
|
(define (save-each-image wikiname source callback)
|
||||||
|
;; gather list of basenames to download (that aren't yet complete)
|
||||||
|
(define rows (query-rows slc "select url, hash from image where wikiname = ? and source <= ? and progress < 1"
|
||||||
|
wikiname source))
|
||||||
|
;; counter of complete/incomplete basenames
|
||||||
|
(define already-done-count
|
||||||
|
(query-value slc "select count(*) from image where wikiname = ? and source <= ? and progress = 1"
|
||||||
|
wikiname source))
|
||||||
|
(define not-done-count
|
||||||
|
(query-value slc "select count(*) from image where wikiname = ? and source <= ? and progress < 1"
|
||||||
|
wikiname source))
|
||||||
|
;; set initial progress
|
||||||
|
(callback already-done-count (+ already-done-count not-done-count) "")
|
||||||
|
;; loop through urls and download
|
||||||
|
(for ([row rows]
|
||||||
|
[i (in-naturals 1)])
|
||||||
|
;; row fragments
|
||||||
|
(define url (vector-ref row 0))
|
||||||
|
(define hash (vector-ref row 1))
|
||||||
|
;; check
|
||||||
|
(printf "~a -> ~a~n" url hash)
|
||||||
|
(define r (get url))
|
||||||
|
(define declared-type (response-headers-ref r 'content-type))
|
||||||
|
(define final-type (if (equal? declared-type #"application/octet-stream")
|
||||||
|
(let ([sniff-entity (message-entity (mime-analyze (response-body r)))])
|
||||||
|
(string->bytes/latin-1 (format "~a/~a" (entity-type sniff-entity) (entity-subtype sniff-entity))))
|
||||||
|
declared-type))
|
||||||
|
(define ext (bytes->string/latin-1 (mime-type->ext final-type)))
|
||||||
|
;; save
|
||||||
|
(define save-dir (hash->save-dir wikiname hash))
|
||||||
|
(make-directory* save-dir)
|
||||||
|
(define save-path (build-path save-dir (string-append hash "." ext)))
|
||||||
|
(define body (response-body r))
|
||||||
|
(display-to-file body save-path #:exists 'replace)
|
||||||
|
(query-exec slc "update image set progress = 1, ext = ? where wikiname = ? and hash = ?"
|
||||||
|
ext wikiname hash)
|
||||||
|
(callback (+ already-done-count i) (+ already-done-count not-done-count) (string-append hash "." ext)))
|
||||||
|
;; TODO: saved all images, register that fact in the database
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(check-equal? (html->xexp "<img src=\"https://example.com/images?src=Blah.jpg&width=150\">")
|
||||||
|
'(*TOP* (img (@ (src "https://example.com/images?src=Blah.jpg&width=150")))))
|
||||||
|
#;(download-list-of-pages "minecraft" values)
|
||||||
|
#;(save-each-page "minecraft" values)
|
||||||
|
#;(check-json-for-images "chiki" (build-path archive-root "chiki" "Fiona.json"))
|
||||||
|
#;(do-step-3 "gallowmere")
|
||||||
|
#;(save-each-image "gallowmere" (hash-ref sources 'style) (λ (a b c) (printf "~a/~a ~a~n" a b c)))
|
||||||
|
|
||||||
|
#;(for ([wikiname (query-list slc "select wikiname from wiki")])
|
||||||
|
(println wikiname)
|
||||||
|
(insert-wiki-entry wikiname))
|
||||||
|
|
||||||
|
#;(for ([wikiname (query-list slc "select wikiname from wiki")])
|
||||||
|
(println wikiname)
|
||||||
|
(do-step-3 wikiname)
|
||||||
|
(save-each-image wikiname (hash-ref sources 'style) (λ (a b c) (printf "~a/~a ~a~n" a b c)))))
|
|
@ -19,8 +19,10 @@
|
||||||
(require-reloadable "src/page-search.rkt" page-search)
|
(require-reloadable "src/page-search.rkt" page-search)
|
||||||
(require-reloadable "src/page-set-user-settings.rkt" page-set-user-settings)
|
(require-reloadable "src/page-set-user-settings.rkt" page-set-user-settings)
|
||||||
(require-reloadable "src/page-static.rkt" static-dispatcher)
|
(require-reloadable "src/page-static.rkt" static-dispatcher)
|
||||||
|
(require-reloadable "src/page-static-archive.rkt" page-static-archive)
|
||||||
(require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher)
|
(require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher)
|
||||||
(require-reloadable "src/page-wiki.rkt" page-wiki)
|
(require-reloadable "src/page-wiki.rkt" page-wiki)
|
||||||
|
(require-reloadable "src/page-wiki-offline.rkt" page-wiki-offline)
|
||||||
(require-reloadable "src/page-file.rkt" page-file)
|
(require-reloadable "src/page-file.rkt" page-file)
|
||||||
|
|
||||||
(reload!)
|
(reload!)
|
||||||
|
@ -42,7 +44,9 @@
|
||||||
page-proxy
|
page-proxy
|
||||||
page-search
|
page-search
|
||||||
page-set-user-settings
|
page-set-user-settings
|
||||||
|
page-static-archive
|
||||||
page-wiki
|
page-wiki
|
||||||
|
page-wiki-offline
|
||||||
page-file
|
page-file
|
||||||
redirect-wiki-home
|
redirect-wiki-home
|
||||||
static-dispatcher
|
static-dispatcher
|
||||||
|
|
4
dist.rkt
4
dist.rkt
|
@ -13,8 +13,10 @@
|
||||||
(require (only-in "src/page-search.rkt" page-search))
|
(require (only-in "src/page-search.rkt" page-search))
|
||||||
(require (only-in "src/page-set-user-settings.rkt" page-set-user-settings))
|
(require (only-in "src/page-set-user-settings.rkt" page-set-user-settings))
|
||||||
(require (only-in "src/page-static.rkt" static-dispatcher))
|
(require (only-in "src/page-static.rkt" static-dispatcher))
|
||||||
|
(require (only-in "src/page-static-archive.rkt" page-static-archive))
|
||||||
(require (only-in "src/page-subdomain.rkt" subdomain-dispatcher))
|
(require (only-in "src/page-subdomain.rkt" subdomain-dispatcher))
|
||||||
(require (only-in "src/page-wiki.rkt" page-wiki))
|
(require (only-in "src/page-wiki.rkt" page-wiki))
|
||||||
|
(require (only-in "src/page-wiki-offline.rkt" page-wiki-offline))
|
||||||
(require (only-in "src/page-file.rkt" page-file))
|
(require (only-in "src/page-file.rkt" page-file))
|
||||||
|
|
||||||
(serve/launch/wait
|
(serve/launch/wait
|
||||||
|
@ -31,7 +33,9 @@
|
||||||
page-proxy
|
page-proxy
|
||||||
page-search
|
page-search
|
||||||
page-set-user-settings
|
page-set-user-settings
|
||||||
|
page-static-archive
|
||||||
page-wiki
|
page-wiki
|
||||||
|
page-wiki-offline
|
||||||
page-file
|
page-file
|
||||||
redirect-wiki-home
|
redirect-wiki-home
|
||||||
static-dispatcher
|
static-dispatcher
|
||||||
|
|
28
lib/archive-file-mappings.rkt
Normal file
28
lib/archive-file-mappings.rkt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/string
|
||||||
|
net/url
|
||||||
|
(only-in net/uri-codec uri-decode)
|
||||||
|
"url-utils.rkt")
|
||||||
|
(provide
|
||||||
|
local-encoded-url->segments
|
||||||
|
url-segments->basename
|
||||||
|
local-encoded-url->basename
|
||||||
|
basename->name-for-query
|
||||||
|
url-segments->guess-title)
|
||||||
|
|
||||||
|
(define (local-encoded-url->segments str) ; '("wiki" "Page_title")
|
||||||
|
(map path/param-path (url-path (string->url str))))
|
||||||
|
|
||||||
|
(define (url-segments->basename segments) ; "Page_title" filename encoded, no extension or dir prefix
|
||||||
|
(define extra-encoded (map (λ (s) (bytes->string/latin-1 (percent-encode s filename-set #f))) (cdr segments)))
|
||||||
|
(define basic-filename (string-join extra-encoded "#"))
|
||||||
|
basic-filename)
|
||||||
|
|
||||||
|
(define (local-encoded-url->basename str) ; '("wiki" "Page_title"), no extension or dir prefix
|
||||||
|
(url-segments->basename (local-encoded-url->segments str)))
|
||||||
|
|
||||||
|
(define (basename->name-for-query str)
|
||||||
|
(uri-decode (regexp-replace* #rx"#" str "/")))
|
||||||
|
|
||||||
|
(define (url-segments->guess-title segments)
|
||||||
|
(regexp-replace* #rx"_" (cadr segments) " "))
|
1887
lib/html-parsing/main.rkt
Normal file
1887
lib/html-parsing/main.rkt
Normal file
File diff suppressed because it is too large
Load diff
34
lib/mime-types.rkt
Normal file
34
lib/mime-types.rkt
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/contract
|
||||||
|
racket/match
|
||||||
|
racket/path
|
||||||
|
racket/runtime-path
|
||||||
|
racket/string)
|
||||||
|
|
||||||
|
(provide
|
||||||
|
(contract-out
|
||||||
|
[ext->mime-type (-> bytes? bytes?)]
|
||||||
|
[mime-type->ext (-> bytes? bytes?)]))
|
||||||
|
|
||||||
|
(define-runtime-path mime.types-path "mime.types")
|
||||||
|
|
||||||
|
(define ls
|
||||||
|
(call-with-input-file mime.types-path
|
||||||
|
(λ (in) (for/list ([line (in-lines in)]
|
||||||
|
#:when (not (regexp-match? #rx"^ *($|#)" line)))
|
||||||
|
(match line
|
||||||
|
[(regexp #rx"^([^ ]+) +(.+)$" (list _ mime ext))
|
||||||
|
(cons (string->bytes/utf-8 ext) (string->bytes/utf-8 mime))]
|
||||||
|
[(regexp #rx"^ *#") (void)]
|
||||||
|
[_ (log-warning "mime-types: failed to parse line ~s" line)])))))
|
||||||
|
|
||||||
|
(define forward-hash (make-immutable-hash ls))
|
||||||
|
(define reverse-hash (make-immutable-hash (map (λ (x) (cons (cdr x) (car x))) ls)))
|
||||||
|
|
||||||
|
(define (ext->mime-type ext-in)
|
||||||
|
(define ext (regexp-replace #rx"^\\." ext-in #""))
|
||||||
|
(hash-ref forward-hash ext))
|
||||||
|
|
||||||
|
(define (mime-type->ext m-in)
|
||||||
|
(define m (regexp-replace #rx";.*" m-in #""))
|
||||||
|
(hash-ref reverse-hash m))
|
85
lib/mime.types
Normal file
85
lib/mime.types
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
text/html html
|
||||||
|
text/css css
|
||||||
|
text/xml xml
|
||||||
|
image/gif gif
|
||||||
|
image/jpeg jpeg
|
||||||
|
application/javascript js
|
||||||
|
text/javascript js
|
||||||
|
application/atom+xml atom
|
||||||
|
application/rss+xml rss
|
||||||
|
|
||||||
|
text/mathml mml
|
||||||
|
text/plain txt
|
||||||
|
text/x-component htc
|
||||||
|
|
||||||
|
image/png png
|
||||||
|
image/tiff tiff
|
||||||
|
image/vnd.wap.wbmp wbmp
|
||||||
|
image/x-icon ico
|
||||||
|
image/x-jng jng
|
||||||
|
image/x-ms-bmp bmp
|
||||||
|
image/svg+xml svg
|
||||||
|
image/webp webp
|
||||||
|
|
||||||
|
application/font-woff2 woff2
|
||||||
|
application/acad woff2
|
||||||
|
font/woff2 woff2
|
||||||
|
application/font-woff woff
|
||||||
|
application/x-font-ttf ttf
|
||||||
|
application/x-font-truetype ttf
|
||||||
|
application/x-truetype-font ttf
|
||||||
|
application/font-sfnt ttf
|
||||||
|
font/sfnt ttf
|
||||||
|
application/vnd.oasis.opendocument.formula-template otf
|
||||||
|
application/x-font-opentype otf
|
||||||
|
application/vnd.ms-opentype otf
|
||||||
|
font/otf otf
|
||||||
|
application/java-archive jar
|
||||||
|
application/json json
|
||||||
|
application/mac-binhex40 hqx
|
||||||
|
application/msword doc
|
||||||
|
application/pdf pdf
|
||||||
|
application/postscript ps
|
||||||
|
application/rtf rtf
|
||||||
|
application/vnd.apple.mpegurl m3u8
|
||||||
|
application/vnd.ms-excel xls
|
||||||
|
application/vnd.ms-fontobject eot
|
||||||
|
application/vnd.ms-powerpoint ppt
|
||||||
|
application/vnd.wap.wmlc wmlc
|
||||||
|
application/vnd.google-earth.kml+xml kml
|
||||||
|
application/vnd.google-earth.kmz kmz
|
||||||
|
application/x-7z-compressed 7z
|
||||||
|
application/x-cocoa cco
|
||||||
|
application/x-java-archive-diff jardiff
|
||||||
|
application/x-java-jnlp-file jnlp
|
||||||
|
application/x-makeself run
|
||||||
|
application/x-perl pl
|
||||||
|
application/x-rar-compressed rar
|
||||||
|
application/x-redhat-package-manager rpm
|
||||||
|
application/x-sea sea
|
||||||
|
application/x-shockwave-flash swf
|
||||||
|
application/x-stuffit sit
|
||||||
|
application/x-tcl tcl
|
||||||
|
application/x-x509-ca-cert pem
|
||||||
|
application/x-xpinstall xpi
|
||||||
|
application/xhtml+xml xhtml
|
||||||
|
application/xspf+xml xspf
|
||||||
|
application/zip zip
|
||||||
|
application/gzip gz
|
||||||
|
|
||||||
|
audio/midi mid midi kar
|
||||||
|
audio/mpeg mp3
|
||||||
|
audio/ogg ogg
|
||||||
|
audio/x-m4a m4a
|
||||||
|
audio/x-realaudio ra
|
||||||
|
|
||||||
|
video/mp2t ts
|
||||||
|
video/mp4 mp4
|
||||||
|
video/mpeg mpeg
|
||||||
|
video/quicktime mov
|
||||||
|
video/webm webm
|
||||||
|
video/x-flv flv
|
||||||
|
video/x-m4v m4v
|
||||||
|
video/x-mng mng
|
||||||
|
video/x-ms-wmv wmv
|
||||||
|
video/x-msvideo avi
|
|
@ -4,13 +4,19 @@
|
||||||
; call the updater on the dictionary key only if it has that key
|
; call the updater on the dictionary key only if it has that key
|
||||||
alist-maybe-update
|
alist-maybe-update
|
||||||
; update a value only if a condition succeeds on it
|
; update a value only if a condition succeeds on it
|
||||||
u)
|
u
|
||||||
|
; like string-join, but for lists
|
||||||
|
list-join
|
||||||
|
u-counter)
|
||||||
|
|
||||||
(module+ test
|
(module+ test
|
||||||
(require "typed-rackunit.rkt"))
|
(require "typed-rackunit.rkt"))
|
||||||
|
|
||||||
|
(define u-counter (box 0))
|
||||||
|
|
||||||
(: alist-maybe-update (∀ (A B) ((Listof (Pairof A B)) A (B -> B) -> (Listof (Pairof A B)))))
|
(: alist-maybe-update (∀ (A B) ((Listof (Pairof A B)) A (B -> B) -> (Listof (Pairof A B)))))
|
||||||
(define (alist-maybe-update alist key updater)
|
(define (alist-maybe-update alist key updater)
|
||||||
|
(set-box! u-counter (add1 (unbox u-counter)))
|
||||||
(map (λ ([p : (Pairof A B)])
|
(map (λ ([p : (Pairof A B)])
|
||||||
(if (eq? (car p) key)
|
(if (eq? (car p) key)
|
||||||
(cons (car p) (updater (cdr p)))
|
(cons (car p) (updater (cdr p)))
|
||||||
|
@ -24,7 +30,16 @@
|
||||||
|
|
||||||
(: u (∀ (A) ((A -> Any) (A -> A) A -> A)))
|
(: u (∀ (A) ((A -> Any) (A -> A) A -> A)))
|
||||||
(define (u condition updater value)
|
(define (u condition updater value)
|
||||||
|
(set-box! u-counter (add1 (unbox u-counter)))
|
||||||
(if (condition value) (updater value) value))
|
(if (condition value) (updater value) value))
|
||||||
(module+ test
|
(module+ test
|
||||||
(check-equal? (u (λ ([x : Integer]) (< x 5)) (λ ([x : Integer]) (* x -1)) 4) -4)
|
(check-equal? (u (λ ([x : Integer]) (< x 5)) (λ ([x : Integer]) (* x -1)) 4) -4)
|
||||||
(check-equal? (u (λ ([x : Integer]) (< x 5)) (λ ([x : Integer]) (* x -1)) 8) 8))
|
(check-equal? (u (λ ([x : Integer]) (< x 5)) (λ ([x : Integer]) (* x -1)) 8) 8))
|
||||||
|
|
||||||
|
(: list-join (∀ (A B) (A (Listof B) -> (Listof (U A B)))))
|
||||||
|
(define (list-join element ls)
|
||||||
|
(if (pair? (cdr ls))
|
||||||
|
(list* (car ls) element (list-join element (cdr ls)))
|
||||||
|
(list (car ls))))
|
||||||
|
(module+ test
|
||||||
|
(check-equal? (list-join "h" '(2 3 4 5)) '(2 "h" 3 "h" 4 "h" 5)))
|
|
@ -5,7 +5,11 @@
|
||||||
; help make a nested if. if/in will gain the same false form of its containing if/out.
|
; help make a nested if. if/in will gain the same false form of its containing if/out.
|
||||||
if/out
|
if/out
|
||||||
; let, but the value for each variable is evaluated within a thread
|
; let, but the value for each variable is evaluated within a thread
|
||||||
thread-let)
|
thread-let
|
||||||
|
; cond, but values can be defined between conditions
|
||||||
|
cond/var
|
||||||
|
; wrap sql statements into lambdas so they can be executed during migration
|
||||||
|
wrap-sql)
|
||||||
|
|
||||||
(module+ test
|
(module+ test
|
||||||
(require rackunit)
|
(require rackunit)
|
||||||
|
@ -17,9 +21,12 @@
|
||||||
;; it's in a submodule so that it can be required in both levels, for testing
|
;; it's in a submodule so that it can be required in both levels, for testing
|
||||||
|
|
||||||
(module transform racket/base
|
(module transform racket/base
|
||||||
|
(require racket/list)
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
transform-if/out
|
transform-if/out
|
||||||
transform-thread-let)
|
transform-thread-let
|
||||||
|
transform/out-cond/var)
|
||||||
|
|
||||||
(define (transform-if/out stx)
|
(define (transform-if/out stx)
|
||||||
(define tree (cdr (syntax->datum stx))) ; condition true false
|
(define tree (cdr (syntax->datum stx))) ; condition true false
|
||||||
|
@ -62,12 +69,46 @@
|
||||||
(define def (list-ref defs n))
|
(define def (list-ref defs n))
|
||||||
`(,(car def) (channel-get (vector-ref chv ,n))))
|
`(,(car def) (channel-get (vector-ref chv ,n))))
|
||||||
counter)
|
counter)
|
||||||
,@forms)))))
|
,@forms))))
|
||||||
|
|
||||||
|
(define (transform/out-cond/var stx)
|
||||||
|
(define tree (transform-cond/var (cdr (syntax->datum stx))))
|
||||||
|
(datum->syntax
|
||||||
|
stx
|
||||||
|
tree))
|
||||||
|
|
||||||
|
(define (transform-cond/var tree)
|
||||||
|
(define-values (els temp) (splitf-at tree (λ (el) (and (pair? el) (not (eq? (car el) 'var))))))
|
||||||
|
(define-values (vars rest) (splitf-at temp (λ (el) (and (pair? el) (eq? (car el) 'var)))))
|
||||||
|
(if (null? rest)
|
||||||
|
`(cond ,@els)
|
||||||
|
`(cond
|
||||||
|
,@els
|
||||||
|
[#t
|
||||||
|
(let ,(for/list ([var vars])
|
||||||
|
(cdr var))
|
||||||
|
,(transform-cond/var rest))]))))
|
||||||
|
|
||||||
;; the syntax definitions and their tests go below here
|
;; the syntax definitions and their tests go below here
|
||||||
|
|
||||||
(require 'transform (for-syntax 'transform))
|
(require 'transform (for-syntax 'transform))
|
||||||
|
|
||||||
|
(define-syntax (wrap-sql stx)
|
||||||
|
; the arguments
|
||||||
|
(define xs (cdr (syntax->list stx)))
|
||||||
|
; wrap each argument
|
||||||
|
(define wrapped (map (λ (xe) ; xe is the syntax of an argument
|
||||||
|
(if (list? (car (syntax->datum xe)))
|
||||||
|
; it's a list of lists (a list of sql migration steps)
|
||||||
|
; return instead syntax of a lambda that will call everything in xe
|
||||||
|
(datum->syntax stx `(λ () ,@xe))
|
||||||
|
; it's just a single sql migration step
|
||||||
|
; return instead syntax of a lambda that will call xe
|
||||||
|
(datum->syntax stx `(λ () ,xe))))
|
||||||
|
xs))
|
||||||
|
; since I'm returning *code*, I need to return the form (list ...) so that runtime makes a list
|
||||||
|
(datum->syntax stx `(list ,@wrapped)))
|
||||||
|
|
||||||
(define-syntax (if/out stx)
|
(define-syntax (if/out stx)
|
||||||
(transform-if/out stx))
|
(transform-if/out stx))
|
||||||
(module+ test
|
(module+ test
|
||||||
|
@ -106,3 +147,15 @@
|
||||||
; check that it assigns the correct value to the correct variable
|
; check that it assigns the correct value to the correct variable
|
||||||
(check-equal? (thread-let ([a (sleep 0) 'a] [b 'b]) (list a b))
|
(check-equal? (thread-let ([a (sleep 0) 'a] [b 'b]) (list a b))
|
||||||
'(a b)))
|
'(a b)))
|
||||||
|
|
||||||
|
(define-syntax (cond/var stx)
|
||||||
|
(transform/out-cond/var stx))
|
||||||
|
(module+ test
|
||||||
|
(check-syntax-equal? (transform/out-cond/var #'(cond/def [#f 0] (var d (* a 2)) [(eq? d 8) d] [#t "not 4"]))
|
||||||
|
#'(cond
|
||||||
|
[#f 0]
|
||||||
|
[#t
|
||||||
|
(let ([d (* a 2)])
|
||||||
|
(cond
|
||||||
|
[(eq? d 8) d]
|
||||||
|
[#t "not 4"]))])))
|
|
@ -3,17 +3,35 @@
|
||||||
racket/function
|
racket/function
|
||||||
racket/match
|
racket/match
|
||||||
racket/string
|
racket/string
|
||||||
"config.rkt"
|
|
||||||
"pure-utils.rkt"
|
"pure-utils.rkt"
|
||||||
"url-utils.rkt"
|
"url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
|
preprocess-html-wiki
|
||||||
update-tree-wiki)
|
update-tree-wiki)
|
||||||
|
|
||||||
|
(define (preprocess-html-wiki html)
|
||||||
|
(define ((rr* find replace) contents)
|
||||||
|
(regexp-replace* find contents replace))
|
||||||
|
((compose1
|
||||||
|
; fix navbox list nesting
|
||||||
|
; navbox on right of page has incorrect html "<td ...><li>" and the xexpr parser puts the <li> much further up the tree
|
||||||
|
; add a <ul> to make the parser happy
|
||||||
|
; usage: /fallout/wiki/Fallout:_New_Vegas_achievements_and_trophies
|
||||||
|
(rr* #rx"(<td[^>]*>\n?)(<li>)" "\\1<ul>\\2")
|
||||||
|
; change <figcaption><p> to <figcaption><span> to make the parser happy
|
||||||
|
(rr* #rx"(<figcaption[^>]*>)[ \t]*<p class=\"caption\">([^<]*)</p>" "\\1<span class=\"caption\">\\2</span>"))
|
||||||
|
html))
|
||||||
|
(module+ test
|
||||||
|
(check-equal? (preprocess-html-wiki "<td class=\"va-navbox-column\" style=\"width: 33%\">\n<li>Hey</li>")
|
||||||
|
"<td class=\"va-navbox-column\" style=\"width: 33%\">\n<ul><li>Hey</li>")
|
||||||
|
(check-equal? (preprocess-html-wiki "<figure class=\"thumb tright\" style=\"width: 150px\"><a class=\"image\"><img></a><noscript><a><img></a></noscript><figcaption class=\"thumbcaption\"> <p class=\"caption\">Caption text.</p></figcaption></figure>")
|
||||||
|
"<figure class=\"thumb tright\" style=\"width: 150px\"><a class=\"image\"><img></a><noscript><a><img></a></noscript><figcaption class=\"thumbcaption\"><span class=\"caption\">Caption text.</span></figcaption></figure>"))
|
||||||
|
|
||||||
(module+ test
|
(module+ test
|
||||||
(require rackunit
|
(require rackunit
|
||||||
html-parsing)
|
"html-parsing/main.rkt")
|
||||||
(define wiki-document
|
(define wiki-document
|
||||||
'(*TOP*
|
'(*TOP*
|
||||||
(div (@ (class "mw-parser-output"))
|
(div (@ (class "mw-parser-output"))
|
||||||
|
@ -47,7 +65,7 @@
|
||||||
(figcaption "Test figure!"))
|
(figcaption "Test figure!"))
|
||||||
(iframe (@ (src "https://example.com/iframe-src")))))))
|
(iframe (@ (src "https://example.com/iframe-src")))))))
|
||||||
|
|
||||||
(define (updater wikiname)
|
(define (updater wikiname #:strict-proxy? [strict-proxy? #f])
|
||||||
(define classlist-updater
|
(define classlist-updater
|
||||||
(compose1
|
(compose1
|
||||||
; uncollapse all navbox items (bottom of page mass navigation)
|
; uncollapse all navbox items (bottom of page mass navigation)
|
||||||
|
@ -101,7 +119,7 @@
|
||||||
'(""))))
|
'(""))))
|
||||||
; proxy images from inline styles, if strict_proxy is set
|
; proxy images from inline styles, if strict_proxy is set
|
||||||
(curry u
|
(curry u
|
||||||
(λ (v) (config-true? 'strict_proxy))
|
(λ (v) strict-proxy?)
|
||||||
(λ (v) (attribute-maybe-update
|
(λ (v) (attribute-maybe-update
|
||||||
'style
|
'style
|
||||||
(λ (style)
|
(λ (style)
|
||||||
|
@ -114,14 +132,14 @@
|
||||||
; and also their links, if strict_proxy is set
|
; and also their links, if strict_proxy is set
|
||||||
(curry u
|
(curry u
|
||||||
(λ (v)
|
(λ (v)
|
||||||
(and (config-true? 'strict_proxy)
|
(and strict-proxy?
|
||||||
#;(eq? element-type 'a)
|
#;(eq? element-type 'a)
|
||||||
(or (has-class? "image-thumbnail" v)
|
(or (has-class? "image-thumbnail" v)
|
||||||
(has-class? "image" v))))
|
(has-class? "image" v))))
|
||||||
(λ (v) (attribute-maybe-update 'href u-proxy-url v)))
|
(λ (v) (attribute-maybe-update 'href u-proxy-url v)))
|
||||||
; proxy images from src attributes, if strict_proxy is set
|
; proxy images from src attributes, if strict_proxy is set
|
||||||
(curry u
|
(curry u
|
||||||
(λ (v) (config-true? 'strict_proxy))
|
(λ (v) strict-proxy?)
|
||||||
(λ (v) (attribute-maybe-update 'src u-proxy-url v)))
|
(λ (v) (attribute-maybe-update 'src u-proxy-url v)))
|
||||||
; don't lazyload images
|
; don't lazyload images
|
||||||
(curry u
|
(curry u
|
||||||
|
@ -208,13 +226,12 @@
|
||||||
|
|
||||||
updater)
|
updater)
|
||||||
|
|
||||||
(define (update-tree-wiki tree wikiname)
|
(define (update-tree-wiki tree wikiname #:strict-proxy? [strict-proxy? #f])
|
||||||
(update-tree (updater wikiname) tree))
|
(update-tree (updater wikiname #:strict-proxy? strict-proxy?) tree))
|
||||||
|
|
||||||
(module+ test
|
(module+ test
|
||||||
(define transformed
|
(define transformed
|
||||||
(parameterize ([(config-parameter 'strict_proxy) "true"])
|
(update-tree-wiki wiki-document "test" #:strict-proxy? #t))
|
||||||
(update-tree-wiki wiki-document "test")))
|
|
||||||
; check that wikilinks are changed to be local
|
; check that wikilinks are changed to be local
|
||||||
(check-equal? (get-attribute 'href (bits->attributes
|
(check-equal? (get-attribute 'href (bits->attributes
|
||||||
((query-selector
|
((query-selector
|
||||||
|
@ -260,8 +277,8 @@
|
||||||
; check that noscript images are removed
|
; check that noscript images are removed
|
||||||
(check-equal? ((query-selector (λ (t a c) (eq? t 'noscript)) transformed)) #f)
|
(check-equal? ((query-selector (λ (t a c) (eq? t 'noscript)) transformed)) #f)
|
||||||
; benchmark
|
; benchmark
|
||||||
(when (file-exists? "Frog.html2")
|
(when (file-exists? "../misc/Frog.html")
|
||||||
(with-input-from-file "Frog.html2"
|
(with-input-from-file "../misc/Frog.html"
|
||||||
(λ ()
|
(λ ()
|
||||||
(define tree (html->xexp (current-input-port)))
|
(define tree (html->xexp (current-input-port)))
|
||||||
(time (length (update-tree-wiki tree "minecraft")))))))
|
(time (length (update-tree-wiki tree "minecraft")))))))
|
|
@ -1,6 +1,5 @@
|
||||||
#lang typed/racket/base
|
#lang typed/racket/base
|
||||||
(require racket/string
|
(require racket/string
|
||||||
"config.rkt"
|
|
||||||
"pure-utils.rkt")
|
"pure-utils.rkt")
|
||||||
(require/typed web-server/http/request-structs
|
(require/typed web-server/http/request-structs
|
||||||
[#:opaque Header header?])
|
[#:opaque Header header?])
|
||||||
|
@ -10,12 +9,14 @@
|
||||||
px-wikiname
|
px-wikiname
|
||||||
; make a query string from an association list of strings
|
; make a query string from an association list of strings
|
||||||
params->query
|
params->query
|
||||||
|
; custom percent encoding (you probably want params->query instead)
|
||||||
|
percent-encode
|
||||||
|
; sets for custom percent encoding
|
||||||
|
path-set urlencoded-set filename-set
|
||||||
; make a proxied version of a fandom url
|
; make a proxied version of a fandom url
|
||||||
u-proxy-url
|
u-proxy-url
|
||||||
; check whether a url is on a domain controlled by fandom
|
; check whether a url is on a domain controlled by fandom
|
||||||
is-fandom-url?
|
is-fandom-url?
|
||||||
; prints "out: <url>"
|
|
||||||
log-outgoing
|
|
||||||
; pass in a header, headers, or something useless. they'll all combine into a list
|
; pass in a header, headers, or something useless. they'll all combine into a list
|
||||||
build-headers
|
build-headers
|
||||||
; try to follow wikimedia's format for which characters should be encoded/replaced in page titles for the url
|
; try to follow wikimedia's format for which characters should be encoded/replaced in page titles for the url
|
||||||
|
@ -41,6 +42,8 @@
|
||||||
)
|
)
|
||||||
path-set))
|
path-set))
|
||||||
|
|
||||||
|
(define filename-set '(#\< #\> #\: #\" #\/ #\\ #\| #\? #\* #\# #\~ #\&))
|
||||||
|
|
||||||
(: percent-encode (String (Listof Char) Boolean -> Bytes))
|
(: percent-encode (String (Listof Char) Boolean -> Bytes))
|
||||||
(define (percent-encode value set space-as-plus)
|
(define (percent-encode value set space-as-plus)
|
||||||
(define b (string->bytes/utf-8 value))
|
(define b (string->bytes/utf-8 value))
|
||||||
|
@ -87,11 +90,6 @@
|
||||||
(λ ([v : String]) (string-append "/proxy?" (params->query `(("dest" . ,url)))))
|
(λ ([v : String]) (string-append "/proxy?" (params->query `(("dest" . ,url)))))
|
||||||
url))
|
url))
|
||||||
|
|
||||||
(: log-outgoing (String -> Void))
|
|
||||||
(define (log-outgoing url-string)
|
|
||||||
(when (config-true? 'log_outgoing)
|
|
||||||
(printf "out: ~a~n" url-string)))
|
|
||||||
|
|
||||||
(: build-headers ((U Header (Listof Header) False Void) * -> (Listof Header)))
|
(: build-headers ((U Header (Listof Header) False Void) * -> (Listof Header)))
|
||||||
(define (build-headers . fs)
|
(define (build-headers . fs)
|
||||||
(apply
|
(apply
|
|
@ -129,7 +129,7 @@
|
||||||
(λ (element-type attributes children)
|
(λ (element-type attributes children)
|
||||||
(equal? (get-attribute name attributes) value)))
|
(equal? (get-attribute name attributes) value)))
|
||||||
|
|
||||||
(define (query-selector selector element)
|
(define (query-selector selector element #:include-text? [include-text? #f])
|
||||||
(generator
|
(generator
|
||||||
()
|
()
|
||||||
(let loop ([element element])
|
(let loop ([element element])
|
||||||
|
@ -140,7 +140,9 @@
|
||||||
[(equal? element-type '*DECL*) #f]
|
[(equal? element-type '*DECL*) #f]
|
||||||
[(equal? element-type '@) #f]
|
[(equal? element-type '@) #f]
|
||||||
[#t
|
[#t
|
||||||
(when (selector element-type attributes children)
|
(when (if include-text?
|
||||||
|
(selector element-type attributes children (filter string? (cdr element)))
|
||||||
|
(selector element-type attributes children))
|
||||||
(yield element))
|
(yield element))
|
||||||
(for ([child children]) (loop child))]))
|
(for ([child children]) (loop child))]))
|
||||||
#f))
|
#f))
|
|
@ -4,7 +4,7 @@
|
||||||
racket/string
|
racket/string
|
||||||
json
|
json
|
||||||
net/http-easy
|
net/http-easy
|
||||||
html-parsing
|
"../lib/html-parsing/main.rkt"
|
||||||
"../src/xexpr-utils.rkt"
|
"../src/xexpr-utils.rkt"
|
||||||
"../src/url-utils.rkt")
|
"../src/url-utils.rkt")
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,16 @@
|
||||||
html-parsing
|
html-parsing
|
||||||
html-writing
|
html-writing
|
||||||
web-server/http
|
web-server/http
|
||||||
|
web-server/http/bindings
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"niwa-data.rkt"
|
"extwiki-data.rkt"
|
||||||
|
"extwiki-generic.rkt"
|
||||||
"static-data.rkt"
|
"static-data.rkt"
|
||||||
"pure-utils.rkt"
|
"../lib/syntax.rkt"
|
||||||
"xexpr-utils.rkt"
|
"../lib/pure-utils.rkt"
|
||||||
"url-utils.rkt")
|
"../lib/xexpr-utils.rkt"
|
||||||
|
"../lib/url-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
; headers to always send on all http responses
|
; headers to always send on all http responses
|
||||||
|
@ -79,32 +82,69 @@
|
||||||
|
|
||||||
;; generate a notice with a link if a fandom wiki has a replacement as part of NIWA or similar
|
;; generate a notice with a link if a fandom wiki has a replacement as part of NIWA or similar
|
||||||
;; if the wiki has no replacement, display nothing
|
;; if the wiki has no replacement, display nothing
|
||||||
(define (niwa-notice wikiname title)
|
(define (extwiki-notice wikiname title)
|
||||||
(define ind (findf (λ (item) (member wikiname (first item))) niwa-data))
|
(define xt (findf (λ (item) (member wikiname (extwiki^-wikinames item))) extwikis))
|
||||||
(if ind
|
(cond/var
|
||||||
(let* ([search-page (format "/Special:Search?~a"
|
[xt
|
||||||
(params->query `(("search" . ,title)
|
(let* ([group (hash-ref extwiki-groups (extwiki^-group xt))]
|
||||||
("go" . "Go"))))]
|
[search-page (format "/Special:Search?~a"
|
||||||
[go (if (string-suffix? (third ind) "/")
|
(params->query `(("search" . ,title)
|
||||||
(regexp-replace #rx"/$" (third ind) (λ (_) search-page))
|
("go" . "Go"))))]
|
||||||
(let* ([joiner (second (regexp-match #rx"/(w[^./]*)/" (third ind)))])
|
[go (if (string-suffix? (extwiki^-home xt) "/")
|
||||||
(regexp-replace #rx"/w[^./]*/.*$" (third ind) (λ (_) (format "/~a~a" joiner search-page)))))])
|
(regexp-replace #rx"/$" (extwiki^-home xt) (λ (_) search-page))
|
||||||
`(aside (@ (class "niwa__notice"))
|
(let* ([joiner (second (regexp-match #rx"/(w[^./]*)/" (extwiki^-home xt)))])
|
||||||
(h1 (@ (class "niwa__header")) ,(second ind) " has its own website separate from Fandom.")
|
(regexp-replace #rx"/w[^./]*/.*$" (extwiki^-home xt) (λ (_) (format "/~a~a" joiner search-page)))))]
|
||||||
(a (@ (class "niwa__go") (href ,go)) "Read " ,title " on " ,(second ind) " →")
|
[props (extwiki-props^ go)])
|
||||||
(div (@ (class "niwa__cols"))
|
(cond
|
||||||
(div (@ (class "niwa__left"))
|
[(eq? (extwiki^-banner xt) 'default)
|
||||||
(p "Most major Nintendo wikis are part of the "
|
`(aside (@ (class "niwa__notice"))
|
||||||
(a (@ (href "https://www.niwanetwork.org/about/")) "Nintendo Independent Wiki Alliance")
|
(h1 (@ (class "niwa__header")) ,(extwiki^-name xt) " has its own website separate from Fandom.")
|
||||||
" and have their own wikis off Fandom. You can help this wiki by "
|
(a (@ (class "niwa__go") (href ,go)) "Read " ,title " on " ,(extwiki^-name xt) " →")
|
||||||
(a (@ (href ,go)) "visiting it directly."))
|
(div (@ (class "niwa__cols"))
|
||||||
(p ,(fifth ind))
|
(div (@ (class "niwa__left"))
|
||||||
(div (@ (class "niwa__divider")))
|
(p ,((extwiki-group^-description group) props))
|
||||||
(p "Why are you seeing this message? Fandom refuses to delete or archive their copy of this wiki, so that means their pages will appear high up in search results. Fandom hopes to get clicks from readers who don't know any better.")
|
(p ,((extwiki^-description xt) props))
|
||||||
(p (@ (class "niwa__feedback")) "This notice brought to you by BreezeWiki / " (a (@ (href "https://www.kotaku.com.au/2022/10/massive-zelda-wiki-reclaims-independence-six-months-before-tears-of-the-kingdom/")) "Info & Context") " / " (a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback?")))
|
(p "This wiki's core community has wholly migrated away from Fandom. You should "
|
||||||
(div (@ (class "niwa__right"))
|
(a (@ (href ,go)) "go to " ,(extwiki^-name xt) " now!"))
|
||||||
(img (@ (class "niwa__logo") (src ,(format "https://www.niwanetwork.org~a" (fourth ind)))))))))
|
(p (@ (class "niwa__feedback"))
|
||||||
""))
|
,@(add-between
|
||||||
|
`(,@(for/list ([link (extwiki-group^-links group)])
|
||||||
|
`(a (@ (href ,(cdr link))) ,(car link)))
|
||||||
|
"This notice is from BreezeWiki"
|
||||||
|
(a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback?"))
|
||||||
|
" / ")))
|
||||||
|
(div (@ (class "niwa__right"))
|
||||||
|
(img (@ (class "niwa__logo") (src ,(extwiki^-logo xt)))))))]
|
||||||
|
[(eq? (extwiki^-banner xt) 'parallel)
|
||||||
|
`(aside (@ (class "niwa__parallel"))
|
||||||
|
(h1 (@ (class "niwa__header-mini"))
|
||||||
|
"See also "
|
||||||
|
(a (@ (href ,go)) ,(extwiki^-name xt)))
|
||||||
|
(p "This topic has multiple communities of editors, some active on the Fandom wiki, others active on " ,(extwiki^-name xt) ".")
|
||||||
|
(p "For thorough research, be sure to check both communities since they may have different information!")
|
||||||
|
(p (@ (class "niwa__feedback"))
|
||||||
|
,@(add-between
|
||||||
|
`(,@(for/list ([link (extwiki-group^-links group)])
|
||||||
|
`(a (@ (href ,(cdr link))) ,(car link)))
|
||||||
|
"This notice is from BreezeWiki"
|
||||||
|
(a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "Feedback?"))
|
||||||
|
" / ")))]
|
||||||
|
[(eq? (extwiki^-banner xt) 'empty)
|
||||||
|
`(aside (@ (class "niwa__notice niwa__notice--alt"))
|
||||||
|
(h1 (@ (class "niwa__header")) "You will be redirected to " ,(extwiki^-name xt) ".")
|
||||||
|
(p (@ (style "position: relative; top: -12px;")) "This independent wiki community has its own site separate from Fandom.")
|
||||||
|
(a (@ (class "niwa__go") (href ,go)) "Take me there! →")
|
||||||
|
|
||||||
|
(p (@ (class "niwa__feedback") (style "text-align: left"))
|
||||||
|
,@(add-between
|
||||||
|
`(,@(for/list ([link (extwiki-group^-links group)])
|
||||||
|
`(a (@ (href ,(cdr link))) ,(car link)))
|
||||||
|
"This notice is from BreezeWiki")
|
||||||
|
" / ")))]))]
|
||||||
|
(var fetched-callback (get-redirect-content wikiname))
|
||||||
|
[fetched-callback
|
||||||
|
(fetched-callback title)]
|
||||||
|
[#t ""]))
|
||||||
|
|
||||||
(define (generate-wiki-page
|
(define (generate-wiki-page
|
||||||
content
|
content
|
||||||
|
@ -114,22 +154,26 @@
|
||||||
#:title title
|
#:title title
|
||||||
#:head-data [head-data-in #f]
|
#:head-data [head-data-in #f]
|
||||||
#:siteinfo [siteinfo-in #f]
|
#:siteinfo [siteinfo-in #f]
|
||||||
#:user-cookies [user-cookies-in #f])
|
#:user-cookies [user-cookies-in #f]
|
||||||
|
#:online-styles [online-styles #t])
|
||||||
(define siteinfo (or siteinfo-in siteinfo-default))
|
(define siteinfo (or siteinfo-in siteinfo-default))
|
||||||
(define head-data (or head-data-in ((head-data-getter wikiname))))
|
(define head-data (or head-data-in ((head-data-getter wikiname))))
|
||||||
(define user-cookies (or user-cookies-in (user-cookies-getter req)))
|
(define user-cookies (or user-cookies-in (user-cookies-getter req)))
|
||||||
(define (required-styles origin)
|
(define origin (format "https://~a.fandom.com" wikiname))
|
||||||
(map (λ (dest-path)
|
(define required-styles
|
||||||
(define url (format dest-path origin))
|
(cond
|
||||||
(if (config-true? 'strict_proxy)
|
[online-styles
|
||||||
(u-proxy-url url)
|
(define styles
|
||||||
url))
|
(list
|
||||||
`(#;"~a/load.php?lang=en&modules=skin.fandomdesktop.styles&only=styles&skin=fandomdesktop"
|
(format "~a/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" origin (user-cookies^-theme user-cookies))
|
||||||
#;"~a/load.php?lang=en&modules=ext.gadget.dungeonsWiki%2CearthWiki%2Csite-styles%2Csound-styles&only=styles&skin=fandomdesktop"
|
(format "~a/load.php?lang=en&modules=skin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles%7Csite.styles&only=styles&skin=fandomdesktop" origin)))
|
||||||
#;"~a/load.php?lang=en&modules=site.styles&only=styles&skin=fandomdesktop"
|
(if (config-true? 'strict_proxy)
|
||||||
; combine the above entries into a single request for potentially extra speed - fandom.com doesn't even do this!
|
(map u-proxy-url styles)
|
||||||
,(format "~~a/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" (user-cookies^-theme user-cookies))
|
styles)]
|
||||||
"~a/load.php?lang=en&modules=skin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles%7Csite.styles&only=styles&skin=fandomdesktop")))
|
[#t
|
||||||
|
(list
|
||||||
|
(format "/archive/~a/styles/themeVariables-~a.css" wikiname (user-cookies^-theme user-cookies))
|
||||||
|
(format "/archive/~a/styles/site.css" wikiname))]))
|
||||||
`(*TOP*
|
`(*TOP*
|
||||||
(*DECL* DOCTYPE html)
|
(*DECL* DOCTYPE html)
|
||||||
(html
|
(html
|
||||||
|
@ -141,7 +185,7 @@
|
||||||
(config-get 'application_name)))
|
(config-get 'application_name)))
|
||||||
,@(map (λ (url)
|
,@(map (λ (url)
|
||||||
`(link (@ (rel "stylesheet") (type "text/css") (href ,url))))
|
`(link (@ (rel "stylesheet") (type "text/css") (href ,url))))
|
||||||
(required-styles (format "https://~a.fandom.com" wikiname)))
|
required-styles)
|
||||||
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "main.css"))))
|
(link (@ (rel "stylesheet") (type "text/css") (href ,(get-static-url "main.css"))))
|
||||||
(script "const BWData = "
|
(script "const BWData = "
|
||||||
,(jsexpr->string (hasheq 'wikiname wikiname
|
,(jsexpr->string (hasheq 'wikiname wikiname
|
||||||
|
@ -154,11 +198,25 @@
|
||||||
(λ (v) (u-proxy-url v))
|
(λ (v) (u-proxy-url v))
|
||||||
(head-data^-icon-url head-data))))))
|
(head-data^-icon-url head-data))))))
|
||||||
(body (@ (class ,(head-data^-body-class head-data)))
|
(body (@ (class ,(head-data^-body-class head-data)))
|
||||||
|
,(if (config-true? 'instance_is_official)
|
||||||
|
(let ([balloon '(img (@ (src "/static/three-balloons.png") (class "bw-balloon") (title "Image Source: pngimg.com/image/4955 | License: CC BY-NC 4.0 | Modifications: Resized") (width "52") (height "56")))]
|
||||||
|
[extension-eligible? (and req (assq 'user-agent (request-headers req)) (string-contains? (string-downcase (cdr (assq 'user-agent (request-headers req)))) "firefox/"))])
|
||||||
|
`(div (@ (class "bw-top-banner"))
|
||||||
|
,balloon
|
||||||
|
(div
|
||||||
|
"BreezeWiki is back! Most major wikis are available.\n"
|
||||||
|
,(if extension-eligible?
|
||||||
|
'(div (@ (class "bw-top-banner-rainbow"))
|
||||||
|
"Try " (a (@ (href "https://addons.mozilla.org/en-GB/firefox/addon/indie-wiki-buddy/")) "our affiliated browser extension") " - redirect to BreezeWiki automatically!\n")
|
||||||
|
"")
|
||||||
|
"As always, " (a (@ (href "https://docs.breezewiki.com/Reporting_Bugs.html")) "please go here") " to report problems, suggest features, or talk about the project.")
|
||||||
|
,balloon))
|
||||||
|
"")
|
||||||
(div (@ (class "main-container"))
|
(div (@ (class "main-container"))
|
||||||
(div (@ (class "fandom-community-header__background tileHorizontally header")))
|
(div (@ (class "fandom-community-header__background tileHorizontally header")))
|
||||||
(div (@ (class "page"))
|
(div (@ (class "page"))
|
||||||
(main (@ (class "page__main"))
|
(main (@ (class "page__main"))
|
||||||
,(niwa-notice wikiname title)
|
,(extwiki-notice wikiname title)
|
||||||
(div (@ (class "custom-top"))
|
(div (@ (class "custom-top"))
|
||||||
(h1 (@ (class "page-title")) ,title)
|
(h1 (@ (class "page-title")) ,title)
|
||||||
(nav (@ (class "sitesearch"))
|
(nav (@ (class "sitesearch"))
|
||||||
|
@ -172,18 +230,18 @@
|
||||||
(div (@ (class "bw-theme__select"))
|
(div (@ (class "bw-theme__select"))
|
||||||
(span (@ (class "bw-theme__main-label")) "Page theme")
|
(span (@ (class "bw-theme__main-label")) "Page theme")
|
||||||
(div (@ (class "bw-theme__items"))
|
(div (@ (class "bw-theme__items"))
|
||||||
,@(for/list ([theme '(default light dark)])
|
,@(for/list ([theme '(default light dark)])
|
||||||
(define class
|
(define class
|
||||||
(if (equal? theme (user-cookies^-theme user-cookies))
|
(if (equal? theme (user-cookies^-theme user-cookies))
|
||||||
"bw-theme__item bw-theme__item--selected"
|
"bw-theme__item bw-theme__item--selected"
|
||||||
"bw-theme__item"))
|
"bw-theme__item"))
|
||||||
`(a (@ (href ,(user-cookies-setter-url
|
`(a (@ (href ,(user-cookies-setter-url
|
||||||
req
|
req
|
||||||
(struct-copy user-cookies^ user-cookies
|
(struct-copy user-cookies^ user-cookies
|
||||||
[theme theme]))) (class ,class))
|
[theme theme]))) (class ,class))
|
||||||
(div (@ (class "bw-theme__icon-container"))
|
(div (@ (class "bw-theme__icon-container"))
|
||||||
,(hash-ref theme-icons theme))
|
,(hash-ref theme-icons theme))
|
||||||
,(format "~a" theme)))))))
|
,(format "~a" theme)))))))
|
||||||
(div (@ (id "content") #;(class "page-content"))
|
(div (@ (id "content") #;(class "page-content"))
|
||||||
(div (@ (id "mw-content-text"))
|
(div (@ (id "mw-content-text"))
|
||||||
,content))
|
,content))
|
||||||
|
|
|
@ -2,11 +2,8 @@
|
||||||
(require racket/function
|
(require racket/function
|
||||||
racket/pretty
|
racket/pretty
|
||||||
racket/runtime-path
|
racket/runtime-path
|
||||||
racket/string)
|
racket/string
|
||||||
(require/typed ini
|
typed/ini)
|
||||||
[#:opaque Ini ini?]
|
|
||||||
[read-ini (Input-Port -> Ini)]
|
|
||||||
[ini->hash (Ini -> (Immutable-HashTable Symbol (Immutable-HashTable Symbol String)))])
|
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
config-parameter
|
config-parameter
|
||||||
|
@ -14,7 +11,7 @@
|
||||||
config-get)
|
config-get)
|
||||||
|
|
||||||
(module+ test
|
(module+ test
|
||||||
(require "typed-rackunit.rkt"))
|
(require "../lib/typed-rackunit.rkt"))
|
||||||
|
|
||||||
(define-runtime-path path-config "../config.ini")
|
(define-runtime-path path-config "../config.ini")
|
||||||
|
|
||||||
|
@ -38,54 +35,58 @@
|
||||||
(instance_is_official . "false") ; please don't turn this on, or you will make me very upset
|
(instance_is_official . "false") ; please don't turn this on, or you will make me very upset
|
||||||
(log_outgoing . "true")
|
(log_outgoing . "true")
|
||||||
(port . "10416")
|
(port . "10416")
|
||||||
(strict_proxy . "true")))
|
(strict_proxy . "false")
|
||||||
|
|
||||||
|
(feature_offline::enabled . "false")
|
||||||
|
(feature_offline::format . "json.gz")
|
||||||
|
(feature_offline::only . "false")))
|
||||||
|
|
||||||
(define loaded-alist
|
(define loaded-alist
|
||||||
(with-handlers
|
(with-handlers
|
||||||
([exn:fail:filesystem:errno?
|
([exn:fail:filesystem:errno?
|
||||||
(λ (exn)
|
(λ (exn)
|
||||||
(begin0
|
(displayln "note: config file not detected, using defaults")
|
||||||
'()
|
'())]
|
||||||
(displayln "note: config file not detected, using defaults")))]
|
|
||||||
[exn:fail:contract?
|
[exn:fail:contract?
|
||||||
(λ (exn)
|
(λ (exn)
|
||||||
(begin0
|
(displayln "note: config file empty or missing [] section, using defaults")
|
||||||
'()
|
'())])
|
||||||
(displayln "note: config file empty or missing [] section, using defaults")))])
|
(define h (in-hash
|
||||||
|
(ini->hash
|
||||||
|
(call-with-input-file path-config
|
||||||
|
(λ (in)
|
||||||
|
(read-ini in))))))
|
||||||
(define l
|
(define l
|
||||||
(hash->list
|
(for*/list : (Listof (Pairof Symbol String))
|
||||||
(hash-ref
|
([(section-key section) h]
|
||||||
(ini->hash
|
[(key value) (in-hash section)])
|
||||||
(call-with-input-file path-config
|
(if (eq? section-key '||)
|
||||||
(λ (in)
|
(cons key value)
|
||||||
(read-ini in))))
|
(cons (string->symbol (string-append (symbol->string section-key)
|
||||||
'||)))
|
"::"
|
||||||
(begin0
|
(symbol->string key)))
|
||||||
l
|
value))))
|
||||||
(printf "note: ~a items loaded from config file~n" (length l)))))
|
(printf "note: ~a items loaded from config file~n" (length l))
|
||||||
|
l))
|
||||||
|
|
||||||
(define env-alist
|
(define env-alist
|
||||||
(let ([e-names (environment-variables-names (current-environment-variables))]
|
(for/list : (Listof (Pairof Symbol String))
|
||||||
[e-ref (λ ([name : Bytes])
|
([name (environment-variables-names (current-environment-variables))]
|
||||||
(bytes->string/latin-1
|
#:when (string-prefix? (string-downcase (bytes->string/latin-1 name)) "bw_"))
|
||||||
(cast (environment-variables-ref (current-environment-variables) name)
|
(cons
|
||||||
Bytes)))])
|
;; key: convert to string, remove bw_ prefix, convert to symbol
|
||||||
(map (λ ([name : Bytes])
|
(string->symbol (string-downcase (substring (bytes->string/latin-1 name) 3)))
|
||||||
(cons (string->symbol (string-downcase (substring (bytes->string/latin-1 name) 3)))
|
;; value: convert to string
|
||||||
(e-ref name)))
|
(bytes->string/latin-1
|
||||||
(filter (λ ([name : Bytes]) (string-prefix? (string-downcase (bytes->string/latin-1 name))
|
(cast (environment-variables-ref (current-environment-variables) name) Bytes)))))
|
||||||
"bw_"))
|
|
||||||
e-names))))
|
|
||||||
(when (> (length env-alist) 0)
|
(when (> (length env-alist) 0)
|
||||||
(printf "note: ~a items loaded from environment variables~n" (length env-alist)))
|
(printf "note: ~a items loaded from environment variables~n" (length env-alist)))
|
||||||
|
|
||||||
(define combined-alist (append default-config loaded-alist env-alist))
|
(define combined-alist (append default-config loaded-alist env-alist))
|
||||||
|
|
||||||
(define config
|
(define config
|
||||||
(make-immutable-hasheq
|
(for/hasheq ([pair combined-alist]) : (Immutable-HashTable Symbol (Parameter String))
|
||||||
(map (λ ([pair : (Pairof Symbol String)])
|
(values (car pair) (make-parameter (cdr pair)))))
|
||||||
(cons (car pair) (make-parameter (cdr pair))))
|
|
||||||
combined-alist)))
|
|
||||||
|
|
||||||
(when (config-true? 'debug)
|
(when (config-true? 'debug)
|
||||||
; all values here are optimised for maximum prettiness
|
; all values here are optimised for maximum prettiness
|
||||||
|
@ -107,4 +108,6 @@
|
||||||
(parameterize ([(config-parameter 'application_name) "JeffWiki"]
|
(parameterize ([(config-parameter 'application_name) "JeffWiki"]
|
||||||
[(config-parameter 'strict_proxy) ""])
|
[(config-parameter 'strict_proxy) ""])
|
||||||
(check-equal? (config-get 'application_name) "JeffWiki")
|
(check-equal? (config-get 'application_name) "JeffWiki")
|
||||||
(check-false (config-true? 'strict_proxy))))
|
(check-false (config-true? 'strict_proxy))
|
||||||
|
(check-equal? (string? (config-get 'feature_offline::format)) #t)))
|
||||||
|
|
||||||
|
|
53
src/data.rkt
53
src/data.rkt
|
@ -5,10 +5,14 @@
|
||||||
net/url-string
|
net/url-string
|
||||||
(only-in net/cookies/server cookie-header->alist cookie->set-cookie-header make-cookie)
|
(only-in net/cookies/server cookie-header->alist cookie->set-cookie-header make-cookie)
|
||||||
(prefix-in easy: net/http-easy)
|
(prefix-in easy: net/http-easy)
|
||||||
|
db
|
||||||
memo
|
memo
|
||||||
"static-data.rkt"
|
"static-data.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"whole-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt"
|
||||||
|
"../archiver/archiver-database.rkt"
|
||||||
|
"config.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
(struct-out siteinfo^)
|
(struct-out siteinfo^)
|
||||||
|
@ -30,25 +34,38 @@
|
||||||
(struct head-data^ (body-class icon-url) #:transparent)
|
(struct head-data^ (body-class icon-url) #:transparent)
|
||||||
|
|
||||||
(define license-default (license^ "CC-BY-SA" "https://www.fandom.com/licensing"))
|
(define license-default (license^ "CC-BY-SA" "https://www.fandom.com/licensing"))
|
||||||
(define siteinfo-default (siteinfo^ "Test Wiki" "Main_Page" license-default))
|
(define siteinfo-default (siteinfo^ "Unknown Wiki" "Main_Page" license-default))
|
||||||
(define head-data-default (head-data^ "skin-fandomdesktop" (get-static-url "breezewiki-favicon.svg")))
|
(define head-data-default (head-data^ "skin-fandomdesktop" (get-static-url "breezewiki-favicon.svg")))
|
||||||
|
|
||||||
(define/memoize (siteinfo-fetch wikiname) #:hash hash
|
(define/memoize (siteinfo-fetch wikiname) #:hash hash
|
||||||
(define dest-url
|
(cond
|
||||||
(format "https://~a.fandom.com/api.php?~a"
|
[(config-true? 'feature_offline::only)
|
||||||
wikiname
|
(when (config-true? 'debug)
|
||||||
(params->query '(("action" . "query")
|
(printf "using offline mode for siteinfo ~a~n" wikiname))
|
||||||
("meta" . "siteinfo")
|
(define row (query-maybe-row slc "select sitename, basepage, license_text, license_url from wiki where wikiname = ?"
|
||||||
("siprop" . "general|rightsinfo")
|
wikiname))
|
||||||
("format" . "json")
|
(if row
|
||||||
("formatversion" . "2")))))
|
(siteinfo^ (vector-ref row 0)
|
||||||
(log-outgoing dest-url)
|
(vector-ref row 1)
|
||||||
(define res (easy:get dest-url))
|
(license^ (vector-ref row 2)
|
||||||
(define data (easy:response-json res))
|
(vector-ref row 3)))
|
||||||
(siteinfo^ (jp "/query/general/sitename" data)
|
siteinfo-default)]
|
||||||
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
[else
|
||||||
(license^ (jp "/query/rightsinfo/text" data)
|
(define dest-url
|
||||||
(jp "/query/rightsinfo/url" data))))
|
(format "https://~a.fandom.com/api.php?~a"
|
||||||
|
wikiname
|
||||||
|
(params->query '(("action" . "query")
|
||||||
|
("meta" . "siteinfo")
|
||||||
|
("siprop" . "general|rightsinfo")
|
||||||
|
("format" . "json")
|
||||||
|
("formatversion" . "2")))))
|
||||||
|
(log-outgoing dest-url)
|
||||||
|
(define res (easy:get dest-url))
|
||||||
|
(define data (easy:response-json res))
|
||||||
|
(siteinfo^ (jp "/query/general/sitename" data)
|
||||||
|
(second (regexp-match #rx"/wiki/(.*)" (jp "/query/general/base" data)))
|
||||||
|
(license^ (jp "/query/rightsinfo/text" data)
|
||||||
|
(jp "/query/rightsinfo/url" data)))]))
|
||||||
|
|
||||||
(define/memoize (head-data-getter wikiname) #:hash hash
|
(define/memoize (head-data-getter wikiname) #:hash hash
|
||||||
;; data will be stored here, can be referenced by the memoized closure
|
;; data will be stored here, can be referenced by the memoized closure
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
(require "syntax.rkt"
|
(require "../lib/syntax.rkt"
|
||||||
(for-syntax racket/base)
|
(for-syntax racket/base)
|
||||||
racket/string
|
racket/string
|
||||||
net/url
|
net/url
|
||||||
web-server/http
|
web-server/http
|
||||||
|
web-server/dispatchers/dispatch
|
||||||
(prefix-in host: web-server/dispatchers/dispatch-host)
|
(prefix-in host: web-server/dispatchers/dispatch-host)
|
||||||
(prefix-in pathprocedure: web-server/dispatchers/dispatch-pathprocedure)
|
(prefix-in pathprocedure: web-server/dispatchers/dispatch-pathprocedure)
|
||||||
(prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
|
(prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
|
||||||
(prefix-in lift: web-server/dispatchers/dispatch-lift)
|
(prefix-in lift: web-server/dispatchers/dispatch-lift)
|
||||||
(prefix-in filter: web-server/dispatchers/dispatch-filter)
|
(prefix-in filter: web-server/dispatchers/dispatch-filter)
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"url-utils.rkt")
|
"../lib/url-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
; syntax to make the hashmap from names
|
; syntax to make the hashmap from names
|
||||||
|
@ -43,8 +44,14 @@
|
||||||
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works))
|
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works))
|
||||||
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category)))
|
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category)))
|
||||||
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file)))
|
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file)))
|
||||||
|
(if (config-true? 'feature_offline::enabled)
|
||||||
|
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki-offline)))
|
||||||
|
(λ (_conn _req) (next-dispatcher)))
|
||||||
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki)))
|
(filter:make (pregexp (format "^/~a/wiki/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-wiki)))
|
||||||
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search)))
|
(filter:make (pregexp (format "^/~a/search$" px-wikiname)) (lift:make (hash-ref ds 'page-search)))
|
||||||
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home)))
|
(filter:make (pregexp (format "^/~a(/(wiki(/)?)?)?$" px-wikiname)) (lift:make (hash-ref ds 'redirect-wiki-home)))
|
||||||
|
(if (config-true? 'feature_offline::enabled)
|
||||||
|
(filter:make (pregexp (format "^/archive/~a/(styles|images)/.+$" px-wikiname)) (lift:make (hash-ref ds 'page-static-archive)))
|
||||||
|
(λ (_conn _req) (next-dispatcher)))
|
||||||
(hash-ref ds 'static-dispatcher)
|
(hash-ref ds 'static-dispatcher)
|
||||||
(lift:make (hash-ref ds 'page-not-found))))
|
(lift:make (hash-ref ds 'page-not-found))))
|
||||||
|
|
401
src/extwiki-data.rkt
Normal file
401
src/extwiki-data.rkt
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(provide
|
||||||
|
(struct-out extwiki-props^)
|
||||||
|
(struct-out extwiki-group^)
|
||||||
|
extwiki-groups
|
||||||
|
(struct-out extwiki^)
|
||||||
|
extwikis)
|
||||||
|
|
||||||
|
(struct extwiki-props^ (go) #:transparent)
|
||||||
|
|
||||||
|
(struct extwiki-group^ (name links description) #:transparent)
|
||||||
|
(define extwiki-groups
|
||||||
|
(hasheq 'NIWA
|
||||||
|
(extwiki-group^
|
||||||
|
"NIWA"
|
||||||
|
'(("Why did editors leave Fandom?" . "https://www.kotaku.com.au/2022/10/massive-zelda-wiki-reclaims-independence-six-months-before-tears-of-the-kingdom/"))
|
||||||
|
(λ (props)
|
||||||
|
`(p "Most major Nintendo wikis are part of the "
|
||||||
|
(a (@ (href "https://www.niwanetwork.org/about/")) "Nintendo Independent Wiki Alliance")
|
||||||
|
" and have their own wikis off Fandom.")))
|
||||||
|
|
||||||
|
'SEIWA
|
||||||
|
(extwiki-group^
|
||||||
|
"SEIWA"
|
||||||
|
'(("SEIWA Website" . "https://seiwanetwork.org/"))
|
||||||
|
(λ (props)
|
||||||
|
`(p "The Square Enix Indpendent Wiki Alliance, or SEIWA, is a network of independent wikis established in 2011 and focused on providing high-quality coverage of Square Enix and its content. We work together, along with our affiliates and others, to co-operate and support one another while providing the best-quality content on the various Square Enix video games and media.")))
|
||||||
|
|
||||||
|
'Terraria
|
||||||
|
(extwiki-group^
|
||||||
|
"Terraria"
|
||||||
|
'(("Announcement: New Official Terraria Wiki!" . "https://forums.terraria.org/index.php?threads/new-official-terraria-wiki-launches-today.111239/") ("In the media" . "https://www.pcgamesn.com/terraria/wiki"))
|
||||||
|
(λ (props) '()))
|
||||||
|
|
||||||
|
'ARK
|
||||||
|
(extwiki-group^
|
||||||
|
"ARK"
|
||||||
|
'(("Announcement: Official Wiki Is Moving!" . "https://survivetheark.com/index.php?/forums/topic/657902-official-ark-wiki-feedback/")
|
||||||
|
("Reasons" . "https://todo.sr.ht/~cadence/breezewiki-todo/4#event-216613")
|
||||||
|
("Browser Extension" . "https://old.reddit.com/r/playark/comments/xe51sy/official_ark_wiki_launched_a_browser_extension_to/"))
|
||||||
|
(λ (props) '()))
|
||||||
|
|
||||||
|
'Astroneer
|
||||||
|
(extwiki-group^
|
||||||
|
"Astroneer"
|
||||||
|
'(("Migration discussion" . "https://old.reddit.com/r/Astroneer/comments/z905id/the_official_astroneer_wiki_has_moved_to_wikigg/") ("Migration info" . "https://astroneer.fandom.com/wiki/Talk:Astroneer_Wiki/Migration_to_Wiki.gg"))
|
||||||
|
(λ (props) '()))
|
||||||
|
|
||||||
|
'empty
|
||||||
|
(extwiki-group^
|
||||||
|
"Misc"
|
||||||
|
'(("This wiki doesn't have a description yet. Add one?" . "https://docs.breezewiki.com/Reporting_Bugs.html"))
|
||||||
|
#f)))
|
||||||
|
|
||||||
|
;; wikiname, niwa-name, url, logo-url
|
||||||
|
(struct extwiki^ (wikinames banner group name home logo description) #:transparent)
|
||||||
|
(define extwikis
|
||||||
|
(list
|
||||||
|
(extwiki^
|
||||||
|
'("arms" "armsgame") 'default
|
||||||
|
'NIWA
|
||||||
|
"ARMS Institute"
|
||||||
|
"https://armswiki.org/wiki/Home"
|
||||||
|
"https://niwanetwork.org/images/logos/armswiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "ARMS Institute is a comprehensive resource for information about the Nintendo Switch game, ARMS. Founded on May 1, 2017 and growing rapidly, the wiki strives to offer in-depth coverage of ARMS from both a competitive and casual perspective. Join us and ARM yourself with knowledge!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("pokemon" "monster") 'default
|
||||||
|
'NIWA
|
||||||
|
"Bulbapedia"
|
||||||
|
"https://bulbapedia.bulbagarden.net/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/bulbapedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "A part of the Bulbagarden community, Bulbapedia was founded on December 21, 2004 by Liam Pomfret. Everything you need to know about Pokémon can be found at Bulbapedia, whether about the games, the anime, the manga, or something else entirely. With its Bulbanews section and the Bulbagarden forums, it's your one-stop online place for Pokémon."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("dragalialost") 'default
|
||||||
|
'NIWA
|
||||||
|
"Dragalia Lost Wiki"
|
||||||
|
"https://dragalialost.wiki/w/Dragalia_Lost_Wiki"
|
||||||
|
"https://niwanetwork.org/images/logos/dragalialost.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "The Dragalia Lost Wiki was originally founded in September 2018 on the Gamepedia platform but went independent in January 2021. The Wiki aims to document anything and everything Dragalia Lost, from in-game data to mechanics, story, guides, and more!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("dragonquest") 'default
|
||||||
|
'NIWA
|
||||||
|
"Dragon Quest Wiki"
|
||||||
|
"https://dragon-quest.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/dragonquestwiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Originally founded on Wikia, the Dragon Quest Wiki was largely inactive until FlyingRagnar became an admin in late 2009. The wiki went independent about a year later when it merged with the Dragon Quest Dictionary/Encyclopedia which was run by Zenithian and supported by the Dragon's Den. The Dragon Quest Wiki aims to be the most complete resource for Dragon Quest information on the web. It continues to grow in the hope that one day the series will be as popular in the rest of the world as it is in Japan."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("fireemblem") 'default
|
||||||
|
'NIWA
|
||||||
|
"Fire Emblem Wiki"
|
||||||
|
"https://fireemblemwiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/fireemblemwiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Growing since August 26, 2010, Fire Emblem Wiki is a project whose goal is to cover all information pertaining to the Fire Emblem series. It aspires to become the most complete and accurate independent source of information on this series."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("fzero" "f-zero") 'default
|
||||||
|
'NIWA
|
||||||
|
"F-Zero Wiki"
|
||||||
|
"https://mutecity.org/wiki/F-Zero_Wiki"
|
||||||
|
"https://niwanetwork.org/images/logos/fzerowiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded on Wikia in November 2007, F-Zero Wiki became independent with NIWA's help in 2011. F-Zero Wiki is quickly growing into the Internet's definitive source for the world of 2200 km/h+, from pilots to machines, and is the founding part of MuteCity.org, the web's first major F-Zero community."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("goldensun") 'default
|
||||||
|
'NIWA
|
||||||
|
"Golden Sun Universe"
|
||||||
|
"https://www.goldensunwiki.net/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/goldensununiverse.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Originally founded on Wikia in late 2006, Golden Sun Universe has always worked hard to meet one particular goal: to be the single most comprehensive yet accessible resource on the Internet for Nintendo's RPG series Golden Sun. It became an independent wiki four years later. Covering characters and plot, documenting all aspects of the gameplay, featuring walkthroughs both thorough and bare-bones, and packed with all manner of odd and fascinating minutiae, Golden Sun Universe leaves no stone unturned!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("tetris") 'default
|
||||||
|
'NIWA
|
||||||
|
"Hard Drop - Tetris Wiki"
|
||||||
|
"https://harddrop.com/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/harddrop.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "The Tetris Wiki was founded by Tetris fans for Tetris fans on tetrisconcept.com in March 2006. The Tetris Wiki torch was passed to harddrop.com in July 2009. Hard Drop is a Tetris community for all Tetris players, regardless of skill or what version of Tetris you play."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("kidicarus") 'default
|
||||||
|
'NIWA
|
||||||
|
"Icaruspedia"
|
||||||
|
"https://www.kidicaruswiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/icaruspedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Icaruspedia is the Kid Icarus wiki that keeps flying to new heights. After going independent on January 8, 2012, Icaruspedia has worked to become the largest and most trusted independent source of Kid Icarus information. Just like Pit, they'll keep on fighting until the job is done."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("splatoon" "uk-splatoon" "splatoon3" "splatoon2") 'default
|
||||||
|
'NIWA
|
||||||
|
"Inkipedia"
|
||||||
|
"https://splatoonwiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/inkipedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Inkipedia is your ever-growing go-to source for all things Splatoon related. Though founded on Wikia on June 10, 2014, Inkipedia went independent on May 18, 2015, just days before Splatoon's release. Our aim is to cover all aspects of the series, both high and low. Come splat with us now!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("starfox") 'default
|
||||||
|
'NIWA
|
||||||
|
"Lylat Wiki"
|
||||||
|
"https://starfoxwiki.info/wiki/Lylat_Wiki"
|
||||||
|
"https://niwanetwork.org/images/logos/lylatwiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Out of seemingly nowhere, Lylat Wiki sprung up one day in early 2010. Led by creator, Justin Folvarcik, and project head, Tacopill, the wiki has reached stability since the move to its own domain. The staff of Lylat Wiki are glad to help out the NIWA wikis and are even prouder to join NIWA's ranks as the source for information on the Star Fox series."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("metroid" "themetroid") 'default
|
||||||
|
'NIWA
|
||||||
|
"Metroid Wiki"
|
||||||
|
"https://www.metroidwiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/metroidwiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Metroid Wiki, founded on January 27, 2010 by Nathanial Rumphol-Janc and Zelda Informer, is a rapidly expanding wiki that covers everything Metroid, from the games, to every suit, vehicle and weapon."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("nintendo" "nintendoseries" "nintendogames") 'default
|
||||||
|
'NIWA
|
||||||
|
"Nintendo Wiki"
|
||||||
|
"http://niwanetwork.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/nintendowiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Created on May 12, 2010, NintendoWiki (N-Wiki) is a collaborative project by the NIWA team to create an encyclopedia dedicated to Nintendo, being the company around which all other NIWA content is focused. It ranges from mainstream information such as the games and people who work for the company, to the most obscure info like patents and interesting trivia."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("animalcrossing" "animalcrossingcf" "acnh") 'default
|
||||||
|
'NIWA
|
||||||
|
"Nookipedia"
|
||||||
|
"https://nookipedia.com/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/nookipedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded in August 2005 on Wikia, Nookipedia was originally known as Animal Crossing City. Shortly after its five-year anniversary, Animal Crossing City decided to merge with the independent Animal Crossing Wiki, which in January 2011 was renamed to Nookipedia. Covering everything from the series including characters, items, critters, and much more, Nookipedia is your number one resource for everything Animal Crossing!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("pikmin") 'default
|
||||||
|
'NIWA
|
||||||
|
"Pikipedia"
|
||||||
|
"https://www.pikminwiki.com/"
|
||||||
|
"https://niwanetwork.org/images/logos/pikipedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Pikipedia, also known as Pikmin Wiki, was founded by Dark Lord Revan on Wikia in December 2005. In September 2010, with NIWA's help, Pikipedia moved away from Wikia to become independent. Pikipedia is working towards their goal of being the foremost source for everything Pikmin."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("pikmin-fan" "pikpikpedia") 'default
|
||||||
|
'NIWA
|
||||||
|
"Pimkin Fanon"
|
||||||
|
"https://www.pikminfanon.com/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/pikifanon.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Pikmin Fanon is a Pikmin wiki for fan stories (fanon). Founded back on November 1, 2008 by Rocky0718 as a part of Wikia, Pikmin Fanon has been independent since September 14, 2010. Check them out for fan created stories based around the Pikmin series."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("supersmashbros") 'default
|
||||||
|
'NIWA
|
||||||
|
"SmashWiki"
|
||||||
|
"https://www.ssbwiki.com/"
|
||||||
|
"https://niwanetwork.org/images/logos/smashwiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Originally two separate wikis (one on SmashBoards, the other on Wikia), SmashWiki as we know it was formed out of a merge on February 29th, 2008, becoming independent on September 28th, 2010. SmashWiki is the premier source of Smash Bros. information, from simple tidbits to detailed mechanics, and also touches on the origins of its wealth of content from its sibling franchises."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("starfy") 'default
|
||||||
|
'NIWA
|
||||||
|
"Starfy Wiki"
|
||||||
|
"https://www.starfywiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/starfywiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded on May 30, 2009, Starfy Wiki's one goal is to become the best source on Nintendo's elusive game series The Legendary Starfy. After gaining independence in 2011 with the help of Tappy and the wiki's original administrative team, the wiki still hopes to achieve its goal and be the best source of Starfy info for all present and future fans."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'() 'default
|
||||||
|
'NIWA
|
||||||
|
"StrategyWiki"
|
||||||
|
"https://www.strategywiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/strategywiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "StrategyWiki was founded in December 2005 by former member Brandon Suit with the idea that the existing strategy guides on the Internet could be improved. Three years later, in December 2008, Scott Jacobi officially established Abxy LLC for the purpose of owning and operating StrategyWiki as a community. Their vision is to bring free, collaborative video game strategy guides to the masses, including Nintendo franchise strategy guides."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("mario" "themario" "imario" "supermarionintendo" "mariokart" "luigi-kart" "mario3") 'default
|
||||||
|
'NIWA
|
||||||
|
"Super Mario Wiki"
|
||||||
|
"https://www.mariowiki.com/"
|
||||||
|
"https://niwanetwork.org/images/logos/mariowiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Online since August 12, 2005, when it was founded by Steve Shinn, Super Mario Wiki has you covered for anything Mario, Donkey Kong, Wario, Luigi, Yoshi—the whole gang, in fact. With its own large community in its accompanying forum, Super Mario Wiki is not only a great encyclopedia, but a fansite for you to talk anything Mario."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("mario64") 'default
|
||||||
|
'NIWA
|
||||||
|
"Ukikipedia"
|
||||||
|
"https://ukikipedia.net/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/ukikipedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded in 2018, Ukikipedia is a wiki focused on expert level knowledge of Super Mario 64, including detailed coverage of game mechanics, glitches, speedrunning, and challenges."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("advancewars") 'default
|
||||||
|
'NIWA
|
||||||
|
"Wars Wiki"
|
||||||
|
"https://www.warswiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/warswiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Created in February 2009, Wars Wiki is a small wiki community with a large heart. Founded by JoJo and Wars Central, Wars Wiki is going strong on one of Nintendo's lesser known franchises. Wars Wiki is keen to contribute to NIWA, and we're proud to be able to support them. With the Wars Central community, including forums, it's definitely worth checking out."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("earthbound") 'default
|
||||||
|
'NIWA
|
||||||
|
"WikiBound"
|
||||||
|
"https://www.wikibound.info/wiki/WikiBound"
|
||||||
|
"https://niwanetwork.org/images/logos/wikibound.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded in early 2010 by Tacopill, WikiBound strives to create a detailed database on the Mother/EarthBound games, a quaint series only having two games officially released outside of Japan. Help spread the PK Love by editing WikiBound!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("kirby") 'default
|
||||||
|
'NIWA
|
||||||
|
"WiKirby"
|
||||||
|
"https://wikirby.com/wiki/Kirby_Wiki"
|
||||||
|
"https://niwanetwork.org/images/logos/wikirby.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "WiKirby. It's a wiki. About Kirby! Amidst the excitement of NIWA being founded, Josh LeJeune decided to create a Kirby Wiki, due to lack of a strong independent one online. Coming online on January 24, 2010, WiKirby continues its strong launch with a dedicated community and a daily growing source of Kirby based knowledge."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("xenoblade" "xenoseries" "xenogears" "xenosaga") 'parallel
|
||||||
|
'NIWA
|
||||||
|
"Xeno Series Wiki"
|
||||||
|
"https://www.xenoserieswiki.org/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/xenoserieswiki.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Xeno Series Wiki was created February 4, 2020 by Sir Teatei Moonlight. While founded by the desire to have an independent wiki for Xenoblade, there was an interest in including the Xenogears and Xenosaga games within its focus as well. This wide range of coverage means it's always in need of new editors to help bolster its many subjects."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("zelda" "zelda-archive") 'default
|
||||||
|
'NIWA
|
||||||
|
"Zeldapedia"
|
||||||
|
"https://zeldapedia.wiki/wiki/Main_Page"
|
||||||
|
"https://niwanetwork.org/images/logos/zeldapedia.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "Founded on April 23, 2005 as Zelda Wiki, today's Zeldapedia is your definitive source for encyclopedic information on The Legend of Zelda series, as well as all of the latest Zelda news. Zeldapedia went independent from Fandom in October 2022, citing Fandom's recent buyouts and staffing decisions among their reasons."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("chrono") 'default
|
||||||
|
'SEIWA
|
||||||
|
"Chrono Wiki"
|
||||||
|
"https://www.chronowiki.org/wiki/Chrono_Wiki"
|
||||||
|
"https://cdn.wikimg.net/en/chronowiki/images/5/59/Site-wiki.png"
|
||||||
|
(λ (props) '((p "A free encyclopedia dedicated to Chrono Trigger, Chrono Cross, Radical Dreamers, and everything else related to the series. A long, rich history and a friendly, encouraging userbase makes this the best Chrono in the entire time/space continuum!"))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("finalfantasy" "finalfantasyxv" "ffxiclopedia") 'parallel
|
||||||
|
'SEIWA
|
||||||
|
"Final Fantasy Wiki"
|
||||||
|
"https://finalfantasywiki.com/wiki/Main_Page"
|
||||||
|
"https://cdn.finalfantasywiki.com/wiki.png"
|
||||||
|
(λ (props) '((p "A new wiki focused on covering Square Enix's flagship franchise, the critically-acclaimed Final Fantasy series. The Final Fantasy Wiki was founded on January 12, 2020 as part of SEIWA and covers all things Final Fantasy and related franchises."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("kingdomhearts") 'default
|
||||||
|
'SEIWA
|
||||||
|
"Kingdom Hearts Wiki"
|
||||||
|
"https://www.khwiki.com/"
|
||||||
|
"https://kh.wiki.gallery/images/b/bc/Wiki.png"
|
||||||
|
(λ (props) '((p "The Kingdom Hearts Wiki attempts to document all things related to the Kingdom Hearts series, from elements of storyline to gameplay. The site was originally founded on April 1, 2006 on Wikia and became independent on February 9, 2011. Since this time, the community of the KHWiki strives to be the most professional and comprehensive Kingdom Hearts resource in the world."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("squareenix") 'default
|
||||||
|
'SEIWA
|
||||||
|
"Square Enix Wiki"
|
||||||
|
"https://wiki.seiwanetwork.org/wiki/Main_Page"
|
||||||
|
"https://cdn.seiwanetwork.org/thumb/9/94/Square_Enix_Wiki_Logo.png/200px-Square_Enix_Wiki_Logo.png"
|
||||||
|
(λ (props) '((p "The Square Enix Wiki was founded on February 8, 2012, and is an up-and-coming wiki project created by SEIWA. It focuses on covering all things Square Enix, from its video game series to its physical publications to its most notable employees and work as a company."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("ark" "ark-survival-evolved-archive") 'default
|
||||||
|
'ARK
|
||||||
|
"ARK Community Wiki"
|
||||||
|
"https://ark.wiki.gg/wiki/ARK_Survival_Evolved_Wiki"
|
||||||
|
"https://ark.wiki.gg/images/e/e6/Site-logo.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "The official ARK: Survival Evolved Wiki launched in 2016. In April 2022 it moved to wiki.gg's hosting to improve creative control and the overall browsing experience."))))
|
||||||
|
|
||||||
|
(extwiki^
|
||||||
|
'("astroneer") 'default
|
||||||
|
'Astroneer
|
||||||
|
"Astroneer Wiki"
|
||||||
|
"https://astroneer.wiki.gg/wiki/Astroneer_Wiki"
|
||||||
|
"https://astroneer.wiki.gg/images/7/74/Icon_Astroneer.png"
|
||||||
|
(λ (props)
|
||||||
|
`((p "“Fandom bought Gamepedia and forced a migration, with their restricted, ad-heavy appearance, and other annoying features that we could not remove, the wiki grew slow and annoying to use, especially for logged out users.")
|
||||||
|
(p "“We decided to move away from Fandom to Wiki.gg, which returns the wiki to how it used to be on gamepedia, without the ads spamming and forced videos.”"))))
|
||||||
|
|
||||||
|
;; fandom wikinames * empty * empty * Name * Home Page
|
||||||
|
(extwiki^ '("aether") 'empty 'empty "Aether Wiki" "https://aether.wiki.gg/wiki/Aether_Wiki" #f #f)
|
||||||
|
(extwiki^ '("before-darkness-falls") 'empty 'empty "Before Darkness Falls Wiki" "https://beforedarknessfalls.wiki.gg/wiki/Before_Darkness_Falls_Wiki" #f #f)
|
||||||
|
(extwiki^ '("calamitymod") 'empty 'empty "Calamity Mod Wiki" "https://calamitymod.wiki.gg/wiki/Calamity_Mod_Wiki" #f #f)
|
||||||
|
(extwiki^ '("chivalry" "chivalry2") 'empty 'empty "Official Chivalry Wiki" "https://chivalry.wiki.gg/wiki/Chivalry_Wiki" #f #f)
|
||||||
|
(extwiki^ '("clockup") 'empty 'empty "CLOCKUP WIKI" "https://en.clockupwiki.org/wiki/CLOCKUP_WIKI:Plan" #f #f)
|
||||||
|
(extwiki^ '("half-life") 'empty 'empty "Combine OverWiki" "https://combineoverwiki.net/wiki/Main_Page" #f #f)
|
||||||
|
(extwiki^ '("coromon") 'empty 'empty "Coromon Wiki" "https://coromon.wiki.gg/wiki/Coromon_Wiki" #f #f)
|
||||||
|
(extwiki^ '("cosmoteer") 'empty 'empty "Cosmoteer Wiki" "https://cosmoteer.wiki.gg/wiki/Cosmoteer_Wiki" #f #f)
|
||||||
|
(extwiki^ '("criticalrole") 'empty 'empty "Encylopedia Exandria" "https://criticalrole.miraheze.org/wiki/Main_Page" #f #f)
|
||||||
|
(extwiki^ '("cuphead") 'empty 'empty "Cuphead Wiki" "https://cuphead.wiki.gg/wiki/Cuphead_Wiki" #f #f)
|
||||||
|
(extwiki^ '("darkdeity") 'empty 'empty "Dark Deity Wiki" "https://darkdeity.wiki.gg/wiki/Dark_Deity_Wiki" #f #f)
|
||||||
|
(extwiki^ '("deeprockgalactic") 'empty 'empty "Deep Rock Galactic Wiki" "https://deeprockgalactic.wiki.gg/wiki/Deep_Rock_Galactic_Wiki" #f #f)
|
||||||
|
(extwiki^ '("doom") 'empty 'empty "DoomWiki.org" "https://doomwiki.org/wiki/Entryway" #f #f)
|
||||||
|
(extwiki^ '("dreamscaper") 'empty 'empty "Official Dreamscaper Wiki" "https://dreamscaper.wiki.gg/wiki/Dreamscaper_Wiki" #f #f)
|
||||||
|
(extwiki^ '("elderscrolls") 'empty 'empty "UESP" "https://en.uesp.net/wiki/Main_Page" #f #f)
|
||||||
|
(extwiki^ '("fiend-folio") 'empty 'empty "Official Fiend Folio Wiki" "https://fiendfolio.wiki.gg/wiki/Fiend_Folio_Wiki" #f #f)
|
||||||
|
(extwiki^ '("foxhole") 'empty 'empty "Foxhole Wiki" "https://foxhole.wiki.gg/wiki/Foxhole_Wiki" #f #f)
|
||||||
|
(extwiki^ '("have-a-nice-death") 'empty 'empty "Have a Nice Death Wiki" "https://haveanicedeath.wiki.gg/wiki/Have_a_Nice_Death_Wiki" #f #f)
|
||||||
|
(extwiki^ '("jojo" "jojos") 'empty 'empty "JoJo's Bizarre Encyclopedia" "https://jojowiki.com/" #f #f)
|
||||||
|
(extwiki^ '("legiontd2") 'empty 'empty "Legion TD 2 Wiki" "https://legiontd2.wiki.gg/wiki/Legion_TD_2_Wiki" #f #f)
|
||||||
|
(extwiki^ '("noita") 'empty 'empty "Noita Wiki" "https://noita.wiki.gg/wiki/Noita_Wiki" #f #f)
|
||||||
|
(extwiki^ '("pathofexile") 'empty 'empty "Official Path of Exile Wiki" "https://www.poewiki.net/wiki/Path_of_Exile_Wiki" #f #f)
|
||||||
|
(extwiki^ '("projectarrhythmia") 'empty 'empty "Project Arrhythmia Wiki" "https://projectarrhythmia.wiki.gg/wiki/Project_Arrhythmia_Wiki" #f #f)
|
||||||
|
(extwiki^ '("sandsofaura") 'empty 'empty "Official Sands of Aura Wiki" "https://sandsofaura.wiki.gg/wiki/Sands_of_Aura_Wiki" #f #f)
|
||||||
|
(extwiki^ '("seaofthieves") 'empty 'empty "Official Sea of Thieves Wiki" "https://seaofthieves.wiki.gg/wiki/Sea_of_Thieves" #f #f)
|
||||||
|
(extwiki^ '("sonsoftheforest") 'empty 'empty "Sons of the Forest Wiki" "https://sonsoftheforest.wiki.gg/wiki/Sons_of_the_Forest_Wiki" #f #f)
|
||||||
|
(extwiki^ '("stardewvalley") 'empty 'empty "Official Stardew Valley Wiki" "https://www.stardewvalleywiki.com/Stardew_Valley_Wiki" #f #f)
|
||||||
|
(extwiki^ '("steamworld") 'empty 'empty "Official SteamWorld Wiki" "https://steamworld.wiki.gg/wiki/SteamWorld_Wiki" #f #f)
|
||||||
|
(extwiki^ '("teamfortress") 'empty 'empty "Official Team Fortress Wiki" "https://wiki.teamfortress.com/wiki/Main_Page" #f #f)
|
||||||
|
(extwiki^ '("temtem") 'empty 'empty "Official Temtem Wiki" "https://temtem.wiki.gg/wiki/Temtem_Wiki" #f #f)
|
||||||
|
(extwiki^ '("thoriummod") 'empty 'empty "Official Thorium Mod Wiki" "https://thoriummod.wiki.gg/wiki/Thorium_Mod_Wiki" #f #f)
|
||||||
|
(extwiki^ '("totherescue") 'empty 'empty "To The Rescue!" "https://totherescue.wiki.gg/wiki/To_The_Rescue%21_Wiki" #f #f)
|
||||||
|
(extwiki^ '("touhou") 'empty 'empty "Touhou Wiki" "https://en.touhouwiki.net/wiki/Touhou_Wiki" #f #f)
|
||||||
|
(extwiki^ '("undermine") 'empty 'empty "Official UnderMine Wiki" "https://undermine.wiki.gg/wiki/UnderMine_Wiki" #f #f)
|
||||||
|
(extwiki^ '("westofloathing" "loathing") 'empty 'empty "Wiki of Loathing" "https://loathing.wiki.gg/wiki/Wiki_of_Loathing" #f #f)
|
||||||
|
(extwiki^ '("willyousnail") 'empty 'empty "Official Will You Snail Wiki" "https://willyousnail.wiki.gg/wiki/Will_You_Snail_Wiki" #f #f)
|
||||||
|
(extwiki^ '("yumenikki" "yume-nikki-dream-diary") 'empty 'empty "Yume Wiki" "https://yume.wiki/Main_Page" #f #f)))
|
||||||
|
|
||||||
|
;; get the current dataset so it can be stored above
|
||||||
|
(module+ fetch
|
||||||
|
(require racket/generator
|
||||||
|
racket/list
|
||||||
|
net/http-easy
|
||||||
|
html-parsing
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
(define r (get "https://www.niwanetwork.org/members/"))
|
||||||
|
(define x (html->xexp (bytes->string/utf-8 (response-body r))))
|
||||||
|
(define english ((query-selector (λ (e a c) (equal? (get-attribute 'id a) "content1")) x)))
|
||||||
|
(define gen (query-selector (λ (e a c) (has-class? "member" a)) english))
|
||||||
|
(for/list ([item (in-producer gen #f)])
|
||||||
|
(define links (query-selector (λ (e a c) (eq? e 'a)) item))
|
||||||
|
(define url (get-attribute 'href (bits->attributes (links))))
|
||||||
|
(define title (third (links)))
|
||||||
|
(define icon (get-attribute 'src (bits->attributes ((query-selector (λ (e a c) (eq? e 'img)) item)))))
|
||||||
|
(define description (second ((query-selector (λ (e a c) (eq? e 'p)) item))))
|
||||||
|
(list '() title url icon description)))
|
125
src/extwiki-generic.rkt
Normal file
125
src/extwiki-generic.rkt
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/list
|
||||||
|
racket/match
|
||||||
|
racket/string
|
||||||
|
memo
|
||||||
|
net/http-easy
|
||||||
|
html-parsing
|
||||||
|
"../lib/pure-utils.rkt"
|
||||||
|
"../lib/syntax.rkt"
|
||||||
|
"../lib/url-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
|
(provide
|
||||||
|
get-redirect-content)
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require rackunit))
|
||||||
|
|
||||||
|
;; fandom wikinames * Title * Main Page * Search page override * API endpoint override
|
||||||
|
(define wikis
|
||||||
|
'(((gallowmere) "Gallowmere Historia" "https://medievil.wiki/w/Main_Page" #f #f)
|
||||||
|
((fallout) "Fallout Wiki" "https://fallout.wiki/wiki/Fallout_Wiki" #f "https://fallout.wiki/api.php")
|
||||||
|
))
|
||||||
|
|
||||||
|
(define wikis-hash (make-hash))
|
||||||
|
(for ([w wikis])
|
||||||
|
(for ([wikiname (car w)])
|
||||||
|
(hash-set! wikis-hash (symbol->string wikiname) w)))
|
||||||
|
(module+ test
|
||||||
|
(check-equal? (cadr (hash-ref wikis-hash "gallowmere"))
|
||||||
|
"Gallowmere Historia"))
|
||||||
|
|
||||||
|
(define (parse-table table)
|
||||||
|
(define rows (query-selector (λ (t a c) (eq? t 'tr)) table))
|
||||||
|
(define header-row (rows))
|
||||||
|
(define column-names
|
||||||
|
(for/list ([th (in-producer (query-selector (λ (t a c) (eq? t 'th)) header-row) #f)])
|
||||||
|
(string->symbol (string-downcase (string-trim (findf string? th))))))
|
||||||
|
(define data-row (rows))
|
||||||
|
(for/hasheq ([col-name column-names]
|
||||||
|
[col-value (in-producer (query-selector (λ (t a c) (eq? t 'td)) data-row) #f)])
|
||||||
|
(values col-name (filter element-is-content? (cdr col-value)))))
|
||||||
|
(module+ test
|
||||||
|
(check-equal? (parse-table (html->xexp "<table> <tbody><tr> <th>Links</th></tr> <tr> <td><a target=\"_blank\" rel=\"nofollow noreferrer noopener\" class=\"external text\" href=\"https://sirdanielfortesque.proboards.com/\">Forum</a></td></tr></tbody></table>"))
|
||||||
|
'#hasheq((links . ((a (@ (target "_blank") (rel "nofollow noreferrer noopener") (class "external text") (href "https://sirdanielfortesque.proboards.com/")) "Forum"))))))
|
||||||
|
|
||||||
|
(define (table->links table)
|
||||||
|
(define v (hash-ref table 'links #f))
|
||||||
|
(cond/var
|
||||||
|
[(not v) (values null '("Data table must have a \"Links\" column"))]
|
||||||
|
(var links (filter (λ (a) (and (pair? a) (eq? (car a) 'a))) v)) ; <a> elements
|
||||||
|
[(null? links) (values null '("Links column must have at least one link"))]
|
||||||
|
[#t (values links null)]))
|
||||||
|
|
||||||
|
(define (table->logo table)
|
||||||
|
(define logo (hash-ref table 'logo #f))
|
||||||
|
(cond/var
|
||||||
|
[(not logo) (values #f '("Data table must have a \"Logo\" column"))]
|
||||||
|
[(null? logo) (values #f '("Logo table column must have a link"))]
|
||||||
|
(var href (get-attribute 'href (bits->attributes (car (hash-ref table 'logo)))))
|
||||||
|
[(not href) (values #f '("Logo table column must have a link"))]
|
||||||
|
[#t (values href null)]))
|
||||||
|
|
||||||
|
(define (get-api-endpoint wiki)
|
||||||
|
(define main-page (third wiki))
|
||||||
|
(define override (fifth wiki))
|
||||||
|
(or override
|
||||||
|
(match main-page
|
||||||
|
[(regexp #rx"/$") (string-append main-page "api.php")]
|
||||||
|
[(regexp #rx"^(.*)/wiki/" (list _ domain)) (string-append domain "/w/api.php")]
|
||||||
|
[(regexp #rx"^(.*)/w/" (list _ domain)) (string-append domain "/api.php")]
|
||||||
|
[_ (error 'get-api-endpoint "unknown url format: ~a" main-page)])))
|
||||||
|
|
||||||
|
(define (get-search-page wiki)
|
||||||
|
(define main-page (third wiki))
|
||||||
|
(define override (fourth wiki))
|
||||||
|
(or override
|
||||||
|
(match main-page
|
||||||
|
[(regexp #rx"/$") (string-append main-page "Special:Search")]
|
||||||
|
[(regexp #rx"^(.*/w[^./]*/)" (list _ wiki-prefix)) (string-append wiki-prefix "Special:Search")]
|
||||||
|
[_ (error 'get-search-page "unknown url format: ~a" main-page)])))
|
||||||
|
|
||||||
|
(define/memoize (get-redirect-content wikiname) #:hash hash
|
||||||
|
(define wiki (hash-ref wikis-hash wikiname #f))
|
||||||
|
(cond
|
||||||
|
[wiki
|
||||||
|
(define display-name (cadr wiki))
|
||||||
|
(define endpoint (string-append (get-api-endpoint wiki) "?action=parse&page=MediaWiki:BreezeWikiRedirect&prop=text&formatversion=2&format=json"))
|
||||||
|
(define res (get endpoint))
|
||||||
|
(define html (jp "/parse/text" (response-json res)))
|
||||||
|
(define content ((query-selector (λ (t a c) (has-class? "mw-parser-output" a))
|
||||||
|
(html->xexp html))))
|
||||||
|
(define body (for/list ([p (in-producer (query-selector (λ (t a c) (eq? t 'p)) content) #f)]) p))
|
||||||
|
(define table (parse-table ((query-selector (λ (t a c) (eq? t 'table)) content))))
|
||||||
|
(define-values (links links-errors) (table->links table))
|
||||||
|
(define-values (logo logo-errors) (table->logo table))
|
||||||
|
(define construct-errors (append links-errors logo-errors))
|
||||||
|
(λ (title)
|
||||||
|
(define go
|
||||||
|
(string-append (get-search-page wiki)
|
||||||
|
"?"
|
||||||
|
(params->query `(("search" . ,title)
|
||||||
|
("go" . "Go")))))
|
||||||
|
`(aside (@ (class "niwa__notice"))
|
||||||
|
(h1 (@ (class "niwa__header")) ,display-name " has its own website separate from Fandom.")
|
||||||
|
(div (@ (class "niwa__cols"))
|
||||||
|
(div (@ (class "niwa__left"))
|
||||||
|
(a (@ (class "niwa__go") (href ,go)) "Read " ,title " on " ,display-name " →")
|
||||||
|
,@body
|
||||||
|
(p "This wiki's core community has wholly migrated away from Fandom. You should "
|
||||||
|
(a (@ (href ,go)) "go to " ,display-name " now!")))
|
||||||
|
(div (@ (class "niwa__right"))
|
||||||
|
(img (@ (class "niwa__logo") (src ,logo)))))
|
||||||
|
,(if (pair? links)
|
||||||
|
`(p (@ (class "niwa__feedback"))
|
||||||
|
,@(add-between links " / "))
|
||||||
|
"")
|
||||||
|
,(if (pair? construct-errors)
|
||||||
|
`(ul
|
||||||
|
,@(for/list ([error construct-errors])
|
||||||
|
`(li ,error)))
|
||||||
|
"")))]
|
||||||
|
[#t #f]))
|
||||||
|
(module+ test
|
||||||
|
((get-redirect-content "gallowmere") "Gallowmere Historia"))
|
|
@ -1,156 +0,0 @@
|
||||||
#lang racket/base
|
|
||||||
|
|
||||||
(provide
|
|
||||||
niwa-data)
|
|
||||||
|
|
||||||
;; wikiname, niwa-name, url, logo-url
|
|
||||||
(define niwa-data
|
|
||||||
'((("arms" "armsgame")
|
|
||||||
"ARMS Institute"
|
|
||||||
"https://armswiki.org/wiki/Home"
|
|
||||||
"/images/logos/armswiki.png"
|
|
||||||
"ARMS Institute is a comprehensive resource for information about the Nintendo Switch game, ARMS. Founded on May 1, 2017 and growing rapidly, the wiki strives to offer in-depth coverage of ARMS from both a competitive and casual perspective. Join us and ARM yourself with knowledge!")
|
|
||||||
(("pokemon" "monster")
|
|
||||||
"Bulbapedia"
|
|
||||||
"https://bulbapedia.bulbagarden.net/wiki/Main_Page"
|
|
||||||
"/images/logos/bulbapedia.png"
|
|
||||||
"A part of the Bulbagarden community, Bulbapedia was founded on December 21, 2004 by Liam Pomfret. Everything you need to know about Pokémon can be found at Bulbapedia, whether about the games, the anime, the manga, or something else entirely. With its Bulbanews section and the Bulbagarden forums, it's your one-stop online place for Pokémon.")
|
|
||||||
(("dragalialost")
|
|
||||||
"Dragalia Lost Wiki"
|
|
||||||
"https://dragalialost.wiki/w/Dragalia_Lost_Wiki"
|
|
||||||
"/images/logos/dragalialost.png"
|
|
||||||
"The Dragalia Lost Wiki was originally founded in September 2018 on the Gamepedia platform but went independent in January 2021. The Wiki aims to document anything and everything Dragalia Lost, from in-game data to mechanics, story, guides, and more!")
|
|
||||||
(("dragonquest")
|
|
||||||
"Dragon Quest Wiki"
|
|
||||||
"https://dragon-quest.org/wiki/Main_Page"
|
|
||||||
"/images/logos/dragonquestwiki.png"
|
|
||||||
"Originally founded on Wikia, the Dragon Quest Wiki was largely inactive until FlyingRagnar became an admin in late 2009. The wiki went independent about a year later when it merged with the Dragon Quest Dictionary/Encyclopedia which was run by Zenithian and supported by the Dragon's Den. The Dragon Quest Wiki aims to be the most complete resource for Dragon Quest information on the web. It continues to grow in the hope that one day the series will be as popular in the rest of the world as it is in Japan.")
|
|
||||||
(("fireemblem")
|
|
||||||
"Fire Emblem Wiki"
|
|
||||||
"https://fireemblemwiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/fireemblemwiki.png"
|
|
||||||
"Growing since August 26, 2010, Fire Emblem Wiki is a project whose goal is to cover all information pertaining to the Fire Emblem series. It aspires to become the most complete and accurate independent source of information on this series.")
|
|
||||||
(("fzero" "f-zero")
|
|
||||||
"F-Zero Wiki"
|
|
||||||
"https://mutecity.org/wiki/F-Zero_Wiki"
|
|
||||||
"/images/logos/fzerowiki.png"
|
|
||||||
"Founded on Wikia in November 2007, F-Zero Wiki became independent with NIWA's help in 2011. F-Zero Wiki is quickly growing into the Internet's definitive source for the world of 2200 km/h+, from pilots to machines, and is the founding part of MuteCity.org, the web's first major F-Zero community.")
|
|
||||||
(("goldensun")
|
|
||||||
"Golden Sun Universe"
|
|
||||||
"https://www.goldensunwiki.net/wiki/Main_Page"
|
|
||||||
"/images/logos/goldensununiverse.png"
|
|
||||||
"Originally founded on Wikia in late 2006, Golden Sun Universe has always worked hard to meet one particular goal: to be the single most comprehensive yet accessible resource on the Internet for Nintendo's RPG series Golden Sun. It became an independent wiki four years later. Covering characters and plot, documenting all aspects of the gameplay, featuring walkthroughs both thorough and bare-bones, and packed with all manner of odd and fascinating minutiae, Golden Sun Universe leaves no stone unturned!")
|
|
||||||
(("tetris")
|
|
||||||
"Hard Drop - Tetris Wiki"
|
|
||||||
"https://harddrop.com/wiki/Main_Page"
|
|
||||||
"/images/logos/harddrop.png"
|
|
||||||
"The Tetris Wiki was founded by Tetris fans for Tetris fans on tetrisconcept.com in March 2006. The Tetris Wiki torch was passed to harddrop.com in July 2009. Hard Drop is a Tetris community for all Tetris players, regardless of skill or what version of Tetris you play.")
|
|
||||||
(("kidicarus")
|
|
||||||
"Icaruspedia"
|
|
||||||
"https://www.kidicaruswiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/icaruspedia.png"
|
|
||||||
"Icaruspedia is the Kid Icarus wiki that keeps flying to new heights. After going independent on January 8, 2012, Icaruspedia has worked to become the largest and most trusted independent source of Kid Icarus information. Just like Pit, they'll keep on fighting until the job is done.")
|
|
||||||
(("splatoon" "uk-splatoon" "splatoon3" "splatoon2")
|
|
||||||
"Inkipedia"
|
|
||||||
"https://splatoonwiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/inkipedia.png"
|
|
||||||
"Inkipedia is your ever-growing go-to source for all things Splatoon related. Though founded on Wikia on June 10, 2014, Inkipedia went independent on May 18, 2015, just days before Splatoon's release. Our aim is to cover all aspects of the series, both high and low. Come splat with us now!")
|
|
||||||
(("starfox")
|
|
||||||
"Lylat Wiki"
|
|
||||||
"https://starfoxwiki.info/wiki/Lylat_Wiki"
|
|
||||||
"/images/logos/lylatwiki.png"
|
|
||||||
"Out of seemingly nowhere, Lylat Wiki sprung up one day in early 2010. Led by creator, Justin Folvarcik, and project head, Tacopill, the wiki has reached stability since the move to its own domain. The staff of Lylat Wiki are glad to help out the NIWA wikis and are even prouder to join NIWA's ranks as the source for information on the Star Fox series.")
|
|
||||||
(("metroid" "themetroid")
|
|
||||||
"Metroid Wiki"
|
|
||||||
"https://www.metroidwiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/metroidwiki.png"
|
|
||||||
"Metroid Wiki, founded on January 27, 2010 by Nathanial Rumphol-Janc and Zelda Informer, is a rapidly expanding wiki that covers everything Metroid, from the games, to every suit, vehicle and weapon.")
|
|
||||||
(("nintendo" "nintendoseries" "nintendogames")
|
|
||||||
"Nintendo Wiki"
|
|
||||||
"http://niwanetwork.org/wiki/Main_Page"
|
|
||||||
"/images/logos/nintendowiki.png"
|
|
||||||
"Created on May 12, 2010, NintendoWiki (N-Wiki) is a collaborative project by the NIWA team to create an encyclopedia dedicated to Nintendo, being the company around which all other NIWA content is focused. It ranges from mainstream information such as the games and people who work for the company, to the most obscure info like patents and interesting trivia.")
|
|
||||||
(("animalcrossing" "animalcrossingcf" "acnh")
|
|
||||||
"Nookipedia"
|
|
||||||
"https://nookipedia.com/wiki/Main_Page"
|
|
||||||
"/images/logos/nookipedia.png"
|
|
||||||
"Founded in August 2005 on Wikia, Nookipedia was originally known as Animal Crossing City. Shortly after its five-year anniversary, Animal Crossing City decided to merge with the independent Animal Crossing Wiki, which in January 2011 was renamed to Nookipedia. Covering everything from the series including characters, items, critters, and much more, Nookipedia is your number one resource for everything Animal Crossing!")
|
|
||||||
(("pikmin")
|
|
||||||
"Pikipedia"
|
|
||||||
"https://www.pikminwiki.com/"
|
|
||||||
"/images/logos/pikipedia.png"
|
|
||||||
"Pikipedia, also known as Pikmin Wiki, was founded by Dark Lord Revan on Wikia in December 2005. In September 2010, with NIWA's help, Pikipedia moved away from Wikia to become independent. Pikipedia is working towards their goal of being the foremost source for everything Pikmin.")
|
|
||||||
(("pikmin-fan" "pikpikpedia")
|
|
||||||
"Pimkin Fanon"
|
|
||||||
"https://www.pikminfanon.com/wiki/Main_Page"
|
|
||||||
"/images/logos/pikifanon.png"
|
|
||||||
"Pikmin Fanon is a Pikmin wiki for fan stories (fanon). Founded back on November 1, 2008 by Rocky0718 as a part of Wikia, Pikmin Fanon has been independent since September 14, 2010. Check them out for fan created stories based around the Pikmin series.")
|
|
||||||
(("supersmashbros")
|
|
||||||
"SmashWiki"
|
|
||||||
"https://www.ssbwiki.com/"
|
|
||||||
"/images/logos/smashwiki.png"
|
|
||||||
"Originally two separate wikis (one on SmashBoards, the other on Wikia), SmashWiki as we know it was formed out of a merge on February 29th, 2008, becoming independent on September 28th, 2010. SmashWiki is the premier source of Smash Bros. information, from simple tidbits to detailed mechanics, and also touches on the origins of its wealth of content from its sibling franchises.")
|
|
||||||
(("starfy")
|
|
||||||
"Starfy Wiki"
|
|
||||||
"https://www.starfywiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/starfywiki.png"
|
|
||||||
"Founded on May 30, 2009, Starfy Wiki's one goal is to become the best source on Nintendo's elusive game series The Legendary Starfy. After gaining independence in 2011 with the help of Tappy and the wiki's original administrative team, the wiki still hopes to achieve its goal and be the best source of Starfy info for all present and future fans.")
|
|
||||||
(()
|
|
||||||
"StrategyWiki"
|
|
||||||
"https://www.strategywiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/strategywiki.png"
|
|
||||||
"StrategyWiki was founded in December 2005 by former member Brandon Suit with the idea that the existing strategy guides on the Internet could be improved. Three years later, in December 2008, Scott Jacobi officially established Abxy LLC for the purpose of owning and operating StrategyWiki as a community. Their vision is to bring free, collaborative video game strategy guides to the masses, including Nintendo franchise strategy guides.")
|
|
||||||
(("mario" "themario" "imario" "supermarionintendo" "mariokart" "luigi-kart" "mario3")
|
|
||||||
"Super Mario Wiki"
|
|
||||||
"https://www.mariowiki.com/"
|
|
||||||
"/images/logos/mariowiki.png"
|
|
||||||
"Online since August 12, 2005, when it was founded by Steve Shinn, Super Mario Wiki has you covered for anything Mario, Donkey Kong, Wario, Luigi, Yoshi—the whole gang, in fact. With its own large community in its accompanying forum, Super Mario Wiki is not only a great encyclopedia, but a fansite for you to talk anything Mario.")
|
|
||||||
(("mario64")
|
|
||||||
"Ukikipedia"
|
|
||||||
"https://ukikipedia.net/wiki/Main_Page"
|
|
||||||
"/images/logos/ukikipedia.png"
|
|
||||||
"Founded in 2018, Ukikipedia is a wiki focused on expert level knowledge of Super Mario 64, including detailed coverage of game mechanics, glitches, speedrunning, and challenges.")
|
|
||||||
(("advancewars")
|
|
||||||
"Wars Wiki"
|
|
||||||
"https://www.warswiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/warswiki.png"
|
|
||||||
"Created in February 2009, Wars Wiki is a small wiki community with a large heart. Founded by JoJo and Wars Central, Wars Wiki is going strong on one of Nintendo's lesser known franchises. Wars Wiki is keen to contribute to NIWA, and we're proud to be able to support them. With the Wars Central community, including forums, it's definitely worth checking out.")
|
|
||||||
(("earthbound")
|
|
||||||
"WikiBound"
|
|
||||||
"https://www.wikibound.info/wiki/WikiBound"
|
|
||||||
"/images/logos/wikibound.png"
|
|
||||||
"Founded in early 2010 by Tacopill, WikiBound strives to create a detailed database on the Mother/EarthBound games, a quaint series only having two games officially released outside of Japan. Help spread the PK Love by editing WikiBound!")
|
|
||||||
(("kirby")
|
|
||||||
"WiKirby"
|
|
||||||
"https://wikirby.com/wiki/Kirby_Wiki"
|
|
||||||
"/images/logos/wikirby.png"
|
|
||||||
"WiKirby. It's a wiki. About Kirby! Amidst the excitement of NIWA being founded, Josh LeJeune decided to create a Kirby Wiki, due to lack of a strong independent one online. Coming online on January 24, 2010, WiKirby continues its strong launch with a dedicated community and a daily growing source of Kirby based knowledge.")
|
|
||||||
(("xenoblade" "xenoseries" "xenogears" "xenosaga")
|
|
||||||
"Xeno Series Wiki"
|
|
||||||
"https://www.xenoserieswiki.org/wiki/Main_Page"
|
|
||||||
"/images/logos/xenoserieswiki.png"
|
|
||||||
"Xeno Series Wiki was created February 4, 2020 by Sir Teatei Moonlight. While founded by the desire to have an independent wiki for Xenoblade, there was an interest in including the Xenogears and Xenosaga games within its focus as well. This wide range of coverage means it's always in need of new editors to help bolster its many subjects.")
|
|
||||||
(("zelda" "zelda-archive")
|
|
||||||
"Zeldapedia"
|
|
||||||
"https://zeldapedia.wiki/wiki/Main_Page"
|
|
||||||
"/images/logos/zeldapedia.png"
|
|
||||||
"Founded on April 23, 2005 as Zelda Wiki, today's Zeldapedia is your definitive source for encyclopedic information on The Legend of Zelda series, as well as all of the latest Zelda news.")))
|
|
||||||
|
|
||||||
;; get the current dataset so it can be stored above
|
|
||||||
(module+ fetch
|
|
||||||
(require racket/generator
|
|
||||||
racket/list
|
|
||||||
net/http-easy
|
|
||||||
html-parsing
|
|
||||||
"xexpr-utils.rkt")
|
|
||||||
(define r (get "https://www.niwanetwork.org/members/"))
|
|
||||||
(define x (html->xexp (bytes->string/utf-8 (response-body r))))
|
|
||||||
(define english ((query-selector (λ (e a c) (equal? (get-attribute 'id a) "content1")) x)))
|
|
||||||
(define gen (query-selector (λ (e a c) (has-class? "member" a)) english))
|
|
||||||
(for/list ([item (in-producer gen #f)])
|
|
||||||
(define links (query-selector (λ (e a c) (eq? e 'a)) item))
|
|
||||||
(define url (get-attribute 'href (bits->attributes (links))))
|
|
||||||
(define title (third (links)))
|
|
||||||
(define icon (get-attribute 'src (bits->attributes ((query-selector (λ (e a c) (eq? e 'img)) item)))))
|
|
||||||
(define description (second ((query-selector (λ (e a c) (eq? e 'p)) item))))
|
|
||||||
(list '() title url icon description)))
|
|
|
@ -16,9 +16,10 @@
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"page-wiki.rkt"
|
"page-wiki.rkt"
|
||||||
"syntax.rkt"
|
"../lib/syntax.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"whole-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
page-category)
|
page-category)
|
||||||
|
|
|
@ -16,9 +16,10 @@
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"page-wiki.rkt"
|
"page-wiki.rkt"
|
||||||
"syntax.rkt"
|
"../lib/syntax.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"whole-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide page-file)
|
(provide page-file)
|
||||||
|
|
||||||
|
@ -101,47 +102,48 @@
|
||||||
`""))))
|
`""))))
|
||||||
|
|
||||||
(define (page-file req)
|
(define (page-file req)
|
||||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
(response-handler
|
||||||
(define prefixed-title (path/param-path (caddr (url-path (request-uri req)))))
|
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||||
(define origin (format "https://~a.fandom.com" wikiname))
|
(define prefixed-title (path/param-path (caddr (url-path (request-uri req)))))
|
||||||
(define source-url (format "~a/wiki/~a" origin prefixed-title))
|
(define origin (format "https://~a.fandom.com" wikiname))
|
||||||
|
(define source-url (format "~a/wiki/~a" origin prefixed-title))
|
||||||
|
|
||||||
(thread-let ([media-detail
|
(thread-let
|
||||||
(define dest-url
|
([media-detail (define dest-url
|
||||||
(format "~a/wikia.php?~a"
|
(format "~a/wikia.php?~a"
|
||||||
origin
|
origin
|
||||||
(params->query `(("format" . "json") ("controller" . "Lightbox")
|
(params->query `(("format" . "json") ("controller" . "Lightbox")
|
||||||
("method" . "getMediaDetail")
|
("method" . "getMediaDetail")
|
||||||
("fileTitle" . ,prefixed-title)))))
|
("fileTitle" . ,prefixed-title)))))
|
||||||
(log-outgoing dest-url)
|
(log-outgoing dest-url)
|
||||||
(define dest-res (easy:get dest-url #:timeouts timeouts))
|
(define dest-res (easy:get dest-url #:timeouts timeouts))
|
||||||
(easy:response-json dest-res)]
|
(easy:response-json dest-res)]
|
||||||
[siteinfo (siteinfo-fetch wikiname)])
|
[siteinfo (siteinfo-fetch wikiname)])
|
||||||
(if (not (jp "/exists" media-detail #f))
|
(if (not (jp "/exists" media-detail #f))
|
||||||
(next-dispatcher)
|
(next-dispatcher)
|
||||||
(response-handler
|
(response-handler
|
||||||
(define file-title (jp "/fileTitle" media-detail ""))
|
(define file-title (jp "/fileTitle" media-detail ""))
|
||||||
(define title
|
(define title
|
||||||
(if (non-empty-string? file-title) (format "File:~a" file-title) prefixed-title))
|
(if (non-empty-string? file-title) (format "File:~a" file-title) prefixed-title))
|
||||||
(define image-content-type
|
(define image-content-type
|
||||||
(if (non-empty-string? (jp "/videoEmbedCode" media-detail ""))
|
(if (non-empty-string? (jp "/videoEmbedCode" media-detail ""))
|
||||||
#f
|
#f
|
||||||
(url-content-type (jp "/imageUrl" media-detail))))
|
(url-content-type (jp "/imageUrl" media-detail))))
|
||||||
(define body
|
(define body
|
||||||
(generate-results-page #:req req
|
(generate-results-page #:req req
|
||||||
#:source-url source-url
|
#:source-url source-url
|
||||||
#:wikiname wikiname
|
#:wikiname wikiname
|
||||||
#:title title
|
#:title title
|
||||||
#:media-detail media-detail
|
#:media-detail media-detail
|
||||||
#:image-content-type image-content-type
|
#:image-content-type image-content-type
|
||||||
#:siteinfo siteinfo))
|
#:siteinfo siteinfo))
|
||||||
(when (config-true? 'debug)
|
(when (config-true? 'debug)
|
||||||
; used for its side effects
|
; used for its side effects
|
||||||
; convert to string with error checking, error will be raised if xexp is invalid
|
; convert to string with error checking, error will be raised if xexp is invalid
|
||||||
(xexp->html body))
|
(xexp->html body))
|
||||||
(response/output #:code 200
|
(response/output #:code 200
|
||||||
#:headers (build-headers always-headers)
|
#:headers (build-headers always-headers)
|
||||||
(λ (out) (write-html body out)))))))
|
(λ (out) (write-html body out))))))))
|
||||||
(module+ test
|
(module+ test
|
||||||
(parameterize ([(config-parameter 'strict_proxy) "true"])
|
(parameterize ([(config-parameter 'strict_proxy) "true"])
|
||||||
(check-equal? (get-media-html "https://static.wikia.nocookie.net/a" "image/jpeg")
|
(check-equal? (get-media-html "https://static.wikia.nocookie.net/a" "image/jpeg")
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
web-server/http
|
web-server/http
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
page-global-search)
|
page-global-search)
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"static-data.rkt"
|
"static-data.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt"
|
"../lib/xexpr-utils.rkt"
|
||||||
"config.rkt")
|
"config.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
|
@ -26,13 +26,16 @@
|
||||||
(define content
|
(define content
|
||||||
`((h2 "BreezeWiki makes wiki pages on Fandom readable")
|
`((h2 "BreezeWiki makes wiki pages on Fandom readable")
|
||||||
(p "It removes ads, videos, and suggested content, leaving you with a clean page that doesn't slow down your device or use up your data.")
|
(p "It removes ads, videos, and suggested content, leaving you with a clean page that doesn't slow down your device or use up your data.")
|
||||||
(p "BreezeWiki can also be called an \"alternative frontend for Fandom\".")
|
|
||||||
(p ,(format "To use BreezeWiki, just replace \"fandom.com\" with \"~a\", and you'll instantly be teleported to a better world."
|
(p ,(format "To use BreezeWiki, just replace \"fandom.com\" with \"~a\", and you'll instantly be teleported to a better world."
|
||||||
(if (config-true? 'canonical_origin)
|
(if (config-true? 'canonical_origin)
|
||||||
(url-host (string->url (config-get 'canonical_origin)))
|
(url-host (string->url (config-get 'canonical_origin)))
|
||||||
"breezewiki.com")))
|
"breezewiki.com")))
|
||||||
(p "If you'd like to be automatically sent to BreezeWiki every time in the future, "
|
(p "If you'd like to be automatically sent to BreezeWiki every time in the future, "
|
||||||
|
(a (@ (href "https://getindie.wiki")) "get our affiliated browser extension (NEW!)")
|
||||||
|
" or "
|
||||||
(a (@ (href "https://docs.breezewiki.com/Automatic_Redirection.html")) "check out the tutorial in the manual."))
|
(a (@ (href "https://docs.breezewiki.com/Automatic_Redirection.html")) "check out the tutorial in the manual."))
|
||||||
|
(p "BreezeWiki is available on several different websites called " (a (@ (href "https://en.wikipedia.org/wiki/Mirror_site")) "mirrors") ". Each is independently run. If one mirror is offline, the others still work. "
|
||||||
|
(a (@ (href "https://docs.breezewiki.com/Links.html#%28part._.Mirrors%29")) "See the list."))
|
||||||
(h2 "Find a page")
|
(h2 "Find a page")
|
||||||
(form (@ (action "/search"))
|
(form (@ (action "/search"))
|
||||||
(label (@ (class "paired__label"))
|
(label (@ (class "paired__label"))
|
||||||
|
@ -50,7 +53,7 @@
|
||||||
examples))
|
examples))
|
||||||
(h2 "Testimonials")
|
(h2 "Testimonials")
|
||||||
(p (@ (class "testimonial")) ">so glad someone introduced me to a F*ndom alternative (BreezeWiki) because that x-factorized spillway of an ad-infested radioactive dumpsite can go die in a fire —RB")
|
(p (@ (class "testimonial")) ">so glad someone introduced me to a F*ndom alternative (BreezeWiki) because that x-factorized spillway of an ad-infested radioactive dumpsite can go die in a fire —RB")
|
||||||
(p (@ (class "testimonial")) ">you are so right that fandom still sucks even with adblock somehow. even zapping all the stupid padding it still sucks —Minimus")
|
(p (@ (class "testimonial")) ">apparently there are thousands of people essentially running our company " (em "for free") " right now, creating tons of content, and we just put ads on top of it and they're not even employees. thousands of people we can't lay off. thousands! —" (a (@ (href "https://hard-drive.net/fandom-ceo-frustrated-its-impossible-to-lay-off-unpaid-users-who-update-wikias-for-fun/?utm_source=breezewiki") (target "_blank")) "Perkins Miller, Fandom CEO"))
|
||||||
(p (@ (class "testimonial")) ">attempting to go to a wiki's forum page with breezewiki doesn't work, which is based honestly —Tom Skeleton")
|
(p (@ (class "testimonial")) ">attempting to go to a wiki's forum page with breezewiki doesn't work, which is based honestly —Tom Skeleton")
|
||||||
(p (@ (class "testimonial")) ">Fandom pages crashing and closing, taking forever to load and locking up as they load the ads on the site... they are causing the site to crash because they are trying to load video ads both at the top and bottom of the site as well as two or three banner ads, then a massive top of site ad and eventually my anti-virus shuts the whole site down because it's literally pulling more resources than WoW in ultra settings... —Anonymous")
|
(p (@ (class "testimonial")) ">Fandom pages crashing and closing, taking forever to load and locking up as they load the ads on the site... they are causing the site to crash because they are trying to load video ads both at the top and bottom of the site as well as two or three banner ads, then a massive top of site ad and eventually my anti-virus shuts the whole site down because it's literally pulling more resources than WoW in ultra settings... —Anonymous")
|
||||||
(p (@ (class "testimonial")) ">reblogs EXTREMELY appreciated I want that twink* (*fandom wiki) obliterated —footlong")
|
(p (@ (class "testimonial")) ">reblogs EXTREMELY appreciated I want that twink* (*fandom wiki) obliterated —footlong")
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
web-server/http
|
web-server/http
|
||||||
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
page-proxy)
|
page-proxy)
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
web-server/http
|
web-server/http
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
redirect-wiki-home)
|
redirect-wiki-home)
|
||||||
|
|
|
@ -13,9 +13,10 @@
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"syntax.rkt"
|
"../lib/syntax.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"whole-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
page-search)
|
page-search)
|
||||||
|
@ -60,6 +61,8 @@
|
||||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||||
(define query (dict-ref (url-query (request-uri req)) 'q #f))
|
(define query (dict-ref (url-query (request-uri req)) 'q #f))
|
||||||
(define origin (format "https://~a.fandom.com" wikiname))
|
(define origin (format "https://~a.fandom.com" wikiname))
|
||||||
|
(when (config-true? 'feature_offline::only)
|
||||||
|
(raise-user-error "Full search is currently not available on breezewiki.com - for now, please use the pop-up search suggestions or wait for me to fix it! Thanks <3"))
|
||||||
(define dest-url
|
(define dest-url
|
||||||
(format "~a/api.php?~a"
|
(format "~a/api.php?~a"
|
||||||
origin
|
origin
|
||||||
|
@ -87,5 +90,6 @@
|
||||||
(λ (out)
|
(λ (out)
|
||||||
(write-html body out))))))
|
(write-html body out))))))
|
||||||
(module+ test
|
(module+ test
|
||||||
(check-not-false ((query-selector (attribute-selector 'href "/test/wiki/Gacha_Capsule")
|
(parameterize ([(config-parameter 'feature_offline::only) "false"])
|
||||||
(generate-results-page test-req "" "test" "Gacha" search-json-data)))))
|
(check-not-false ((query-selector (attribute-selector 'href "/test/wiki/Gacha_Capsule")
|
||||||
|
(generate-results-page test-req "" "test" "Gacha" search-json-data))))))
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
web-server/http
|
web-server/http
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"url-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"xexpr-utils.rkt")
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
page-set-user-settings)
|
page-set-user-settings)
|
||||||
|
|
90
src/page-static-archive.rkt
Normal file
90
src/page-static-archive.rkt
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/file
|
||||||
|
racket/path
|
||||||
|
racket/port
|
||||||
|
racket/runtime-path
|
||||||
|
racket/string
|
||||||
|
net/url
|
||||||
|
web-server/http
|
||||||
|
web-server/servlet-dispatch
|
||||||
|
web-server/dispatchers/filesystem-map
|
||||||
|
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
||||||
|
"../archiver/archiver.rkt"
|
||||||
|
"../lib/mime-types.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt"
|
||||||
|
"../src/config.rkt")
|
||||||
|
|
||||||
|
(provide
|
||||||
|
page-static-archive)
|
||||||
|
|
||||||
|
(define-runtime-path path-archive "../storage/archive")
|
||||||
|
|
||||||
|
(define ((replacer wikiname) whole url)
|
||||||
|
(format
|
||||||
|
"url(~a)"
|
||||||
|
(if (or (equal? url "")
|
||||||
|
(equal? url "'")
|
||||||
|
(string-contains? url "/resources-ucp/")
|
||||||
|
(string-contains? url "/fonts/")
|
||||||
|
(string-contains? url "/drm_fonts/")
|
||||||
|
(string-contains? url "//db.onlinewebfonts.com/")
|
||||||
|
(string-contains? url "//bits.wikimedia.org/")
|
||||||
|
(string-contains? url "dropbox")
|
||||||
|
(string-contains? url "only=styles")
|
||||||
|
(string-contains? url "https://https://")
|
||||||
|
(regexp-match? #rx"^%20|^'" url)
|
||||||
|
(regexp-match? #rx"^\"?data:" url))
|
||||||
|
url
|
||||||
|
(let* ([norm-url
|
||||||
|
(cond
|
||||||
|
[(string-prefix? url "https://") url]
|
||||||
|
[(string-prefix? url "http://") (regexp-replace #rx"http:" url "https:")]
|
||||||
|
[(string-prefix? url "//") (string-append "https:" url)]
|
||||||
|
[(string-prefix? url "/") (format "https://~a.fandom.com~a" wikiname url)]
|
||||||
|
[else (raise-user-error "While calling replace-style-for-images, this URL had an unknown format and couldn't be saved:" url)])])
|
||||||
|
(define p (image-url->values norm-url))
|
||||||
|
;; (printf "hashed: ~a~n -> ~a~n #-> ~a~n" url (car p) (cdr p))
|
||||||
|
(format "/archive/~a/images/~a" wikiname (cdr p))))))
|
||||||
|
|
||||||
|
(define (replace-style-for-images wikiname path)
|
||||||
|
(define content (file->string path))
|
||||||
|
(regexp-replace* #rx"url\\(([^)]*)\\)" content (replacer wikiname)))
|
||||||
|
|
||||||
|
(define (handle-style wikiname dest)
|
||||||
|
(when (config-true? 'debug)
|
||||||
|
(printf "using offline mode for style ~a ~a~n" wikiname dest))
|
||||||
|
(define fs-path (build-path path-archive wikiname "styles" dest))
|
||||||
|
(println fs-path)
|
||||||
|
(unless (file-exists? fs-path)
|
||||||
|
(next-dispatcher))
|
||||||
|
(response-handler
|
||||||
|
(define new-content (replace-style-for-images wikiname fs-path))
|
||||||
|
(response/output
|
||||||
|
#:code 200
|
||||||
|
#:headers (list (header #"Content-Type" #"text/css")
|
||||||
|
(header #"Referrer-Policy" #"same-origin"))
|
||||||
|
(λ (out) (displayln new-content out)))))
|
||||||
|
|
||||||
|
(define (handle-image wikiname dest) ;; dest is the hash with no extension
|
||||||
|
(unless ((string-length dest) . >= . 40) (next-dispatcher))
|
||||||
|
(response-handler
|
||||||
|
(define dir (build-path path-archive wikiname "images" (substring dest 0 1) (substring dest 0 2)))
|
||||||
|
(unless (directory-exists? dir) (next-dispatcher))
|
||||||
|
(define candidates (directory-list dir))
|
||||||
|
(define target (path->string (findf (λ (f) (string-prefix? (path->string f) dest)) candidates)))
|
||||||
|
(unless target (next-dispatcher))
|
||||||
|
(define ext (substring target 41))
|
||||||
|
(response/output
|
||||||
|
#:code 200
|
||||||
|
#:headers (list (header #"Content-Type" (ext->mime-type (string->bytes/latin-1 ext))))
|
||||||
|
(λ (out)
|
||||||
|
(call-with-input-file (build-path dir target)
|
||||||
|
(λ (in)
|
||||||
|
(copy-port in out)))))))
|
||||||
|
|
||||||
|
(define (page-static-archive req)
|
||||||
|
(define path (url-path (request-uri req)))
|
||||||
|
(define-values (_ wikiname kind dest) (apply values (map path/param-path path)))
|
||||||
|
(cond [(equal? kind "styles") (handle-style wikiname dest)]
|
||||||
|
[(equal? kind "images") (handle-image wikiname dest)]
|
||||||
|
[else (response-handler (raise-user-error "page-static-archive: how did we get here?" kind))]))
|
|
@ -7,6 +7,7 @@
|
||||||
web-server/dispatchers/filesystem-map
|
web-server/dispatchers/filesystem-map
|
||||||
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
(only-in web-server/dispatchers/dispatch next-dispatcher)
|
||||||
(prefix-in files: web-server/dispatchers/dispatch-files)
|
(prefix-in files: web-server/dispatchers/dispatch-files)
|
||||||
|
"../lib/mime-types.rkt"
|
||||||
"config.rkt")
|
"config.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
(require rackunit))
|
(require rackunit))
|
||||||
|
|
||||||
(define-runtime-path path-static "../static")
|
(define-runtime-path path-static "../static")
|
||||||
|
(define-runtime-path path-archive "../storage/archive")
|
||||||
|
|
||||||
(define hash-ext-mime-type
|
(define hash-ext-mime-type
|
||||||
(hash #".css" #"text/css"
|
(hash #".css" #"text/css"
|
||||||
|
@ -25,45 +27,49 @@
|
||||||
#".woff2" #"font/woff2"
|
#".woff2" #"font/woff2"
|
||||||
#".txt" #"text/plain"))
|
#".txt" #"text/plain"))
|
||||||
|
|
||||||
(define (ext->mime-type ext)
|
|
||||||
(hash-ref hash-ext-mime-type ext))
|
|
||||||
(module+ test
|
|
||||||
(check-equal? (ext->mime-type #".png") #"image/png"))
|
|
||||||
|
|
||||||
(define (make-path segments)
|
(define (make-path segments)
|
||||||
(map (λ (seg) (path/param seg '())) segments))
|
(map (λ (seg) (path/param seg '())) segments))
|
||||||
(module+ test
|
(module+ test
|
||||||
(check-equal? (make-path '("static" "main.css"))
|
(check-equal? (make-path '("static" "main.css"))
|
||||||
(list (path/param "static" '()) (path/param "main.css" '()))))
|
(list (path/param "static" '()) (path/param "main.css" '()))))
|
||||||
|
|
||||||
|
;; given a request path, return a rewritten request path and the source directory on the filesystem to serve based on
|
||||||
(define (path-rewriter p)
|
(define (path-rewriter p)
|
||||||
(cond
|
(cond
|
||||||
; url is ^/static/... ?
|
; url is ^/static/... ?
|
||||||
[(equal? (path/param-path (car p)) "static")
|
[(equal? (path/param-path (car p)) "static")
|
||||||
; rewrite to ^/... which will be treated as relative to static/ on the filesystem
|
; rewrite to ^/... which will be treated as relative to static/ on the filesystem
|
||||||
(cdr p)]
|
(values (cdr p) path-static)]
|
||||||
|
; url is ^/archive/... ?
|
||||||
|
[(equal? (path/param-path (car p)) "archive")
|
||||||
|
; rewrite req to ^/<wikiname> and dir to /storage/archive
|
||||||
|
(values (cdr p) path-archive)]
|
||||||
; url is literally ^/robots.txt
|
; url is literally ^/robots.txt
|
||||||
[(equal? p (make-path '("robots.txt")))
|
[(equal? p (make-path '("robots.txt")))
|
||||||
; rewrite to ^/... -- it already is!
|
; rewrite to ^/... -- it already is!
|
||||||
p]
|
(values p path-static)]
|
||||||
; not going to use the static file dispatcher
|
; not going to use the static file dispatcher
|
||||||
[#t (next-dispatcher)]))
|
[#t (next-dispatcher)]))
|
||||||
(module+ test
|
(module+ test
|
||||||
(check-equal? (path-rewriter (make-path '("static" "main.css")))
|
(check-equal? (call-with-values (λ () (path-rewriter (make-path '("static" "main.css")))) cons)
|
||||||
(make-path '("main.css")))
|
(cons (make-path '("main.css")) path-static))
|
||||||
(check-equal? (path-rewriter (make-path '("static" "robots.txt")))
|
(check-equal? (call-with-values (λ () (path-rewriter (make-path '("static" "robots.txt")))) cons)
|
||||||
(make-path '("robots.txt")))
|
(cons (make-path '("robots.txt")) path-static))
|
||||||
(check-equal? (path-rewriter (make-path '("robots.txt")))
|
(check-equal? (call-with-values (λ () (path-rewriter (make-path '("robots.txt")))) cons)
|
||||||
(make-path '("robots.txt"))))
|
(cons (make-path '("robots.txt")) path-static))
|
||||||
|
(check-equal? (call-with-values (λ () (path-rewriter (make-path '("archive" "minecraft" "styles" "main.css")))) cons)
|
||||||
|
(cons (make-path '("minecraft" "styles" "main.css")) path-archive)))
|
||||||
|
|
||||||
(define (static-dispatcher conn old-req)
|
(define (static-dispatcher conn old-req)
|
||||||
(define old-uri (request-uri old-req))
|
(define old-uri (request-uri old-req))
|
||||||
(define old-path (url-path old-uri))
|
(define old-path (url-path old-uri))
|
||||||
(define new-path (path-rewriter old-path))
|
(define-values (new-path source-dir) (path-rewriter old-path))
|
||||||
(define new-uri (struct-copy url old-uri [path new-path]))
|
(define new-uri (struct-copy url old-uri [path new-path]))
|
||||||
(define new-req (struct-copy request old-req [uri new-uri]))
|
(define new-req (struct-copy request old-req [uri new-uri]))
|
||||||
((files:make
|
((files:make
|
||||||
#:url->path (lambda (u) ((make-url->path path-static) u))
|
#:url->path (lambda (u) ((make-url->path source-dir) u))
|
||||||
|
#:path->headers (lambda (p) (list (header #"Access-Control-Allow-Origin" #"*")
|
||||||
|
(header #"Referrer-Policy" #"same-origin")))
|
||||||
#:path->mime-type (lambda (u) (ext->mime-type (path-get-extension u)))
|
#:path->mime-type (lambda (u) (ext->mime-type (path-get-extension u)))
|
||||||
#:cache-no-cache (config-true? 'debug)
|
#:cache-no-cache (config-true? 'debug)
|
||||||
#:cache-immutable (not (config-true? 'debug))
|
#:cache-immutable (not (config-true? 'debug))
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
(prefix-in lift: web-server/dispatchers/dispatch-lift)
|
(prefix-in lift: web-server/dispatchers/dispatch-lift)
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"syntax.rkt"
|
"../lib/syntax.rkt"
|
||||||
"xexpr-utils.rkt")
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
subdomain-dispatcher)
|
subdomain-dispatcher)
|
||||||
|
|
142
src/page-wiki-offline.rkt
Normal file
142
src/page-wiki-offline.rkt
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#lang racket/base
|
||||||
|
(require racket/dict
|
||||||
|
racket/file
|
||||||
|
racket/function
|
||||||
|
racket/list
|
||||||
|
racket/match
|
||||||
|
racket/runtime-path
|
||||||
|
racket/string
|
||||||
|
; libs
|
||||||
|
(prefix-in easy: net/http-easy)
|
||||||
|
file/sha1
|
||||||
|
file/gunzip
|
||||||
|
json
|
||||||
|
; html libs
|
||||||
|
"../lib/html-parsing/main.rkt"
|
||||||
|
html-writing
|
||||||
|
; web server libs
|
||||||
|
net/url
|
||||||
|
web-server/http
|
||||||
|
web-server/dispatchers/dispatch
|
||||||
|
; my libs
|
||||||
|
"application-globals.rkt"
|
||||||
|
"config.rkt"
|
||||||
|
"data.rkt"
|
||||||
|
"page-wiki.rkt"
|
||||||
|
"../lib/archive-file-mappings.rkt"
|
||||||
|
"../lib/pure-utils.rkt"
|
||||||
|
"../lib/syntax.rkt"
|
||||||
|
"../lib/tree-updater.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt"
|
||||||
|
"../lib/url-utils.rkt")
|
||||||
|
|
||||||
|
(provide
|
||||||
|
; used by the web server
|
||||||
|
page-wiki-offline)
|
||||||
|
|
||||||
|
(module+ test
|
||||||
|
(require rackunit))
|
||||||
|
|
||||||
|
(define-runtime-path path-archive "../storage/archive")
|
||||||
|
|
||||||
|
(define (page-wiki-offline req)
|
||||||
|
(response-handler
|
||||||
|
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||||
|
(define segments (map path/param-path (cdr (url-path (request-uri req)))))
|
||||||
|
(define basename (url-segments->basename segments))
|
||||||
|
(define maybe-hashed-basename (if ((string-length basename) . > . 240)
|
||||||
|
(sha1 (string->bytes/latin-1 basename))
|
||||||
|
basename))
|
||||||
|
(define archive-format
|
||||||
|
(case (config-get 'feature_offline::format)
|
||||||
|
[(".json" "json") (cons "~a.json" (λ () (read-json)))]
|
||||||
|
[(".json.gz" "json.gz") (cons "~a.json.gz" (λ ()
|
||||||
|
(define-values (in out) (make-pipe))
|
||||||
|
(gunzip-through-ports (current-input-port) out)
|
||||||
|
(read-json in)))]
|
||||||
|
[else (error 'archive-format "unknown archive format configured")]))
|
||||||
|
(define fs-path (build-path path-archive wikiname (format (car archive-format) maybe-hashed-basename)))
|
||||||
|
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname (basename->name-for-query basename)))
|
||||||
|
(cond
|
||||||
|
[(not (file-exists? fs-path))
|
||||||
|
(unless (config-true? 'feature_offline::only)
|
||||||
|
(next-dispatcher))
|
||||||
|
(define mirror-path (url->string (request-uri req)))
|
||||||
|
(define body
|
||||||
|
(generate-wiki-page
|
||||||
|
`(div (@ (class "unsaved-page"))
|
||||||
|
(style ".unsaved-page a { text-decoration: underline !important }")
|
||||||
|
(p "breezewiki.com doesn't have this page saved.")
|
||||||
|
(p "You can see this page by visiting a BreezeWiki mirror:")
|
||||||
|
(ul
|
||||||
|
(li (a (@ (href ,(format "https://antifandom.com~a" mirror-path))) "View on antifandom.com"))
|
||||||
|
(li (a (@ (href ,(format "https://bw.artemislena.eu~a" mirror-path))) "View on artemislena.eu"))
|
||||||
|
(li (a (@ (href ,source-url)) "or, you can see the original page on Fandom (ugh)")))
|
||||||
|
(p "If you'd like " ,wikiname ".fandom.com to be added to breezewiki.com, " (a (@ (href "https://lists.sr.ht/~cadence/breezewiki-requests")) "let me know about it!")))
|
||||||
|
#:req req
|
||||||
|
#:source-url source-url
|
||||||
|
#:wikiname wikiname
|
||||||
|
#:title (url-segments->guess-title segments)
|
||||||
|
#:online-styles #f
|
||||||
|
#:siteinfo (siteinfo-fetch wikiname)
|
||||||
|
))
|
||||||
|
(when (config-true? 'debug)
|
||||||
|
; used for its side effects
|
||||||
|
; convert to string with error checking, error will be raised if xexp is invalid
|
||||||
|
(xexp->html body))
|
||||||
|
(response/output
|
||||||
|
#:code 200
|
||||||
|
#:headers always-headers
|
||||||
|
(λ (out)
|
||||||
|
(write-html body out)))]
|
||||||
|
[#t
|
||||||
|
(when (config-true? 'debug)
|
||||||
|
(printf "using offline mode for ~v~n" fs-path))
|
||||||
|
(response-handler
|
||||||
|
(define data (with-input-from-file fs-path (cdr archive-format)))
|
||||||
|
(define article-title (jp "/parse/title" data))
|
||||||
|
(define original-page (html->xexp (preprocess-html-wiki (jp "/parse/text" data))))
|
||||||
|
(define page ((query-selector (λ (t a c) (has-class? "mw-parser-output" a)) original-page)))
|
||||||
|
(define initial-head-data ((head-data-getter wikiname) data))
|
||||||
|
(define user-cookies (user-cookies-getter req))
|
||||||
|
(define theme (user-cookies^-theme user-cookies))
|
||||||
|
(define head-data
|
||||||
|
(case theme
|
||||||
|
[(light dark)
|
||||||
|
(struct-copy head-data^ initial-head-data
|
||||||
|
[body-class (regexp-replace #rx"(theme-fandomdesktop-)(light|dark)"
|
||||||
|
(head-data^-body-class initial-head-data)
|
||||||
|
(format "\\1~a" theme))])]
|
||||||
|
[else initial-head-data]))
|
||||||
|
(define body
|
||||||
|
(generate-wiki-page
|
||||||
|
(update-tree-wiki page wikiname)
|
||||||
|
#:req req
|
||||||
|
#:source-url source-url
|
||||||
|
#:wikiname wikiname
|
||||||
|
#:title article-title
|
||||||
|
#:online-styles #f
|
||||||
|
#:head-data head-data
|
||||||
|
#:siteinfo (siteinfo-fetch wikiname)
|
||||||
|
))
|
||||||
|
(define redirect-msg ((query-selector (attribute-selector 'class "redirectMsg") body)))
|
||||||
|
(define redirect-query-parameter (dict-ref (url-query (request-uri req)) 'redirect "yes"))
|
||||||
|
(define headers
|
||||||
|
(build-headers
|
||||||
|
always-headers
|
||||||
|
; redirect-query-parameter: only the string "no" is significant:
|
||||||
|
; https://github.com/Wikia/app/blob/fe60579a53f16816d65dad1644363160a63206a6/includes/Wiki.php#L367
|
||||||
|
(when (and redirect-msg
|
||||||
|
(not (equal? redirect-query-parameter "no")))
|
||||||
|
(let* ([dest (get-attribute 'href (bits->attributes ((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))))]
|
||||||
|
[value (bytes-append #"0;url=" (string->bytes/utf-8 dest))])
|
||||||
|
(header #"Refresh" value)))))
|
||||||
|
(when (config-true? 'debug)
|
||||||
|
; used for its side effects
|
||||||
|
; convert to string with error checking, error will be raised if xexp is invalid
|
||||||
|
(xexp->html body))
|
||||||
|
(response/output
|
||||||
|
#:code 200
|
||||||
|
#:headers headers
|
||||||
|
(λ (out)
|
||||||
|
(write-html body out))))])))
|
|
@ -7,7 +7,7 @@
|
||||||
; libs
|
; libs
|
||||||
(prefix-in easy: net/http-easy)
|
(prefix-in easy: net/http-easy)
|
||||||
; html libs
|
; html libs
|
||||||
html-parsing
|
"../lib/html-parsing/main.rkt"
|
||||||
html-writing
|
html-writing
|
||||||
; web server libs
|
; web server libs
|
||||||
net/url
|
net/url
|
||||||
|
@ -17,11 +17,12 @@
|
||||||
"application-globals.rkt"
|
"application-globals.rkt"
|
||||||
"config.rkt"
|
"config.rkt"
|
||||||
"data.rkt"
|
"data.rkt"
|
||||||
"pure-utils.rkt"
|
"../lib/pure-utils.rkt"
|
||||||
"syntax.rkt"
|
"../lib/syntax.rkt"
|
||||||
"tree-updater.rkt"
|
"../lib/tree-updater.rkt"
|
||||||
"xexpr-utils.rkt"
|
"../lib/url-utils.rkt"
|
||||||
"url-utils.rkt")
|
"whole-utils.rkt"
|
||||||
|
"../lib/xexpr-utils.rkt")
|
||||||
|
|
||||||
(provide
|
(provide
|
||||||
; used by the web server
|
; used by the web server
|
||||||
|
@ -33,24 +34,6 @@
|
||||||
(module+ test
|
(module+ test
|
||||||
(require rackunit))
|
(require rackunit))
|
||||||
|
|
||||||
(define (preprocess-html-wiki html)
|
|
||||||
(define ((rr* find replace) contents)
|
|
||||||
(regexp-replace* find contents replace))
|
|
||||||
((compose1
|
|
||||||
; fix navbox list nesting
|
|
||||||
; navbox on right of page has incorrect html "<td ...><li>" and the xexpr parser puts the <li> much further up the tree
|
|
||||||
; add a <ul> to make the parser happy
|
|
||||||
; usage: /fallout/wiki/Fallout:_New_Vegas_achievements_and_trophies
|
|
||||||
(rr* #rx"(<td[^>]*>\n?)(<li>)" "\\1<ul>\\2")
|
|
||||||
; change <figcaption><p> to <figcaption><span> to make the parser happy
|
|
||||||
(rr* #rx"(<figcaption[^>]*>)[ \t]*<p class=\"caption\">([^<]*)</p>" "\\1<span class=\"caption\">\\2</span>"))
|
|
||||||
html))
|
|
||||||
(module+ test
|
|
||||||
(check-equal? (preprocess-html-wiki "<td class=\"va-navbox-column\" style=\"width: 33%\">\n<li>Hey</li>")
|
|
||||||
"<td class=\"va-navbox-column\" style=\"width: 33%\">\n<ul><li>Hey</li>")
|
|
||||||
(check-equal? (preprocess-html-wiki "<figure class=\"thumb tright\" style=\"width: 150px\"><a class=\"image\"><img></a><noscript><a><img></a></noscript><figcaption class=\"thumbcaption\"> <p class=\"caption\">Caption text.</p></figcaption></figure>")
|
|
||||||
"<figure class=\"thumb tright\" style=\"width: 150px\"><a class=\"image\"><img></a><noscript><a><img></a></noscript><figcaption class=\"thumbcaption\"><span class=\"caption\">Caption text.</span></figcaption></figure>"))
|
|
||||||
|
|
||||||
(define (page-wiki req)
|
(define (page-wiki req)
|
||||||
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
(define wikiname (path/param-path (first (url-path (request-uri req)))))
|
||||||
(define user-cookies (user-cookies-getter req))
|
(define user-cookies (user-cookies-getter req))
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
;;; Source: https://github.com/tonyg/racket-reloadable/blob/master/reloadable/main.rkt
|
;;; Source: https://github.com/tonyg/racket-reloadable/blob/master/reloadable/main.rkt
|
||||||
;;; Source commit: cae2a14 from 24 May 2015
|
;;; Source commit: cae2a14 from 24 May 2015
|
||||||
;;; Source license: LGPL 3 or later
|
;;; Source license: LGPL 3 or later
|
||||||
|
;;; Further modifications by Cadence as seen in this repo's git history.
|
||||||
|
|
||||||
(provide (struct-out reloadable-entry-point)
|
(provide (struct-out reloadable-entry-point)
|
||||||
reload-poll-interval
|
reload-poll-interval
|
||||||
|
@ -19,8 +20,8 @@
|
||||||
(require racket/match)
|
(require racket/match)
|
||||||
(require racket/rerequire)
|
(require racket/rerequire)
|
||||||
|
|
||||||
(define reload-poll-interval 0.5) ;; seconds
|
(define reload-poll-interval 0.5) ; seconds
|
||||||
(define reload-failure-retry-delay (make-parameter 5)) ;; seconds
|
(define reload-failure-retry-delay (make-parameter 5)) ; seconds
|
||||||
|
|
||||||
(struct reloadable-entry-point (name
|
(struct reloadable-entry-point (name
|
||||||
module-path
|
module-path
|
||||||
|
|
11
src/whole-utils.rkt
Normal file
11
src/whole-utils.rkt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#lang typed/racket/base
|
||||||
|
(require "config.rkt")
|
||||||
|
|
||||||
|
(provide
|
||||||
|
; prints "out: <url>"
|
||||||
|
log-outgoing)
|
||||||
|
|
||||||
|
(: log-outgoing (String -> Void))
|
||||||
|
(define (log-outgoing url-string)
|
||||||
|
(when (config-true? 'log_outgoing)
|
||||||
|
(printf "out: ~a~n" url-string)))
|
136
static/breezewiki-color.svg
Normal file
136
static/breezewiki-color.svg
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg:svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="36.41787"
|
||||||
|
width="224.9014"
|
||||||
|
version="1.1"
|
||||||
|
id="svg912"
|
||||||
|
sodipodi:docname="breezewiki-color.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<svg:metadata
|
||||||
|
id="metadata918">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</svg:metadata>
|
||||||
|
<svg:defs
|
||||||
|
id="defs916" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1882"
|
||||||
|
inkscape:window-height="1059"
|
||||||
|
id="namedview914"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="123.17581"
|
||||||
|
inkscape:cy="7.7496502"
|
||||||
|
inkscape:window-x="38"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg912"
|
||||||
|
showborder="true"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-smooth-nodes="true" />
|
||||||
|
<div
|
||||||
|
id="saka-gui-root">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<style />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg:g
|
||||||
|
aria-label="wiki"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;line-height:2.67644334px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="text926"
|
||||||
|
transform="translate(97.9947,1.1875002)">
|
||||||
|
<svg:path
|
||||||
|
d="m 81.588122,13.020001 q 0,0.08533 0,0.298666 -0.04267,0.256001 -0.04267,0.384001 0,0.128 -0.341333,0.213333 -0.810667,0.170667 -0.981334,0.256 -0.341333,0.170667 -0.725333,0.725333 -0.512,0.725334 -1.237333,2.517334 l -4.480001,11.008 q -0.128,0.298667 -0.725333,0.298667 -0.554667,0 -0.682667,-0.298667 l -3.584,-8.874667 -3.626667,8.874667 q -0.128,0.298667 -0.725333,0.298667 -0.554667,0 -0.682667,-0.298667 L 58.846788,16.561334 q -0.554667,-1.322666 -1.066667,-1.92 -0.469333,-0.64 -1.706667,-0.896 -0.341333,-0.08533 -0.341333,-0.256 v -0.725333 q 0,-0.298667 0.298667,-0.298667 0.896,0 2.474666,0.08533 1.749334,0.08533 2.432,0.08533 0,0 4.821334,0 1.152,0 3.456,-0.08533 2.304,-0.08533 3.456,-0.08533 0.341333,0 0.341333,0.469333 0,0.08533 0,0.298667 -0.04267,0.170667 -0.04267,0.256 0,0.256 -0.981334,0.384 -0.981333,0.128 -0.981333,0.682667 0,0.256 0.128,0.597333 l 3.157333,8.064 2.986667,-7.552 q 0.213333,-0.554666 0.213333,-0.938666 0,-0.554667 -0.810666,-0.682667 -0.810667,-0.128 -0.810667,-0.384 v -0.725334 q 0,-0.298666 0.469333,-0.298666 0.384,0 1.194667,0.128 0.810667,0.08533 1.194667,0.08533 0.426667,0 1.194667,-0.08533 0.768,-0.128 1.024,-0.128 0.64,0 0.64,0.384 z m -13.525334,4.693333 -0.853333,-2.133333 q -0.554667,-1.408 -2.517334,-1.408 h -1.536 q -0.810667,0 -0.810667,0.64 0,0.256 0.128,0.597333 l 3.242667,8.149334 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path842"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 91.700066,5.6813339 q 0,1.1093334 -0.768,1.8773334 -0.768,0.768 -1.877333,0.768 -1.109333,0 -1.877333,-0.768 -0.768,-0.768 -0.768,-1.8773334 0,-1.1093333 0.768,-1.8773334 0.768,-0.768 1.877333,-0.768 1.109333,0 1.877333,0.768 0.768,0.7680001 0.768,1.8773334 z m 1.706667,22.3573341 q 0,0.298667 -0.298667,0.298667 -0.298666,0 -0.298666,0 -1.962667,-0.170667 -3.669334,-0.170667 -1.621333,0 -3.584,0.170667 0,0 -0.170666,0 -0.469334,0 -0.469334,-0.426667 0,-0.08533 0.04267,-0.298667 0,-0.213333 0,-0.298666 0,-0.256 0.853333,-0.384 0.853334,-0.128 1.066667,-0.554667 0.256,-0.469333 0.256,-1.834667 v -6.528 q 0,-1.322667 -0.256,-1.749333 -0.170667,-0.298667 -1.109333,-0.682667 -0.896,-0.384 -0.896,-0.768 0,-0.256 0.128,-0.469333 0.04267,-0.08533 2.858666,-1.194667 2.816,-1.152 3.029334,-1.152 0.341333,0 0.341333,0.341333 v 11.904 q 0,1.749334 0.170667,2.090667 0.256,0.469334 1.109333,0.597334 0.896,0.128 0.896,0.384 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path844"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 114.52672,27.526668 q 0,0.810667 -0.29867,0.810667 -0.59733,0 -1.87733,-0.08533 -1.23733,-0.08533 -1.87733,-0.08533 -0.59734,0 -1.83467,0.08533 -1.19467,0.08533 -1.792,0.08533 -0.384,0 -0.384,-0.469334 0,-0.08533 0.0427,-0.298666 0,-0.170667 0,-0.256 0,-0.256 0.55466,-0.341334 0.59734,-0.128 0.59734,-0.512 0,-0.298666 -0.512,-1.152 l -3.24267,-5.418667 -1.87733,1.408 v 3.2 q 0,1.408001 0.21333,1.877334 0.21333,0.426667 1.152,0.554667 0.81067,0.128 0.81067,0.384 v 0.725333 q 0,0.298667 -0.59734,0.298667 -0.59733,0 -1.83466,-0.08533 -1.19467,-0.08533 -1.792004,-0.08533 -0.597333,0 -1.834667,0.08533 -1.194666,0.08533 -1.792,0.08533 -0.597333,0 -0.597333,-0.426667 v -0.597333 q 0,-0.256 0.853333,-0.384 0.853334,-0.128 1.066667,-0.554667 0.256,-0.469333 0.256,-1.834667 V 6.4920006 q 0,-1.3226667 -0.256,-1.7493334 -0.170667,-0.2986666 -1.109333,-0.6826666 -0.896001,-0.3840001 -0.896001,-0.7680001 0,-0.256 0.128,-0.4693333 0.04267,-0.085333 2.858667,-1.1946667 2.816001,-1.15200005 3.029331,-1.15200005 0.34134,0 0.34134,0.34133334 V 19.718668 q 5.71733,-4.309334 5.71733,-5.290667 0,-0.682667 -1.792,-0.682667 -0.46933,0 -0.46933,-0.512 0,-0.768 0.42666,-0.768 0.55467,0 1.70667,0.08533 1.19467,0.08533 1.74933,0.08533 0.55467,0 1.70667,-0.08533 1.152,-0.08533 1.70667,-0.08533 0.42666,0 0.42666,0.469333 0,0.08533 0,0.298667 -0.0427,0.170667 -0.0427,0.298667 0,0.128 -0.384,0.213333 -0.64,0.08533 -1.83467,0.426667 -0.512,0.213333 -2.048,1.578667 -1.024,0.896 -2.09067,1.834666 l 4.224,6.656 q 1.57867,2.474667 2.60267,2.688001 0.29867,0.04267 0.59733,0.128 0.29867,0.08533 0.29867,0.469333 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path846"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 123.69999,5.6813339 q 0,1.1093334 -0.768,1.8773334 -0.768,0.768 -1.87734,0.768 -1.10933,0 -1.87733,-0.768 -0.768,-0.768 -0.768,-1.8773334 0,-1.1093333 0.768,-1.8773334 0.768,-0.768 1.87733,-0.768 1.10934,0 1.87734,0.768 0.768,0.7680001 0.768,1.8773334 z m 1.70666,22.3573341 q 0,0.298667 -0.29866,0.298667 -0.29867,0 -0.29867,0 -1.96267,-0.170667 -3.66933,-0.170667 -1.62134,0 -3.584,0.170667 0,0 -0.17067,0 -0.46933,0 -0.46933,-0.426667 0,-0.08533 0.0427,-0.298667 0,-0.213333 0,-0.298666 0,-0.256 0.85334,-0.384 0.85333,-0.128 1.06666,-0.554667 0.256,-0.469333 0.256,-1.834667 v -6.528 q 0,-1.322667 -0.256,-1.749333 -0.17066,-0.298667 -1.10933,-0.682667 -0.896,-0.384 -0.896,-0.768 0,-0.256 0.128,-0.469333 0.0427,-0.08533 2.85867,-1.194667 2.816,-1.152 3.02933,-1.152 0.34133,0 0.34133,0.341333 v 11.904 q 0,1.749334 0.17067,2.090667 0.256,0.469334 1.10933,0.597334 0.896,0.128 0.896,0.384 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path848"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg:g>
|
||||||
|
<svg:g
|
||||||
|
aria-label="breeze"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;line-height:2.57638931px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#252525;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="text930"
|
||||||
|
transform="translate(-69.005304,1.5000002)">
|
||||||
|
<svg:path
|
||||||
|
d="m 162.59026,13.697235 q 0,0.782126 -0.48131,1.363707 -0.46126,0.581581 -1.22333,0.581581 -0.46125,0 -0.92251,-0.421145 -0.44119,-0.421144 -0.80218,-0.421144 -0.16043,0 -0.9225,0.80218 -1.28349,1.323598 -2.68731,3.690031 -1.34365,2.266161 -2.00545,4.111176 -0.48131,1.504089 -1.52414,4.452103 -0.28077,0.701908 -0.80219,0.701908 -0.50136,0 -0.98267,-0.240654 -0.58158,-0.280763 -0.58158,-0.721962 0,-0.180491 0.10028,-0.461254 l 2.52686,-7.259736 q 0.8824,-2.526869 0.8824,-4.111176 0,-1.383761 -0.80218,-1.383761 -1.103,0 -2.08567,1.263434 -0.96261,1.263435 -0.98267,1.263435 -0.14038,0 -0.46125,-0.2206 -0.32087,-0.240654 -0.32087,-0.381036 0,-0.140381 0.0802,-0.280763 0.80218,-1.383762 1.88513,-2.607087 1.50409,-1.664525 2.52687,-1.664525 2.12578,0 2.12578,3.10845 0,1.443925 -0.42115,3.288941 3.9708,-6.4375 6.05647,-6.4375 0.76207,0 1.28348,0.601635 0.54148,0.601636 0.54148,1.383762 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path853"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 148.05073,15.502142 q 0,4.672702 -5.71554,9.485786 -4.25155,3.569704 -7.74104,3.569704 -1.38376,0 -2.24611,-0.661799 -0.96261,-0.721962 -0.96261,-2.065615 0,-0.561526 0.20054,-1.24338 l 5.85592,-20.1748444 q 0.12033,-0.4211449 0.12033,-0.7420171 0,-0.8823988 -1.12305,-1.1832166 -1.12306,-0.3008177 -1.12306,-0.5013629 0,-0.7821262 0.62169,-1.04283489 0.30082,-0.0200545 0.78213,-0.0601636 0.56153,-0.0601635 2.28621,-0.4612539 Q 140.73083,-2.0861626e-7 140.791,-2.0861626e-7 q 0.42114,0 0.42114,0.38103582861626 0,0.18049066 -1.20327,4.33177578 L 136.09824,18.06912 q 5.03368,-6.357282 9.06464,-6.357282 1.34365,0 2.16589,1.263434 0.72196,1.102999 0.72196,2.52687 z m -2.92796,0.200545 q 0,-1.764798 -1.48403,-1.764798 -2.54693,0 -6.05647,4.331776 -3.42932,4.211449 -3.42932,6.83859 0,1.965343 1.36371,1.965343 2.78758,0 6.25701,-4.311721 3.3491,-4.17134 3.3491,-7.05919 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path851"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 175.36498,14.760125 q 0,3.028232 -3.66997,4.572429 -2.18595,0.902454 -6.29712,1.403817 -0.16044,1.123053 -0.16044,2.145833 0,3.54965 3.46943,3.54965 1.12306,0 2.4266,-1.183217 1.30354,-1.183216 1.22332,-1.183216 0.14039,0 0.46126,0.320872 0.32087,0.300818 0.32087,0.441199 0,0.140382 -0.12033,0.300818 -2.54692,3.429322 -5.89602,3.429322 -2.18595,0 -3.5296,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82769,-8.12208 2.84774,-3.50954 6.61799,-3.50954 1.50409,0 2.50681,0.762072 1.103,0.842289 1.103,2.286215 z m -2.54692,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84502,0 -3.54965,2.205997 -1.3236,1.704634 -2.04556,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path855"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 188.96195,14.760125 q 0,3.028232 -3.66998,4.572429 -2.18594,0.902454 -6.29712,1.403817 -0.16043,1.123053 -0.16043,2.145833 0,3.54965 3.46943,3.54965 1.12305,0 2.4266,-1.183217 1.30354,-1.183216 1.22332,-1.183216 0.14038,0 0.46125,0.320872 0.32088,0.300818 0.32088,0.441199 0,0.140382 -0.12033,0.300818 -2.54692,3.429322 -5.89603,3.429322 -2.18594,0 -3.52959,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82768,-8.12208 2.84775,-3.50954 6.618,-3.50954 1.50408,0 2.50681,0.762072 1.103,0.842289 1.103,2.286215 z m -2.54693,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84501,0 -3.54964,2.205997 -1.3236,1.704634 -2.04557,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path857"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 204.80502,12.514019 q 0,0.721962 -0.92251,1.704634 -0.36098,0.40109 -6.39739,6.477609 -1.34366,1.283489 -3.7502,3.850467 l 0.36098,0.180491 q 2.72742,1.363707 4.17134,4.411993 0.80218,1.68458 1.48404,1.68458 0.58158,0 1.103,-1.042835 0.54147,-1.042835 1.22332,-1.042835 0.94256,0 0.94256,0.942562 0,1.102999 -1.40381,1.865071 -1.20327,0.641744 -2.4266,0.641744 -1.58431,0 -3.16861,-1.363707 -0.42115,-0.381036 -2.36644,-2.546924 -1.22332,-1.363707 -1.96534,-1.363707 -0.48131,0 -1.12305,0.742017 -0.62169,0.742017 -1.103,0.742017 -0.68185,0 -0.68185,-0.561526 0,-0.4412 1.90518,-2.226052 0.0802,-0.08022 3.32905,-3.369159 1.8049,-1.764797 5.27433,-5.394665 -0.84229,-0.160436 -1.60436,-0.421145 -0.40109,-0.140381 -1.98539,-1.123052 -1.34366,-0.822236 -1.90518,-0.822236 -0.56153,0 -0.94257,0.461254 -0.38103,0.4412 -0.38103,1.002726 0,0.340927 0.34092,0.862344 0.34093,0.501363 0.34093,0.762072 0,0.481308 -0.58158,0.481308 -0.64174,0 -1.103,-0.982671 -0.38103,-0.802181 -0.38103,-1.544198 0,-1.504089 1.12305,-2.647196 1.12305,-1.163162 2.60709,-1.163162 1.20327,0 3.40926,1.24338 2.206,1.223326 3.38922,1.223326 0.70191,0 1.28349,-1.203272 0.58158,-1.203271 1.24338,-1.203271 0.6618,0 0.6618,0.742018 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path859"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 217.6399,14.760125 q 0,3.028232 -3.66997,4.572429 -2.18595,0.902454 -6.29712,1.403817 -0.16044,1.123053 -0.16044,2.145833 0,3.54965 3.46943,3.54965 1.12306,0 2.4266,-1.183217 1.30354,-1.183216 1.22333,-1.183216 0.14038,0 0.46125,0.320872 0.32087,0.300818 0.32087,0.441199 0,0.140382 -0.12032,0.300818 -2.54693,3.429322 -5.89603,3.429322 -2.18595,0 -3.5296,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82769,-8.12208 2.84774,-3.50954 6.61799,-3.50954 1.50409,0 2.50682,0.762072 1.10299,0.842289 1.10299,2.286215 z m -2.54692,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84502,0 -3.54965,2.205997 -1.3236,1.704634 -2.04556,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path861"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg:g>
|
||||||
|
<svg:path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fa005a;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 16.886254,1.9938812 c -3.910133,-0.022008 -6.9535917,2.245681 -9.0800783,4.662109 -2.0973298,2.383295 -3.833062,5.3337288 -5.6621092,8.9804688 -2.05699735,4.100418 1.0356001,9.111984 5.6230472,9.111328 H 22.208519 c 1.140931,2.016544 2.30672,3.900631 3.666016,5.457031 2.103842,2.40891 5.109808,4.690926 9.011718,4.712891 3.908789,0.02201 6.953047,-2.243435 9.080078,-4.660157 2.097507,-2.383176 3.833076,-5.333755 5.66211,-8.980468 C 51.685439,17.176666 48.59284,12.1651 44.005394,12.165756 H 29.563988 C 28.422971,10.14915 27.258741,8.2650182 25.899925,6.7087242 23.796753,4.2998952 20.789506,2.0158522 16.886254,1.9938812 Z m -0.02344,4.4375 c 2.180149,0.012272 4.038227,1.299637 5.693359,3.1953118 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 a 2.2186407,2.2186407 0 0 0 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 C 27.969301,25.86123 26.721297,23.841706 25.429222,21.46849 A 2.2186407,2.2186407 0 0 0 23.480003,20.310287 H 7.7671137 C 6.2946566,20.310498 5.448665,18.940873 6.1089102,17.62474 7.8599547,14.133521 9.4461247,11.50847 11.138206,9.585677 12.801131,7.6960182 14.689541,6.4191492 16.862816,6.4313812 Z"
|
||||||
|
id="path819"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path837"
|
||||||
|
d="m 43.966331,30.257552 c -29.310887,4.106878 -14.655444,2.053439 0,0 z M 16.862814,6.4313812 c 2.180149,0.012272 4.038227,1.299637 5.693359,3.1953118 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 0.388515,0.713988 1.136369,1.158355 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 -1.245075,-1.425619 -2.493079,-3.445143 -3.785154,-5.818359 -0.388515,-0.713988 -1.13637,-1.158355 -1.949219,-1.158203 H 7.7671137 C 6.2946566,20.310498 5.448665,18.940873 6.1089102,17.62474 7.8599547,14.133521 9.4461247,11.50847 11.138206,9.585677 12.801131,7.6960182 14.689541,6.4191492 16.862816,6.4313812 Z"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffc500;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
sodipodi:nodetypes="cccsccccscsccccscc" />
|
||||||
|
</svg:svg>
|
After Width: | Height: | Size: 22 KiB |
83
static/breezewiki-icon-color.svg
Normal file
83
static/breezewiki-icon-color.svg
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg:svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="68.119537"
|
||||||
|
width="68.119537"
|
||||||
|
version="1.1"
|
||||||
|
id="svg912"
|
||||||
|
sodipodi:docname="breezewiki-icon.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||||
|
inkscape:export-filename="/tmp/breezewiki-icon.png"
|
||||||
|
inkscape:export-xdpi="721.55511"
|
||||||
|
inkscape:export-ydpi="721.55511">
|
||||||
|
<svg:metadata
|
||||||
|
id="metadata918">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</svg:metadata>
|
||||||
|
<svg:defs
|
||||||
|
id="defs916" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1882"
|
||||||
|
inkscape:window-height="1059"
|
||||||
|
id="namedview914"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="8"
|
||||||
|
inkscape:cx="40.321257"
|
||||||
|
inkscape:cy="38.370751"
|
||||||
|
inkscape:window-x="38"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg912"
|
||||||
|
showborder="true"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-smooth-nodes="true" />
|
||||||
|
<svg:circle
|
||||||
|
style="opacity:1;fill:#520044;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.75294118;paint-order:stroke fill markers"
|
||||||
|
id="path835"
|
||||||
|
cx="34.059769"
|
||||||
|
cy="34.059769"
|
||||||
|
r="34.059769" />
|
||||||
|
<div
|
||||||
|
id="saka-gui-root">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<style />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg:path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fa005a;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 25.016194,17.638372 c -3.910133,-0.02201 -6.953592,2.245681 -9.080078,4.662109 -2.09733,2.383295 -3.833062,5.333729 -5.662109,8.980469 -2.0569978,4.100418 1.0356,9.111984 5.623047,9.111328 h 14.441405 c 1.140931,2.016544 2.30672,3.900631 3.666016,5.457031 2.103842,2.40891 5.109808,4.690926 9.011718,4.712891 3.908789,0.02201 6.953047,-2.243435 9.080078,-4.660157 2.097507,-2.383176 3.833076,-5.333755 5.66211,-8.980468 2.056998,-4.100418 -1.035601,-9.111984 -5.623047,-9.111328 H 37.693928 c -1.141017,-2.016606 -2.305247,-3.900738 -3.664063,-5.457032 -2.103172,-2.408829 -5.110419,-4.692872 -9.013671,-4.714843 z m -0.02344,4.4375 c 2.180149,0.01227 4.038227,1.299637 5.693359,3.195312 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 a 2.2186407,2.2186407 0 0 0 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 -1.245075,-1.425619 -2.493079,-3.445143 -3.785154,-5.818359 A 2.2186407,2.2186407 0 0 0 31.609943,35.954778 H 15.897054 c -1.472457,2.11e-4 -2.318449,-1.369414 -1.658204,-2.685547 1.751045,-3.491219 3.337215,-6.11627 5.029296,-8.039063 1.662925,-1.889659 3.551335,-3.166528 5.72461,-3.154296 z"
|
||||||
|
id="path819"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path837"
|
||||||
|
d="m 52.096271,45.902043 c -29.310887,4.106878 -14.655444,2.053439 0,0 z M 24.992754,22.075872 c 2.180149,0.01227 4.038227,1.299637 5.693359,3.195312 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 0.388515,0.713988 1.136369,1.158355 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 -1.245075,-1.425619 -2.493079,-3.445143 -3.785154,-5.818359 -0.388515,-0.713988 -1.13637,-1.158355 -1.949219,-1.158203 H 15.897054 c -1.472457,2.11e-4 -2.318449,-1.369414 -1.658204,-2.685547 1.751045,-3.491219 3.337215,-6.11627 5.029296,-8.039063 1.662925,-1.889659 3.551335,-3.166528 5.72461,-3.154296 z"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffc500;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
sodipodi:nodetypes="cccsccccscsccccscc" />
|
||||||
|
</svg:svg>
|
After Width: | Height: | Size: 6.9 KiB |
142
static/breezewiki-master.svg
Normal file
142
static/breezewiki-master.svg
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg:svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="36.41787"
|
||||||
|
width="224.9014"
|
||||||
|
version="1.1"
|
||||||
|
id="svg912"
|
||||||
|
sodipodi:docname="breezewiki-master.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<svg:metadata
|
||||||
|
id="metadata918">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</svg:metadata>
|
||||||
|
<svg:defs
|
||||||
|
id="defs916" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1882"
|
||||||
|
inkscape:window-height="1059"
|
||||||
|
id="namedview914"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:zoom="2.8284271"
|
||||||
|
inkscape:cx="123.17581"
|
||||||
|
inkscape:cy="7.7496502"
|
||||||
|
inkscape:window-x="38"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg912"
|
||||||
|
showborder="true"
|
||||||
|
inkscape:pagecheckerboard="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-smooth-nodes="true" />
|
||||||
|
<svg:circle
|
||||||
|
style="opacity:1;fill:#520044;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.75294118;paint-order:stroke fill markers"
|
||||||
|
id="path835"
|
||||||
|
cx="25.929829"
|
||||||
|
cy="18.415277"
|
||||||
|
r="34.059769" />
|
||||||
|
<div
|
||||||
|
id="saka-gui-root">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<style />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg:g
|
||||||
|
aria-label="wiki"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;line-height:2.67644334px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="text926"
|
||||||
|
transform="translate(97.9947,1.1875002)">
|
||||||
|
<svg:path
|
||||||
|
d="m 81.588122,13.020001 q 0,0.08533 0,0.298666 -0.04267,0.256001 -0.04267,0.384001 0,0.128 -0.341333,0.213333 -0.810667,0.170667 -0.981334,0.256 -0.341333,0.170667 -0.725333,0.725333 -0.512,0.725334 -1.237333,2.517334 l -4.480001,11.008 q -0.128,0.298667 -0.725333,0.298667 -0.554667,0 -0.682667,-0.298667 l -3.584,-8.874667 -3.626667,8.874667 q -0.128,0.298667 -0.725333,0.298667 -0.554667,0 -0.682667,-0.298667 L 58.846788,16.561334 q -0.554667,-1.322666 -1.066667,-1.92 -0.469333,-0.64 -1.706667,-0.896 -0.341333,-0.08533 -0.341333,-0.256 v -0.725333 q 0,-0.298667 0.298667,-0.298667 0.896,0 2.474666,0.08533 1.749334,0.08533 2.432,0.08533 0,0 4.821334,0 1.152,0 3.456,-0.08533 2.304,-0.08533 3.456,-0.08533 0.341333,0 0.341333,0.469333 0,0.08533 0,0.298667 -0.04267,0.170667 -0.04267,0.256 0,0.256 -0.981334,0.384 -0.981333,0.128 -0.981333,0.682667 0,0.256 0.128,0.597333 l 3.157333,8.064 2.986667,-7.552 q 0.213333,-0.554666 0.213333,-0.938666 0,-0.554667 -0.810666,-0.682667 -0.810667,-0.128 -0.810667,-0.384 v -0.725334 q 0,-0.298666 0.469333,-0.298666 0.384,0 1.194667,0.128 0.810667,0.08533 1.194667,0.08533 0.426667,0 1.194667,-0.08533 0.768,-0.128 1.024,-0.128 0.64,0 0.64,0.384 z m -13.525334,4.693333 -0.853333,-2.133333 q -0.554667,-1.408 -2.517334,-1.408 h -1.536 q -0.810667,0 -0.810667,0.64 0,0.256 0.128,0.597333 l 3.242667,8.149334 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path842"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 91.700066,5.6813339 q 0,1.1093334 -0.768,1.8773334 -0.768,0.768 -1.877333,0.768 -1.109333,0 -1.877333,-0.768 -0.768,-0.768 -0.768,-1.8773334 0,-1.1093333 0.768,-1.8773334 0.768,-0.768 1.877333,-0.768 1.109333,0 1.877333,0.768 0.768,0.7680001 0.768,1.8773334 z m 1.706667,22.3573341 q 0,0.298667 -0.298667,0.298667 -0.298666,0 -0.298666,0 -1.962667,-0.170667 -3.669334,-0.170667 -1.621333,0 -3.584,0.170667 0,0 -0.170666,0 -0.469334,0 -0.469334,-0.426667 0,-0.08533 0.04267,-0.298667 0,-0.213333 0,-0.298666 0,-0.256 0.853333,-0.384 0.853334,-0.128 1.066667,-0.554667 0.256,-0.469333 0.256,-1.834667 v -6.528 q 0,-1.322667 -0.256,-1.749333 -0.170667,-0.298667 -1.109333,-0.682667 -0.896,-0.384 -0.896,-0.768 0,-0.256 0.128,-0.469333 0.04267,-0.08533 2.858666,-1.194667 2.816,-1.152 3.029334,-1.152 0.341333,0 0.341333,0.341333 v 11.904 q 0,1.749334 0.170667,2.090667 0.256,0.469334 1.109333,0.597334 0.896,0.128 0.896,0.384 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path844"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 114.52672,27.526668 q 0,0.810667 -0.29867,0.810667 -0.59733,0 -1.87733,-0.08533 -1.23733,-0.08533 -1.87733,-0.08533 -0.59734,0 -1.83467,0.08533 -1.19467,0.08533 -1.792,0.08533 -0.384,0 -0.384,-0.469334 0,-0.08533 0.0427,-0.298666 0,-0.170667 0,-0.256 0,-0.256 0.55466,-0.341334 0.59734,-0.128 0.59734,-0.512 0,-0.298666 -0.512,-1.152 l -3.24267,-5.418667 -1.87733,1.408 v 3.2 q 0,1.408001 0.21333,1.877334 0.21333,0.426667 1.152,0.554667 0.81067,0.128 0.81067,0.384 v 0.725333 q 0,0.298667 -0.59734,0.298667 -0.59733,0 -1.83466,-0.08533 -1.19467,-0.08533 -1.792004,-0.08533 -0.597333,0 -1.834667,0.08533 -1.194666,0.08533 -1.792,0.08533 -0.597333,0 -0.597333,-0.426667 v -0.597333 q 0,-0.256 0.853333,-0.384 0.853334,-0.128 1.066667,-0.554667 0.256,-0.469333 0.256,-1.834667 V 6.4920006 q 0,-1.3226667 -0.256,-1.7493334 -0.170667,-0.2986666 -1.109333,-0.6826666 -0.896001,-0.3840001 -0.896001,-0.7680001 0,-0.256 0.128,-0.4693333 0.04267,-0.085333 2.858667,-1.1946667 2.816001,-1.15200005 3.029331,-1.15200005 0.34134,0 0.34134,0.34133334 V 19.718668 q 5.71733,-4.309334 5.71733,-5.290667 0,-0.682667 -1.792,-0.682667 -0.46933,0 -0.46933,-0.512 0,-0.768 0.42666,-0.768 0.55467,0 1.70667,0.08533 1.19467,0.08533 1.74933,0.08533 0.55467,0 1.70667,-0.08533 1.152,-0.08533 1.70667,-0.08533 0.42666,0 0.42666,0.469333 0,0.08533 0,0.298667 -0.0427,0.170667 -0.0427,0.298667 0,0.128 -0.384,0.213333 -0.64,0.08533 -1.83467,0.426667 -0.512,0.213333 -2.048,1.578667 -1.024,0.896 -2.09067,1.834666 l 4.224,6.656 q 1.57867,2.474667 2.60267,2.688001 0.29867,0.04267 0.59733,0.128 0.29867,0.08533 0.29867,0.469333 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path846"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 123.69999,5.6813339 q 0,1.1093334 -0.768,1.8773334 -0.768,0.768 -1.87734,0.768 -1.10933,0 -1.87733,-0.768 -0.768,-0.768 -0.768,-1.8773334 0,-1.1093333 0.768,-1.8773334 0.768,-0.768 1.87733,-0.768 1.10934,0 1.87734,0.768 0.768,0.7680001 0.768,1.8773334 z m 1.70666,22.3573341 q 0,0.298667 -0.29866,0.298667 -0.29867,0 -0.29867,0 -1.96267,-0.170667 -3.66933,-0.170667 -1.62134,0 -3.584,0.170667 0,0 -0.17067,0 -0.46933,0 -0.46933,-0.426667 0,-0.08533 0.0427,-0.298667 0,-0.213333 0,-0.298666 0,-0.256 0.85334,-0.384 0.85333,-0.128 1.06666,-0.554667 0.256,-0.469333 0.256,-1.834667 v -6.528 q 0,-1.322667 -0.256,-1.749333 -0.17066,-0.298667 -1.10933,-0.682667 -0.896,-0.384 -0.896,-0.768 0,-0.256 0.128,-0.469333 0.0427,-0.08533 2.85867,-1.194667 2.816,-1.152 3.02933,-1.152 0.34133,0 0.34133,0.341333 v 11.904 q 0,1.749334 0.17067,2.090667 0.256,0.469334 1.10933,0.597334 0.896,0.128 0.896,0.384 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:42.66666794px;font-family:Alfios;-inkscape-font-specification:'Alfios Bold';text-align:start;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="path848"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg:g>
|
||||||
|
<svg:g
|
||||||
|
aria-label="breeze"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;line-height:2.57638931px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#252525;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.72772277;paint-order:stroke fill markers"
|
||||||
|
id="text930"
|
||||||
|
transform="translate(-69.005304,1.5000002)">
|
||||||
|
<svg:path
|
||||||
|
d="m 162.59026,13.697235 q 0,0.782126 -0.48131,1.363707 -0.46126,0.581581 -1.22333,0.581581 -0.46125,0 -0.92251,-0.421145 -0.44119,-0.421144 -0.80218,-0.421144 -0.16043,0 -0.9225,0.80218 -1.28349,1.323598 -2.68731,3.690031 -1.34365,2.266161 -2.00545,4.111176 -0.48131,1.504089 -1.52414,4.452103 -0.28077,0.701908 -0.80219,0.701908 -0.50136,0 -0.98267,-0.240654 -0.58158,-0.280763 -0.58158,-0.721962 0,-0.180491 0.10028,-0.461254 l 2.52686,-7.259736 q 0.8824,-2.526869 0.8824,-4.111176 0,-1.383761 -0.80218,-1.383761 -1.103,0 -2.08567,1.263434 -0.96261,1.263435 -0.98267,1.263435 -0.14038,0 -0.46125,-0.2206 -0.32087,-0.240654 -0.32087,-0.381036 0,-0.140381 0.0802,-0.280763 0.80218,-1.383762 1.88513,-2.607087 1.50409,-1.664525 2.52687,-1.664525 2.12578,0 2.12578,3.10845 0,1.443925 -0.42115,3.288941 3.9708,-6.4375 6.05647,-6.4375 0.76207,0 1.28348,0.601635 0.54148,0.601636 0.54148,1.383762 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path853"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 148.05073,15.502142 q 0,4.672702 -5.71554,9.485786 -4.25155,3.569704 -7.74104,3.569704 -1.38376,0 -2.24611,-0.661799 -0.96261,-0.721962 -0.96261,-2.065615 0,-0.561526 0.20054,-1.24338 l 5.85592,-20.1748444 q 0.12033,-0.4211449 0.12033,-0.7420171 0,-0.8823988 -1.12305,-1.1832166 -1.12306,-0.3008177 -1.12306,-0.5013629 0,-0.7821262 0.62169,-1.04283489 0.30082,-0.0200545 0.78213,-0.0601636 0.56153,-0.0601635 2.28621,-0.4612539 Q 140.73083,-2.0861626e-7 140.791,-2.0861626e-7 q 0.42114,0 0.42114,0.38103582861626 0,0.18049066 -1.20327,4.33177578 L 136.09824,18.06912 q 5.03368,-6.357282 9.06464,-6.357282 1.34365,0 2.16589,1.263434 0.72196,1.102999 0.72196,2.52687 z m -2.92796,0.200545 q 0,-1.764798 -1.48403,-1.764798 -2.54693,0 -6.05647,4.331776 -3.42932,4.211449 -3.42932,6.83859 0,1.965343 1.36371,1.965343 2.78758,0 6.25701,-4.311721 3.3491,-4.17134 3.3491,-7.05919 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path851"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 175.36498,14.760125 q 0,3.028232 -3.66997,4.572429 -2.18595,0.902454 -6.29712,1.403817 -0.16044,1.123053 -0.16044,2.145833 0,3.54965 3.46943,3.54965 1.12306,0 2.4266,-1.183217 1.30354,-1.183216 1.22332,-1.183216 0.14039,0 0.46126,0.320872 0.32087,0.300818 0.32087,0.441199 0,0.140382 -0.12033,0.300818 -2.54692,3.429322 -5.89602,3.429322 -2.18595,0 -3.5296,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82769,-8.12208 2.84774,-3.50954 6.61799,-3.50954 1.50409,0 2.50681,0.762072 1.103,0.842289 1.103,2.286215 z m -2.54692,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84502,0 -3.54965,2.205997 -1.3236,1.704634 -2.04556,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path855"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 188.96195,14.760125 q 0,3.028232 -3.66998,4.572429 -2.18594,0.902454 -6.29712,1.403817 -0.16043,1.123053 -0.16043,2.145833 0,3.54965 3.46943,3.54965 1.12305,0 2.4266,-1.183217 1.30354,-1.183216 1.22332,-1.183216 0.14038,0 0.46125,0.320872 0.32088,0.300818 0.32088,0.441199 0,0.140382 -0.12033,0.300818 -2.54692,3.429322 -5.89603,3.429322 -2.18594,0 -3.52959,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82768,-8.12208 2.84775,-3.50954 6.618,-3.50954 1.50408,0 2.50681,0.762072 1.103,0.842289 1.103,2.286215 z m -2.54693,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84501,0 -3.54964,2.205997 -1.3236,1.704634 -2.04557,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path857"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 204.80502,12.514019 q 0,0.721962 -0.92251,1.704634 -0.36098,0.40109 -6.39739,6.477609 -1.34366,1.283489 -3.7502,3.850467 l 0.36098,0.180491 q 2.72742,1.363707 4.17134,4.411993 0.80218,1.68458 1.48404,1.68458 0.58158,0 1.103,-1.042835 0.54147,-1.042835 1.22332,-1.042835 0.94256,0 0.94256,0.942562 0,1.102999 -1.40381,1.865071 -1.20327,0.641744 -2.4266,0.641744 -1.58431,0 -3.16861,-1.363707 -0.42115,-0.381036 -2.36644,-2.546924 -1.22332,-1.363707 -1.96534,-1.363707 -0.48131,0 -1.12305,0.742017 -0.62169,0.742017 -1.103,0.742017 -0.68185,0 -0.68185,-0.561526 0,-0.4412 1.90518,-2.226052 0.0802,-0.08022 3.32905,-3.369159 1.8049,-1.764797 5.27433,-5.394665 -0.84229,-0.160436 -1.60436,-0.421145 -0.40109,-0.140381 -1.98539,-1.123052 -1.34366,-0.822236 -1.90518,-0.822236 -0.56153,0 -0.94257,0.461254 -0.38103,0.4412 -0.38103,1.002726 0,0.340927 0.34092,0.862344 0.34093,0.501363 0.34093,0.762072 0,0.481308 -0.58158,0.481308 -0.64174,0 -1.103,-0.982671 -0.38103,-0.802181 -0.38103,-1.544198 0,-1.504089 1.12305,-2.647196 1.12305,-1.163162 2.60709,-1.163162 1.20327,0 3.40926,1.24338 2.206,1.223326 3.38922,1.223326 0.70191,0 1.28349,-1.203272 0.58158,-1.203271 1.24338,-1.203271 0.6618,0 0.6618,0.742018 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path859"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
d="m 217.6399,14.760125 q 0,3.028232 -3.66997,4.572429 -2.18595,0.902454 -6.29712,1.403817 -0.16044,1.123053 -0.16044,2.145833 0,3.54965 3.46943,3.54965 1.12306,0 2.4266,-1.183217 1.30354,-1.183216 1.22333,-1.183216 0.14038,0 0.46125,0.320872 0.32087,0.300818 0.32087,0.441199 0,0.140382 -0.12032,0.300818 -2.54693,3.429322 -5.89603,3.429322 -2.18595,0 -3.5296,-1.544197 -1.28349,-1.46398 -1.28349,-3.669977 0,-4.612539 2.82769,-8.12208 2.84774,-3.50954 6.61799,-3.50954 1.50409,0 2.50682,0.762072 1.10299,0.842289 1.10299,2.286215 z m -2.54692,0.220599 q 0,-1.784852 -1.46398,-1.784852 -1.84502,0 -3.54965,2.205997 -1.3236,1.704634 -2.04556,3.890576 7.05919,-0.822235 7.05919,-4.311721 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:41.07165146px;font-family:Alexander;-inkscape-font-specification:'Alexander Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#252525;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:0.72772277"
|
||||||
|
id="path861"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg:g>
|
||||||
|
<svg:path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fa005a;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 16.886254,1.9938812 c -3.910133,-0.022008 -6.9535917,2.245681 -9.0800783,4.662109 -2.0973298,2.383295 -3.833062,5.3337288 -5.6621092,8.9804688 -2.05699735,4.100418 1.0356001,9.111984 5.6230472,9.111328 H 22.208519 c 1.140931,2.016544 2.30672,3.900631 3.666016,5.457031 2.103842,2.40891 5.109808,4.690926 9.011718,4.712891 3.908789,0.02201 6.953047,-2.243435 9.080078,-4.660157 2.097507,-2.383176 3.833076,-5.333755 5.66211,-8.980468 C 51.685439,17.176666 48.59284,12.1651 44.005394,12.165756 H 29.563988 C 28.422971,10.14915 27.258741,8.2650182 25.899925,6.7087242 23.796753,4.2998952 20.789506,2.0158522 16.886254,1.9938812 Z m -0.02344,4.4375 c 2.180149,0.012272 4.038227,1.299637 5.693359,3.1953118 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 a 2.2186407,2.2186407 0 0 0 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 C 27.969301,25.86123 26.721297,23.841706 25.429222,21.46849 A 2.2186407,2.2186407 0 0 0 23.480003,20.310287 H 7.7671137 C 6.2946566,20.310498 5.448665,18.940873 6.1089102,17.62474 7.8599547,14.133521 9.4461247,11.50847 11.138206,9.585677 12.801131,7.6960182 14.689541,6.4191492 16.862816,6.4313812 Z"
|
||||||
|
id="path819"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<svg:path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path837"
|
||||||
|
d="m 43.966331,30.257552 c -29.310887,4.106878 -14.655444,2.053439 0,0 z M 16.862814,6.4313812 c 2.180149,0.012272 4.038227,1.299637 5.693359,3.1953118 1.245218,1.426187 2.495246,3.44536 3.78711,5.818359 0.388515,0.713988 1.136369,1.158355 1.949218,1.158204 h 15.712891 c 1.472457,-2.11e-4 2.318449,1.369414 1.658203,2.685546 -1.751077,3.491284 -3.337367,6.116702 -5.029297,8.039063 -1.662405,1.888817 -3.551482,3.164578 -5.724609,3.152344 -2.180003,-0.01227 -4.040284,-1.298343 -5.695313,-3.19336 -1.245075,-1.425619 -2.493079,-3.445143 -3.785154,-5.818359 -0.388515,-0.713988 -1.13637,-1.158355 -1.949219,-1.158203 H 7.7671137 C 6.2946566,20.310498 5.448665,18.940873 6.1089102,17.62474 7.8599547,14.133521 9.4461247,11.50847 11.138206,9.585677 12.801131,7.6960182 14.689541,6.4191492 16.862816,6.4313812 Z"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffc500;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.72772277;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
sodipodi:nodetypes="cccsccccscsccccscc" />
|
||||||
|
</svg:svg>
|
After Width: | Height: | Size: 22 KiB |
|
@ -24,7 +24,7 @@
|
||||||
--theme-body-background-color: #286cab;
|
--theme-body-background-color: #286cab;
|
||||||
--theme-body-background-color--rgb: 40,108,171;
|
--theme-body-background-color--rgb: 40,108,171;
|
||||||
|
|
||||||
--theme-body-text-color: #fff;
|
--theme-body-text-color: #000;
|
||||||
--theme-body-text-color--rgb: 255,255,255;
|
--theme-body-text-color--rgb: 255,255,255;
|
||||||
--theme-body-text-color--hover: #cccccc;
|
--theme-body-text-color--hover: #cccccc;
|
||||||
--theme-sticky-nav-background-color: #ffffff;
|
--theme-sticky-nav-background-color: #ffffff;
|
||||||
|
|
|
@ -31,6 +31,8 @@ body.skin-fandomdesktop, button, input, textarea, .wikitable, .va-table {
|
||||||
font-family: "Source Sans Pro", "Segoe UI", sans-serif;
|
font-family: "Source Sans Pro", "Segoe UI", sans-serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
margin: 1.2em 0 0.6em;
|
margin: 1.2em 0 0.6em;
|
||||||
|
@ -67,6 +69,25 @@ p {
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* global top banner message */
|
||||||
|
.bw-top-banner {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
white-space: pre-line;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.bw-top-banner a, .bw-top-banner a:visited {
|
||||||
|
color: #ffdd57;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.bw-top-banner-rainbow {
|
||||||
|
animation: bw-rainbow-color 1.6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
/* custom footer with source and license info */
|
/* custom footer with source and license info */
|
||||||
.custom-footer {
|
.custom-footer {
|
||||||
clear: both;
|
clear: both;
|
||||||
|
@ -338,8 +359,11 @@ figcaption, .lightbox-caption, .thumbcaption {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
.niwa__notice--alt {
|
||||||
|
background: #e5fdd8;
|
||||||
|
}
|
||||||
.niwa__header {
|
.niwa__header {
|
||||||
font-size: max(2.9vw, 26px);
|
font-size: max(2.75vw, 26px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.niwa__notice a {
|
.niwa__notice a {
|
||||||
|
@ -403,6 +427,10 @@ figcaption, .lightbox-caption, .thumbcaption {
|
||||||
.niwa__right {
|
.niwa__right {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
/* remove balloons in top banner */
|
||||||
|
.bw-balloon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 560px) { /* wider than 560 px */
|
@media (min-width: 560px) { /* wider than 560 px */
|
||||||
|
@ -418,6 +446,16 @@ figcaption, .lightbox-caption, .thumbcaption {
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
}
|
}
|
||||||
|
/* make text content hit the edges of the screen (no space for the background) */
|
||||||
|
.page {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.page__main {
|
||||||
|
background: linear-gradient(to bottom, rgba(var(--theme-page-background-color--rgb), 0), rgba(var(--theme-page-background-color--rgb), 1) 160px);
|
||||||
|
}
|
||||||
|
.page-title {
|
||||||
|
color: var(--theme-body-text-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *****
|
/* *****
|
||||||
|
@ -456,3 +494,12 @@ figcaption, .lightbox-caption, .thumbcaption {
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
src: url("/static/source-sans-pro-v21-vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic-700italic.woff2") format("woff2");
|
src: url("/static/source-sans-pro-v21-vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic-700italic.woff2") format("woff2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes bw-rainbow-color {
|
||||||
|
0% {
|
||||||
|
filter: hue-rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
filter: hue-rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
BIN
static/three-balloons.png
Normal file
BIN
static/three-balloons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
Loading…
Reference in a new issue