diff --git a/resources/public/index.html b/resources/public/index.html index 3857862..94741ca 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -5,8 +5,8 @@ + + +

SCI script tag

-

What is this

+

What is this?

This project exposes the Small Clojure Interpreter in the browser in such a way that you can use it with the script tag.

Project status

@@ -43,56 +46,67 @@

Usage

+ Include sci-script-tag.js and write a script tag + where type is set + to application/x-sci. Use :export to make the function + available in the JavaScript environment. The name is processed + using munge. +
-      <head>
-        <script src="https://borkdude.github.io/sci-script-tag/js/sci-script-tag.js" type="application/javascript"></script>
-        <script type="application/x-sci">
-          (defn my-alert []
-           (js/alert "alert!"))
-        </script>
-      </head>
+      <head>
+  <script src="https://borkdude.github.io/sci-script-tag/js/sci-script-tag.js"
+          type="application/javascript">
+  </script>
 
-      <body>
-        <button onclick="user.myAlert()">
-          Click me!
-        </button>
-      </body>
-    
+ <script type="application/x-sci"> + (defn ^:export my-alert [] + (js/alert "You clicked!")) + </script> -

Source from file

When you have a file on your server, say cljs/script.cljs, you can load it using the src attribute: -
-      <script src="cljs/script.cljs" type="application/x-sci"></script>
-    
+

+<script src="cljs/script.cljs" type="application/x-sci"></script>
+    

Reagent plugin

To enable reagent, in addition to sci-script-tag.js, you need to include sci-script-tag-plugin-reagent.js. -
-      <script src="https://borkdude.github.io/sci-script-tag/js/sci-script-tag-plugin-reagent.js" type="application/javascript"></script>
-      <script type="application/x-sci">
-        (require '[reagent.core :as r]
-                 '[reagent.dom :as rdom])
+    

+<script src="https://borkdude.github.io/sci-script-tag/js/sci-script-tag-plugin-reagent.js" type="application/javascript"></script>
+<script type="application/x-sci">
+  (require '[reagent.core :as r]
+           '[reagent.dom :as rdom])
 
-        (def state (r/atom {:clicks 0}))
+  (def state (r/atom {:clicks 0}))
 
-        (defn my-component []
-          [:div
-           [:p "Clicks: " (:clicks @state)]
-           [:p [:button {:on-click #(swap! state update :clicks inc)}
-            "Click me!"]]])
+  (defn my-component []
+    [:div
+      [:p "Clicks: " (:clicks @state)]
+      [:p [:button {:on-click #(swap! state update :clicks inc)}
+       "Click me!"]]])
 
-        (rdom/render [my-component] (.getElementById js/document "app"))
-      </script>
-    
+ (rdom/render [my-component] (.getElementById js/document "app")) +</script> +
diff --git a/src/sci/script_tag.cljs b/src/sci/script_tag.cljs index 7b993cf..fe271ed 100644 --- a/src/sci/script_tag.cljs +++ b/src/sci/script_tag.cljs @@ -1,50 +1,59 @@ (ns sci.script-tag - (:require [clojure.string :as str] + (:refer-clojure :exclude [defn]) + (:require [clojure.core :as c] + [clojure.string :as str] [goog.object :as gobject] [goog.string] [sci.core :as sci])) -(defn kebab->camel [s] - (str/replace s #"-[a-zA-Z0-9]" - (fn [s] - (str/upper-case (.charAt s 1))))) +(c/defmacro defn [fn-name & args] + (let [ns-sym (gensym "ns")] + `(let [~ns-sym (ns-name *ns*)] + (clojure.core/defn ~fn-name ~@args) + ~(when (:export (meta fn-name)) + `(sci.script-tag/-export ~fn-name (str ~ns-sym "." '~fn-name)))))) -(defn- defn-macro [_ _ fn-name & args] - `(let [ns# (ns-name *ns*)] - (clojure.core/defn ~fn-name ~@args) - (sci.script-tag/-export ~fn-name (str ns# "." '~fn-name)))) +(c/defn -export [f k] + (let [k (munge k) + parts (str/split k #"\.")] + (loop [parts parts + prev js/window] + (let [fpart (first parts)] + (cond (= "user" fpart) + (recur (rest parts) prev) + (= 1 (count parts)) + (gobject/set prev fpart f) + :else + (if-let [obj (gobject/get prev fpart)] + (recur (rest parts) obj) + (let [obj #js {}] + (gobject/set prev fpart obj) + (recur (rest parts) + obj)))))) + (gobject/set js/window k f))) -(def ctx (atom (sci/init {:namespaces {'sci.script-tag - {'defn (with-meta defn-macro - {:sci/macro true}) - '-export (fn [f k] - (let [parts (str/split k #"\.")] - (loop [parts parts - prev js/window] - (let [fpart (first parts) - fpart (kebab->camel fpart)] - (if (= 1 (count parts)) - (gobject/set prev fpart f) - (if-let [obj (gobject/get prev fpart)] - (recur (rest parts) obj) - (let [obj #js {}] - (gobject/set prev fpart obj) - (recur (rest parts) - obj)))))) - (gobject/set js/window k f)))} - 'clojure.core {'println println}} +(def stns (sci/create-ns 'sci.script-tag nil)) + +(def namespaces + {'sci.script-tag + {'defn (sci/copy-var defn stns) + '-export (sci/copy-var -export stns)} + 'clojure.core {'println (sci/copy-var println stns) + 'prn (sci/copy-var prn stns)}}) + +(def ctx (atom (sci/init {:namespaces namespaces :classes {'js js/window :allow :all}}))) -(defn eval-string [s] +(c/defn eval-string [s] (sci/eval-string* @ctx (str "(require '[sci.script-tag :refer :all])" s))) -(defn merge-ctx [opts] +(c/defn merge-ctx [opts] (swap! ctx sci/merge-opts opts)) -(defn- load-contents [script-tags] +(c/defn- load-contents [script-tags] (when-first [tag script-tags] (if-let [text (not-empty (gobject/get tag "textContent"))] (do (eval-string text)