From 10bff13c6dc68045ca4fbfa5c1ee0d2f282a20b3 Mon Sep 17 00:00:00 2001 From: Valentin Boettcher Date: Tue, 1 Jan 2019 20:10:17 +0100 Subject: [PATCH] Initial Commit --- css/highlight-style.css | 147 +++++++++++ js/.gitignore | 50 ++++ js/background.js | 36 +++ js/highlight-lisp.js | 531 ++++++++++++++++++++++++++++++++++++++++ js/main.js | 120 +++++++++ js/tocbot.js | 1 + manifest.json | 31 +++ 7 files changed, 916 insertions(+) create mode 100644 css/highlight-style.css create mode 100644 js/.gitignore create mode 100644 js/background.js create mode 100644 js/highlight-lisp.js create mode 100644 js/main.js create mode 100644 js/tocbot.js create mode 100644 manifest.json diff --git a/css/highlight-style.css b/css/highlight-style.css new file mode 100644 index 0000000..539d22f --- /dev/null +++ b/css/highlight-style.css @@ -0,0 +1,147 @@ +/** + * Inspired by github's default code highlighting + */ + +pre code.hl-highlighted { + max-width: 900px; + white-space: pre; + margin: 0; + padding: 0; + background: none; + border: none; + overflow-x: auto; + font-size: 15px; +} +code.hl-highlighted { + margin: 0 2px; + padding: 0 5px; + white-space: nowrap; + font-family: "Fira Code", Consolas, "Liberation Mono", Courier, monospace; + background: #f8f8f8; + border: 1px solid #eaeaea; + border-radius: 3px; +} +code.hl-highlighted { + color: #008080; +} +code.hl-highlighted .function { + color: #008080; +} +code.hl-highlighted .function.known { + color: #800603; +} +code.hl-highlighted .function.known.special { + color: #2d2d2d; + font-weight: bold; +} +code.hl-highlighted .keyword { + color: #990073; +} +code.hl-highlighted .keyword.known { + color: #990073; +} +code.hl-highlighted .symbol { + color: #75a; +} +code.hl-highlighted .lambda-list { + color: #966; +} +code.hl-highlighted .number { + color: #800; +} +code.hl-highlighted .variable.known { + color: #c3c; +} +code.hl-highlighted .variable.global { + color: #939; +} +code.hl-highlighted .variable.constant { + color: #229; +} +code.hl-highlighted .nil { + color: #f00; +} +code.hl-highlighted .list { + color: #222; +} +code.hl-highlighted .string, +code.hl-highlighted .string * { + color: #d14 !important; +} +code.hl-highlighted .comment, +code.hl-highlighted .comment *, +code.hl-highlighted .comment .string code.hl-highlighted .comment .string * { + color: #777777 !important; +} +code.hl-highlighted .string .comment { + color: #d14 !important; +} +code.hl-highlighted .list.active { + display: inline-block; + background: #aefff7; +} +/* Table of Contents */ + +.toc { + position: fixed; + left: 3em; + top: 5em; + padding: 1em; + width: 14em; + line-height: 2; + font-size: 14px; +} +.toc li a { + text-decoration: none; +} +.toc { + overflow-y: auto +} +.toc>.toc-list { + overflow: hidden; + position: relative +} +.toc>.toc-list li { + list-style: none +} +.toc-list { + margin: 0; + padding-left: 10px +} +a.toc-link { + color: currentColor; + height: 100% +} +.is-collapsible { + max-height: 1000px; + overflow: hidden; + transition: all 300ms ease-in-out +} +.is-collapsed { + max-height: 0 +} +.is-position-fixed { + position: fixed !important; + top: 0 +} +.is-active-link { + font-weight: 700 +} +.toc-link::before { + background-color: #EEE; + content: ' '; + display: inline-block; + height: inherit; + left: 0; + margin-top: -1px; + position: absolute; + width: 2px +} +.is-active-link::before { + background-color: black +} +@media (max-width: 80em) { + .toc { + display: none; + } +} diff --git a/js/.gitignore b/js/.gitignore new file mode 100644 index 0000000..8a3b740 --- /dev/null +++ b/js/.gitignore @@ -0,0 +1,50 @@ +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + +.ignored \ No newline at end of file diff --git a/js/background.js b/js/background.js new file mode 100644 index 0000000..1b00b56 --- /dev/null +++ b/js/background.js @@ -0,0 +1,36 @@ +function fetchChapters() { + return browser.storage.local.get('chapters').then(chapters => { + if(chapters.hasOwnProperty('chapters')) + return Promise.resolve(chapters.chapters); + + return fetch("http://www.gigamonkeys.com/book/index.html").then(function(response) { + return response.text().then(text => { + const regex = /li>(.*?) respond(chapters)).catch(() => { + respond(); + }); + return true; + } +} + +browser.runtime.onMessage.addListener(handleMessage); diff --git a/js/highlight-lisp.js b/js/highlight-lisp.js new file mode 100644 index 0000000..1a225ba --- /dev/null +++ b/js/highlight-lisp.js @@ -0,0 +1,531 @@ +/** + * Common Lisp syntax highlighter + * + * @version 0.1.1 + * @author Andrew "Danger" Lyon + * @copyright Lyon Bros. Enterprises, LLC + * @licence MIT + */ +var highlight_lisp = function() { + // all of the following functions were pulled straight from my syntax/lisp.vim + // file in my vim directory. + var funcs = + '\\* find-method pprint-indent find-package pprint-linear find-restart ' + + 'pprint-logical-block \\+ find-symbol pprint-newline finish-output ' + + 'pprint-pop first pprint-tab - fixnum pprint-tabular / flet prin1 // float ' + + 'prin1-to-string /// float-digits princ /= float-precision princ-to-string 1\\+ ' + + 'float-radix print 1- float-sign print-not-readable < floating-point-inexact ' + + 'print-not-readable-object <= floating-point-invalid-operation print-object = ' + + 'floating-point-overflow print-unreadable-object > floating-point-underflow ' + + 'probe-file >= floatp proclaim abort floor prog abs fmakunbound prog\\* access ' + + 'force-output prog1 acons format prog2 acos formatter progn acosh fourth ' + + 'program-error add-method fresh-line progv adjoin fround provide adjust-array ' + + 'ftruncate psetf adjustable-array-p ftype psetq allocate-instance funcall push ' + + 'alpha-char-p function pushnew alphanumericp function-keywords putprop and ' + + 'function-lambda-expression quote append functionp random apply gbitp ' + + 'random-state applyhook gcd random-state-p apropos generic-function rassoc ' + + 'apropos-list gensym rassoc-if aref gentemp rassoc-if-not arithmetic-error get ' + + 'ratio arithmetic-error-operands get-decoded-time rational ' + + 'arithmetic-error-operation get-dispatch-macro-character rationalize array ' + + 'get-internal-real-time rationalp array-dimension get-internal-run-time read ' + + 'array-dimension-limit get-macro-character read-byte array-dimensions ' + + 'get-output-stream-string read-char array-displacement get-properties ' + + 'read-char-no-hang array-element-type get-setf-expansion read-delimited-list ' + + 'array-has-fill-pointer-p get-setf-method read-eval-print array-in-bounds-p ' + + 'get-universal-time read-from-string array-rank getf read-line array-rank-limit ' + + 'gethash read-preserving-whitespace array-row-major-index go read-sequence ' + + 'array-total-size graphic-char-p reader-error array-total-size-limit handler-bind ' + + 'readtable arrayp handler-case readtable-case ash hash-table readtablep asin ' + + 'hash-table-count real asinh hash-table-p realp assert hash-table-rehash-size ' + + 'realpart assoc hash-table-rehash-threshold reduce assoc-if hash-table-size ' + + 'reinitialize-instance assoc-if-not hash-table-test rem atan host-namestring ' + + 'remf atanh identity remhash atom if remove base-char if-exists ' + + 'remove-duplicates base-string ignorable remove-if bignum ignore remove-if-not ' + + 'bit ignore-errors remove-method bit-and imagpart remprop bit-andc1 import ' + + 'rename-file bit-andc2 in-package rename-package bit-eqv in-package replace ' + + 'bit-ior incf require bit-nand initialize-instance rest bit-nor inline restart ' + + 'bit-not input-stream-p restart-bind bit-orc1 inspect restart-case bit-orc2 ' + + 'int-char restart-name bit-vector integer return bit-vector-p ' + + 'integer-decode-float return-from bit-xor integer-length revappend block ' + + 'integerp reverse boole interactive-stream-p room boole-1 intern rotatef ' + + 'boole-2 round boole-and intersection ' + + 'row-major-aref boole-andc1 invalid-method-error rplaca boole-andc2 ' + + 'invoke-debugger rplacd boole-c1 invoke-restart safety boole-c2 ' + + 'invoke-restart-interactively satisfies boole-clr isqrt sbit boole-eqv keyword ' + + 'scale-float boole-ior keywordp schar boole-nand labels search boole-nor ' + + 'second boole-orc1 lambda-list-keywords sequence boole-orc2 ' + + 'lambda-parameters-limit serious-condition boole-set last set boole-xor lcm ' + + 'set-char-bit boolean ldb set-difference both-case-p ldb-test ' + + 'set-dispatch-macro-character boundp ldiff set-exclusive-or break ' + + 'least-negative-double-float set-macro-character broadcast-stream ' + + 'least-negative-long-float set-pprint-dispatch broadcast-stream-streams ' + + 'least-negative-normalized-double-float set-syntax-from-char built-in-class ' + + 'least-negative-normalized-long-float setf butlast ' + + 'least-negative-normalized-short-float setq byte ' + + 'least-negative-normalized-single-float seventh byte-position ' + + 'least-negative-short-float shadow byte-size least-negative-single-float ' + + 'shadowing-import call-arguments-limit least-positive-double-float ' + + 'shared-initialize call-method least-positive-long-float shiftf ' + + 'call-next-method least-positive-normalized-double-float short-float capitalize ' + + 'least-positive-normalized-long-float short-float-epsilon car ' + + 'least-positive-normalized-short-float short-float-negative-epsilon case ' + + 'least-positive-normalized-single-float short-site-name catch ' + + 'least-positive-short-float signal ccase least-positive-single-float ' + + 'signed-byte cdr length signum ceiling simple-condition cell-error ' + + 'simple-array cell-error-name lisp simple-base-string cerror ' + + 'lisp-implementation-type simple-bit-vector change-class ' + + 'lisp-implementation-version simple-bit-vector-p char list ' + + 'simple-condition-format-arguments char-bit list\\* ' + + 'simple-condition-format-control char-bits list-all-packages simple-error ' + + 'char-bits-limit list-length simple-string char-code listen simple-string-p ' + + 'char-code-limit listp simple-type-error char-control-bit load simple-vector ' + + 'char-downcase load-logical-pathname-translations simple-vector-p char-equal ' + + 'load-time-value simple-warning char-font locally sin char-font-limit log ' + + 'single-flaot-epsilon char-greaterp logand single-float char-hyper-bit logandc1 ' + + 'single-float-epsilon char-int logandc2 single-float-negative-epsilon ' + + 'char-lessp logbitp sinh char-meta-bit logcount sixth char-name logeqv sleep ' + + 'char-not-equal logical-pathname slot-boundp char-not-greaterp ' + + 'logical-pathname-translations slot-exists-p char-not-lessp logior ' + + 'slot-makunbound char-super-bit lognand slot-missing char-upcase lognor ' + + 'slot-unbound char/= lognot slot-value char< logorc1 software-type char<= ' + + 'logorc2 software-version char= logtest some char> logxor sort char>= ' + + 'long-float space character long-float-epsilon special characterp ' + + 'long-float-negative-epsilon special-form-p check-type long-site-name ' + + 'special-operator-p cis loop speed class loop-finish sqrt class-name ' + + 'lower-case-p stable-sort class-of machine-instance standard clear-input ' + + 'machine-type standard-char clear-output machine-version standard-char-p close ' + + 'macro-function standard-class clrhash macroexpand standard-generic-function ' + + 'code-char macroexpand-1 standard-method coerce macroexpand-l standard-object ' + + 'commonp macrolet step compilation-speed make-array storage-condition compile ' + + 'make-array store-value compile-file make-broadcast-stream stream ' + + 'compile-file-pathname make-char stream-element-type compiled-function ' + + 'make-concatenated-stream stream-error compiled-function-p make-condition ' + + 'stream-error-stream compiler-let make-dispatch-macro-character ' + + 'stream-external-format compiler-macro make-echo-stream streamp ' + + 'compiler-macro-function make-hash-table streamup complement make-instance ' + + 'string complex make-instances-obsolete string-capitalize complexp make-list ' + + 'string-char compute-applicable-methods make-load-form string-char-p ' + + 'compute-restarts make-load-form-saving-slots string-downcase concatenate ' + + 'make-method string-equal concatenated-stream make-package string-greaterp ' + + 'concatenated-stream-streams make-pathname string-left-trim cond ' + + 'make-random-state string-lessp condition make-sequence string-not-equal ' + + 'conjugate make-string string-not-greaterp cons make-string-input-stream ' + + 'string-not-lessp consp make-string-output-stream string-right-strim constantly ' + + 'make-symbol string-right-trim constantp make-synonym-stream string-stream ' + + 'continue make-two-way-stream string-trim control-error makunbound ' + + 'string-upcase copy-alist map string/= copy-list map-into string< ' + + 'copy-pprint-dispatch mapc string<= copy-readtable mapcan string= copy-seq ' + + 'mapcar string> copy-structure mapcon string>= copy-symbol maphash stringp ' + + 'copy-tree mapl structure cos maplist structure-class cosh mask-field ' + + 'structure-object count max style-warning count-if member sublim count-if-not ' + + 'member-if sublis ctypecase member-if-not subseq debug merge subsetp decf ' + + 'merge-pathname subst declaim merge-pathnames subst-if declaration method ' + + 'subst-if-not declare method-combination substitute decode-float ' + + 'method-combination-error substitute-if decode-universal-time method-qualifiers ' + + 'substitute-if-not defclass min subtypep defconstant minusp svref defgeneric ' + + 'mismatch sxhash define-compiler-macro mod symbol define-condition ' + + 'most-negative-double-float symbol-function define-method-combination ' + + 'most-negative-fixnum symbol-macrolet define-modify-macro ' + + 'most-negative-long-float symbol-name define-setf-expander ' + + 'most-negative-short-float symbol-package define-setf-method ' + + 'most-negative-single-float symbol-plist define-symbol-macro ' + + 'most-positive-double-float symbol-value defmacro most-positive-fixnum symbolp ' + + 'defmethod most-positive-long-float synonym-stream defpackage ' + + 'most-positive-short-float synonym-stream-symbol defparameter ' + + 'most-positive-single-float sys defsetf muffle-warning system defstruct ' + + 'multiple-value-bind deftype multiple-value-call tagbody defun ' + + 'multiple-value-list tailp defvar multiple-value-prog1 tan delete ' + + 'multiple-value-seteq tanh delete-duplicates multiple-value-setq tenth ' + + 'delete-file multiple-values-limit terpri delete-if name-char the delete-if-not ' + + 'namestring third delete-package nbutlast throw denominator nconc time ' + + 'deposit-field next-method-p trace describe translate-logical-pathname ' + + 'describe-object nintersection translate-pathname destructuring-bind ninth ' + + 'tree-equal digit-char no-applicable-method truename digit-char-p ' + + 'no-next-method truncase directory not truncate directory-namestring notany ' + + 'two-way-stream disassemble notevery two-way-stream-input-stream ' + + 'division-by-zero notinline two-way-stream-output-stream do nreconc type do\\* ' + + 'nreverse type-error do-all-symbols nset-difference type-error-datum ' + + 'do-exeternal-symbols nset-exclusive-or type-error-expected-type ' + + 'do-external-symbols nstring type-of do-symbols nstring-capitalize typecase ' + + 'documentation nstring-downcase typep dolist nstring-upcase unbound-slot ' + + 'dotimes nsublis unbound-slot-instance double-float nsubst unbound-variable ' + + 'double-float-epsilon nsubst-if undefined-function ' + + 'double-float-negative-epsilon nsubst-if-not unexport dpb nsubstitute unintern ' + + 'dribble nsubstitute-if union dynamic-extent nsubstitute-if-not unless ecase ' + + 'nth unread echo-stream nth-value unread-char echo-stream-input-stream nthcdr ' + + 'unsigned-byte echo-stream-output-stream null untrace ed number unuse-package ' + + 'eighth numberp unwind-protect elt numerator ' + + 'update-instance-for-different-class encode-universal-time nunion ' + + 'update-instance-for-redefined-class end-of-file oddp ' + + 'upgraded-array-element-type endp open upgraded-complex-part-type ' + + 'enough-namestring open-stream-p upper-case-p ensure-directories-exist optimize ' + + 'use-package ensure-generic-function or use-value eq otherwise user eql ' + + 'output-stream-p user-homedir-pathname equal package values equalp ' + + 'package-error values-list error package-error-package vector etypecase ' + + 'package-name vector-pop eval package-nicknames vector-push eval-when ' + + 'package-shadowing-symbols vector-push-extend evalhook package-use-list vectorp ' + + 'evenp package-used-by-list warn every packagep warning exp pairlis when export ' + + 'parse-error wild-pathname-p expt parse-integer with-accessors extended-char ' + + 'parse-namestring with-compilation-unit fboundp pathname ' + + 'with-condition-restarts fceiling pathname-device with-hash-table-iterator ' + + 'fdefinition pathname-directory with-input-from-string ffloor pathname-host ' + + 'with-open-file fifth pathname-match-p with-open-stream file-author ' + + 'pathname-name with-output-to-string file-error pathname-type ' + + 'with-package-iterator file-error-pathname pathname-version with-simple-restart ' + + 'file-length pathnamep with-slots file-namestring peek-char ' + + 'with-standard-io-syntax file-position phase write file-stream write-byte ' + + 'file-string-length plusp write-char file-write-date pop write-line fill ' + + 'position write-sequence fill-pointer position-if write-string find ' + + 'position-if-not write-to-string find-all-symbols pprint y-or-n-p find-class ' + + 'pprint-dispatch yes-or-no-p find-if pprint-exit-if-list-exhausted zerop ' + + 'find-if-not pprint-fill'; + + // common lisp global variables. also from lisp.vim + var standard_vars = + '\\*applyhook\\* \\*load-pathname\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* ' + + '\\*load-print\\* \\*print-pprint-dispatch\\* \\*break-on-signals\\* \\*load-truename\\* ' + + '\\*print-pretty\\* \\*break-on-warnings\\* \\*load-verbose\\* \\*print-radix\\* ' + + '\\*compile-file-pathname\\* \\*macroexpand-hook\\* \\*print-readably\\* ' + + '\\*compile-file-pathname\\* \\*modules\\* \\*print-right-margin\\* \\*compile-file-truename\\* ' + + '\\*package\\* \\*print-right-margin\\* \\*compile-file-truename\\* \\*print-array\\* ' + + '\\*query-io\\* \\*compile-print\\* \\*print-base\\* \\*random-state\\* \\*compile-verbose\\* ' + + '\\*print-case\\* \\*read-base\\* \\*compile-verbose\\* \\*print-circle\\* ' + + '\\*read-default-float-format\\* \\*debug-io\\* \\*print-escape\\* \\*read-eval\\* ' + + '\\*debugger-hook\\* \\*print-gensym\\* \\*read-suppress\\* \\*default-pathname-defaults\\* ' + + '\\*print-length\\* \\*readtable\\* \\*error-output\\* \\*print-level\\* \\*standard-input\\* ' + + '\\*evalhook\\* \\*print-lines\\* \\*standard-output\\* \\*features\\* \\*print-miser-width\\* ' + + '\\*terminal-io\\* \\*gensym-counter\\* \\*print-miser-width\\* \\*trace-output\\* ' + + 'pi internal-time-units-per-second'; + + // common lisp known keywords + var keywords = + ':abort :from-end :overwrite :adjustable :gensym :predicate :append :host ' + + ':preserve-whitespace :array :if-does-not-exist :pretty :base :if-exists :print ' + + ':case :include :print-function :circle :index :probe :conc-name :inherited ' + + ':radix :constructor :initial-contents :read-only :copier :initial-element ' + + ':rehash-size :count :initial-offset :rehash-threshold :create :initial-value ' + + ':rename :default :input :rename-and-delete :defaults :internal :size :device ' + + ':io :start :direction :junk-allowed :start1 :directory :key :start2 ' + + ':displaced-index-offset :length :stream :displaced-to :level :supersede ' + + ':element-type :name :test :end :named :test-not :end1 :new-version :type :end2 ' + + ':nicknames :use :error :output :verbose :escape :output-file :version ' + + ':external :documentation :shadowing-import-from :modern :export ' + + ':case-sensitive :case-inverted :shadow :import-from :intern :fill-pointer ' + + ':upcase :downcase :preserve :invert :load-toplevel :compile-toplevel :execute ' + + ':while :until :for :do :if :then :else :when :unless :in :across :finally ' + + ':collect :nconc :maximize :minimize :sum :and :with :initially :append :into ' + + ':count :end :repeat :always :never :thereis :from :to :upto :downto :below ' + + ':above :by :on :being :each :the :hash-key :hash-keys :hash-value :hash-values ' + + ':using :of-type :upfrom :downfrom :arguments :return-type :library :full ' + + ':malloc-free :none :alloca :in :out :in-out :stdc-stdcall :stdc :c :language ' + + ':built-in :typedef :external :fini :init-once :init-always'; + + var lambda = '&allow-other-keys &aux &body &environment &key &optional &rest &whole'; + + var special = 'let let\\* lambda'; + + /** + * Given a list of items in a string: 'item1 item2 item2 ...' + * + * return a regex *string*: '(item1|item2|item2|...)' + */ + var list_to_regex = function(list) + { + var items = list.replace(/(^ | $)/gm, '').split(/ /g); + return '('+items.join('|')+')'; + }; + + var is_in_list = function(item, list) + { + var items = list.replace(/(^ | $)/gm, '').split(/ /g); + for(var i = 0, n = items.length; i < n; i++) + { + if(items[i] == item) return true; + } + return false; + }; + + /** + * Collections of search and replaces to make. + */ + var replace = [ + // --------------------------------------------------------------------- + // strings (should !!ALWAYS!! be first, lest our tags be destroyed...) + // --------------------------------------------------------------------- + {regex: /"([\s\S]*?)"/gm, replace: '"$1"'}, + + // --------------------------------------------------------------------- + // comments + // --------------------------------------------------------------------- + {regex: /(;.*)(\n|$)/gm, replace: '$1$2'}, + + // --------------------------------------------------------------------- + // "special" (let/lambda) + // --------------------------------------------------------------------- + { + regex: new RegExp('.'+list_to_regex(special)+'(?=[\\s()])', 'g'), + replace: function(fullmatch, fnname) { + if(fullmatch[0] == '(') + { + return '(' + fnname + ''; + } + else + { + return fullmatch; + } + } + }, + + + // --------------------------------------------------------------------- + // function matches + // --------------------------------------------------------------------- + // known functions + { + regex: new RegExp('.'+list_to_regex(funcs)+'(?=[\\s()])', 'g'), + replace: function(fullmatch, fnname) { + if(fullmatch[0] == '(') + { + return '(' + fnname + ''; + } + else + { + return fullmatch; + } + } + }, + // symbol functions (#'my-fn) + { + regex: /([\s()])(#'(\w[\w_-]*))(?=[\s()])/g, + replace: function(fullmatch, delim1, symfun, sym) + { + var known = false; + if(is_in_list(sym, funcs)) + { + known = true; + } + return delim1 +''+ symfun +''; + } + }, + + // --------------------------------------------------------------------- + // lambda keywords + // --------------------------------------------------------------------- + {regex: new RegExp('([\\s()])'+list_to_regex(lambda)+'(?=[\\s()])', 'g'), replace: '$1$2'}, + + // --------------------------------------------------------------------- + // symbols/keywords/variables + // --------------------------------------------------------------------- + // generic symbols + {regex: /([\s()])('\w[\w_-]*)(?=[\s()])/g, replace: '$1$2'}, + // known keywords + { + regex: new RegExp('([\\s()])'+list_to_regex(keywords)+'([\\s()])', 'g'), + replace: function(fullmatch, whitespace, keyword, whitespace2) { + return whitespace + ''+ keyword +''+ whitespace2; + } + }, + // generic keywords + { + regex: /([\s()])(:\w[\w_-]*)/g, + replace: function(fullmatch, delim, keyword) { + if(fullmatch[0].match(/[\s()]/gm)) + { + return delim + ''+ keyword +''; + } + return fullmatch; + } + }, + // known variables + { + regex: new RegExp('([\\s()])'+list_to_regex(standard_vars)+'([\\s()])', 'g'), + replace: function(fullmatch, whitespace, varname, whitespace2) { + return whitespace + ''+ varname +''+ whitespace2; + } + }, + // globals/constants + {regex: /([\s()])(\*\w[\w_-]*\*)(?=[\s()])/g, replace: '$1$2'}, + {regex: /([\s()])(\+\w[\w_-]*\+)(?=[\s()])/g, replace: '$1$2'}, + + // --------------------------------------------------------------------- + // numbers + // --------------------------------------------------------------------- + // binary + {regex: /([\s()])(#b[01]+)(?=[\s()])/ig, replace: '$1$2'}, + // hex + {regex: /([\s()])(#x[\da-f]+)(?=[\s()])/ig, replace: '$1$2'}, + // float + {regex: /([\s()])([+-]?(?:\d+\.\d+|\d+\.|\.\d+))(?=[\s()])/g, replace: '$1$2'}, + // ratio + {regex: /([\s()])([+-]?\d+(?:\/\d+)?)(?=[\s()])/g, replace: '$1$2'}, + // integers + {regex: /([\s()])([+-]?\d+)(?=[\s()])/g, replace: '$1$2'}, + + // --------------------------------------------------------------------- + // misc parsers + // --------------------------------------------------------------------- + // t/nil + {regex: /([\s()])(nil|t)(?=[\s()])/g, replace: '$1$2'}, + + // generic "maybe a function" forms. best second to last + {regex: /\((\w[\w_:-]*)(?=[\s()])/g, replace: '($1'}, + + // ()'s (should most probably be last, unless there's a good reason) + {regex: /([()])/g, replace: '$1'} + ]; + + /** + * Main highlight function. + */ + this.highlight_element = function(code_el) + { + code_el.className += ' hl-highlighted'; + var html = code_el.innerHTML; + // can't have &...;'s running wild like a pack of animals... + html = html.replace(/&/g, '&'); + html = html.replace(/</g, '<'); + html = html.replace(/>/g, '>'); + // pad the HTML string (makes regexs much simpler) + html = "\n" + html + "\n"; + for(var i = 0, n = replace.length; i < n; i++) + { + var rep = replace[i]; + html = html.replace(rep.regex, rep.replace); + } + // unpad HTML string + html = html.replace(/(^\n|\n$)/g, ''); + html = html.replace(/<(?!(\/?span|\/?i|\/?b))/g, '<'); + // Re-encode stray &s to conform with XHTML + //html = html.replace(/&/g, '&'); + + code_el.innerHTML = html; + }, + + /** + * Automatically highlight all blocks + * + * Takes an options arg, which can be used to specify the classname of the + * tags you wish to highlight. + */ + this.highlight_auto = function(options) + { + options || (options = {}); + var classname = options.className ? options.className : 'lisp'; + var codes = document.getElementsByTagName('code'); + for(var i = 0, n = codes.length; i < n; i++) + { + var code = codes[i]; + if(code && code.className.match(classname)) + { + this.highlight_element(code); + } + } + }, + + /** + * If called, enables paren matching (hovering over a paren will add the + * "active" class to both the highlighted and the matching paren) + */ + this.paren_match = function(options) + { + options || (options = {}); + + if(!('querySelector' in document)) + { + console.error('HighlightLisp.paren_match: browser does not support querySelector/matches'); + return; + } + + var matches = function(element, selector) + { + if(!element) return; + var domatch; + var tests = ['matches', 'msMatchesSelector', 'mozMatchesSelector', 'webkitMatchesSelector']; + for(var i = 0; i < tests.length; i++) + { + if(!(tests[i] in element)) continue; + domatch = element[tests[i]]; + break; + } + + return domatch.call(element, selector); + }; + + var is_paren = function(el) + { + return matches(el, 'code > .list, code span:not(.comment):not(.string) .list'); + }; + + var find_match = function(paren) + { + // grab all non-commented/stringed parens + var children = paren.parentElement.querySelectorAll('code > span.list, code span:not(.comment):not(.string) .list'); + // turn them into a real array + children = Array.prototype.slice.call(children); + + var is_opening = function(el) { return el.innerHTML == '('; }; + + // tracks when to start counting parens + var count = false; + // tests if this is an opening or closing paren + var opening = is_opening(paren); + // if this is a closing paren, reverse the children so we can search + // backwards just by going forwards + if(!opening) children.reverse(); + + for(var i = 0; i < children.length; i++) + { + var child = children[i]; + var open = is_opening(child); + // mark the first occurance of the paren, and start counting + // from there + if(child === paren) + { + count = 1; + continue; + } + if(count === false) continue; + if(opening == open) count++; + else count--; + if(count === 0) return child; + } + }; + + var toggle_class = function(element, classname) + { + if (element.classList) + { element.classList.toggle(classname); } + else + { + //for IE9 + var classes = element.className.split(" "); + var i = classes.indexOf(classname); + if (i >= 0) + { classes.splice(i, 1); } + else + { + classes.push(classname); + element.className = classes.join(" "); + } + } + } + + var codes = document.getElementsByClassName('hl-highlighted'); + for(var i = 0; i < codes.length; i++) + { + var code = codes[i]; + var listener = function(add, e) + { + var hovered = e.target; + if(!is_paren(hovered)) return; + var match = find_match(hovered); + toggle_class(hovered, 'active'); + toggle_class(match, 'active'); + }; + code.addEventListener('mouseover', listener.bind(this, true)); + code.addEventListener('mouseout', listener.bind(this, false)); + } + } +}; + +var HighlightLisp = new highlight_lisp(); diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..ddc8af7 --- /dev/null +++ b/js/main.js @@ -0,0 +1,120 @@ +// Create Table of Contents +let chapters = []; + +// get chapter for navigation +function loadChapters(callback) { + let sending = browser.runtime.sendMessage({ + msg: "getChapters" + }); + return sending; +} + +// Inject the Table of Contents +function createTOC() { + let TOC = document.getElementById('toc') || document.createElement('nav'), + headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6'); + + // fix headings + for(let heading of headings) { + heading.id = heading.parentNode.getAttribute('name'); + } + + TOC.id = 'toc'; + TOC.classList.add('toc', 'toc-right'); + TOC.innerHTML = ''; + + document.body.insertBefore(TOC, document.body.childNodes[0]); + + tocbot.init({ + // Where to render the table of contents. + tocSelector: '#toc', + // Where to grab the headings to build the table of contents. + contentSelector: 'body', + // Which headings to grab inside of the contentSelector element. + headingSelector: 'h2, h3', + // smooth it out + scrollSmooth: true + }); +} + +function wrapInner(parent, wrapper, attribute, attributevalue) { + if (typeof wrapper === "string") { + wrapper = document.createElement(wrapper); + } + var div = parent.appendChild(wrapper) + .setAttribute(attribute, attributevalue); + + while (parent.firstChild !== wrapper) { + wrapper.appendChild(parent.firstChild); + } +} + + + +// Wrap the insides of the
 tags in  tags for highlight.js
+function wrapPre() {
+    for(let el of document.querySelectorAll('pre')) {
+	let orig = el.innerHTML;
+
+	el.innerHTML = '' + orig + "";
+    }
+}
+
+// get the index of the currently viewed chapter
+function findCurrentChapter() {
+    let file = document.URL.split('/').pop().replace(/\#.*$/, '');
+    let currentChapter = -1;
+    for(let chapter in chapters) {
+	if(chapters[chapter].url === file)
+	    currentChapter = chapter;
+    }
+    return parseInt(currentChapter);
+}
+
+// go to previous chapter
+function goToPrevious() {
+    if(!chapters) return;
+    let currentChapter = findCurrentChapter();
+    if(currentChapter > 0) {
+    	document.location.href =  chapters[currentChapter-1].url;
+    }
+}
+
+// go to next chapter
+function gotoNext() {
+    if(!chapters) return;
+    let currentChapter = findCurrentChapter();
+    if(currentChapter < chapters.length - 1) {
+    	document.location.href =  chapters[currentChapter+1].url;
+    }
+}
+
+
+// set up the navigation shortcuts
+function setUpNav() {
+    document.addEventListener('keypress', (event) => {
+	switch(event.keyCode) {
+	case 37:
+	    goToPrevious();
+	    break;
+	case 39:
+	    gotoNext();
+	    break;
+	case 36:
+	    document.location.href = 'index.html';
+	    break;
+	}
+    });
+}
+
+
+loadChapters().then(newChapters => {
+    chapters = newChapters;
+});
+
+// Let's apply that stuff.
+wrapPre();
+HighlightLisp.highlight_auto();
+HighlightLisp.paren_match();
+createTOC();
+setUpNav();
diff --git a/js/tocbot.js b/js/tocbot.js
new file mode 100644
index 0000000..943d8fd
--- /dev/null
+++ b/js/tocbot.js
@@ -0,0 +1 @@
+!function(e){function t(o){if(n[o])return n[o].exports;var l=n[o]={i:o,l:!1,exports:{}};return e[o].call(l.exports,l,l.exports,t),l.l=!0,l.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){(function(o){var l,i,s;!function(n,o){i=[],l=o(n),void 0!==(s="function"==typeof l?l.apply(t,i):l)&&(e.exports=s)}(void 0!==o?o:this.window||this.global,function(e){"use strict";function t(){for(var e={},t=0;te.fixedSidebarOffset?-1===n.className.indexOf(e.positionFixedClass)&&(n.className+=h+e.positionFixedClass):n.className=n.className.split(h+e.positionFixedClass).join("")}function s(t){var n=document.documentElement.scrollTop||f.scrollTop;e.positionFixedSelector&&i();var o,l=t;if(m&&null!==document.querySelector(e.tocSelector)&&l.length>0){d.call(l,function(t,i){if(t.offsetTop>n+e.headingsOffset+10){return o=l[0===i?i:i-1],!0}if(i===l.length-1)return o=l[l.length-1],!0});var s=document.querySelector(e.tocSelector).querySelectorAll("."+e.linkClass);u.call(s,function(t){t.className=t.className.split(h+e.activeLinkClass).join("")});var c=document.querySelector(e.tocSelector).querySelectorAll("."+e.listItemClass);u.call(c,function(t){t.className=t.className.split(h+e.activeListItemClass).join("")});var a=document.querySelector(e.tocSelector).querySelector("."+e.linkClass+".node-name--"+o.nodeName+'[href="#'+o.id+'"]');-1===a.className.indexOf(e.activeLinkClass)&&(a.className+=h+e.activeLinkClass);var p=a.parentNode;p&&-1===p.className.indexOf(e.activeListItemClass)&&(p.className+=h+e.activeListItemClass);var C=document.querySelector(e.tocSelector).querySelectorAll("."+e.listClass+"."+e.collapsibleClass);u.call(C,function(t){-1===t.className.indexOf(e.isCollapsedClass)&&(t.className+=h+e.isCollapsedClass)}),a.nextSibling&&-1!==a.nextSibling.className.indexOf(e.isCollapsedClass)&&(a.nextSibling.className=a.nextSibling.className.split(h+e.isCollapsedClass).join("")),r(a.parentNode.parentNode)}}function r(t){return-1!==t.className.indexOf(e.collapsibleClass)&&-1!==t.className.indexOf(e.isCollapsedClass)?(t.className=t.className.split(h+e.isCollapsedClass).join(""),r(t.parentNode.parentNode)):t}function c(t){var n=t.target||t.srcElement;"string"==typeof n.className&&-1!==n.className.indexOf(e.linkClass)&&(m=!1)}function a(){m=!0}var u=[].forEach,d=[].some,f=document.body,m=!0,h=" ";return{enableTocAnimation:a,disableTocAnimation:c,render:n,updateToc:s}}},function(e,t){e.exports=function(e){function t(e){return e[e.length-1]}function n(e){return+e.nodeName.split("H").join("")}function o(t){var o={id:t.id,children:[],nodeName:t.nodeName,headingLevel:n(t),textContent:t.textContent.trim()};return e.includeHtml&&(o.childNodes=t.childNodes),o}function l(l,i){for(var s=o(l),r=n(l),c=i,a=t(c),u=a?a.headingLevel:0,d=r-u;d>0;)a=t(c),a&&void 0!==a.children&&(c=a.children),d--;return r>=e.collapseDepth&&(s.isCollapsed=!0),c.push(s),c}function i(t,n){var o=n;e.ignoreSelector&&(o=n.split(",").map(function(t){return t.trim()+":not("+e.ignoreSelector+")"}));try{return document.querySelector(t).querySelectorAll(o)}catch(e){return console.warn("Element not found: "+t),null}}function s(e){return r.call(e,function(e,t){return l(o(t),e.nest),e},{nest:[]})}var r=[].reduce;return{nestHeadingsArray:s,selectHeadings:i}}},function(e,t){function n(e){function t(e){return"a"===e.tagName.toLowerCase()&&(e.hash.length>0||"#"===e.href.charAt(e.href.length-1))&&(n(e.href)===s||n(e.href)+"#"===s)}function n(e){return e.slice(0,e.lastIndexOf("#"))}function l(e){var t=document.getElementById(e.substring(1));t&&(/^(?:a|select|input|button|textarea)$/i.test(t.tagName)||(t.tabIndex=-1),t.focus())}!function(){document.documentElement.style}();var i=e.duration,s=location.hash?n(location.href):location.href;!function(){function n(n){!t(n.target)||n.target.className.indexOf("no-smooth-scroll")>-1||"#"===n.target.href.charAt(n.target.href.length-2)&&"!"===n.target.href.charAt(n.target.href.length-1)||-1===n.target.className.indexOf(e.linkClass)||o(n.target.hash,{duration:i,callback:function(){l(n.target.hash)}})}document.body.addEventListener("click",n,!1)}()}function o(e,t){function n(e){s=e-i,window.scrollTo(0,c.easing(s,r,u,d)),s