css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <!--  These scc links are normaly included in pen "settings"...
but to facilitate bl.ocks powered gist of this, include here...-->
<link rel="stylesheet" href="https://codemirror.net/lib/codemirror.css">
<link rel="stylesheet" href="https://codemirror.net/addon/lint/lint.css">
<link rel="stylesheet" href="https://codemirror.net/addon/hint/show-hint.css"> 

<link rel="stylesheet" href="https://codemirror.net/addon/dialog/dialog.css">
<link rel="stylesheet" href="https://codemirror.net/addon/search/matchesonscrollbar.css"> 

<div id="code_editors" class="split split-horizontal">
  <div id="html" class="split content">
    
    <a id="HTML_label" title="HTML pane template">HTML</a>
    <div id="gist_id_label" >
      <input readonly id="gist_id_value" data-no-id="  --- --- No Github Gist ID --- --- " title="Github Gist ID this example is backed with">
    </div>
    <textarea id="html_ed"></textarea>
  </div>
  
  
  <div id="css" class="split content">
    <a>CSS</a>
    <textarea id="css_ed"></textarea>
  </div>
  <div id="lisp" class="split content">
    <a id='Lisp_label' title="Lisp Pane Editing History">&nbsp;&nbsp;Lisp&nbsp;&nbsp;&nbsp;</a>
    <textarea id="lisp_ed"></textarea>
  </div>
  <div id="js" class="split content">
    <a id='tidy_js' title="Indent and Format the JavaScript Pane for Readability">TidyUp JS</a>
    <textarea id="js_ed"></textarea>
  </div>
</div>

<div id="lisp_history">
  <span>Lisp Editing history (last 5 edits remembered in Browser's LocalStorage)</span>
  <button id=lisp_history_close>close</button><br><br>
  <textarea id="hist0" rows=8 cols=80></textarea>
  <textarea id="hist1" rows=5 cols=80></textarea>
  <textarea id="hist2" cols=80></textarea>
  <textarea id="hist3" cols=80></textarea>
  <textarea id="hist4" cols=80></textarea>
  <textarea id="hist5" cols=80></textarea>
</div>

<div id="html_template">
  <span>Template for Output HTML page</span>
  <button id=html_template_ok>OK</button>
  <button id=html_template_cancel>Cancel</button><br><br>
  <textarea id="html_template_ta" cols=40 rows=10></textarea>
</div>

<div id="gist_save_notification">
  <span id="gist_save_msg">Saved!</span><br>
  <a target="_blank" id="gist_url"></a><br>
  <span>note: clicking the link is safe - it opens in a new tab.</span><br>
  <span>You can check it standalone (also opens in a new tab):</span><br>
  <a target="_blank" id="run_gist_from_blocks">Run the Gist</a><br><br>
  <span id="gist_personal_at_note">note: gist is anonymous. to create/update your own gist,<br> provide access token</span><br>
  <button id=gist_dialog_close>OK</button>
</div>


<div id="gist_request_token_dialog">
  <div id="gist_request_token_dialog_purpose">
    <input id="gist_request_token_dialog_patch" type="radio" checked="true" name="gist_patch_or_new">
    Save your code as a new revison of the exisitng gist
    <a target="_blank" id="gist_request_token_dialog_gist_id"></a>(link opens in a new tab)<br>note: you must be the owner or have valid access token<br>to be able to update it sucessfully<br>
    <input type="radio" name="gist_patch_or_new">Save the code as a new gist you'll own<br>
  </div>
  <span>Enter your gist-scoped access token or github uname:pass</span><br>
  <input id=gist_request_token_dialog_input>
  <button id=gist_request_token_dialog_submit>Submit</button>
  <button id=gist_request_token_dialog_cancel>Cancel</button>
</div>

<div id="output"  class="split split-horizontal">
    <!--<div class='lisp_arrows' id="nav_panel">-->
    <a id='nav_panel' title="Use Arrows to Iterate Demo Examples">
      <span id='prev_example' title="Previous Demo Example">&nbsp;&lt;&nbsp;&nbsp;</span>
      Examples
      <span id='new_example' title="New Page">&nbsp;+&nbsp;</span>
      <span id='next_example' title="Next Demo Example">&nbsp;&gt;&nbsp;&nbsp;</span>
    </a>
    <!--</div>-->
    <a id='save_panel'>
      <span id='save_result_html' title="Save Current Demo Example to Disk">SaveFile</span>&nbsp;
      <span id='save_result_gist' title="Save Current Demo Example to Github Gist">SaveGist</span>
    </a>
  
  
<a id="new_example_dialog">
  <span  title="Create New Blank Example/Snippet Page">
    <input id="new_example_dialog_blank" 
           type="radio" checked="true"
           name="new_example_dialog_options">
    Blank
  </span>
  <span  title="Open Exisitng GitHub Gist">
    <input id="new_example_dialog_from_gist" 
           type="radio" 
           name="new_example_dialog_options">
    GistId:
  </span>
  <input id=new_example_dialog_gist_id size=20 disabled><br>
  <button id=new_example_dialog_create>Create</button>
  <button id=new_example_dialog_cancel>Cancel</button>
</a>

  
  <iframe id="out_iframe"></iframe>
</div>

<div class="code_source">
<pre>
<!-- This provides variois macros that extend the basic set provided by Lisp.
Currently, load time is completely negligible, under 10ms. 
In a very unlikely case extension lisp code  grows so big it becomes a startup-time slowdown,
then some of lisp code can be  pre-ranslated. Thus, macros 'when' and 'unless' in JS are

  var defs = LispTrans.global_macros; // or use LispTrans.specials.defmacro;
  defs.when = function() { 
    var forms = arr2list(arguments);
    return new Cons("if", new Cons(forms.car, new Cons(new Cons("progn", forms.cdr))));
  };
  defs.unless = function() {
    arguments[0] = list("not", arguments[0]);
    return LispTrans.global_macros["when"].apply(null, arguments);
  }; -->
<textarea id='extensions'>
(defmacro when (test &rest body)
  `(if ,test (progn ,@body)))
         
(defmacro unless (test &rest body) 
  `(if ,test nil ,@body))
  
(defmacro cond (&rest body)
  (let* ((build (lambda (code) 
                  (if code
                      (let ((test (car (car code)))
                            (yes (cdr (car code)))
                            (no (build (cdr code))))
                        `(if ,test (progn ,@yes)
                           ,no))))))

    (build body)))

;; js-conscious variant of let*. Variable hoisting makes let body extended to the outer func. 
;; Compare: (let ((x 0)(y i)) (foo x y)) and (var x 0 y 1)(foo x y)
(defmacro var (&rest expr)
          `(let* ,(maplist (lambda x 
                              (let* ((pair (list (car x) (car (cdr x)))))
                                (if x (set x.cdr (cdr x.cdr)))
                                pair))
                            expr)))    


;; Object/Array comprehension for applicative use. syntax:
;;   (for name in expr ...  )  or (for (name ..) in expr .... )
;; examples:
;;   (for k in myob count++)   (for (k v) in {a b} (console.log k v))
(defmacro for (&rest body)
  (let* ((par (nth 0 body))
         (kwd (nth 1 body))
         (obj (nth 2 body)))
    (if (and par kwd obj (eq kwd "in")(or (symbolp par) (consp par)))
        (let* ((rest (or (cdr (cdr (cdr body))) (list nil) )))
          (if (symbolp par) ;; no obj ref or var obj ref in fbody
              `(. (Object.keys ,obj) (map (lambda (,par) ,@rest)))
            (let* ((objv  (if (symbolp obj) obj (gensym)))
                   (fbody (cons `(set ,(nth 1 par) (aref ,objv ,(nth 0 par))) rest))
                   (iter `(. (Object.keys ,objv) (map (lambda ,par ,@fbody)))))
              (if (symbolp obj) iter ;; no need to close obj over
                `((lambda (,objv) ,iter ) ,obj)))))
      this.count-- ;; fall-though branch, tell the expander nothing was expanded.
      `(for ,@body))))

;; Array comprehension for use inside [ and ]. Generic format:
;;  [expr for param-or-list in expr opt-predicate opt-expr]
;; Examples:
;;  [v+v for v in x]
;;  [v+i for (v i) in [7 6 5 4] unless v==5] also if,unless etc
;;  a predicate on value can be specifid without the expresion as in
;;  [v+v for v in x Number.isInteger]
;; nesting can be as deep as desired: [v+v for v in [v+v for v in [v+v for v in [7 6 5 4]]]]
(defmacro new$array (ve kw1 par kw2 obj pred test)
  (if (and ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par)))
      (let* ((subj (if (and pred test) ;; pred must take 2nd arg 'true' for if,when,unless
                       `(. ,obj (filter (lambda ,par (,pred ,test true))))
                     (if pred `(. ,obj (filter ,pred)) obj)))) 
        `(. ,subj (map (lambda ,par ,ve))))
    (set specials.new$array_ specials.new$array)
    `(new$array_ ,@(arr2list arguments))))

;; Object comprehension for use inside { and }.  Generic format:
;;  [expr for param-or-list in expr opt-predicate opt-expr]. 
;; Examples (predicates can be added as for the array variant, see above):
;;  {k (foo k) for k in x} 
;;  {k k+v for (k v) in {a 'x b 'yy c 'zz}}
(defmacro new$object (ke ve kw1 par kw2 obj pred test)
  (if (and ke ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par)))
      (let* ((r (gensym "__g_new")) (setter `(set (aref ,r ,ke) ,ve)))
        (if pred 
            (set setter (if test `(,pred ,test ,setter)
                          `(if ,pred ,setter))))
        (if (symbolp par) ;; no obj ref or var obj ref in fbody
            `(. (Object.keys ,obj) (reduce (lambda (,r ,par) ,setter  ,r) {}))
          (let* ((objv  (if (symbolp obj) obj (gensym "__g_old")))
                 (ctor `(. (Object.keys ,objv)
                           (reduce (lambda (,r ,@par) 
                                     (set ,(nth 1 par) (aref ,objv ,(nth 0 par)))
                                     ,setter
                                     ,r) {}))))
            (if (symbolp obj) ctor
              `((lambda (,objv) ,ctor) ,obj)))))
    this.count-- ;; fall-though branch, tell the expander nothing was expanded.
    `(new$object ,@(arr2list arguments))))
      

</textarea>
<!-- footnotes
1. the old, more ;classical version of comprehension macros uses &rest body"
(defmacro new$array (&rest body)
  (let* ((ve  (nth 0 body))
         (kw1 (nth 1 body))
         (par (nth 2 body))
         (kw2 (nth 3 body))
         (obj (nth 4 body)))
    (if (and ve kw1 par kw2 obj (eq kw1 "for") (eq kw2 "in") (or (symbolp par) (consp par)))
    ...
      `(new$array ,@body))))
but it is much mored convenient and efficient to use explicit parameters and JS arguments array instead as in the textarea above.

 -->
</pre></div> <!-- end of lisp extensions section -->

<div class="code_source" id='examples'>
<pre>
<textarea id=example1__html><p id=p1>Hello World</p>
<script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js></script></textarea>
<textarea id='example1__css'>
body { color: blue; }
</textarea>

<textarea id='example1'>;;;; Generate new random color and use it
(defun new_color() 
  (+ "#"
   (. (Math.random) (toString 16) (slice -6))))
;;runner  
(defun tick() 
  (set ;; examples of styles of DOM access 
   (. document (getElementById 'p1) style color) (new_color)
   (. p1 (aref style 'background-color)) (new_color)
   document.body.style.background (new_color)
   p1.innerHTML (new Date))
  (setTimeout tick 1000))
(tick)

;; The code in each pane is editatble. CodeMirror is the underlying engine. 
;; JS code is checked with Jhint. TidyUp formats it with Beautifier. 
;; The Lisp pane provides lisp-oriented formatting and editing commands.
;; In addition to standard Ctrl-a Ctrl-x Ctrl-v, useful Keyboard commands are:

;; Ctrl-f              Search
;; Ctrl-F Ctrl-R       Replace (note: uppercase For R)
;; Ctrl-Space          Hints/completion list
;; Shift-Tab           Indent line or selected region

;; RECOVER: "Lisp" button displays history of last 5 edits 
;; saved in Local Storage


</textarea>
</pre>

<pre>
<textarea id=example3>;;;; Fun with d3 and lambdas part 1
(. d3 (select 'body)
   (selectAll "div")
   (data [{text "aaaa"}
          {text "bbbb"}
          ])
   (enter)
   (append 'div)
   (text \d.d.text)
   (attr 'id \di.i)
   (style 
    {'background-color 
     #ffffff 
     'color #000000
     })    
   (transition)
   (duration 10000)
   (style {'background-color #000000
           'color #ffffff}))
</textarea>
</pre>



<pre>
<textarea id='example4__css'>
  .axis path {
    fill: none;
    stroke: #777;
    shape-rendering: crispEdges;
  }
  .axis text {
    font-family: Lato;
    font-size: 13px;
  }
</textarea>
<textarea id=example4>;;;; D3 chart with Lisp 
(var
 price  \.(+ 150 (* 60 (Math.random)))
 years  (d3.range 2000 2012)
 data   [{sale (price) year y} for y in years]
 data2  [{sale (price) year y} for y in years]
 WIDTH  1000 HEIGHT 500 MARGINS {top 20 right 20 bottom 20 left 50 }
 vis    (.(d3.select'body)(append'svg)(attr {width WIDTH height HEIGHT}))
 xScale (. d3.scale (linear)(range [ MARGINS.left WIDTH-MARGINS.right])(domain [ 2000 2010]))
 yScale (. d3.scale (linear)(range [ HEIGHT-MARGINS.top MARGINS.bottom])(domain [ 134 215]))
 xAxis  (. d3.svg (axis)(scale xScale))
 yAxis  (. d3.svg (axis )(scale yScale)(orient "left"))
 mkLine (. d3.svg (line)
           (x \d.(xScale d.year))
           (y \d.(yScale d.sale))
           (interpolate "basis")))
(. vis (append "svg:g")
     (attr "class"  "x axis")
     ;;(attr "transform"  (+ "translate(0," (String HEIGHT-MARGINS.bottom) ")")) ;; use String()
     (attr "transform" "translate(0,{(HEIGHT-MARGINS.bottom)})") ;; use interpolation.
     (call xAxis))
  
  (. vis (append "svg:g")
     (attr "class"  "y axis")
     (attr "transform" "translate({MARGINS.left},0)") ;; use interpolation.
     (call yAxis))
  (. vis (append "svg:path")
     (attr 'd  (mkLine data))
     (attr 'stroke  'green)
     (attr 'stroke-width  2)
     (attr 'fill  'none))
  (. vis (append "svg:path")
     (attr 'd  (mkLine data2))
     (attr 'stroke  'blue)
     (attr 'stroke-width  2)
     (attr 'fill  'none)))</textarea>
</pre>
  
<pre>
<textarea id=example5>;;;; Sortable html table generation with d3 & lisp
(var ;; the table rows typically loaded from data file using d3.csv
 movies 
 [{ title "The Godfather" year 1972 length 175 budget 6000000 rating 9.1 }
  { title "The Shawshank Redemption" year 1994 length 142 budget 25000000 rating 9.1 }
  { title "The Godfather Part II" year 1974 length 200 budget 13000000 rating 8.9 }
  { title "Buono il brutto il cattivo Il" year 1966 length 180 budget 1200000 rating 8.8 }
  { title "Casablanca" year 1942 length 102 budget 950000 rating 8.8 }
  { title "Pulp Fiction" year 1994 length 168 budget 8000000 rating 8.8 }
  ]
 columns 
 [{ head 'Movie   d 1  f \d.d.title  html \d.d.title}
  { head 'Year'   d 1  f \d.d.year   html \d.d.year}
  { head 'Length  d 1  f \d.d.length html \d.(let* ((p (new Date))) (p.setMinutes d.length)((d3.time.format "%_Hh %Mm") p))  }
  { head 'Budget  d 1  f \d.d.budget html \d.((d3.format "$,") d.budget) }
  { head 'Rating  d 1  f \d.d.rating html \d.((d3.format ".1f") d.rating) }
  ]
 table (. d3 (select 'body) (append 'table)
          (style 'border "1px solid black"))
 rdata \r.(columns.map \c.(c.html r)) ;; helper function
 mk_table_body 
 \.(. table (select 'tbody)
      (selectAll 'tr)
      (data movies)(enter)
      (append 'tr)
      (style { 'background-color \di.(if i&1 "#e0e0e0" "#f0f0f0")})
      (selectAll 'td)
      (data rdata)
      (enter )
      (append 'td)
      (html \d.d)))
(. table (append 'tbody))
(. table (append 'thead) (append 'tr)
   (selectAll 'th)(data columns)(enter)
   (append 'th)
   (style {color 'yellow 'background-color 'black})
   (text  \d.d.head)
   (on 'click \d.(progn 
                  (movies.sort \ab.(if (> (d.f a) (d.f b)) d.d
                                       (if (< (d.f a) (d.f b)) -d.d 0)))
                  (set d.d -d.d)
                  (. d3 (select 'tbody) (selectAll 'tr)(remove))
                  (mk_table_body))))

(mk_table_body)
(set p1.innerHTML "Hello Movies")</textarea>
</pre>

  <pre>
<textarea id=example5aa>;;;; Fun with lisp: how to define a macro that includes lisp code from anothere example:

(defmacro load_1 (id) 
  (let* ((elt (document.getElementById id.text))
         (text (if elt elt.value "(alert '(bad lib id))")))
   (macroexpand (read text))))
   
;; load_1 reads the content of html element with given id, and adds it 
;; to the code stream  being translated. Because load_1 uses the 
;; built-in function 'read', only the 1st top lecvel s-expr from id's text is included:
(load_1 "example3")  ;; this translates & runs the previous example here in place

;; Macro 'load_all' that loops over all forms with (read_from_string text curr) 
;; can be trivially defined in similat way but for convenicence
;; this playground provides even more flexible  built-in macro 'include' that 
;; takes any number of CSS selectors so that multiple groups of mutiple nodes containing
;; lisp code can be included with  just one line of code.

;; Example of use:
 (include #example1 #example4 #example5) 
   
</textarea>
</pre>
  

<pre>
<textarea id=example6>;;;; Fun with lambdas part2: Y Combinator
(define Y
  (lambda (f)
    ((lambda (x) (x x))
     (lambda (x) (f (lambda (y) ((x x) y)))))))

(define almost_factorial
  (lambda (f)
    (lambda (n)
      (if (= n 0)
          1
          (* n (f (- n 1)))))))

(define factorial (Y almost_factorial))
(define result (factorial 5))
(set p1.innerHTML result)
</textarea>
</pre>

<pre>
<textarea id=example7>;;;; Fun with lambdas part3: shorthand Y
(define Y \f.(\x.(x x) \x.(f \y.((x x) y))))
(define almost_factorial
  \f.\n.(if (= n 0) 1
            (* n (f (- n 1)))))

(define factorial (Y almost_factorial))
(define result (factorial 5))
(set p1.innerHTML result)
</textarea>
</pre>

<pre>
<textarea id=example8>;;;; Fun with lambdas part4 : λ looks good
(define almost_factorial
  λf.λn.(if (= n 0) 1
            (* n (f (- n 1)))))
;; one version of Y Combinator
(define Y1 λf.(λx.(x x) λx.(f λy.((x x) y))))
;; a bit longer version of Y
(define Y2 λf.(λx.(f λy.((x x) y)) λx.(f λy.((x x) y))))
;; the famous version of Y
(define Y λf.(λx.(f (x x)) λx.(f (x x))))
  
(+= p1.innerHTML ((Y1 almost_factorial) 5))
(+= p1.innerHTML ((Y2 almost_factorial) 5))
(+= p1.innerHTML ((Y  almost_factorial) 5))
;; explain the result (yes, the last Y is not for strict languages)

</textarea>
</pre>


<pre>
<textarea id=example2>[ ;;;; Fun with lambdas. Code compactness.
 
 (lambda (x) x)   ;; i'm lambda 
 \x.x             ;; i'm the same lambda
 λx.x             ;; i'm the same lambda too
 λxy.y 
 λxyz.x+y+z 
 λx.λy.x+y
 
 ] ;; note on []: this array container prevents syntax errors.

;; These lambdas translate to JS functions and are admittedly 
;; succinct and beat classic JS anonymous functions in brevity. 
;; JS arrow functions are great space savers, but lambdas are 
;; often even more succinct:
;;
;; ======================================================
;;          |  case1  case2  case3       case4
;; ---------|--------------------------------------------
;; Lisp λ   |  λx.x   λ.x    λxy(x y)    λxyz(x y z)
;; JS Arrow |  x=>x   ()=>x  (x,y)=>x(y) (x,y,z)=>x(y,z)
;; ------------------------------------------------------

;; 
;; Aside from that, long chanined expressions with some anonymous functions and some data literals as args, which is typical, 
;; are often more compact in lisp

;; d3 selections (white space deliberately ommited, where possible, in each case)
;;   js: ... data([{n:"foo",v:1},{n:"bar",v:2}],function(d){return d.v;}).enter() ...
;; lisp: ... (data[{n'foo v 1}{n'bar v 2}]\d.d.v)(enter) ...

;; another comarison from real example
;; body.append('div').attr('id','gui_dock').append(function(){return gui.domElement;});
;; (.(body.append'div)(attr'id'gui_dock)(append \.gui.domElement))

;; Arithmetic expression on the first glance wase a lot of space in lisp.
;; This is the case, normally, but but with some practice, 
; lots of expressions can be coded densely as JS-convertable terms:
;;   js: while(i-->0){d+=x/2+y/3;x.y.z=p/q-r;}
;; lisp  (while i-->0 d+=x/2+y/3 x.y.z=p/q-r)

;; the above works because i-->0 d+=x/2+y/3 x.y.z=p/q-r are terms.

</textarea>
</pre>

<pre>
<textarea id=example9>;;;; Simple Web Playground created with D3 and Lisp

(define panels 
  [{lang "HTML" tag 'html   text "<h1 id=e>Hello</h1>" }
   {lang "CSS"  tag 'style  text "body { color: blue;}"}
   {lang "JS"   tag 'script text "function f(){e.innerHTML+='!';setTimeout(f,3000)}f()"}])

(set p1.innerHTML "Little Web Playground Powered by Lisp")

(. d3 (select 'body) (selectAll 'panels)
   (data panels)(enter)(append 'textarea)
   (text \d.d.text)
   (on 'keyup \d.(let* ((doc output.contentDocument)(v ""))
                    (. d3 (selectAll 'textarea)
                       (each \d.(+= v (+ "<"d.tag">"this.value"</"d.tag">"))))
                    (doc.open)(doc.write v)(doc.close))))
(. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400}))
((. d3 (select 'textarea) (on 'keyup)))
</textarea>
</pre>

<pre>
<textarea id=example10>;;;; Same Web Playground, with its html window showing the
;; famous code for minimalist web playground (~ 120 bytes of html+js code or so)
(define panels 
  [{lang "HTML" tag 'html   text (. "{body oninput='e.firstChild.srcdoc=t2[v=\"value\"]+\"{script }\"+t0[v]+\"{/script }{style }\"+t1[v]'onload='for(i=3;i--;)e.innerHTML+=\"{textarea id=t\"+i+\" rows=9}\"'id=e}{iframe }" (replace (RegExp "{" 'g ) "<") (replace (RegExp "}" 'g ) ">")) }
   {lang "CSS"  tag 'style  text ""}
   {lang "JS"   tag 'script text ""}])

(set p1.innerHTML "Little Web Playground Powered by Lisp, Running MiniCodeEditor")
(. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400}))

(. d3 (select 'body) (selectAll 'panels)
   (data panels)(enter)(append 'textarea)
   (text \d.d.text)
   (on 'keyup \d.(let* ((doc output.contentDocument)(v ""))
                    (. d3 (selectAll 'textarea)
                       (each \d.(+= v (+ '< d.tag '> this.value '< '/ d.tag '>))))
                    (doc.open)(doc.write v)(doc.close))))
((. d3 (select 'textarea) (on 'keyup)))</textarea>
</pre>

<pre>
<textarea id=example11>;;;; Same, with v1.2 and using atob btoa to avoid encoding hassles
(define panels 
  [{lang "HTML" tag 'html   text (atob  "PGJvZHkgb25pbnB1dD0nZi5zcmNkb2M9dDBbdj0idmFsdWUiXSsiPHNjcmlwdD4iK3QyW3ZdKyI8L3NjcmlwdD48c3R5bGU+Iit0MVt2XSdvbmxvYWQ9J2ZvcihpPTM7aS0tOylmLm91dGVySFRNTCs9Ijx0ZXh0YXJlYSBpZD10IitpKyIgcm93cz05PiInPjxpZnJhbWUgaWQ9Zj4=") }
   {lang "CSS"  tag 'style  text ""}
   {lang "JS"   tag 'script text ""}])

(set p1.innerHTML "Little Web Playground Powered by Lisp, Running MiniCodeEditor v.1.2")
(. d3 (select 'body)(append 'iframe) (attr {id 'output width '100% height 400}))

(. d3 (select 'body) (selectAll 'panels)
   (data panels)(enter)(append 'textarea)
   (text \d.d.text)
   (on 'keyup \d.(let* ((doc output.contentDocument)(v ""))
                    (. d3 (selectAll 'textarea)
                       (each \d.(+= v (+ '&lt; d.tag '&gt; this.value '&lt; '/ d.tag '&gt;))))
                    (doc.open)(doc.write v)(doc.close))))
(. d3 (select 'textarea) (on 'keyup))
</textarea>
</pre>

<pre>
<textarea id=example12>;;;; Wall Clock in D3 and Lisp. Derived from https://bl.ocks.org/tomgp/6475678
(let*
  ((radius 180)
   (radians 2*Math.PI/360)
   (defScale \n.(. d3.scale (linear)
                   (range  [ 0 360-360/n])
                   (domain [ 0 n-1 ])))
   (hours (  defScale 12))
   (minutes (defScale 60))
   (seconds (defScale 60))
   (decimal_hour \d.(let* ((h (d.getHours))(m (d.getMinutes)))
                      h%12+m/70)) ;; 70 so that at 12 we see some animation
                      
   (hands
    [{scale hours   len {a 1/1.5 b 0}   width 1/16 f decimal_hour     }  
     {scale minutes len {a 1 b 0.1}     width 1/25 f \d(d.getMinutes) }
     {scale seconds len {a 0.94 b 0.15} width 1/60 f \d(d.getSeconds) }])

   (moveHands 
    \.(. (d3.select #clock-hands)(selectAll 'line)(data hands)
         (transition)
         (attr 'transform \d.(+ "rotate(" (d.scale d.val) ")"))))

   (updateData
    \.(let ((time (new Date)))
        (. hands (forEach \d.(set d.val (d.f time))))))
   
   ;; next, create all the clock elements
   (init_them (updateData)) ;; draw them in the correct starting position
   (svg (. (d3.select "body")(append "svg")
           (style {'stroke #000})
           (attr {width radius*2.8 height radius*2.6})))
   (face (. svg (append 'g)
            (attr 
             {id 'clock-face transform (. "translate(_,_)" 
                                          (replace /_/g radius*1.3 ))})))
     ;; generator of tick marks and label
     (gen_ticks 
      (lambda (v)
        (. (face.selectAll '.ticks)
           (data (d3.range v.from v.to))(enter)
           (append 'line)
           (style {'stroke #225 'stroke-width (/ radius v.kstr)})
           (attr {x1 0 x2 0 y1 radius y2 radius*v.ky2 transform \d.(+ "rotate(" (v.scale d) ")")}))
        (. (face.selectAll '.labels)
           (data (d3.range v.step (+ v.to 1) v.step))(enter)
           (append 'text)
           (style {'stroke #225 'font-size (+ radius/v.kfont 'px)})
           (attr 
            {'text-anchor 'middle x \d.(radius*v.kt*Math.sin (* radians (v.scale d)))
                          y \d.(- radius/v.kfont/3
                                  (radius*v.kt*Math.cos (* radians (v.scale d))))})
           (text \d.d)))))
  ;; start painting by adding the face plate
  (. (face.append 'g)(append 'circle)(style {'stroke-width radius/30 fill #e0e0c0})
     (attr {x 0 y 0 r 1.18*radius}))
  ;; next, add the ticks
  (gen_ticks {scale hours from 0 to 12 step 3 kstr 25 kfont 4 ky2 0.92 kt 0.8})
  (if (> radius 80)
      (gen_ticks {scale seconds from 0 to 60 step 5 kstr 60 kfont 10 ky2 0.96 kt 1.09})) ;; minutes/seconds
  ;; add hands
  (. (face.append 'g) (attr 'id 'clock-hands)
     (selectAll 'line)
     (data hands)
     (enter)
     (append 'line)
     (style {'stroke-width \d.d.width*radius 'stroke-linecap 'round 'stroke #000})
     (attr { x1 0 y1 \d.d.len.b*radius x2 0 y2 \d.d.len.a*-radius})
     (attr 'transform \d.(+ "rotate(" (d.scale d.val) ")")))
  ;; add cosmetic center cap
  (. (face.append 'g)(append 'circle)(style {'stroke-width radius/50 fill #ddb})
     (attr {x 0 y 0 r radius/20}))
  (setInterval \.(moveHands (updateData)) 1000)
  (. (d3.select 'body)(style 'background "url(http://deskpicture.com/DPs/Miscellaneous/StoneWall.jpg)")))
</textarea>
</pre>

<pre>
<textarea id=example13>;;;; working with aliases, playing some tricks with .chains when possible
;; showing useful prefix wrappers to avoiding dot expressions
;; note: more efficient wrapping can be done with macros, if there is a need
(let*
  ((random0to10 (d3.random.normal 5 5)) ;; dotted chains can be used applicatively!
   (df4p2 (d3.format "4.2f")) ;; which is a bit shorter than (. ....) equivalent
   (cos Math.cos)         ;; local aliases such as math functions can speed code up;
   (s String.prototype)   ;; prototype aliases allow to use methods in prefic position
   (op Object.prototype)  ;; which oten requires .call notation as whown below
   (charAt s.charAt)
   (ch λsi.(s.charAt.call s i)) ;; .call can be wrapped if performance permits
   (has_prop λop.(op.hasOwnProperty.call o p))
   (concat λ.(s.concat.apply "" arguments)) ;; for multiargs, .apply is better
   (examples
    [(s.charAt.call 'qwerty 1) ;; invoke charAt on "qwerty"
     (charAt.call 'qwerty 1) ;; same but shorter
     (ch 'qwerty 1)          ;; even shorter
     (s.concat.call 'abcd 'ef 'gh)
     (concat 'abcd 'ef 'gh)
     (op.hasOwnProperty.call {} 'toString) 
     (has_prop {} 'toString) ;; same as above but faster
     (cos 1)
     (+ "aaa" (+ 1  2) "bbb" 1+2/4 "ccc") ;; + used for string concatenation affects numeric +
     (+ "aaa" (String 1+2) "bbb") ;; to cope, use suitable number->string wrapper, among which
     (+ "translate(" (df4p2 3+5/9) "," (df4p2 2+3) ")") ;; d3 formatters are ideal!
     ]))
  ;;(console.log examples)
  (.(d3.select 'body)(selectAll 'sel)(data examples)(enter)
    (append 'p)
    (text String)))
</textarea>
</pre>

<pre>
<textarea id=example13a>;; Fun with macros part 3.  Classic Eliza profgram  is simple when translated to JS. Macros help.
;; Explanation: the basic code logic and the rules are taken from  the PAIP book; 
;; macros helped to convert all formats best fitting JS.
;; The original code is very cons-heavy at runtime; here, with the help from a couple of macros, 
;; the original cons-based representation is converted to JS arrays and objects. No cons processing happens at runtime and the output JS code has no dependency on Lisp functionality. 
 
(set p1.innerHTML  "ELIZA, AI DOCTOR")        
 
(defun eliza ()
  (let* ((input (read_line_no_punct))
         (response (flatten (use_eliza_rules input))))
    (print_with_spaces response)))

(defun read_line_no_punct ()
  (str_subst [[" i " " I "]["'" "_"]["\\W+" " "]] " {input.value} "))

(defun flatten (x) (aref (aref x 0) 0))

(defun use_eliza_rules (input)
  (. eliza_rules 
     (map \(rule)
          (let ((result  (input.match rule.pattern)))
            (if result
                (substitute 
                 {re "var1|var2|var3" 
                  vals {"var{(+i+1)}" v for (i v) in (switch_viewpoint result)}}
                 (random_elt rule.responses)))))
     (filter \v.v)))

(defun print_with_spaces (text) 
  (set output.value (+ output.value "\n> " 
                       (pprint_input) "\n"
                       (pprint_output text))
       output.scrollTop output.scrollHeight
       input.value "> "))


(defun random_elt (arr) 
  (var range arr.length-1 random_idx (Math.round (* range (Math.random)))) 
  [(aref arr random_idx)])

;; this helps to convert 'switch_viewpoint'
(defmacro sublis (spec arg)
  (defun mkkey(n v)(string (+ " "(nth n v) " ")))
  (if (and (consp spec) (eq "quote" spec.car)) 
      (let* ((re)(rep))
        (mapcar \v(set re (cons (nth 0 v) re) rep (cons (mkkey 2 v) (cons (mkkey 0 v) rep))) (nth 1 spec) )
        (set re (string (+ " " (.(list2arr (reverse re)) (join " | ")) " " ) )
             spec (list "new$object" "re" re "vals" (cons "new$object" (reverse rep))))))
  `(substitute ,spec ,arg ))

(defun switch_viewpoint (words)
  (words.shift 1) ;; array of matches
  (sublis '((I . you) (you . I) (me . you) (am . are)
            (my . your)(mine . your)(I_ve . you_ve)(you_ve . I_ve)(I_m . you_re)) ;; I added  few more here
          (words.map \s(+ " " s " "))))


;; What remains is to define rules. Take the original pattern-action list, 
;; Usa a macro to convert that to array of convenient JS objects, see 'var eliza_rules = ' in the JS panel.
(defmacro defparam) ;; clear it first just in case
(defmacro defparam (nm val) 
  (if (and (consp val) (eq "quote" val.car))
      (let* ((vars {"?x" "var1" "?y" "var2" "?z" "var3"})
             (f \r(list "new$object"  "pattern" 
                        (list "RegExp" (string (.(list2arr (mapcar \p(if (consp p) "(.*)" p)
                                                                   r.car))(join " ") )))

                        "responses"
                        (cons "new$array" (mapcar \r(string (. (list2arr (mapcar \w(or (aref vars w) w) 
                                                                                 r) ) 
                                                               (join " "))) 
                                                  r.cdr)) ))
             (rule_arr (cons "new$array" (mapcar f val.cdr.car)) ))
        (set val rule_arr)))
  `(defvar ,nm ,val))


(defparam eliza_rules
 '((((?* ?x) hello (?* ?y))      
    (How do you do.  Please state your problem.))
   (((?* ?x) computer (?* ?y))
    (Do computers worry you?) (What do you think about machines?)
    (Why do you mention computers?)
    (What do you think machines have to do with your problem?))
   (((?* ?x) name (?* ?y))
    (I am not interested in names))
   (((?* ?x) sorry (?* ?y))
    (Please do not apologize) (Apologies are not necessary)
    (What feelings do you have when you apologize))
   (((?* ?x) I remember (?* ?y)) 
    (Do you often think of ?y)
    (Does thinking of ?y bring anything else to mind?)
    (What else do you remember) (Why do you recall ?y right now?)
    (What in the present situation reminds you of ?y)
    (What is the connection between me and ?y))
   (((?* ?x) do you remember (?* ?y))
    (Did you think I would forget ?y ?)
    (Why do you think I should recall ?y now)
    (What about ?y) (You mentioned ?y))
   (((?* ?x) if (?* ?y)) 
    (Do you really think its likely that ?y) (Do you wish that ?y)
    (What do you think about ?y) (Really-- if ?y))

   (((?* ?x) I dreamt (?* ?y))
    (Really-- ?y) (Have you ever fantasized ?y while you were awake?)
    (Have you dreamt ?y before?))
   (((?* ?x) dream about (?* ?y))
    (How do you feel about ?y in reality?))
   (((?* ?x) dream (?* ?y))    
    (What does this dream suggest to you?) (Do you dream often?)
    (What persons appear in your dreams?)
    (Do not you believe that dream has to do with your problem?))
   (((?* ?x) my mother (?* ?y))
    (Who else in your family ?y) (Tell me more about your family))
   (((?* ?x) my father (?* ?y))
    (Your father) (Does he influence you strongly?) 
    (What else comes to mind when you think of your father?))

   (((?* ?x) I want (?* ?y))     
    (What would it mean if you got ?y)
    (Why do you want ?y) (Suppose you got ?y soon))
   (((?* ?x) I am glad (?* ?y))
    (How have I helped you to be ?y) (What makes you happy just now)
    (Can you explain why you are suddenly ?y))
   (((?* ?x) I am sad (?* ?y))
    (I am sorry to hear you are depressed)
    (I am sure its not pleasant to be sad))
   (((?* ?x) are like (?* ?y))   
    (What resemblance do you see between ?x and ?y))
   (((?* ?x) is like (?* ?y))    
    (In what way is it that ?x is like ?y)
    (What resemblance do you see?)
    (Could there really be some connection?) (How?))
   (((?* ?x) alike (?* ?y))      
    (In what way?) (What similarities are there?))
   (((?* ?x) same (?* ?y))       
    (What other connections do you see?))

   (((?* ?x) I was (?* ?y))       
    (Were you really?) (Perhaps I already knew you were ?y)
    (Why do you tell me you were ?y now?))
   (((?* ?x) was I (?* ?y))
    (What if you were ?y ?) (Do you thin you were ?y)
    (What would it mean if you were ?y))
   (((?* ?x) I am (?* ?y))       
    (In what way are you ?y) (Do you want to be ?y ?))
   (((?* ?x) am I (?* ?y))
    (Do you believe you are ?y) (Would you want to be ?y)
    (You wish I would tell you you are ?y)
    (What would it mean if you were ?y))
   (((?* ?x) am (?* ?y))
    (Why do you say *AM?*) (I do not understand that))
   (((?* ?x) are you (?* ?y))
    (Why are you interested in whether I am ?y or not?)
    (Would you prefer if I were not ?y)
    (Perhaps I am ?y in your fantasies))
   (((?* ?x) you are (?* ?y))   
    (What makes you think I am ?y ?))

   (((?* ?x) because (?* ?y))
    (Is that the real reason?) (What other reasons might there be?)
    (Does that reason seem to explain anything else?))
   (((?* ?x) were you (?* ?y))
    (Perhaps I was ?y) (What do you think?) (What if I had been ?y))
   (((?* ?x) I ca not (?* ?y))    
    (Maybe you could ?y now) (What if you could ?y ?))
   (((?* ?x) I feel (?* ?y))     
    (Do you often feel ?y ?))
   (((?* ?x) I felt (?* ?y))     
    (What other feelings do you have?))
   (((?* ?x) I (?* ?y) you (?* ?z))   
    (Perhaps in your fantasy we ?y each other))
   (((?* ?x) why do not you (?* ?y))
    (Should you ?y yourself?)
    (Do you believe I do not ?y) (Perhaps I will ?y in good time))
   (((?* ?x) yes (?* ?y))
    (You seem quite positive) (You are sure) (I understand))
   (((?* ?x) no (?* ?y))
    (Why not?) (You are being a bit negative)
    (Are you saying *NO* just to be negative?))

   (((?* ?x) someone (?* ?y))
    (Can you be more specific?))
   (((?* ?x) everyone (?* ?y))
    (surely not everyone) (Can you think of anyone in particular?)
    (Who for example?) (You are thinking of a special person))
   (((?* ?x) always (?* ?y))
    (Can you think of a specific example) (When?)
    (What incident are you thinking of?) (Really-- always))
   (((?* ?x) what (?* ?y))
    (Why do you ask?) (Does that question interest you?)
    (What is it you really want to know?) (What do you think?) 
    (What comes to your mind when you ask that?))
   (((?* ?x) perhaps (?* ?y))    
    (You do not seem quite certain))
   (((?* ?x) are (?* ?y))
    (Did you think they might not be ?y)
    (Possibly they are ?y))
   (((?* ?x))               
    (Very interesting) (I am not sure I understand you fully)
    (What does that suggest to you?) (Please continue) (Go on) 
    (Do you feel strongly about discussing such things?))))


;; a few aux functions go here
(defun substitute (spec args) ;; arr->arr
  (args.map \a(a.replace (RegExp spec.re "g") \m(aref spec.vals m))
            args))
(defun str_subst (spec str) ;; str->str
  (spec.forEach \v(set str (str.replace (RegExp (aref v 0) "g") (aref v 1))))
  str)
(defun pprint_input  () (str_subst [["^\\s*>\\s*" ""]["\\s+" " "]]  input.value ))
(defun pprint_output (text) (str_subst [["\\s+" " "]["_" "'"]] text))

;; finally, setup a primitvie dialog env: two text areas.   
(var 
  sel_elt \(sel) (. (aref (aref sel 0) 0)) 
  div (.(d3.select document.body)(append 'div)) 
  output (sel_elt (. div (append 'textarea) (attr {id 'output_a cols 54 rows 23  readonly 1})) )
  input   (sel_elt  (. div (append 'textarea) (attr {id 'input_a cols 54 rows 2 ;; autofocus -1
                                                     }) (text "> I want an apple")
                       (on "keyup" \d(if d3.event.keyCode==13 (eliza) ) )))) 
  
;; ...and a bit of silly animation 
(. div (transition) (duration 3000) (style 'background "radial-gradient(#662850, #202444, #162030)"))
(. (d3.select p1) (transition) (duration 3000)(ease 'bounce) (style {color "red" "letter-spacing" "20px" } )</textarea>

<textarea id=example13a__css>body{
  background:#33a;
} 
#p1 {
  border: 4px dashed #7cb4fc; 
  border-radius:10px;
  position:absolute;
  top: -20px; left: 20px; width: 800px; height: 46px;

  color:#7cb4fc;
  text-align:center;

  font:900 2em Arial, Helvetica, Geneva, sans-serif;
  text-transform:uppercase;
  letter-spacing: 1px;
  -webkit-filter:blur(2px);
  filter:blur(2px);
  text-shadow:#99d3fd 0 4px 5px;
}

div::before {
  content: " ";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
  z-index: 2;
  background-size: 100% 4px, 1px 100%;
  pointer-events: none;
}

div  {
  -webkit-border-radius:50px;
  -moz-border-radius:50px;
  border-radius:50px;
  border-left:20px solid #1b1b1b;
  border-right:20px solid #1c1c1c;
  border-top:25px solid #0f1310;
  border-bottom:25px solid #0f1310;
  position:absolute;
  
  overflow:hidden;
  top: 100px; left: 20px; width: 700px; height: 500px;
  padding:1.5em 1.5em 1.5em 2em;
  -webkit-box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6),  0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97);
  -moz-box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6),  0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97);
  box-shadow:inset 0 0 0 9px rgba(21,21,20,0.6),  0 1px 60px 10px rgba(40,46,40,9), inset 0 1px 80px 5px rgba(0,0,0,0.97);
   
  background-color: #202444;  /*#162030;*/
  background: radial-gradient(#262860, #202444, #162030);
  
}
textarea {
  background: transparent;
  border: none;
  outline: none;
  font:600 24px VT323, monospace;
  line-height: 20px;
  color:#7cb4fc;
  
  letter-spacing:-1px;
  
  -webkit-filter:blur(0.8px);
  filter:blur(0.8px);
  text-shadow:#363860 5px -4px 40px;

  white-space: pre-wrap;       /* css-3 */
  white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
  white-space: -pre-wrap;      /* Opera 4-6 */
  white-space: -o-pre-wrap;    /* Opera 7 */
  word-wrap: break-word; 
  overflow:hidden;
  resize:none;
}</textarea>
</pre>

<pre>
<textarea id=example14>;;;; Lisp's prefix notation and underscore.js are a great synergy
;; TBD...

;; Lisp's Array and Object comprehensions & use of _.range:
(var x (_.range 7 3 -1) br "<br>" out \s.p1.innerHTML+=br+s)

(set p1.innerHTML "")
(. [[v+v for v in x]
    [v+v for v in (_.range 7 3 -1)]
    [v+v for v in [7 6 5 4]]
    [v+i for (v i) in x]
    [v+v for v in x when v%2]] 
   (map out))


(set x { a 'x b 'y c 'z} out \o.p1.innerHTML+=br+o.a+o.b+o.c)
(. [{k k+k for k in x}
    {k k+v for (k v) in x}
    {k k+k for k in { a 'x b 'y c 'z}}
    {k k+v for (k v) in { a 'x b 'y c 'z}}]
   (map out))


</textarea>
<textarea id=example14__html><p id=p1>Hello World</p>
&lt;script src='https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js'&gt;&lt;/script&gt;
&lt;script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'&gt;&lt;/script&gt;
</textarea>
</pre>

<pre>
<textarea id=example14a>;;;; Haskel style array ranges with translation time expansion
;; when the parameters are constants.
;; the idea here is to further elaborate the new$array macro (see the 'extensions' code section)
;; with several cases recognizing the following constructs:
;; [1..10] [1 3..22] [x..y] [x y..z] and the same with spaces around ..
;; and expand either as arrays or array generation calls. 

(defmacro new$array_(v1 v2 v3 v4) (set specials.new$array__ specials.new$array)
  (defun nddn(v) (and (eq (typeof v) "string")(v.match (RegExp "^([+-]?\\d+[.]?\\d*)\\.\\.([+-]?\\d+[.]?\\d*)$"))))
  (defun edde(v) (and (eq (typeof v) "string")(v.match (RegExp "(.*(?=\\.\\.))\\.\\.(.*)")))) (defun arr(v1 v2 v3) (cons "new$array__" (arr2list (range v1 v2 v3 1))))
  (var alen arguments.length frange "(function(f,s,n){f=+f||0,n=+n||1,null===s&&(s=f,f=0);for(var _=-1,o=Math.max(0,Math.ceil((1+s-f)/(n||1))),a=Array(o);o>++_;)a[_]=f,f+=n;return a;})")
  ;; case1: (number..number) 
  (if (and alen==1 (set v4 (nddn v1)))
      (arr (aref v4 1)(aref v4 2) 1)
    ;; case2: (number number..number)
    (if (and alen==2 (!isNaN v1) (set v4 (nddn v2)))
        (arr +v1 (aref v4 2) (- (aref v4 1) v1))
      ;; case3 (number .. number)
      (if (and alen==3 (!isNaN v1) (eq v2 "..") (!isNaN v3))
          (arr +v1 +v3 1)
        ;; case4 (number number .. number)
        (if (and alen==4 (!isNaN v1) (!isNaN v2) (eq v3 "..") (!isNaN v4))
            (arr +v1 +v4 v2-v1)
          ;; case5: (expr..expr) 
          (if (and alen==1 (set v4 (edde v1)))
              `(,frange ,(aref v4 1) ,(aref v4 2))
            ;; case6: (expr expr..expr) 
            (if (and alen==2 v1 (set v4 (edde v2)))
                `(,frange ,v1 ,(aref v4 2) (- ,(aref v4 1) ,v1))
              ;; case7 (expr .. expr)
              (if (and alen==3 v1 (eq v2 "..") v3)
                  `(,frange  ,v1 ,v3 1)
                ;; case8 (expr expr .. expr)
                (if (and alen==4 v1 v2 (eq v3 "..") v4)
                    `(,frange  ,v1 ,v4 (- ,v2 ,v1))
                  `(new$array__ ,@(arr2list arguments)))))))))))
                  
;; now, tests:
(when 
 test.innerHTML
 (var br "<br>" out \s.test.innerHTML+=br+s x 1 y 3)
 (out [1..10])
 (out [1 3..10])
 (out [x*1..10+3]) 
 (out [x*1 y+5..10+y])
 (out [1 .. 10])
 (out [1 3 .. 10])
 (out [x*1 .. 10+y]) 
 (out [x*1 y+5 .. 10+y])
 )

;; in a similar fashion, macro range can be defined and be occasionally useful, although the above comprehension does make it redunand.
;; it is mainly here to show case that the generated handler function (named frange above) can be created as a lisp s-expr and 
;; inserted in the return s-form with ,f. In such case the pair of parentheses is generated by the translator. The macro range also 
;; uses traditional lisp &rest instead of using explicitly named parameters and JS arguments array.

(defmacro range) ;; clear it just in case
(defmacro range (&rest e)
  (if (and (eq (list_length e) 3) (!isNaN (nth 0 e))
           (eq (nth 1 e) "..")
           (!isNaN (nth 2 e)))
      (cons "new$array" (arr2list (range (parseFloat (nth 0 e)) (+ 1 (parseFloat(nth 2 e))))))
    (if (and (eq (list_length e) 4) (!isNaN (nth 0 e)) (!isNaN (nth 1 e))
           (eq (nth 2 e) "..")
           (!isNaN (nth 3 e)))
        (cons "new$array" (arr2list (range (parseFloat (nth 0 e)) (+ 1 (parseFloat(nth 3 e)))(- (parseFloat(nth 1 e)) (parseFloat(nth 0 e))))))
      (if (or (and (eq (list_length e)3)(eq (nth 1 e) "..")(set e (list (nth 0 e)(nth 2 e))))
                  (and (eq (list_length e)4)(eq (nth 2 e) "..")(set e (list (nth 0 e)(nth 3 e)(nth 1 e)))))
          (let* ((f '(lambda (start limit second step)
  (if second==null (set second start+1))
  (set step second-start)
  (let* ((length (Math.max (Math.ceil limit/step) 0))
         (a (Array length)) (idx 0))
    (while length>idx
      (set (aref a idx) start idx idx+1 start start+step))
    a))))
   `(,f ,@e))
      this.count-- ;; fall-though branch, tell the expander nothing was expanded.
      `(range ,@e)))))

(when 
 test.innerHTML
 (out (range 0 3 .. 17)) ;; inline JS array expanded at translate time
 (out (range -3  .. 3))  ; same
 (var xx 10)
 (out (range 1 3 .. xx)) ;; func application
 )
 </textarea>
<textarea id=example14a__html><p id=test>Range comprehensions</p> </textarea>
</pre>

<!-- with-GUI,with_GUI: 2 ways to generate a GUI invoker
for a function -->
<pre data-gist-id="dac9718f1a018a75551f27fbf3646d4d"></pre>


<pre>
<textarea id=example16>;;;; Metal Clock Train test 1. inspired by gears from https://bl.ocks.org/mbostock/1353700
;; expanded with involute tooth shape modeled with cubic beziers, simple cutouts added.
(set gear \(g)
     (let* ((r g.radius)(n g.teeth)
            (h g.h||16)(off g.off||h/8)(flat g.flat||h/16)
            (r0 g.r0||g.radius+off-h/2) (rc1 r+h/16)(rc2 r+h/4) 
            (r1 g.radius+off+h/2-flat)
            (rax g.rax||8) (r0r g.r0r||r0) (r1r g.r1r||r1)(r0dir g.r0dir||1)
            (da 2*Math.PI/n) ;; polar/radial width of a complete tooth
            (dar0 da*0.5)(dar1 da/6)(daq da-dar0-dar1)
            (a -Math.PI/2)(path ["M" 0 "," -r0 ])(i -1))
       (while n>++i
              (path.push
               "A" r0r "," r0r " 0 0," r0dir " " (* r0 (Math.cos a+=dar0)) "," (* r0 (Math.sin a))
               "L" (* r (Math.cos a)) "," (* r (Math.sin a))
               "C" ;; two involute Cs elaborate Mike Bostock’ L-A-L tooth
               (* rc1 (Math.cos a)) "," (* rc1 (Math.sin a))  " "
               (* rc2 (Math.cos a)) "," (* rc2 (Math.sin a))  " "
               (* r1 (Math.cos a+=daq/2)) "," (* r1 (Math.sin a))
               "A" r1r "," r1r " 0 0,1 " (* r1 (Math.cos a+=dar1)) "," (* r1 (Math.sin a))
               "C" (* rc2 (Math.cos a+=daq/2)) "," (* rc2 (Math.sin a)) " "
               (* rc1 (Math.cos a)) "," (* rc1 (Math.sin a)) " "
               (* r (Math.cos a)) "," (* r (Math.sin a))
               "L" (* r0 (Math.cos a)) "," (* r0 (Math.sin a))))
       (path.push "M0," -rax "A" rax "," rax " 0 0,0 0," rax "A" rax "," rax " 0 0,0 0," -rax "Z")
       (+ (path.join "")
          (if g.cuts (\cr ;; gen cutouts
                      (let* ((k1 c.k1||0.2)(k2 c.k2||0.15)(k3 c.k3||0.08)
                             (n c.n||4)(r1 r*c.r1||20)(r2 r*c.r2||90)
                             (da 2*Math.PI/n) ;; 
                             (da1 da*k1) (da2 da-da1)(da3 da*k3) (da4 da*k3)
                             (a -Math.PI/2+da1/2)(path []))
                        (while n--
                               (path.push
                                "M" (* r1 (Math.cos a)) "," (* r1 (Math.sin a)) 
                                "A" r1 "," r1 " 0 0,1 " (* r1 (Math.cos a+=da2)) "," (* r1 (Math.sin a))
                                "L" (* r2 (Math.cos a+=da3)) "," (* r2 (Math.sin a))
                                "A" r2 "," r2 " 0 0,0 " (* r2 (Math.cos a-=da2+2*da3)) "," (* r2 (Math.sin a))
                                "L" (* r1 (Math.cos a+=da3)) "," (* r1 (Math.sin a))
                                "Z"
                                )
                               (set a a+da)
                               )
                        (path.join "")
                        ) g.cuts g.radius||100) "")
          ))

     gear_train \(frame spec generator) 
     (let* ((y 0) (idx 0) (k 1) prev (fill #6baed6) axle (axles []) g_offset g_rot)
       (if !spec (set spec [])) ;; default, empty  train, possible placeholder etc
       (if (isNaN (.(aref spec 0) y)) (spec.unshift {k 1 y 0 nm "driver" g []})) ;; insert default 1st axle
       (set generator generator||gear)
       (.(spec.forEach 
          (lambda (g i a)
            (if g.fill (set fill g.fill))
            (if (not (isNaN g.y)) ;; g is axle. g.y is link direction indicator, 1, -1 or 0
                (set axle g
                     axle.a (if (isNaN axle.a) 0 axle.a) 
                     g (aref a i+1) ;; grab next one. the next spec entry after axle MUST be a gear 
                     y (if prev (+ y (* axle.y (+ prev.radius g.radius))) axle.y)
                     axle.k (if (not (isNaN axle.k)) axle.k ;; use specified number (0 is still) or lookup or compute
                                (if (and (eq (typeof axle.k) 'string) (.(axles.filter \v.v.nm==axle.k)(slice -1)(pop)))
                                    (.(axles.filter \v.v.nm==axle.k)(slice -1)(pop) k) ;; use the k of k-named axle
                                    (if (eq (typeof axle.k) 'function) (axle.k axles k y g) ;; if k is a func, call it
                                        -k*prev.teeth/g.teeth))) ;; compute k using theeth numbers 
                     k axle.k ;; remember k 
                     axle.y y
                     g_offset (.(frame.append'g)(attr'transform "translate(0,{y})"))
                     axle.g []
                     g_rot (.(g_offset.append'g)(attr'class 'g_rot)(datum axle))
                     axle (if axle.init (axle.init) axle) ;; if defined, init must return axle (old or new)
                     idx (axles.push axle))
                ;; gear or props 
                (if !g.radius nil
                    ;; use same gear gs, same k
                    (set prev g)
                    (.(g_rot.datum) g (push g))
                    (.(g_rot.append'path)(datum g)
                      (attr {d generator fill fill
                               transform "rotate({axle.a})" }))))
            nil)))
       axles))

(let*
  ((x (Math.sin 2*Math.PI/3))
   (y (Math.cos 2*Math.PI/3))
   (width 560)
   (height 500)
   (radius 300)
   (offset 0)
   (speed 3/80)
   (start (Date.now))   
   (svg (.(d3.select "body")(append "svg") 
          (attr {width width height height}) 
          (append "g")
          (attr "transform" 
                (+ "translate(" width/3  "," height/4  ")scale(0.4)")) 
          (append "g")))
   (frame (. svg (append "g")
             (datum {radius Infinity})))
   (t1 80)(r1 radius)(k1 1)
   (t12 8)(r12 r1*t12/t1)
   (t2 60)(r2 r1*60/80)(k2 -t2/t12)(off2 (+ 4 r2 r12))
   (t21 10)(r21 r2*t21/t2)
   (t3 60)(r3 r2)(off3 (+ 2 off2 r3 r21 )) (k3 -k2*t3/t21)
   )

   (console.log (gear_train
   frame 
   [{teeth t1 radius r1 k k1 cuts {n 6 r1 0.2 r2 0.94} }
    {fill #6b8ea6}
    {teeth t12 radius r12 k k1 }
    {y 1.02 nm "second" fill #9ecae1}
    {teeth t2 radius r2 k k2 off 3 cuts {n 4 r1 0.3 r2 0.9 k3 0.07} }
    {fill #6baed6}
    {teeth t21 radius r21 k k2}
    {y 1.01 fill #c6dbef}
    {teeth 60 radius radius*60/80 k k3 cuts {n 4 r1 0.2 r2 0.84 k1 0.3 k3 0.12}}
    ;;{y 0 k "driver"  nm "hour hand axle" a 45 init \.this }
    ;;{nm "hour" teeth 3 h 200 off -30 radius 50  flat -200 } ;;  iw 0.427

    ]))
  
  ;; debug (. (frame.selectAll ".g_rot")(each \d( \.(console.log d))

  ;;( \.(console.log 'dd))  
  (d3.timer \.(let* 
                ((angle (*(- (Date.now) start) speed))
                 (transform \d.(+ "rotate(" d.k*angle")")))
                ;;(console.log angle)
                (. (frame.selectAll ".g_rot")(attr "transform" transform))
                ;;(. frame (attr "transform" transform))
                nil
                )))</textarea>
<textarea id='example16__css'>
path {
  fill-rule: evenodd;
  stroke: #333;
  stroke-width: 2px;
}
.g1 path {
  fill: #6baed6;
}
.g2 path {
  fill: #9ecae1;
}
.g3 path {
  fill: #c6dbef;
}
</textarea>
</pre>

<pre>
<textarea id=example17>;;;; Wooden Clock Gears, test 1. inspired by gears from https://bl.ocks.org/mbostock/1353700 and various wooden clock designs
(defmacro defun_gear()(read example16.value))
(defun_gear)

(let*
  ((x (Math.sin 2*Math.PI/3))
   (y (Math.cos 2*Math.PI/3))
   (width 560)
   (height 500)
   (radius 300)
   (offset 0)
   (speed 3)
   (start (Date.now))
 
   (svg (.(d3.select "body")(append "svg") 
          (attr {width width height height})))
   (svg_g (. svg
             (append "g")
             (attr "transform" 
                   (+ "translate(" width/3  "," height/4 
                      ")scale(5)")) 
             (append "g")))

   (defs (. svg (append 'defs)))
   (deftex  \(name url)
            (.(defs.append "pattern")
              (attr {x 0 y 0 width 1 height 1 id name 
                       patternContentUnits "objectBoundingBox"
                       patternUnits 'objectBoundingBox
                       patternTransform "scale(1,1)"})
              (append "image")
              (attr {width 1 height 1 "xlink:href"  url
                           preserveAspectRatio "none" ;; allows to use non-square images.
                           })))
   ;; old woods - seem gone from the web now 
   ;;  (woods (. ["wood-textures-high-quality-1" "wood+pattern+texture+16" "wood+pattern+texture+6" "wood+pattern+texture+17" "wood+pattern+texture+27" "wood+pattern+texture+28" "wood+pattern+texture+21"]
   ;;          (forEach \ni.(deftex (+ "w" i) (+ "http://gdj.gdj.netdna-cdn.com/wp-content/uploads/2013/03/" 
   ;;                                            n ".jpg")))))
   (woods (. ["7e/df/7edfd8262b0df8403e5abacac62d680d.jpg" "7e/df/7edfd8262b0df8403e5abacac62d680d.jpg" 
              "fe/9a/fe9afcbcdb81ccbc54c244be04f08a05.jpg" "12/fe/12fe01a696450d50e4546a5f8648ee43.jpg"
              "b0/ba/b0baa9a4bdbcf8d6bff48fa4ff25e821.jpg" "c1/b7/c1b701ae5e3fedc1d8a4d7d3ba5f97fa.jpg"
              "7c/18/7c185ba40dc9b1c6322b47146d4be837.jpg" ]
             (forEach \ni.(deftex (+ "w" i) (+ "https://media.freestocktextures.com/cache/"  n)))))                                        

   (frame (. svg_g (append "g")
             (datum {radius Infinity}))) 
   (t1 80)(r1 radius)(k1 1)
   (t12 8)(r12 r1*t12/t1)
   (t2 60)(r2 r1*60/80)(k2 -t2/t12)(off2 (+ 4 r2 r12))
   (t21 10)(r21 r2*t21/t2)
   (t3 60)(r3 r2)(off3 (+ 2 off2 r3 r21 )) (k3 -k2*t3/t21)
   )
  (. frame (append "g")
     (append "rect")
     (attr {x -0.4*radius width 0.8*radius y -1.2*radius height 4*radius fill "url(#w5)"}))
 
   (gear_train 
   frame
   [{fill "url(#w1)"}
    {teeth t1 radius r1 k k1  cuts {n 6 r1 0.2 r2 0.9 k1 0.56 k3 0.25}}
    {fill "url(#w5)"}
    {teeth t12 radius r12 k k1 r0 r12-3 r0r 4 r1r 4 r0dir "0"} 
    {y 1.01 fill "url(#w2)"}
    {teeth t2 radius r2 k k2 off 3 r0 r2-2 r0r 4 r1r 4 r0dir "0"  cuts {n 4 r1 0.3 r2 0.82 k1 0.5 k3 0.2} }
    {fill "url(#w2)"}
    {teeth t21 radius r21 k k2 r0 r21-2 r0r 4 r1r 4 r0dir "0" }
    {y 1.01 fill "url(#w3)"}
    {teeth 60 radius r3 k k3 r0 r3-2 r0r 4 r1r 4 r0dir "0" cuts {n 5 r1 0.2 r2 0.8 k1 0.73 k3 0.3}}
    ])
  
  ;;( \.(console.log 'dd))
  (d3.timer \.(let* 
                ((angle (*(- (Date.now) start) speed))
                 (transform \d.(+ "rotate(" d.k*angle/radius ")")))
                ;;(console.log angle)
                (. (frame.selectAll ".g_rot")(attr "transform" transform))
                ;;(. frame (attr "transform" transform))
                nil
                ))
  (if 1.01 (. svg_g
            (transition)(duration 15000)(ease (d3.ease 'poly 0.2)) 
            (attr "transform" 
                  (+ "scale(0.08)"))  
            ))
  )</textarea>
<textarea id='example17__css'>
path {
  fill-rule: evenodd;
  stroke: #333;
  stroke-width: 2px;
}
</textarea>
</pre>

<pre>
<textarea id=example15>;;;; Cog Gear Designer. Tooth nomenclature:
;; n   number of theeth    pr    pitch radius           
;; rr  root radius         rrf   root radius fill radius     rw   root fill width
;; or  outside radius      orf   outside radius fill radius
;; the same terms apply to cutouts; a few more added to control beziers and skew

(defmacro com2a(command) ;; define X as 2rg path command generator
          `\ar.(+ ,command (* r (cos a)) "," (* r (sin a))))
(defmacro com2ad(c flag) ;; path or a blank string depending on flag
          `\ar.(if ,flag (+ ,c (* r (cos a)) "," (* r (sin a))) empty))
(defmacro numval(val dflt) `(if (isNaN ,val) ,dflt ,val))

(let*
  ((cos Math.cos)(sin Math.sin)(empty "")(keys Object.keys)
   (object_p \v.(eq (typeof v) "object"))
   (reduce \(obj beg end fkey fobj fnum)
           (+ (. (Object.keys obj)
                 (reduce \pk.(set p (+ p (fkey k)
                                       (let ((v (aref obj k)))
                                         (if (eq (typeof v) "object")
                                             (fobj v beg end fkey fobj fnum)
                                             (fnum v))) " "))
                         beg))
              end))
   ;; draw svg path elements, can take optional stepper param 
   (M (com2a 'M))(L (com2a 'L))(C (com2a "C"))(_ (com2a " "))(LD (com2ad 'L mk_gear.DEBUG))
   (A \(a dist rf dir)(+ "A" rf "," rf dir  " " (* dist (cos a))  "," (* dist (sin a))))
   (mk_cuts 
    \cd.(let* 
          ((PR d.radius+d.off-d.h/2||100)(n (numval c.n 4))
           (rw (numval c.rw 0.2))(ow (numval c.ow rw))(skw n*c.skw||0)(iw c.iw||0)
           (s1skw n*c.s1skw||0)(s2skw n*c.s2skw||0)
           (rr (numval c.rr 0.2))(rrf (numval c.rrf rr))
           (bot_dir (if rrf>0 " 0 0,1 " 	" 0 0,0 "))
           (or c.or||0.9)(orf (numval c.orf or))
           (s1r (numval c.s1r rr/2+or/2))(s2r (numval c.s2r s1r))
           (inits ;;  because of dat.GUI, must put all dervied props back to d. "_" is indicator of derived section
              (set c._ c._+1||0 c.n n c.rw rw c.ow ow c.skw skw/n c.iw iw c.s1skw s1skw/n c.s2skw s2skw/n c.rr rr c.rrf rrf 
                   c.or or c.orf orf c.s1r s1r c.s2r s2r rr PR*rr rrf PR*rrf or PR*or orf PR*orf s1r PR*s1r s2r PR*s2r ))
           (top_dir (if orf>0 " 0 0,0 " 	" 0 0,1 "))
           (da 2*Math.PI/n) ;; radial size of each cutout (see gear's diametral pitch)
           (da1 da*rw)(daorw2 (* da/2 (- ow rw)))(dask da*skw) (dap1 da*s1skw)(dap2 da*s2skw)
           (a -Math.PI/2-da1/2-da*iw) 
           (path [])
           )
          ;; svg is OK with ngative (rrf and orf.... set rrf (Math.abs rrf))
          (while n-->0
                 (path.push
                  (M a rr a+=da1)
                  (A a rr rrf bot_dir) 
                  (LD a+dap1 s1r)(LD a rr)
                  (C a+=dap1 s1r  a+=daorw2+dask-dap1)(_ a+dap2 s2r)(_ a or )
                  (LD a+dap2 s2r)(LD a or)
                  (A a-=da1+2*daorw2 or orf top_dir ) ;; (L a rr a+=da) 
                  (LD a+dap2 s2r)(LD a or)
                  (C a+dap2 s2r a+=daorw2-dask)(_ a+dap1 s1r)(_ a rr a+=da)
                  (LD a+dap1-da s1r)(LD a-da rr)
                  "Z"))
          (path.join "")
          )))
  (set mk_gear ;; theeth and h are recommended to always supply
       \d.(let*
            ((n (numval d.teeth 30))
             (h (numval d.h 16))
             (off (numval d.off h/8))(flat (numval  d.flat h/16))
             (PR (numval d.radius 200))
             (rax (numval d.axr h/2)) 
             (rrw (numval d.rrw 0.5))(pw (numval d.pw rrw))(ow (numval d.ow 1/6))(skw n*d.skw||0)(iw d.iw||0)
             (inits ;;  because of dat.GUI, must put all dervied props back to d. "_" is indicator of derived section
              (set d._ d._+1||0 d.teeth n d.h h d.off off d.flat flat d.radius PR
                   d.axr rax d.rrw rrw d.pw pw d.ow ow d.skw skw/n d.iw iw)) 
             (rr PR+off-h/2)(or PR+off+h/2-flat) (ha or-PR) (rc1 PR+ha/8)(rc2 PR+ha/2)
             (da 2*Math.PI/n) ;; also known as diametral pitch
             (darr da*rrw) (dask da*skw)
             (daor da*ow)(daq da-darr-daor)
             (darpw da-darr-da*pw)
             (a -Math.PI/2+da*iw) ;; was: -PI/2+darr/2 which loosk good but is unfriendly for gear trains...
             (path [(M a rr)])(i -1))
            (while n>++i
                   (path.push
                    (A a+=darr rr rr " 0 0,1 " )
                    (L a+=darpw/2+dask PR)
                    (C a rc1)(_  a rc2 )(_ a+=daq/2-darpw/2 or)
                    (A a+=daor or or " 0 0,1 " )
                    (C a+=daq/2-darpw/2 rc2) (_ a rc1)(_ a PR) 
                    (L a+=darpw/2-dask rr )))
            (path.push "M0," -rax "A" rax "," rax " 0 0,0 0," rax "A" rax "," rax " 0 0,0 0," -rax "Z")
            (reduce d (path.join "") "" \."" \c(mk_cuts c d) \."")
            )
       mk_gear.DEBUG false
       gear ;; in this section gear used in clocks, hence most are named 
       [{nm"escapement1" teeth 30 h 38 iw -0.35 radius 100 k 1 axr 4 cuts {n 10 rr 0.5 or 0.9 orf 0.95 skw 0.025 rr 0.21  iw 0.3 ow 0.9 rw 0.8}  ow 0.05 pw 0.08 axr 8 skw 0.020 off 1 flat 17.5 }  
        {nm"escapement2" teeth 30 h 38 iw -0.35 radius 100 k 1 axr 8 cuts {n 15 rr 0.300 or 0.950 orf 0.950 skw 0.029 iw -0.270 ow 0.873 r2r 0.900 rw 0.719 rrf 0 s1skw 0.001 s2skw 0.015 s1r 0.567 s2r 0.616 } ow 0.050 pw 0.080  skw 0.020 off 1 flat 18 rrw 0.500 }  
        {nm"escapement3" teeth 30.000 h 38.000 iw -0.350 radius 100.149 k 1.000 axr 8.000 cuts {n 10.000 rr 0.266 or 0.932 orf 0.950 skw 0.178 iw -0.150 ow 0.828 r2r 0.900 rw 0.267 rrf 0.000 s1skw 0.011 s2skw -0.044 s1r 0.346 s2r 0.641 _ 4065.000 } ow 0.050 pw 0.080 skw 0.020 off 1.000 flat 18.000 rrw 0.500 _ 4065.000 }
        {nm"pinion_t10h7r18" teeth 10 h 7  radius 18 off 2  cuts { n 10 rr 0.5 or 0.8 iw 0.3 ow 0.7  rw 0.6}  }
        {nm"intermed_t60h7r6x18" teeth 6*10 h 7 radius 6*18 off 2 cuts {n 10 rr 0.1 rrf -0.569 or 0.960 orf 0.960 rw 0.750 ow 0.943 skw -0.220 s1skw  0.006 s2skw 0.220 s1r 0.148 s2r 0.579 iw 0 } ow 0.170 pw 0.500 rrw 0.500 skw 0 flat 1 axr 4 }
        {nm"pinion_t6h9r15" teeth 6 radius 15  h 9}
        {nm"min_gear" teeth 10*6 radius 10*15 h 9 off 2 cuts {n 6 rr 0.465 rrf 0.200 or 0.95 orf 0.95 rw 0.023 ow 0.2 skw 0 s1skw  0 s2skw 0 iw 0 s1r 0.636 s2r 0.920 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.95 orf 0.95 rw 0.812 ow 0.67 skw 0 s1skw  0 s2skw 0 s1r 0.677 s2r 0.920 } }
        {nm"pinion_t12h8r22" teeth 12 radius 22 h 8 off 2 } 
        {nm"intermed_t4x12h8r4x22" teeth 4*12 radius 4*22 h 8 off 2 cuts  {n 6 rr 0.2 rrf 0.200 or 0.920 orf 0.900 rw 0.023 ow 0.200 skw 0 s1skw  0 s2skw 0 iw 0 s1r 0.636 s2r 0.920 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.920 orf 0.920 rw 0.812 ow 0.643 skw 0 s1skw  0 s2skw 0 s1r 0.677 s2r 0.920 } }
        {nm"pinion_t12h8r5x22d4" teeth 12 radius 5*22/4  h 8 cuts { n 6 rr 0.1 or 0.2 iw 0.3 ow 0.7  rw 0.6} }
        {nm"hour_gear_t3x12h8" teeth 3*12 radius 5*3*22/4 off 2 h 8 cuts {n 5 rr 0.12 rrf 0  or 0.93 orf 0.93 rw 0 ow 0.94 skw -0.02 s1skw  0.01 s2skw 0.1223 s1r 0.06 s2r 0.737 iw 0 } }
        {nm"face_plate" teeth 1 h 0 radius 230 off 0 flat 0 ow 0.5 pw 0.5  axr 17 
               cuts1  {n 1 rr 0.7 or 0.7 rrf -0.1 orf 0.1 rw 0.5 ow 0.5  }
               cuts2 {n 4  rr 0.856  rrf -0.1 or 0.856 orf 0.1 rw 0.17 ow 0.17  skw 0 s1skw  0 s2skw 0 }
               cuts3 {n 12  rr 0.85  rrf -0.01 or 0.85 orf 0.01 rw 0.3 ow 0.3  skw 0 s1skw  0 s2skw 0 } }
        {nm"min_hand" teeth 1 h 150 radius 191 ow 1.002 pw 1.009 rrw -0.6 iw 0.3 off -110 flat -36.5 axr 3}
        {nm"" teeth 1 h 200 radius 216 ow 1 pw 1.008 rrw -0.600 iw 0.299 skw 0 off -110 flat -19 axr 9 } ;; iw 0.3
        {nm"hour_hand" teeth 1 h 51  radius 142.165 ow 1 pw 1.020 rrw -0.850 iw 0.425 skw -0 off -97 flat -85 axr 5 } ;;  iw 0.427
        {nm"" teeth 1.000 h 35.300 radius 142.044 ow 1.002 pw 1.016 rrw -0.850 iw 0.425 skw 0.000 off -97.054 flat -85.127 axr 2.000 cuts {n 1.000 or 0.578 rw 0.124 ow 0.000 skw 0.000 iw 0.000 s1skw  0.000 s2skw 0.000 rr 0.648 rrf 1.203 orf 0.160 s1r 0.566 s2r 9.836 _ 1877.000 } _ 1877.000 }
        {nm"seconds_hand" teeth 1 h 174 radius 200 ow 1 pw 1.003 rrw -0.6 iw 0.3 off -110 flat -26 axr 3}
        ]))

(if window.dat
    (let* 
      ((geom {width 700 height 600 radius 200 scale 1.5  anch_y 100 anch_r 110}) 
       (body (d3.select "body"))
       (svg (. body (append "svg")(attr geom)
               (append "g")(attr 'transform "translate({geom.width/2},{geom.height/2})") 
               (append "g")(attr 'transform "scale({geom.scale})")
               ))
       (frame (. svg (append "g")
                 (datum {radius Infinity})))
       (g1c {h 15 teeth 55 radius 234 k 1 cuts {n 9 rr 0.120 rrf -0.025 or 0.890 orf 0.496 rw 0.358 ow 0.845 skw -0.020 s1skw -0.001 s2skw 0.061 s1r 0.182 s2r 0.871 } })

       (gear 
       (gear.concat 
        [ ;;{h 16 teeth 48 radius 207.886 k 1 cuts {n 6 rr 0.465 rrf 0.200 or 0.920 orf 0.90 rw 0.023 ow 0.200 skw 0.000 s1skw 0 s2skw 0 iw 0 s1r 0.636 s2r 0.92 } cuts2 {n 6 iw -0.500 rr 0.200 rrf 0.200 or 0.92 orf 0.92 rw 0.812 ow 0.643 skw 0.000 s1skw 0.000 s2skw 0.000 s1r 0.677 s2r 0.92 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 }
         {h 13 teeth 49 radius 216.459 k 1 cuts {n 10 rr 0.100 rrf -0.020 or 0.770 orf 0.170 rw 0.430 ow 0.880 skw 0.130 s1skw 0.032 s2skw -0.050 s1r 0.190 s2r 0.370 iw 0 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 }        
         {h 15 teeth 60 radius 234 k 1 cuts {n 5 rr 0.149 rrf -0.328 or 0.930 orf 0.930 rw 0 ow 0.943 skw -0.220 s1skw 0.042 s2skw 0.123 s1r 0.143 s2r 0.637 iw 0 } ow 0.170 pw 0.500 rrw 0.500 iw 0 skw 0 off 4 flat 1 axr 8 }{h 16 teeth 55 radius 234 k 1 cuts {n 3 rr 0.239 rrf -0.589 or 0.913 orf 0.910 rw 0.452 ow 0.924 skw 0.169 s1skw 0.038 s2skw -0.026 s1r 0.308 s2r 0.481 iw 0 } ow 0.170 pw 0.500 rrw 0.500 iw 0 skw 0 off 2 flat 1 axr 15 }         {h 16 teeth 60 radius 234 k 1 cuts {n 2 rr 0.179 rrf 0.037 or 0.894 orf 0.897 rw 0.176 ow 0.904 skw 0.220 s1skw 0.220 s2skw -0.220 s1r 0.001 s2r 0.437 } off 3 flat 1 rc1 1 rc2 4 }
         {h 15 teeth 60 radius 234 k 1 cuts {n 4 rr 0.348 rrf -0.316 or 0.904 orf 0.897 rw 0.044 ow 0.960 skw -0.186 s1skw -0.079 s2skw 0.115 s1r 0.424 s2r 0.212 } ow 0.170 pw 0.500 axr 8 skw 0 off 3 flat 1 }
         {h 14 teeth 60 radius 212.283 k 1 cuts {n 6 rr 0.749 rrf -0.029 or 0.739 orf 0.037 rw 0.631 ow 0.631 skw 0 s1skw 0 s2skw 0 iw 0 s1r 0.731 s2r 0.758 } cuts2 {n 6 iw 0.500 rr 0.400 rrf -0.008 or 0.400 orf 0.060 rw 0.770 ow 0.770 skw 0 s1skw 0 s2skw 0 s1r 0.400 s2r 0.400 } ow 0.170 pw 0.500 axr 8 skw 0 off 2 flat 1 }
         {h 14.000 teeth 60.000 radius 212.268 k 1.000 cuts {n 3.000 rr 0.650 rrf -0.028 or 0.650 orf 0.038 rw 0.544 ow 0.544 skw 0.000 s1skw 0.000 s2skw 0.000 iw 0.000 s1r 0.650 s2r 0.650 _ 4483.000 } cuts2 {n 3.000 rr 0.211 rrf -0.034 or 0.880 orf 0.880 rw 0.021 ow 0.585 skw 0.000 s1skw 0.000 s2skw 0.000 iw -0.500 s1r 0.764 s2r 0.863 _ 4996.000 } ow 0.170 pw 0.500 axr 12.000 skw 0.000 off 2.000 flat 1.00 }
         {teeth 1.000 h 242.096 radius 242.592 ow 1.002 pw 1.008 rrw -0.600 iw 0.300 skw 0.000 off -109.746 flat 2.400 axr 3.000 cuts {n 1.000 or 0.957  rw 0.197 ow 0.000 skw 0.000 iw 0.000 s1skw 0.000 s2skw 0.000 rr 0.966 rrf 1.000 orf 0.366 s1r 0.646 s2r 43.414 }  }
         ;; gN
         ]))
       (gui)(gear_0 (aref gear 0))(gui_dock (.(body.append'div)(attr'id'gui_dock)))
       (set_gui \g.(let* ((init (lambda (gui obj was_closed_p)  
                                  (if !gui nil (set was_closed_p gui.closed) (gui.destroy))
                                  (set dat.GUI.prototype.knob knob
                                       gui (new dat.GUI { autoPlace false }))
                                  (gui.remember obj) 
                                  (if was_closed_p (gui.close))
                                  gui))
                          (repath (lambda()
                                    (. frame (selectAll "path")(attr "d" mk_gear))
                                    (. frame (selectAll "circle")(attr {cy \d(if d.skw -geom.anch_y 0) r \d(if d.skw geom.anch_r d.radius)}))
                                    (set curr_def.value (print gear_0))))
                          (knob \(obj prop min max step dflt onchange_f)
                                (let* (ctrl 
                                       (realval (aref obj prop))) ;; stupid bug in DAT.gui
                                  (set realval (numval realval dflt||min/2+max/2)  )
                                  (if (eq (typeof realval) 'number)
                                      (set min (Math.min min realval) 
                                           max (Math.max max 1.2*realval) 
                                           step step||max/1000-min/1000
                                           (aref obj prop) (Math.abs realval))) 
                                  (set ctrl  (this.add obj prop min max))
                                  (if step (ctrl.step step))
                                  (ctrl.setValue realval) ;; fixes stupid bug in DAT.gui
                                  (ctrl.onChange onchange_f||repath)
                                  this))
                          cuts tooth_gui)
                     (set gui (init gui g) 
                          tooth_gui (gui.addFolder "gear & tooth")
                          geom_gui (gui.addFolder "svg/anchor/env geometry")
                          tooth_gui.closed gui.closed)
                     ;;(console.log gui.knob)
                     (. geom_gui 
                        (knob geom 'scale .1 6 0.1 undefined \.(.(svg.attr 'transform 
                                                                           (+ "scale("geom.scale")"))))
                         (knob geom 'anch_y 20 300 1 undefined)(knob geom 'anch_r 20 300 1 undefined)
                        )
                     (. tooth_gui
                        (knob g 'radius 1 400)
                        (knob  g 'teeth 1 200 1) 
                        (knob  g 'h -50 50 )
                        (knob  g 'ow -1 1 nil 1/6)
                        (knob  g 'pw -1 1 nil 0.5)
                        (knob  g 'rrw -1 1 nil 0.5)
                        (knob  g 'iw -1 1 nil 1e-5) ;;stupid dat.gui bug
                        (knob  g 'skw -0.22 0.22 nil 1e-5)
                        (knob  g 'off -50 50 nil g.h/4) 
                        (knob  g 'flat -50 50 nil g.h/16)
                        (knob  g 'axr 0 40 1 g.h/2))
                     (reduce g "" "" \k.cuts=k 
                             \c(progn 
                                (gui.remember c) 
                                (. (gui.addFolder cuts) 
                                   (knob  c 'n 0 24 1)  
                                   (knob  c 'rr 0.1 1)
                                   (knob  c 'rrf -1 1)
                                   (knob  c 'or 0.1 1)
                                   (knob  c 'orf -1 1)
                                   (knob  c 'iw -0.7 0.7 0.01 0) 
                                   (knob  c 'rw 0 1) 
                                   (knob  c 'ow 0 1)
                                   (knob  c 'skw -0.22 0.22)
                                   (knob  c 's1skw -0.22 0.22)
                                   (knob  c 's2skw -0.22 0.22)
                                   (knob  c 's1r 0 1.2)
                                   (knob  c 's2r 0 1.2)
                                   (knob  mk_gear 'DEBUG)))
                             \.""
                             )
                     (.(gui_dock.html "")(append \.gui.domElement))
                     (. d3 (selectAll ".dg .property-name") (style 'width "20%"))
                     (. d3 (selectAll ".dg .c") (style 'width "80%"))
                     (set gui.width 200)
                     (set gui.domElement.style.right "200px")
                     ))
       (format3f (d3.format ".3f"))(format_d (d3.format "d")) 
       (print \g.(reduce g "{" "}"  \k(+ k " ") reduce  \v.(or nil ;; (format_d v)
                                                               (format3f v))))
       (fg (. frame (append "g")(attr "class" "g1"))) 
       (gg1 (. fg (selectAll 'path) (data [gear_0]) (enter)))
       (gg1p (. gg1 (append "path")(attr "d" mk_gear)))
       (txt_display (. body (append 'textarea)))
       (reinit_aux
        \d.(let* ((cir (.(fg.selectAll 'circle))) (rm (cir.remove))
                  (e (.(fg.selectAll 'circle)(data [d])(enter)))
                  ;; (a1 (.(e.append "circle")(attr {cy \d.-1.5*d.radius r \d.1.1*d.radius})))
                  (a2 (.(e.append "circle")(attr {cy \d(if d.skw -geom.anch_y 0) r \d(if d.skw geom.anch_r d.radius)}))))))
       (reinit
        \d.(let* () 
             (set gear_0 d geom.scale 180/gear_0.radius)
             (. fg (selectAll 'path)(remove))
             (console.log d)
             (. fg (selectAll 'path) (data [d]) (enter) (append "path")(attr {d mk_gear 'stroke-width 2/geom.scale}))
             (.(svg.attr 'transform "scale({geom.scale})"))
             (set_gui d)
             (reinit_aux d)
             (. txt_display (text (print gear_0)))))
       ) ;; end let* binds
      (set_gui gear_0)  
      
       ;;(. fg (selectAll 'circle) (data [gear_0])(enter) (append "circle")(attr {x 0 y 0 r \d.d.radius}))

      (. txt_display (attr {id 'curr_def cols 80})
         (text (print gear_0))
         (on 'keyup \d.(reinit (eval (+ "d=" (Lisp.translate (Lisp.read this.value)))))))
      (. body (append 'img)
         (attr {src "http://image.slidesharecdn.com/mahesh-140825084947-phpapp02/95/modelling-of-spur-gear-in-pro-e-software-9-638.jpg"
                    'width '500px height 'auto}))

      (. body (insert 'div 'svg) (attr 'class 'example_panel)
         (selectAll 'a)   
         (data gear)(enter)
         (append 'a)(text \di.(+ "Example " ++i))
         (on 'click reinit))

      ))</textarea>
<textarea id=example15__html>
&lt;script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5.1/dat.gui.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://codepen.io/dmitrynizh/pen/GogBaw.js&quot;&gt;&lt;/script&gt;
</textarea>
<textarea id='example15__css'>body {background: beige; } 
path { 
  stroke: #333;
  /*stroke-width: 2px;*/
}
circle { 
  stroke: #900;
  stroke-width: 1px; stroke-dasharray: 8,4;
  fill: none;
}
.g1 { fill: lightblue; } 
#gui_dock { position: absolute; top: 0px; right: -2px;  opacity: 0.5; }

.example_panel {
  font: 12px Arial;
  cursor: pointer;
  left: 6px; top: 15px;
  position: absolute;
  border: 2px solid black;
  padding: 3px;
  opacity: 0.5; 
}
.example_panel:hover,  #gui_dock:hover {  opacity: 1; }

.example_panel a {
  border: 1px solid black;
  padding: 2px;
    color: orange;
  background: #444;
    display: block;
}

.example_panel a:hover {
  border: 1px solid white;
    color: white;
}</textarea>
</pre>

<!--  code example -------------------------- - -->

<pre>
<textarea id='pendulum_clock'>;;;; Pendulum + Escapement + Geares = Wall Clock!
;; gist:f7248b52ed92b57e442e

(defmacro incl_gear_train()(read example16.value))(incl_gear_train)

(include "#example15") 


(\p(set p.appendStd p.append
        p.appendWithLink
        \n(let* ((s (this.appendStd n))) s.__ps=this s)
        p.append p.appendWithLink
        p.end \.this.__ps
        ) d3.selection.prototype)


(let* 
  ((start (Date.now)) (PI Math.PI)(min Math.min) (w window)
   (rAF (or w.requestAnimationFrame w.webkitRequestAnimationFrame w.mozRequestAnimationFrame 
            \f(setTimeout \.(f (- (Date.now) start)) 1000/60)))
   (degr2rad  \d.d*PI/180)
   (rad2degr \r.r*180/PI)
   lastTime
   (pendulum { ;; SI meter kg radian
              mass 1
              length 0.99
              init_swing (degr2rad 5) 
              TH PI/2 W 0 A 0 I 0 qual_k 1-0.1/60 driveW 150
              init \.(set this.I this.mass*this.length*this.length this.TH this.TH-this.init_swing)})
   (weight (\w(set w.r w.p/2/PI w.x 1000*w.r w.hmm 1000*w.h w w) ;; radius of the pulley m and mm
              {h 1.2 ;; in m, from the floor and defines how long the clock will run
               p 0.1 ;; this gives 0.1m of perimeter
               y 701 ;; in mm, offset from the pendulum axis
               mass 5})) 
   (wheel { ;; escapement wheel
           mass 0.1 R 0.05  ;; *equivalent* gear train mass and radius
           offset 136 ;; mm, from the pendulum axis
           TH 0 ;; -3.6 ;; in degrees not radians for convenience
           W 0  A 0  I 0  limit_TH weight.h/weight.p*60*360 
           init \.(set this.lock_TH this.TH this.I this.mass*this.R*this.R)})
   (gear_a  260) ;; acceleration of the whole of the gear train: Ttrain/Itrain
   (body (d3.select 'body))
   (geom {width 700 height 700 radius 200})
   (border (.(body.select "svg")(append'rect)(attr geom)(style{stroke "black" fill #cfc7af})))
   (svg_zg (.(body.select "svg")(attr geom)
             (call (. (d3.behavior.zoom)(scaleExtent [0.1 2]) ;;(scale 0.5);;(center [geom.width/2 geom.height/2])
                      (on 'zoom \.(progn ;;(d3.event.preventDefault)(d3.event.stopPropagation)
                                    (svg_zg.attr 'transform "translate({d3.event.translate}) scale({d3.event.scale})")))))
             (append "g")))
   (svg_g (.(svg_zg.append "g") 
            (attr "transform" "translate({geom.width/2},{geom.height/16})")
             (append "g");; (attr "transform" "scale(0.5)")
             ))
   (pendulum_g  (. svg_g (append 'g)))
   (weight_g  (. svg_g (append 'g)(attr 'transform "translate({weight.x},{weight.y})")))
   (gear_train_frame (. svg_g (append 'g)(attr "transform" 
                                               (+ "translate(0,{wheel.offset})"))))
   
   movement ;; our gear train having 4+ axles, see gear_train call below
   (stopped_by_left true) 
   (stopped_by_right false)
   (displ p1) (format42 (d3.format "4.2f"))(format_int (d3.format "d"))
   ;;(wheel_update \.(escapement.attr "transform" (+ "rotate(" (format42 wheel.TH)  ")")))
 
   (wheel_update 
    \.(let ((rope_y weight.y-movement.minutes_gear.y-wheel.offset))
        (.(gear_train_frame.selectAll '.g_rot)(attr "transform" \d."rotate({d.k*wheel.TH})"))
        (. (d3.select "#rope") (attr {y -weight.k*wheel.TH-rope_y height weight.k*wheel.TH+rope_y}))
        (. weight_g (attr "transform" "translate({weight.x},{(weight.k*wheel.TH+weight.y)})")))) 
   (safe_interval \t(if 0.05>t t (if 45>wheel.lock_TH (update_hands)) 0.05)) ;; 
   (tick \(time) (let*
                   ((dt (safe_interval (/ (- time lastTime) 1000))) ;; in secs, normally dt=0.0166
                    (pendulum_TH_prev pendulum.TH)
                    ;; get the new angle using last tick's data
                    (pth (set pendulum.TH (+ pendulum.TH pendulum.W*dt 0.5*pendulum.A*dt*dt)))
                    ;; svg operates in degrees
                    (degr (- (rad2degr pendulum.TH) 90))
                    ;; calculate Torque from current position. T = F*R 
                    (T (* pendulum.mass 9.81 (Math.cos pendulum.TH) pendulum.length))
                    ;; angular acceleration a = T/I
                    (A   T/pendulum.I))
                   (set lastTime time)
                   (if logger.log (logger.run time degr A))
                   ;; https://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet
                   ;; Velocity_Verlet: velocity change is dt * Aaverage
                   (set pendulum.W (* (+ pendulum.W (* 0.5  (+ A  pendulum.A) dt)) pendulum.qual_k)
                        pendulum.A A)
                   ;;(console.log dt pendulum_TH_prev pendulum.TH pth degr T A pendulum.W)
                   (pendulum_g.attr "transform"  "rotate({degr})")
                   (if (> 1 (Math.abs degr)) ;;free wheel zone: about +-1 degree or so
                       (if pendulum.TH>=pendulum_TH_prev ;; swings from right to left (clock-wise)
                           (if stopped_by_left ;; the left-side stopper has just released the wheel.
                               (set stopped_by_left false  ;; start moving
                                    wheel.release_TH  wheel.TH ;; record the start angle.
                                    wheel.lock_TH  wheel.release_TH+6))
                           ;; else it must be swinging from left to neutral. (rotating CCW)
                           (if stopped_by_right ;; the right-side stopper has just released the wheel.
                               (set stopped_by_right  false ;;  start moving
                                    wheel.release_TH wheel.TH ;; record the start angle.
                                    wheel.lock_TH wheel.release_TH+6))))
                   (if wheel.TH>wheel.lock_TH ;; just got locked!
                       (progn
                        (if logger.log==2 (console.log wheel.W pendulum.W))
                        (set wheel.TH wheel.lock_TH) ;; make sure it is set exactly
                         (if wheel.lock_TH>wheel.limit_TH wheel.W=0 ;; do nothing, done rotating!
                           ;; determine who locked (simplification here...)
                           (if degr>0 
                             (set stopped_by_right true  ;;  the wheel got locked by the right anchor
                                  stopped_by_left  false) ;; likely redundant (check it)
                             (set stopped_by_left  true  ;; the wheel got locked by the left side anchor
                                  stopped_by_right false)) ;;   likely redundant (check it)
                           (wheel_update))))
                   ;;  wheel dynamics update
                   (if stopped_by_left||stopped_by_right ;; do not update a locked wheel.
                       wheel.W=0
                       ;; else
                       (set wheel.W wheel.W+gear_a*dt
                            wheel.TH (+ wheel.TH  wheel.W*dt  (* 0.5 gear_a dt dt)))
                       (let ((temp  (* pendulum.driveW  (Math.abs pendulum.W))))
                         (if wheel.W>temp ;;  wheel drives pendulum
                             (progn 
                              (if logger.log==2 (console.log "transfer torque to pendulum"))
                              wheel.W=temp
                              pendulum.W*=1.012)))
                       (wheel_update))
                   (rAF tick) ;; tick will be passed the curent time from rAF
                   ))
   (hand_initial_angles 
    \.(let* ( (date (new Date))(hr (date.getHours))(m (date.getMinutes))(s (date.getSeconds)))
       {hr (* hr%12 360/12)  m  (* 360/60 m) s (* 360/60 s)}) )
   (ha (hand_initial_angles))
   (gear (gear.reduce \agi(set (aref a (or g.nm "gear_t{g.teeth}h{g.h}r{g.radius}")) g a a) {}))
   (ttt (console.log gear))
   (gears ;; the set of gears and hands for the clock  
    [ gear.escapement1
      {fill #6b8ea6} 
      gear.pinion_t10h7r18  
     {y 1.03 fill #9ecae1}
     gear.intermed_t60h7r6x18 
     {fill #6baed6}
     gear.pinion_t6h9r15 
     {y 1.025 fill #c6dbef nm "minutes_gear" }
     gear.min_gear 
     gear.pinion_t12h8r22 
     {y 1.04 fill #9ecae1}
     gear.intermed_t4x12h8r4x22 
     {fill #6baed6}
     {teeth 12 radius 5*22/4  h 8 cuts { n 6 rr 0.1 or 0.2 iw 0.3 ow 0.7  rw 0.6} }
     {y -1.04 fill #c6dbef nm "hours_gear" }
     gear.hour_gear_t3x12h8
     ;; face plate and hands are defined last ONLY because of the SVG layour/zindex reasons. The k of the plate and minutes hand adjusted.
     {y 0 k 0 nm 'face} ;; k=0 - face must be still
     gear.face_plate 
     {y 0 k "minutes_gear" nm "minute hand axle" a ha.m+ha.s/60} ;; a sets inital, date-derived angle
     gear.min_hand 
     ;;{nm "minute" teeth 1 h 200 radius 216 ow 1 pw 1.008 rrw -0.600 iw 0.299 skw 0 off -110 flat -19 axr 9 } ;; iw 0.3
     {y 0 k "hours_gear" nm "hour hand axle" a ha.hr+ha.m/12} ;; a sets inital, date-derived angle
     gear.hour_hand 
     ;;{teeth 1.000 h 35.300 radius 142.044 ow 1.002 pw 1.016 rrw -0.850 iw 0.425 skw 0.000 off -97.054 flat -85.127 axr 2.000 cuts {n 1.000 or 0.578 rw 0.124 ow 0.000 skw 0.000 iw 0.000 s1skw  0.000 s2skw 0.000 rr 0.648 rrf 1.203 orf 0.160 s1r 0.566 s2r 9.836 _ 1877.000 } _ 1877.000 }
     ;; the seconds hand. it is optional - uncomment this line to see it...
      {y 0 k "driver" nm "secs hand axle" a ha.s}
     gear.seconds_hand 
     ]
    )
   (logger {
            log true last_tick_time 0 degr_max 0 tick_counter  0 
            degr_max_displayed (rad2degr pendulum.init_swing)
            swing_msg "" tick_msg ""
            run 
            \(time degr A) 
            (let* ((logger_time time))
              (if degr>0  
                  (set this.degr_max (Math.max degr  this.degr_max))
                  (if this.degr_max>0 this.degr_max_displayed=this.degr_max)
                  (set this.degr_max 0))
              (set this.swing_msg (+ "swing: " (format42 this.degr_max_displayed)))

              (if A>=0&&0>=pendulum.A||0>=A&&pendulum.A>=0 
                  ;; for average tick duration, time with correction for the 1st tick time
                  (let* ((tick_time time-this.last_tick_time)
                         (tick_time_aver logger_time/this.tick_counter))
                    ++this.tick_counter
                    (if tick_time_aver>1.2*tick_time||0.8*tick_time>=tick_time_aver
                        ;; browser stopped animation in inactive tab/window
                        (set logger_time tick_time*this.tick_counter))
                    (set this.tick_msg 
                         " tick aver: {format42(logger_time/this.tick_counter)} last tick: {format42(tick_time)}" 
                         this.last_tick_time time)))
              (set displ.innerHTML this.swing_msg+this.tick_msg))

            })
   	(update_hands ;; needed because some browsers stop ruuning code in invisible windows
    \.(let ((ha (hand_initial_angles)))
        (console.log "page was idle, correcting hands postions...")
        (movement.forEach \g(if (eq g.nm "minute hand axle")
                                (set g.a ha.m+ha.s/60)
                              (if (eq g.nm "hour hand axle")
                                (set g.a ha.hr+ha.m/12)
                              		(if (eq g.nm "secs hand axle")
                                (set g.a ha.s)))))))
   ) ;; end of let*
  (pendulum.init)
  (wheel.init)
  (. pendulum_g (append 'rect)
     (attr { x -10 y -10 width 20 height 1000})
     (end)
     (append 'rect)
     (attr { x -4 y -10 width 8 height 1000})
     (style{stroke #772211 'stroke-width "1px" fill #cc5522})
     (end) (append 'circle)(style { fill "url(#bob)"})
     (attr { cx 0 cy 1000 r 70})
     (end) (append 'circle) 
     (attr { cx 0 cy 1000 r 46 })
     (style{stroke #331111 'stroke-width "1px" fill #dd7722})
     (end) (append 'circle)
     (attr { cx 0 cy 1000 r 40 })
     (style{stroke #331111 'stroke-width "2px" fill #964000})
     (end) (append 'path)
     (attr 'd (let** ((r1 92)(r2 100.6)(r3 18)(a01 47)(a012 12)(adrive 2)
                      (a1 (degr2rad 90-a01))(a2 (degr2rad 90-a01-a012))
                      (da (degr2rad 2.5)) ;; affects arm 'wrist' width
                      (a3 (degr2rad -90+45))(a4 (degr2rad -90-45))
                      (a5 (degr2rad 90+a01+a012))(a6 (degr2rad 90+a01+adrive))(a7 (degr2rad 90+a01))
                      (a8 (degr2rad -90-105))(a9 (degr2rad -90+105))
                      (a10 (degr2rad 90-a01-adrive))
                      (M (com2a 'M))(L (com2a 'L))
                      (A \(r dir a)(+ "A" r "," r  " "  dir " " (* r (Math.cos a))  "," (* r (Math.sin a))))
                      ) 
                     (+ (M a1 r2)
                        (A r2 "0,0,0" a2)(L a3 r3) ;; right  arm, outer
                        (A r3 "0,0,0" a4) ;; top central radius
                        (L a5 r2)(A r2 "0,0,0" a6) ;; right  arm, outer
                        (L a7 r1) ;; driving slope, left
                        (A r1 "0,0,1" a5-da) (L a8 r3)
                        (A r3 "0,0,0" a9) ;; bottom center
                        (L a2+da r1) (A r1 "0,0,1" a10)
                        "Z" ;; driving slope, right
                        )
                     ))
     (end) (append 'circle)
     (attr { cx 0 cy 0 r 10 })
     (style {stroke #333 'stroke-width '1px})
     )

  (. weight_g (append 'rect)(style {fill #965 stroke #444 "stroke-width" "0.5px"})
     (attr {id 'rope x -1 y -350 width 2 height 350})(end)
     (append 'rect)(style {fill "url(#weight)" stroke #856300})
     (attr {x -15/2 y -15 width 15 height 14})(end)
     (append 'rect)(style {fill "url(#weight)" stroke #856300})
     (attr {x -60/2 y 0 width 60 height 204}))

  (set movement (gear_train gear_train_frame gears mk_gear))
  (movement.forEach \ai(if a.nm (set (aref movement a.nm) a)))
  (set weight.k movement.minutes_gear.k*100/360) ;; here 100mm is pulley's perimeter
  
  (console.log  "movement "movement) 

  (.(gear_train_frame.selectAll 'path)
    (style {fill \d(if d.teeth==1 'black) opacity \d(if d.teeth==1 0)})
    )
  
  
  ;;(.(svg_g.append'rect)(attr {id 'floor x -10000 y weight.y+204+1200 width 20000 height 10}) )
  (let* ((floor_y weight.y+weight.hmm+204)
         (wall {x -3000 y -1000 width 6000 height 1000+floor_y}))
    (.(svg_g.append'rect)(attr wall)(style {"stroke-width" "10px" fill 'transparent})(end)
      (append'line)(attr {x1 -10000 x2 -3000 y1 floor_y+2000 y2 floor_y})(end)
      (append'line)(attr {x1 10000 x2 3000 y1 floor_y+2000 y2 floor_y})(end)
      (append'line)(attr {x1 -10000 x2 -3000 y1 -4000 y2 -1000})(end)
      (append'line)(attr {x1 10000 x2 3000 y1 -4000 y2 -1000})) )

  (set lastTime (if w.performance&&w.performance.now
                    (w.performance.now)
                    (- (Date.now) start)) 
       logger.last_tick_time  lastTime)
  (var fzoom \di(.(svg_g.transition)(duration 4000)(ease (d3.ease 'linear)) 
                             (attr "transform" "scale({d.z})")))
  (.(body.insert 'div 'svg) (attr 'class 'example_panel)(selectAll 'a)   
    (data [{z 0.9 txt "zoom closer"     f fzoom}  
           {z 2.8 txt "zoom very close" f fzoom}
           {z 0.5 txt "zoom far"        f fzoom}
           {txt "show face/hands" flg false 
                f \di(set d.flg !d.flg 
                          this.innerHTML (if d.flg "hide face" "show face")
                          doit (.(gear_train_frame.selectAll 'path)(transition)(duration 1000)
                         (style {opacity \g(if g.teeth==1 (if d.flg 0.45 0.03))})
                         ))}
           {txt "hit pendulum" f
                \d(set stopped_by_right false stopped_by_left false 
                       wheel.lock_TH wheel.limit_TH ;;; 12 hrs of free-wheeling
                       pendulum.driveW 1e8 pendulum.W (+ pendulum.W -10 (* 20 (Math.random)))
                       doit (. pendulum_g (select 'path) (transition)(duration 3000)(ease 'bounce) (attr 'transform "translate(0,930)")))}])
    (enter)(append 'a)(text \di.d.txt)
    (on 'click \d.(d.f d)))
  
  ;;(console.log p)
  ;;(. (d3.select 'body)(style {'background-size "10%" 'background "url(http://previews.123rf.com/images/alexmakarova/alexmakarova1209/alexmakarova120900040/15236036-vector-seamless-vintage-wallpaper-pattern-on-gradient-background-Stock-Vector.jpg)"})))
  (tick lastTime))</textarea>
<textarea id='pendulum_clock__html'><p id=p1>Hello World</p>
<svg>
  <defs>
    <linearGradient id="weight" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%"   style="stop-color:#856300;" />
      <stop offset="40%"  style="stop-color:#f5b800;" />
      <stop offset="100%" style="stop-color:#856300;" />
    </linearGradient>
    <radialGradient id="bob" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="40%"  style="stop-color:#222;" />
      <stop offset="85%"  style="stop-color:#bf5200;" />
      <stop offset="100%" style="stop-color:#852600;" />
    </radialGradient>
  </defs>
</svg>
<p>pendulum clock by dmitrynizh'15</p>

&lt;script src=https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js&gt;&lt;/script&gt;
</textarea>
<textarea id='pendulum_clock__css'>body { color: lightblue; 
  background: #aabbcc; 
  /*url(http://thumbs.dreamstime.com/x/seamless-vintage-wallpaper-pattern-1592949.jpg);
  background-size: 30%;*/
}
p {
  font-size: 9px; 
  background: #226; margin: 4px 0 4px 0; padding: 2px;
}
path {
  fill-rule: evenodd;
  stroke: #333;
  stroke-width: 1px;
  fill: #6baed6;
}
circle, rect  {
  stroke: #a33;
  stroke-width: 2px;
  fill: #bf5200;
}
line {
  stroke: #a33;
  stroke-width: 10px;
}
.example_panel {
  cursor: pointer;
  left: 8px; top: 30px;
  position: absolute;
  border: 2px solid black;
  padding: 3px;
  opacity: 0.5; 
}
.example_panel:hover {  opacity: 1; }

.example_panel a {
  border: 1px solid black;
  padding: 2px;
  color: orange;
  background: #444;
  display: block;
}

.example_panel a:hover {
  border: 1px solid orange;
  color: white;
}</textarea>
</pre>

<!-- This is Pendulum Clock's (prev example) GIST
<pre data-gist-id="f7248b52ed92b57e442e"></pre> -->

<pre data-gist-id="dd312ebbb5711c20e35ade2957d32ae2"></pre>

<!--  template for new code example ---------------------------

<pre>
<textarea id=lisp>
</textarea>
<textarea id='__html'>
</textarea>
<textarea id='__css'>
</textarea>
</pre>

------------end of template ------------------------------ -->

</div>  <!-- end of lisp code examples  -->

<script src="https://codemirror.net/lib/codemirror.js"></script>

<script src="https://codemirror.net/mode/xml/xml.js"></script>
<script src="https://codemirror.net/mode/htmlmixed/htmlmixed.js"></script>
<script src="https://codemirror.net/mode/css/css.js"></script>
<!--<script src="https://codemirror.net/mode/commonlisp/commonlisp.js"></script>-->
<script src="https://codemirror.net/mode/javascript/javascript.js"></script>

<!--<script src="https://codemirror.net/2/lib/util/formatting.js"></script>-->


<script src="https://codemirror.net/addon/edit/matchbrackets.js"></script>

<!-- this one has some access issues: 
Your connection is not private
Attackers might be trying to steal your information from jsbeautifier.org (for example, passwords, messages, or credit cards). Learn more
NET::ERR_CERT_COMMON_NAME_INVALID etc...
         <script src="http://jsbeautifier.org/js/lib/beautify.js"></script> 
         so.. we try the one below. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.7.1/beautify.js"></script>

<script src="https://ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js"></script>

<script src="https://codemirror.net/addon/lint/lint.js"></script>
<script src="https://codemirror.net/addon/lint/javascript-lint.js"></script>

<script src="https://codemirror.net/addon/hint/show-hint.js"></script>
<script src="https://codemirror.net/addon/edit/closebrackets.js"></script>

<!-- for search and repace with ctrl-f ctrl-g etc -->
<script src="https://codemirror.net/addon/dialog/dialog.js"></script>
<script src="https://codemirror.net/addon/search/searchcursor.js"></script>
<script src="https://codemirror.net/addon/search/search.js"></script>
<script src="https://codemirror.net/addon/scroll/annotatescrollbar.js"></script>
<script src="https://codemirror.net/addon/search/matchesonscrollbar.js"></script>
<script src="https://codemirror.net/addon/search/jump-to-line.js"></script>  


<!-- 
inculded in "Settings" -->

<!--resizeable CM panels, derived from Split.js -->
<script src="https://codepen.io/dmitrynizh/pen/GoRbWG.js"></script>

<!-- Lisp15 -->
<script src="https://codepen.io/dmitrynizh/pen/GogBaw.js"></script>

<!-- Lisp15 mode for CodeMirror -->
<script src="https://codepen.io/dmitrynizh/pen/zrGZbW.js"></script>

<!-- Gistachio extended to provide Basic Aith variant. original gistachio is:
https://stuartpb.github.io/gistachio/gistachio.js  -->
<script src="https://codepen.io/dmitrynizh/pen/pQWgde.js"></script>

<!-- This section facilitates running saved gist from bl.ocks -->
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>


            
          
!
            
              

html,
body {
  font-size: 14px;
  width: 100%;
  height: 100%;
}
.split {
  /*border: 1px solid red;*/
  padding: 0px;
  margins: 0px;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  overflow-y: hidden;
  overflow-x: hidden;
}

/* for extensions, examples of lisp code */
.code_source  {
  display: none;
}

.content {
  border: 1px solid #C0C0C0;
  box-shadow: inset 0 1px 2px #e4e4e4;
  background-color: #fff;
}

.gutter {
  background-color: transparent;
  background-repeat: no-repeat;
  background-position: 50%;
}

.gutter.gutter-horizontal {
  cursor: col-resize;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAnCAIAAABMnMWyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAzSURBVDhPY/z//z8DBmCC0qgAJMrIyIhG4laLCUZFIQC7KKlxgQlAoqMxRDtR4mOIgQEAiOAJVSqQeZQAAAAASUVORK5CYII=');
}

.gutter.gutter-vertical {
  cursor: row-resize;
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAIAAAD1ZpjZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAmSURBVDhPY/z//z/DQACoxYyMjBA+kQDuXPI1jvqYXmCk+ZiBAQCRTibzFTZDSgAAAABJRU5ErkJggg==');
}

.split.split-horizontal,
.gutter.gutter-horizontal {
  height: 100%;
  float: left;
}

.CodeMirror {
  /*border: 1px solid blue;*/
  height: auto;
}

/* time for Codemirror scrollbars???
https://codemirror.net/demo/simplescrollbars.html
*/

#code_editors {}

.code_box {
  position: relative;
  float: left;
  padding: 6px;
  border: 1px solid black;
  border-radius: 6px;
  /*position: absolute;*/
  resize: both;
  overflow: auto;
  /*left: 60px;
   top: 30px;*/
}

.split a {
  cursor: pointer;
  margin-top: -8px;
  margin-left: -10px;
  position: absolute;
  padding: 1px;
  font-size: 12px;
  border: 1px solid black;
  color: black; height: 14px;
  background: lightblue;
  z-index: 10;
}

#nav_panel {
  margin-left: -45px;
}
.split a.a_next_ex {
  margin-left:12px;
}
.split a.a_prev_ex {
  /*margin-left:16px;*/
}

#save_panel {
  right: 0px; top: 9px;  
}

#tidy_js:hover, #Lisp_label:hover, 
#next_example:hover,  #prev_example:hover, #new_example:hover,
#save_result_html:hover, #save_result_gist:hover
{
  color: red;
  background: orange;
}

.code_box a:hover,
.code_box_last a:hover {
  color: blue;
  border: 2px solid blue;
  padding: 0 4px 0 4px;
}

.code_box textarea {
}

#output {
  height: 100%;
  border: 1px solid #444;
}

#output iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

/* scrollbars: so far only webkit */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
::-webkit-scrollbar-button { }
::-webkit-scrollbar-track-piece {
    background: #f0f0f0
}
::-webkit-scrollbar-thumb {
    background: #e0e0e0
}
::-webkit-scrollbar-thumb:hover {
    background: lightblue
}

/* search box adjustments */
.CodeMirror-dialog {
  border: 1px solid blue;
  background: lightblue;
  white-space:nowrap;
  font-family: 'Arial Narrow', Arial, sans-serif;
}
.CodeMirror-dialog input {
  background: #fff;
}

.CodeMirror-search-match {
  background: transparent;
  border-top: 1px solid orange;
  border-bottom: 1px solid orange;
  -moz-box-sizing: border-box;;
  box-sizing: border-box;
  opacity: .5;
}

#lisp_history, #html_template {
  position: absolute;
  top: 100px; left: 100px;
  padding: 10px;
  width:  360px;
  border: 3px solid black;
  background: beige;
  display: none; 
  resize: both;
  overflow: auto;
}

#gist_request_token_dialog_purpose {
  display: none; 
}

#gist_request_token_dialog {
  position: absolute;
  top: 20px; right: 0px;
  padding: 6px;
  width:  260px;
  border: 3px solid black;
  background: beige;
  display: none; 
  font-size: 10px;
}

#new_example_dialog {
  margin-left: -25px;
  margin-top: 16px;
  position: absolute;
  border: 1px solid black;
  z-index: 10;

  padding: 6px;
  width:  260px; height: 40px;
  border: 3px solid black;
  background: beige;
  display: none; 
  font-size: 10px; 
}


#gist_request_token_dialog  button, #new_example_dialog button {
  font-size: 10px;
}


#gist_save_notification {
  position: absolute;
  top: 20px; right: 0px;
  padding: 6px;
  width:  350px;
  border: 3px solid black;
  background: beige;
  display: none; 
}

#lisp_history_close, #html_template_cancel {
  position: absolute;
  top: 8px; right: 10px;
}

 #html_template_ok {
  position: absolute;
  top: 8px; right: 70px;
}


#gist_id_label {
  display: none;
  cursor: pointer;
  position: absolute;
  margin-top: -12px;
   margin-left: 70px; /*
  padding: 0 8px 0 8px;
  font-size: 10px;
  border: 1px solid green;
  color: black; height: 10px;
  
  //background: lightblue;
  font-size: 10px; */
  z-index: 9;
}


#gist_id_value {
  cursor: pointer;
/*   position: absolute;
  margin-top: -10px;
  margin-left: 70px;
  padding: 0 8px 0 8px; */
  border: 1px solid green; 
  color: black; 
  font-size: 9px;
  height: 9px;
  background: lightblue;
  width: 180px;
  
  z-index: 10;
}


</style><script>window.mylog = console.log;</script><style>
            
          
!
            
              
(function() {  
  // get rid of console_runner overwiring stuff....
  // delete window.console.log;  // this removes just log
  
  for (var key in console) { // all of them
    if (console.hasOwnProperty(key) && console[key].toString().indexOf("unction ")) {
      // console.log("deleting console.", key);
      // delete console[key];
    }
  }


  var location = { hash: "", qs: {}}; // hash string, guery params
  if (window.location.search) {
    var qs = window.location.search.substr(1).split('&');
      for (var p, i = 0; i < qs.length; i++) {
        p = qs[i].split('=', 2);
        location.qs[p[0]] =  (p.length == 1) ? "" : decodeURIComponent(p[1].replace(/\+/g, " "));
      }
  }
  location.hash = window.location.hash.replace("#", "");
  
  //delete window.console.log;
  //window.console.log(console, "location: ", location);;
  // Base template
  var output_page_template =
    "<!doctype html>\n" +
    "<html>\n" +
    "  <head>\n" +
    "    <meta charset=\"utf-8\">\n" +
    "    <title>My Web Lisp App</title>\n" +
    "  </head>\n" +
    "  <body>\n" +
    "  </body>\n" +
    "</html>";
  var lisp_panel_title;

  var mk_tag = function(tag, text, attrs) {
    attrs = attrs ? (" " + attrs) : "";
    return "<" + tag + attrs + ">" + text + "</" + tag + ">";
  }

  var prepareSource = function(incl_js) {
    var lisp = lisp_editor.getValue();
    var html = html_editor.getValue();
    var css = css_editor.getValue();
    var js = js_editor.getValue();
    
    var tmp, src = output_page_template;
    if ((tmp=lisp.match(/\s*;+\s*(\w+.*)\n/))) 
      src = // I decided to comment this out... 19 nov 18/ output_page_template = 
          src.replace(/title>(.*)<\/title>/, 
                      "title>"+(lisp_panel_title=tmp[1])+"<\/title>");
    
    src = src.replace('</body>', html + "\n</body>");
    css = "\n<style>\n" + css + "\n</style>";
    src = src.replace('</head>', css + "\n</head>");
    if (incl_js) // this style includes the whole of code.js
      js = mk_tag('script', "\n" + js + "\n") + "\n";
    else // this style includes scriptsrc="code.js"
      js = mk_tag('script', "", "src=\"code.js\"") + "\n";
    src = src.replace('</body>', js + '</body>');
    //console.log(src);
    return src;
  };

  var get_elt = function(id) {
    return (document).getElementById(id);
  };

  var get_text = function(code_elt) {
    if (code_elt.taName === "textarea")
      return code_elt.value;
    return (code_elt.innerText || code_elt.textContent).trim();
  };

  var set_text = function(code_elt, code) {
    console.log("set_text", code_elt);
    // if (code_elt.tagName === "TEXTAREA")
    //   code_elt.value = code;
    // else if (code_elt.innerText)
    //   code_elt.innerText = code;
    // else code_elt.textContent = code;
    code_elt.innerHTML = code;
  };
  
  var render = function() {
    console.log("render");
    var source = prepareSource(true);;
    var iframe = document.querySelector('#output iframe');
    var section = iframe.parentElement;
    section.removeChild(iframe);
    iframe = document.createElement("iframe");
    section.appendChild(iframe);
    var iframe_doc = iframe.contentDocument;
    iframe_doc.open();
    iframe_doc.write(source);
    iframe_doc.close();
  };

  var html_box = document.querySelector('#html textarea');
  var html_editor = CodeMirror.fromTextArea(html_box, {
    mode: 'text/html',
    gutter: true,
    lineNumbers: true,
    matchBrackets: true
      //,extraKeys:{"Shift-Tab":autoFormatSelection}
  });
  var css_box = document.querySelector('#css textarea');
  var css_editor = CodeMirror.fromTextArea(css_box, {
    mode: 'css',
    gutter: true,
    lineNumbers: true,
    matchBrackets: true
      //,extraKeys:{"Shift-Tab":autoFormatSelection}
  });;;

  var lisp_box = document.querySelector('#lisp textarea');
  var lisp_editor = CodeMirror.fromTextArea(lisp_box, {
    mode: 'lisp', // commonlisp',
    gutter: true,
    lineNumbers: true,
    matchBrackets: true,
    autoCloseBrackets: true,
    extraKeys: {
      "Alt-F": "findPersistent" // for search extensions
        ,
      "Ctrl-Space": "autocomplete"
        // had to extend keymap for # this way. problem is that closebrackets.js
        // does not allow to have # to double-enter
        // in predefiend way (such as () etc and as "".... due to the way how
        // its function handleChar is written. 
        ,
      "'#'": function(cm) {
        var pos = cm.getCursor();
        var line = cm.getLine(pos.line);
        console.log(line, pos);
        //todo: maybe more cases....
        if (pos.ch == 0 || line.charAt(pos.ch - 1).match(/[\s.(\[]/)) {
          cm.replaceSelection("##");
          cm.setCursor({
            line: pos.line,
            ch: pos.ch + 1
          });
        } else
          cm.replaceSelection("#");
      }
    },
    undoDepth: 300
      //,extraKeys:{"Shift-Tab":autoFormatSelection}
  });
  
  var js_box = document.querySelector('#js textarea');
  var js_editor = CodeMirror.fromTextArea(js_box, {
    mode: 'javascript'
      //,gutter: true
      ,
    lineNumbers: true,
    matchBrackets: true
      //,lineWrapping: true
      ,
    gutters: ["CodeMirror-lint-markers"],
    lint: true
      //,extraKeys: {"Ctrl-Space": "autocomplete"}
      //,extraKeys:{"Shift-Tab":autoFormatSelection}
  });

  Split(['#code_editors', '#output'], {
    gutterSize: 14,
    cursor: 'col-resize',
    onDrag: resize_CM_editors_on_drag,
    editors: [html_editor, css_editor, lisp_editor, js_editor]
  });

  var all_editors = [html_editor, css_editor, lisp_editor, js_editor];

  Split(['#html', '#css', '#lisp', '#js'], {
    onDrag: resize_CM_editors_on_drag,
    direction: 'vertical',
    sizes: [10, 10, 50, 30],
    gutterSize: 6,
    editors: all_editors,
    minSize: 10,
    cursor: 'row-resize'
  });
  
  var cms = document.querySelectorAll('.CodeMirror');
  for (var i = 0; i < cms.length; i++) {

    /*cms[i].style.position = 'absolute';
		cms[i].style.top = '0px';
		cms[i].style.bottom = '0';
		cms[i].style.left = '0';
		cms[i].style.right = '0';*/
  }

  cms = document.querySelectorAll('.CodeMirror-scroll');
  /*for (i = 0; i < cms.length; i++) 
		cms[i].style.height = '50%';*/

  // note use_package will make it global global
  Lisp.range =  function(start, stop, step, inclusive) { // these values can be numbers or string holding numbers
    // console.log("range", start, stop, step);
    if (stop == null) stop = +start || 0, start = 0;
    else start = +start||0, stop = +stop;
    if (inclusive) stop++;
    step = +step || 1;
    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);
    for (var idx = 0; idx < length; idx++, start += step) 
      range[idx] = start;

    return range;
  };
  
  Lisp.use_package("Lisp");
  
  var js_code = []; // macro 'include' needs this
  var js_out = function(jscode, beautify) {
    jscode = semicolon_cleanup(jscode);
    if (!jscode.match(/.+[;\}]\s*$/)) jscode += ";";
    if (beautify) jscode = tidyup_js_code(jscode);
    js_editor.setValue(jscode);
    return jscode;
  };
  var processToplevels = function(lisp_text, action, end_regexp) {
    action = action || js_out;
    end_regexp = end_regexp || /_^/; // default is 'match nothing'
    var curr = 0;
    while (!lisp_text.substring(curr).match(end_regexp)) {
      LispTrans.resetState();
      var res = read_from_string(lisp_text, curr);
      if (!res) break;
      curr = res.cdr;
      action(res.car);
    }
  };

  // Extension: Removal of Option Semicolons
  var semicolon_cleanup = (function() {
    LispTrans.setTokens({ //COMMA: ",\n", SEMICOL: ";\n", SEMICOL_RET: " ;\n", 
      RBC_CODE: "}/*{C}*/\n",
      RBC_OBJ: "}/*{O}*/\n",
      RBC_FUNC: "}\n/*{F}*/"
    });
    return function(js_code) { // ugly regexp stuff, but fast and helps to keep Jhint happier
      js_code = js_code.replace(/\n(\/\*[^\*]*\*\/\n);/g, "$1"); // for macros
      js_code = js_code.replace(/\/\*{F}\*\/;(\w*[^\(])/g, "$1"); // for non application
      js_code = js_code.replace(/\/\*{[COIW]}\*\/\n*;/g, ""); // for all blocks followed by ;
      js_code = js_code.replace(/\/\*{[COIWF]}\*\//g, ""); // remove markers
      return js_code;
    }; })();

  // This loads useful macros extending the basic set provided by Lisp.
  // Currently, load time is completely negligible, under 10ms. 
  var time = new Date();
  processToplevels(get_text(get_elt("extensions")), function(e) {
          translate(macroexpand(e));
  }); 
  console.log("extensions load time, ms:", new Date() - time);

  var import_lisp_from_gist = function(gist_id, handler) {
    gistachio.getFiles(gist_id, function(err, files) {
      // console.log("import_lisp_from_gist files", files);
      if (err || !files) {
        console.log("can not read specified gist id="+gist_id, err, files);
      }
      else 
        handler(gist_id, files["code.lisp"]);
    });
  } // end of import_lisp_from_gist

  
  LispTrans.setSpecialTerms({"nil":undefined}); //this excludes t:true, use #t or 't or 1 or true or ""
  // One more useful extension. Example: (include #example15 #example1)
  LispTrans.global_macros["include"] = LispTrans.global_macros["import"] = function() { // vararg macro!
    var msg = "";
    for (var i = 0; i < arguments.length; i++) {
      var name = arguments[i];
      if (name.constructor === LispString) 
        name = name.text;
      var node_list = document.querySelectorAll(name);
      var msg_ = " included:";
      for (var j = 0; j < node_list.length; j++) {
        var elt = node_list[j]; // console.log(elt);
        processToplevels(get_text(elt), function(e) {
          js_code.push(translate(macroexpand(e)));
        });
        msg_ += " " + elt.id || elt.name || elt.dir || elt.tagName;
      }
      msg += msg_;
    }
    return "\n/* " + msg + " */\n";
  };
  
  var gist_lisp_dir = {}; // collection of various gist lisp code content

  LispTrans.global_macros["import-gist"] = function() { // 1-arg macro
    var id = arguments[0];
    if (!id) console.log("import-gist: bad gist id value in:", arguments);
    else {
      if (id.constructor === LispString) 
        id = id.text;  
      var code = gist_lisp_dir[id];
      // if code  is available, process it
      if (code == -1)  
        // lookup was done and failed, just quit
        return "/* bad gist id */";
      if (code && typeof code != "number") { // number is timestamp
        js_code.push("/* begin " + id + " */");
        processToplevels(gist_lisp_dir[id], function(e) {
          js_code.push(translate(macroexpand(e)));
        });
      } else {
        // save timestamp 
        gist_lisp_dir[id] = Date.now();
        // find in github gist, process async-style
        // ASYNC!
        import_lisp_from_gist(id, function(id, lisp) { 
          // console.log("lisp:", lisp);
          if (!lisp) {
            console.log("include/import: can not find lisp code in gist id=", id, lisp);
            gist_lisp_dir[id] = -1;
          } else { 
            // work in progress.. what to do with this code?
            // prcoessing seem not work... 
            // processToplevels(lisp, function(e) {
            //   js_code.push(translate(macroexpand(e)));
            // });

            // insted, save the code and re-process lisp  now
            var stamp = gist_lisp_dir[id];
            if (typeof stamp == "number") 
              console.log("imported gist in " + (Date.now()-stamp)+ "ms");
            gist_lisp_dir[id] = lisp;
            // insert and add a foot comment that the import is now available
            lisp_editor.setValue(lisp_editor.getValue()
                                 // +"\n;;gist exp" // if you want a 'proof'
                                );
          }
        });
      }
    }
    return "\n/* end " + id + " */\n";
  };
  // Extension:  String Interpolation feature
  (function(prefix) { // $ @ . # or empty
    Lisp.string_interpolation_enabled = true; // interpolation is new feature in Lisp15
    var re = new RegExp("\\" + prefix + "{[^ ;{}]+}", "g"); // alternatives: /\${[^{}]+}/g or /\
    LispTrans.setReadStringWrapper(function(str) {
      if (!Lisp.string_interpolation_enabled)  return new LispString(str);
      var match = str.match(re);
      if (!match) return new LispString(str);
      str = str.replace(re, match[0]).split(match[0]); // use it as splitter
      var plus_args = [new LispString(str[0])];
      match.forEach(function(s, i) {
        plus_args.push(s.slice(1+prefix.length, -1));
        plus_args.push(new LispString(str[i+1]))});
      plus_args = plus_args.filter(function(e) {  return !(e && e.constructor == LispString && e.text == "");});
      return cons("+", arr2list(plus_args));
    });
  })("");

  var lisp_to_js = function(lisp_code, beautify) {
    js_code = [];
    processToplevels(lisp_code, function(e) {
      js_code.push(translate(macroexpand(e)))
    });
    js_out(js_code.join(";"), beautify);
    var totalLines = js_editor.lineCount();
    //js_editor.autoFormatRange({line:0, ch:0}, {line:totalLines});
  }

  var tidyup_js_code = function(code) {
    return js_beautify(code, {
      indent_size: 2,
      break_chained_methods: true,
      wrap_line_length: 40,
      brace_style: "collapse"
    }).replace(/    return\n[ ]+/g, // work around horrible bug in js_beautify
      "    return ");
  };

  lisp_editor.on("change", function(cm, change) {
    //console.log("lisp changed: ", change);
    for (var i = 5; i>0; i--) localStorage["hist" + i] = localStorage["hist" + (i-1)];
    var lisp = localStorage["hist0"] =  lisp_editor.getValue();
    lisp_to_js(lisp);
    // the following hack prevents losing paren highlighting
    // in the lisp editor caused by rendering into js editor
    setTimeout(function() {
      var cursor = lisp_editor.getCursor()
      lisp_editor.setCursor({
        line: cursor.line,
        ch: cursor.ch - 1
      });
      lisp_editor.setCursor(cursor);

    }, 100);;

  });

  js_editor.on("change", function(cm, change) {
    //console.log("js changed: ", change);
    render();
  });

  get_elt("tidy_js").onclick = function() {
    var pp_text = tidyup_js_code(js_editor.getValue());
    js_editor.setValue(pp_text);
  };
  var popups_zindex = 99;
  get_elt("Lisp_label").onclick = function() {
    var popup = get_elt("lisp_history");
    for (var i = 5; i>-1; i--) 
      get_elt("hist"+i).value = localStorage["hist"+i];
    get_elt("hist1").value = localStorage["hist1"];
    popup.style.display = "block";
    popup.style["z-index"] = popups_zindex++;
  };
  
  get_elt("lisp_history_close").onclick = function() {
    var popup = get_elt("lisp_history");
    popup.style.display = "none";
  };
  
  get_elt("HTML_label").onclick = function() {
    var popup = get_elt("html_template");
    get_elt("html_template_ta").value = output_page_template;
    popup.style.display = "block";
    popup.style["z-index"] = popups_zindex++;
  };
  
  get_elt("html_template_ok").onclick = function() {
    output_page_template = get_elt("html_template_ta").value;
    get_elt("html_template").style.display = "none";
  };
  
  get_elt("html_template_cancel").onclick = function() {
    get_elt("html_template").style.display = "none";
  };
  
  function wrap_lisp_code() {
   return "\n<code style='display:none;'>" + lisp_editor.getValue() + "\n</code>\n";
  }
  
  get_elt("save_result_html").onclick = function() {
    var link = document.createElement("a");
    link.download = "result.html";
    // simple method. problem -  sometimes get  ^R in JS code. 
    // link.href = "data:text/html," + prepareSource();
    // blob method:
    var blob = new Blob([
      prepareSource(true)
      ,wrap_lisp_code()
    ], { type: "octet/stream" });
    link.href = window.URL.createObjectURL(blob);
    link.click();
  };
  
  // works for relatively small numbers only (not GIST IDs)
  // example: correct for f7248b52ed999, incorrect for f7248b52ed9999
  function isBaseN(s, n) {
    var a = parseInt(s,n);
    return (a.toString(n) === s.toLowerCase())
  }
  
  function isValidHex(s) { 
    var m = s.match(/[0-9a-fA-F]+/);
    return m && m[0] == s;
  }
  
  // some additional gist services
  var your_secure_gist_credentials_api; // see https://developer.github.com/v3/oauth/#web-application-flow
  var gist_pat = (your_secure_gist_credentials_api && your_secure_gist_credentials_api()) 
  ||
      location.qs.pat 
  //|| localStorage["pat"] // localStorage: ONLY FOR TESTS! NOT SECURE!!
  ; 
  // if (!gist_pat) { // ONLY FOR TESTS
  //   gist_pat = prompt("pat?");
  //   if (gist_pat) localStorage.setItem("pat", gist_pat);
  // }

  // when both gist id and pat are specified, will 'update' rather than create a new gist.

  function set_gist_id (id) {
    gist_id_label.style.display = id ? "block" : "none";
    gist_id_value.value = id || 
      // see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
      // verose: ist_id_value.getAttribute("data-no-id")
      // compact:
      gist_id_value.dataset.noId;
    ;
  }
  
  function get_gist_id () {
    var gist_id_txt = gist_id_value.value;
    return (isValidHex(gist_id_txt) // gist_id_txt.match(/^[0-9a-f]+$/)
               ? gist_id_txt : undefined);
  }

  get_elt("gist_dialog_close").onclick = function() {
    get_elt("gist_save_notification").style.display = "none";
  };

  get_elt("gist_request_token_dialog_cancel").onclick = function() {
    gist_pat = undefined;
    get_elt("gist_request_token_dialog").style.display = "none";
  };

  get_elt("save_result_gist").onclick = function() {
    var ops;
    var files;
    var cb = function(err, gist_id) {
      if (err) { 
        console.log("Error: new gist creation:", err, " info: ", gist_id);
        gist_pat = undefined;
      } else {
        // console.log("new gist:", gist_id);
        set_gist_id(gist_id);
        // purge old cache entry 
        gist_lisp_dir[gist_id] = undefined;
        var patched_gist_p = gist_id && gist_request_token_dialog_patch.checked;
        var popup = get_elt("gist_save_notification");
        get_elt("gist_save_msg").innerHTML = 
          (patched_gist_p 
           ? "Work was saved permanently as a new revision of your gist:" 
           : "Work was saved permanently into your new gist:");
        var link = get_elt("gist_url");
        link.innerHTML = link.href = "https://gist.github.com/" + gist_id;
        link = get_elt("run_gist_from_blocks");
        link.href = "https://bl.ocks.org/" + gist_id;
        get_elt("gist_personal_at_note").style.display = gist_pat ? "none" : "block" ;
        popup.style.display = "block";
        popup.style["z-index"] = popups_zindex++;
        
      }
    }; // end of func cb
    
    function save() {
      if (gist_pat) {
        // Note: gist_pat maybe proper oauth token or U:PASS format.
        // teh later gives the user a chance to save code as gists
        // when he does not have a token at hand. 
        // an extension to gistachio allows that. See Pen pQWgde. 
        // See also a standalone exmple in Pen https://codepen.io/dmitrynizh/pen/vQJyXX 
        ops.accessToken = gist_pat; 
      }
      var gist_id = get_gist_id();
      console.log("Saving... options:", ops, "gist id:", gist_id);
      if (gist_id && gist_request_token_dialog_patch.checked)
        gistachio.patchFiles (gist_id, files, ops, cb);
      else 
        gistachio.postFiles(files, ops, cb);
    } // end of save()
    
    var prepare = function() {
      // re-get gist id
      // read_gist_id_input();
      
      var js = tidyup_js_code(js_editor.getValue());
      js_editor.setValue(js);
      files = {
        'code.lisp': lisp_editor.getValue()
        ,'code.html': html_editor.getValue()
        ,'code.css': css_editor.getValue()
        ,'code.js': js_editor.getValue()
        ,'index.html': prepareSource(false) + wrap_lisp_code()
      };
      console.log("files:", files["code.lisp"].indexOf("\n"));
      ops = { description: lisp_panel_title || "my lisp work"};

      console.log("gist_pat", gist_pat);

      // Phase this OUT...
      // // provide gist id on the lisp panel if it is not there already
      // if (gist_id)  {
      //   var lisp_text = files["code.lisp"];
      //   if (lisp_text.indexOf("\n") >= 0 && !lisp_text.match(/;;\sgist:(\w+)/)) {
      //     lisp_text = lisp_text.replace("\n", "\n;; gist:" + gist_id + "\n");
      //     files["code.lisp"] = lisp_text;
      //     lisp_editor.setValue(lisp_text);
      //   }
      // } else {// code snippets from gist storage do contain id; use it.
      //   var match  = files["code.lisp"].match(/;;\sgist:(\w+)/);
      //   if (match)  set_gist_id(match[1]);
      // }

      var gist_id = get_gist_id();
      if (gist_pat && !gist_id) { // saves the code as your new gist
        save();
      } else { // render the dialog and let it continue from here
        gist_request_token_dialog_patch.checked = true;
        gist_request_token_dialog_purpose.style.display = 
          gist_id ? "block" : "none"; 
        gist_request_token_dialog_gist_id.innerHTML = gist_id || "none";
        gist_request_token_dialog_gist_id.href = "https://gist.github.com/" + gist_id;
        var dialog = get_elt("gist_request_token_dialog");
        dialog.style.display = "block";
        dialog.style["z-index"] = popups_zindex++; 
      }

      // var gist_uname = localStorage["gist_uname"] || prompt("gist uname?");
      // if (gist_uname) { 
      //   localStorage.setItem("gist_uname", gist_uname); 
      //   ops.username = gist_uname;
      // }
      // var gist_ps = localStorage["gist_ps"] || prompt("gist pswd?");
      // if (gist_ps) {
      //   localStorage.setItem("gist_ps", gist_ps); 
      //   ops.password = gist_ps;
      // }
    }
    
    get_elt("gist_request_token_dialog_submit").onclick = function() {
      gist_pat = gist_request_token_dialog_input.value.trim();
      get_elt("gist_request_token_dialog").style.display = "none";
      save();
    };
    
    prepare();
  }; // end of save_result_gist onclick method

  
  (function() { // The Rolodeck of Examples 
    var current_example_pre;
    
    function load_example (pre_elt) {
      current_example_pre = pre_elt;
      if (pre_elt.children.length == 0) {
        // maybe gist?
        if (pre_elt.dataset.gistId) 
          // ASYNC! must return!!
          return load_example_from_gist(pre_elt);
      }
      
      set_gist_id(pre_elt.dataset.gistId); // id or undefined
      var lisp_elt = pre_elt.querySelector("textarea:not([id*=__])");
      if (!lisp_elt) {
        console.log("warning: no Lisp text found in rolodeck");
        lisp_elt = ensure_textarea(pre_elt, undefined, 
                                   "Example" + example_n, 
                                   ";; New Sketch");
      }
      var html_elt = ensure_textarea(pre_elt, "__html", lisp_elt.id, get_text(get_elt("example1__html")));
      var css_elt  = ensure_textarea(pre_elt, "__css", lisp_elt.id, get_text(get_elt("example1__css")));
      html_editor.setValue(get_text(html_elt));
      css_editor.setValue(get_text(css_elt));
      lisp_editor.setValue(get_text(lisp_elt));
    };

    function ensure_textarea (pre_elt, suffix, lisp_id, content) {
      var ta_elt = (suffix 
                    ? pre_elt.querySelector("textarea[id*=" + suffix + "]")
                    : pre_elt.querySelector("textarea[id='" + lisp_id+ "']"));
      if (!ta_elt) {
        ta_elt = document.createElement("textarea");
        ta_elt.id = lisp_id + (suffix || ""); // eg, example777_html
        ta_elt.innerHTML = content;
        pre_elt.appendChild(ta_elt);
      }
      return ta_elt;
    }
    
    var load_example_from_gist = function(pre_elt) {
      var gist_id = pre_elt.dataset.gistId;
      gistachio.getFiles(gist_id, function(err, files) {
        if (err || !files)
          console.log("can not read specifyed gist id="+gist_id, err, files);
        else {
          set_gist_id(gist_id); 
          Object.keys(files).forEach(function(file) {
            // hack! fixup cdnjs in old gists 
            var content = files[file].replace("http://cdnjs.cloudflare.com", "https://cdnjs.cloudflare.com");
            switch (file) {
              case "code.html": 
              ensure_textarea(pre_elt, "__html", gist_id, content);
              html_editor.setValue(content); 
              break;
              case "code.css" :  
              ensure_textarea(pre_elt, "__css", gist_id, content);
              css_editor.setValue(content); 
              break;
              case "code.lisp": 
              ensure_textarea(pre_elt, undefined, gist_id, content);
              lisp_editor.setValue(content); 
              break;
            }
          });
        }
      });
    } // end of load_example_from_gist
    
    
    
    var examples = document.querySelectorAll("#examples pre");
    var example_n = 0; // idx of current example
    
    // this syncs up the editors and the pre
    function save_changes_into_current_pre() { 
      if (!current_example_pre) return;
      var pre_elt = current_example_pre;
      // syncup gist input box and gist_id  
      // read_gist_id_input();
      var gist_id = get_gist_id();

      // // Phase this out! Now it is done by load_example_from_gist.
      // if (pre_elt.children.length == 0) { // typically, a gist example
      //   // new/dom-unsaved gist, save the code and gist id into DOM
      //   // the following i slegal but worth logging the fact
      //   if (pre_elt.dataset.gistId != gist_id) 
      //     console.log("warning: saving code with gist_id=" + gist_id + 
      //                 " into roloeck with previous id=" + pre_elt.dataset.gistId);
      //   // create 3 text areas and a dataset property gistId 
      //   populate_rolodeck_pre(pre_elt, gist_id || "an_example");
      // }
      if (pre_elt.children.length == 0) {
        // should not happen unless gist_id logic is broken
        console.log("warning: saving not possible, no textareas");
      }

      if (gist_id) { // this prevents saving "bad" text from gist_id_value input.
        if (pre_elt.dataset.gistId != gist_id) 
          console.log("warning: saving code with gist_id=" + gist_id + 
                      " into rolodeck with previous id=" + pre_elt.dataset.gistId);
        pre_elt.dataset.gistId = gist_id;
      }
      // var lisp_elt = pre_elt.querySelectorAll("textarea:not([id*=__])")[0];
      // var html_elt = pre_elt.querySelectorAll("textarea[id*=html]")[0] || get_elt("example1__html");
      // var css_elt = pre_elt.querySelectorAll("textarea[id*=css]")[0] || get_elt("example1__css");
      var tas = pre_elt.getElementsByTagName("textarea");
      for (var i = 0; i < tas.length; i++) {
        if (tas[i].id.endsWith("__html"))
          set_text(tas[i], html_editor.getValue());
        else if (tas[i].id.endsWith("__css"))
          set_text(tas[i], css_editor.getValue());
        else 
          set_text(tas[i], lisp_editor.getValue());
      }
    }
    
    var next_example = get_elt("next_example").onclick = function() {
      save_changes_into_current_pre();
      example_n = ++example_n % examples.length;
      load_example(examples[example_n]);
    };
    var prev_example = get_elt("prev_example").onclick = function() {
      save_changes_into_current_pre();
      if (--example_n < 0) example_n = examples.length - 1;
      load_example(examples[example_n]);
    };
    
    get_elt("new_example").onclick = function() {
      // let user to figure what to create 
      // by opening dialog and exiting from here
      new_example_dialog.style.display = "block";
    }
    
    new_example_dialog_blank.onclick = 
      new_example_dialog_from_gist.onclick = function() {
        new_example_dialog_gist_id.disabled = new_example_dialog_blank.checked;
    }
    
    var new_example_dialog_cancel_func = new_example_dialog_cancel.onclick = function() {
      new_example_dialog_blank.checked = true;
      new_example_dialog_gist_id.value  = "";
      new_example_dialog_gist_id.disabled = true;
      new_example_dialog.style.display = "none";
    }
    new_example_dialog_create.onclick = function() {
      save_changes_into_current_pre();
      var id_or_null  = null;
      if (new_example_dialog_from_gist.checked) {
        var id = new_example_dialog_gist_id.value.trim();
          if (id.length > 0)
            id_or_null = id;
      }
      new_example_dialog_cancel_func();
      add_new_rolodeck_page(id_or_null);
      example_n  = examples.length - 1;
      load_example(examples[example_n]);
    }

    // getUrlParameter: ideally, URLSearchParams.get, but it is not yet 100% supported, so...
    // from https://davidwalsh.name/query-string-javascript
    function getUrlParameter(name) {
      var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.search);
      return results === null ? results : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }
    
    var location_hash = window.location.hash.replace("#", "");
    // console.log("location_hash:", location_hash);
    
    function add_new_rolodeck_page (gist_id) {
      var pre_elt = document.createElement("pre"); 
      if (gist_id) pre_elt.dataset.gistId = gist_id;
      get_elt("examples").appendChild(pre_elt);
      examples = document.querySelectorAll("#examples pre");
    }

    // Phase out. now done by load_example_from_gist
    // function populate_rolodeck_pre (pre_elt, name) { // name is GIST ID
    //   pre_elt.dataset.gistId = name;
    //   var html = document.createElement("textarea"); html.id = name + "__html";
    //   var css = document.createElement("textarea");  css.id = name + "__css";
    //   var lisp = document.createElement("textarea"); lisp.id = name; 
    //   pre_elt.appendChild(html);
    //   pre_elt.appendChild(css);  
    //   pre_elt.appendChild(lisp);
    // }
    
    // What to load. It could be either a rolodeck example or a gist.
    // First, url parameters are tried, then url location.  Use either
    // keyword
    var example_id = getUrlParameter("example") || getUrlParameter("gist")
      ||  getUrlParameter("code") || getUrlParameter("snippet") || getUrlParameter("id") ||
        location_hash;
    if (isBaseN(example_id, 10) && example_id < 1000)
      next_example(example_n = Number.parseInt(example_id));
    else if (example_id != "") { // gist id or example name which is elt id
      if (example_id.length > 10 && isValidHex(example_id)) { // gist id 
        // first, add a new example 'page' to the rolodeck
        save_changes_into_current_pre();
        add_new_rolodeck_page(example_id);
        example_n = examples.length - 1;
        load_example(examples[example_n]);
      } else {
        var txt_a = document.querySelector("#" + example_id);
        var pre = txt_a && txt_a.parentElement;
        for (var i = 0; i < examples.length; i++)
          if (examples[i] == pre) {
            example_n = i;
            save_changes_into_current_pre();
            load_example(pre);
          }
      }
    } else {
      // console.log("example_n", example_n);
      load_example(examples[example_n]);
    }
    
  })();
  

  // detect css reloading (inspired by codepen, work in progress)
  var CSSReload = {
    head: null,
    init: function() {
      this._storeHead(), this._listenToPostMessages()
    },
    _storeHead: function() {
      this.head = document.head || document.getElementsByTagName("head")[0]
    },
    _listenToPostMessages: function() {
      var e = this;
      window[this._eventMethod()](this._messageEvent(), function(t) {
        try {
          var s = JSON.parse(t.data);
          "string" == typeof s.css && e._refreshCSS(s)
        } catch (n) {}
      }, !1)
    },
    _messageEvent: function() {
      return "attachEvent" === this._eventMethod() ? "onmessage" : "message"
    },
    _eventMethod: function() {
      return window.addEventListener ? "addEventListener" : "attachEvent"
    },
    _refreshCSS: function(e) {
      var t = this._findPrevCPStyle(),
        s = document.createElement("style");
      s.type = "text/css", s.className = "cp-pen-styles", s.styleSheet ? s.styleSheet.cssText = e.css : s.appendChild(document.createTextNode(e.css)), this.head.appendChild(s), t && t.parentNode.removeChild(t), "prefixfree" === e.css_prefix && StyleFix.process()
    },
    _findPrevCPStyle: function() {
      for (var e = document.getElementsByTagName("style"), t = e.length - 1; t >= 0; t--)
        if ("cp-pen-styles" === e[t].className) return e[t];
      return !1
    }
  };
  // CSSReload.init();

  window.onkeydown = function(e) {
    var e = e || event;
    if (e.ctrlKey && !e.shiftKey && e.keyCode == 82) {
      console.log("got Ctrl-R in LispPlayground, deflect!!!");
      e.preventDefault();
      e.stopPropagation();
      return;
    }
  }

  window.onresize = function() {
    //console.log("window.onresize");
    resize_CM_editors_on_drag(all_editors);
  };
   window.onresize(); // force scrollbars to render on startup

  // if hash location specified, jump to the given example. 

}());

// for debugging only
function output_window() {
  return document.querySelector('#output iframe').contentWindow;
}

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console