added test for authentication sql datastore

error: pathspec 'test/blog/auth/auth_sql_datastore_impl_test.clj' did not match any file(s) known to git

--- test/blog/auth/auth_sql_datastore_impl_test.clj
+++ test/blog/auth/auth_sql_datastore_impl_test.clj
@@ -0,0 +1,35 @@
+(ns blog.auth.auth-sql-datastore-impl-test
+  (:require [clojure.test :refer :all]
+            [blog.auth.auth-sql-datastore-impl :refer :all]
+            [clojure.java.jdbc :as jdbc]))
+
+
+(deftest sql-datastore
+  (let [db-spec {:subprotocol "hsqldb"
+                 :subname (str "mem:testdb"
+                               ";shutdown=true"
+                               ";sql.syntax_pgs=true")
+                 :user "SA"
+                 :password ""}]
+    (jdbc/with-db-connection [con db-spec]
+
+      (testing "create-auth-table"
+        (is (= (create-auth-table {:db con})
+               :ok)))
+
+      (testing "add-credentials"
+        (is (= (add-credentials {:db con}
+                                "correct user"
+                                "correct password")
+               :ok)))
+
+      (testing "authenticate-impl"
+        (is (authenticate-impl {:db con}
+                               "correct user"
+                               "correct password"))
+        (is (not (authenticate-impl {:db con}
+                                    "wrong user"
+                                    "correct password")))
+        (is (not (authenticate-impl {:db con}
+                                    "correct user"
+                                    "wrong password")))))))


Simplify CORS header generation.

Simplify CORS header generation.

There is now only a catch-all OPTIONS route.

--- src/ruuvi_server/api.clj
+++ src/ruuvi_server/api.clj
@@ -65,38 +65,27 @@
   "Matches commaseparated list of integers"
   #"([0-9]+,?)+")

-;; TODO do some macro thing that creates automatically OPTIONS route
 (defroutes api-routes-internal

   ;; Client-API
-  (OPTIONS "/ping" []
-           (-> #'success-handler))
   (GET "/ping" []
        (-> #'client-api/ping))

-  (OPTIONS "/trackers" []
-           (-> #'success-handler))
   (POST "/trackers" []
         (-> client-api/create-tracker))

   (PUT "/trackers" []
        (-> client-api/create-tracker))

-  (OPTIONS ["/trackers/:ids" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/trackers/:ids" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-tracker request ids))
            ))

-  (OPTIONS ["/trackers/:ids/events" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/trackers/:ids/events" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-events (merge request {:tracker_ids ids})))
            ))

   ;; TODO missing regexp for id list and order
-  (OPTIONS ["/trackers/:ids/events/:order" :order #"latest|latestStored"] [ids latest]
-           (-> #'success-handler))
   (GET ["/trackers/:ids/events/:order" :order #"latest|latestStored"] [ids order]
        (-> (fn [request]
              (let [order-by (cond (= order "latest") :latest-event-time
@@ -104,51 +93,35 @@
                (client-api/fetch-events (merge request {:tracker_ids ids :order-by order-by}))))
            ))

-  (OPTIONS ["/trackers/:ids/sessions" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/trackers/:ids/sessions" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-session (merge request {:tracker_ids ids})))
            ))

-  (OPTIONS ["/sessions/:ids/events" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/sessions/:ids/events" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-events
                           (merge request {:event_session_ids ids})))
            ))

-  (OPTIONS ["/sessions/:ids/events/latest" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/sessions/:ids/events/latest" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-events
                           (merge request {:event_session_ids ids})))
            ))

-  (OPTIONS ["/sessions/:ids" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/sessions/:ids" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-session (merge request {:event_session_ids ids})))
            ))

-  (OPTIONS ["/sessions/:ids/events" :ids id-list-regex] [ids]
-           (-> #'success-handler))
   (GET ["/sessions/:ids/events" :ids id-list-regex] [ids]
        (-> (fn [request] (client-api/fetch-tracker request ids))
            ))

-  (OPTIONS "/trackers" []
-           (-> #'success-handler))
   (GET "/trackers" []
        (-> #'client-api/fetch-trackers ))

-  (OPTIONS ["/events/:ids" :ids #"([0-9+],?)+"] [ids]
-           (-> #'success-handler))
   (GET ["/events/:ids" :ids #"([0-9+],?)+"] [ids]
        (-> (fn [request] (client-api/fetch-event request ids))
            ))

-  (OPTIONS "/events" []
-           (-> #'success-handler))
   (GET "/events" []
        (-> #'client-api/fetch-events))
   ;; Websockets API
@@ -156,7 +129,10 @@
        (websocket-api/websocket-api-handler))
   ;; Tracker-API
   (POST "/events" []
-        (-> #'tracker-api/handle-create-event)))
+        (-> #'tracker-api/handle-create-event))
+  ;; Accept OPTIONS method to enable CORS headers
+  (OPTIONS "*" []
+           (-> #'success-handler)) )

 (defroutes api-routes
   (context url-prefix []


product search

product search

error: pathspec 'src/clj/ikea_clojure_cup/product.clj' did not match any file(s) known to git

--- src/clj/ikea_clojure_cup/product.clj
+++ src/clj/ikea_clojure_cup/product.clj
@@ -0,0 +1,1718 @@
+(ns ikea-clojure-cup.product
+  (:require [org.httpkit.client :as http-kit]
+            [clojure.pprint :refer [pprint]]
+            [hickory.select :as s]
+            [hickory.core :as hick]
+            [clojure.string :as cs]))
+
+(defn- get-hick-packages [site-htree]
+  ;(s/or ..) doesn't work so have to use concat
+  (let [table-rows (concat (s/select (s/class "ikea-measurements-table-row") site-htree)
+                           (s/select (s/class "ikea-measurements-table-row-child") site-htree))
+        filter-fn #(remove cs/blank? (map cs/trim (filter string? (:content %))))]
+    (reduce (fn [v m]
+              (conj v (concat (filter-fn m)
+                              (filter-fn (first (s/select (s/class "measure-quantity") m))))))
+            []
+            table-rows)))
+
+(defn- filter-string-contains [v string]
+  (filter #(.contains % string) v))
+
+(defn- dimension->int [coll]
+  (Integer/parseInt (nth (cs/split (first coll) #"\s") 2)))
+
+(defn- transform-packages [hick-packages]
+  (reduce (fn [v m]
+            (conj v {:width (dimension->int (filter-string-contains m "Width"))
+                     :height (dimension->int (filter-string-contains m "Height"))
+                     :length (dimension->int (filter-string-contains m "Length"))
+                     :weight (let [w (cs/split (first (filter-string-contains m "Weight")) #"\s")]
+                               (str (nth w 2) (nth w 3)))
+                     :quantity (Integer/parseInt (first (cs/split (first (filter-string-contains m "packages")) #" ")))}))
+          []
+          hick-packages))
+
+(defn product [region product-context id]
+  (let [url (format "http://m.ikea.com/%s/en/catalog/products/%s/%s/measurements/" region product-context id)
+        response (http-kit/get url)]
+    (pprint (-> @response :body hick/parse hick/as-hickory))))
+
+;; (count (get-hick-packages single))
+;; (count (get-hick-packages multi))
+;; (transform-packages (get-hick-packages multi))
+
+
+
+(comment
+  ;;multi
+  (product "au" "art" "20227125")
+  ;;single
+  (product "au" "art" "90263855")
+  )
+
+(comment
+(def single
+  {:type :document,
+   :content
+   [{:type :document-type,
+     :attrs {:name "html", :publicid "", :systemid ""}}
+    {:type :element,
+     :attrs nil,
+     :tag :html,
+     :content
+     [{:type :element,
+       :attrs nil,
+       :tag :head,
+       :content
+       ["\r\n"
+        {:type :comment, :content [" config "]}
+        "\r\n"
+        {:type :element,
+         :attrs {:charset "utf-8"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "viewport",
+          :id "viewport",
+          :content
+          "width=device-width,minimum-scale=1.0,maximum-scale=1.0,initial-scale=1.0"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs {:name "format-detection", :content "telephone=no"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "title",
+          :content "BILLY - Package measurement and weight - IKEA"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs {:name "ROBOTS", :content "NOINDEX, FOLLOW"},
+         :tag :meta,
+         :content nil}
+        "\n"
+        {:type :comment, :content [" libraries "]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "stylesheet",
+          :href "/irmw-resources/external/jquery.mobile-1.2.0.min.css"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :src "/irmw-resources/external/jquery-1.8.2.min.js"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :src
+          "/irmw-resources/external/jquery.mobile.custom-1.2.0.min.js"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :comment, :content [" custom stylesheets and scripts "]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "stylesheet",
+          :href "/irmw-resources/css/ikea.mobile.min.css?v=5.20"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :charset "utf-8",
+          :src "/irmw-resources/js/ikea.mobile.min.js?v=5.20"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :comment, :content [" page specifc scripts "]}
+        "\r\n"
+        {:type :element,
+         :attrs nil,
+         :tag :script,
+         :content
+         ["\r\n(function($){\r\nfunction loadSettings(){\r\n//Initialize dynamic configuration.\r\n$.mobile.retailUnit = 'au';\r\n$.mobile.languageCode = 'en';\r\n$.mobile.loadingMessage = \"Loading\";\r\n$.mobile.defaultShoppingBagName = \"Shopping list\";\r\n$.mobile.offlineLoadError = \"The page you tried to go to can\\'t be shown right now. You can still view your shopping list.\";\r\n$.mobile.loggedInAsMessage =\"Logged in as {1}\";\r\n$.mobile.currencySettings = {symbol: \"$\", decimalSymbol: \".\", positiveFormat: \"%s%n\", negativeFormat: \"-%s%n\", groupSymbol: \",\", fractionDigits: \"2\", showFractionDigitsOnInteger: \"false\", integerSymbol: \"\", currencyCode: \"AUD\"};\r\n$.mobile.showLocalPrices = false;\r\n$.mobile.baseUrl = '/au/en/';\r\n$.mobile.isProduction = true;\r\n$.mobile.cookieDomain = '.ikea.com';\r\n$.mobile.fullSiteUrl = 'www.ikea.com';\r\n$.mobile.secureFullSiteUrl = 'secure.ikea.com';\r\n$.mobile.enableTealium =false;\r\n}\r\nloadSettings();\r\n}(jQuery));\r\n"]}
+        "\r\n"
+        {:type :element,
+         :attrs {:id "txtHtmlTitle"},
+         :tag :title,
+         :content ["BILLY - Package measurement and weight - IKEA"]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "shortcut icon",
+          :type "image/ico",
+          :href "/irmw-resources/img/favicon.ico"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :href
+          "/irmw-resources/img/apple-touch-icon-57x57-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "114x114",
+          :href
+          "/irmw-resources/img/apple-touch-icon-114x114-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "144x144",
+          :href
+          "/irmw-resources/img/apple-touch-icon-144x144-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "32x32",
+          :href
+          "/irmw-resources/img/apple-touch-icon-32x32-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "57x57",
+          :href
+          "/irmw-resources/img/apple-touch-icon-57x57-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "72x72",
+          :href
+          "/irmw-resources/img/apple-touch-icon-72x72-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :comment,
+         :content [" For Windows Phone (only 144x144 used)"]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "msapplication-TileImage",
+          :content
+          "/irmw-resources/img/apple-touch-icon-144x144-precomposed.png"},
+         :tag :meta,
+         :content nil}
+        "\r\n"]}
+      "\r\n"
+      {:type :element,
+       :attrs {:itemscope "", :itemtype "http://schema.org/WebPage"},
+       :tag :body,
+       :content
+       ["\r\n"
+        {:type :element,
+         :attrs
+         {:data-role "page",
+          :data-theme "d",
+          :id "mls1449292019259",
+          :data-dom-cache "false"},
+         :tag :div,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs
+           {:data-role "header",
+            :id "ikea-homeheader",
+            :data-backbtn "false"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:id "ikea-home-topbar"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:id "ikea-topbar-logocell"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:tabindex "-1", :href "/au/en/", :rel "external"},
+                 :tag :a,
+                 :content
+                 [{:type :element,
+                   :attrs {:class "ikea-logo", :title ""},
+                   :tag :span,
+                   :content nil}]}
+                "\r\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs {:id "ikea-topbar-iconcell"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:id "search-icon",
+                  :onclick
+                  "$(this).parent().siblings('#ikea-topbar-searchfield').first().searchdecorate('toggle');"},
+                 :tag :div,
+                 :content nil}
+                "\r\n"
+                {:type :element,
+                 :attrs {:id "list-icon"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs
+                   {:href "http://m.ikea.com/au/en/shoppinglist/",
+                    :rel "external"},
+                   :tag :a,
+                   :content nil}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "list-icon"},
+                   :tag :div,
+                   :content nil}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs
+               {:data-role "searchdecorate",
+                :id "ikea-topbar-searchfield"},
+               :tag :div,
+               :content nil}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs
+             {:id "ikea-navbar",
+              :data-role "headerbar",
+              :data-morelistid "#ikea-more-list"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs nil,
+               :tag :ul,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:class "ikea-navbar-item ikea-navbar-item-active",
+                  :data-icon "none"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs
+                   {:href "http://m.ikea.com/au/en/catalog/functional/"},
+                   :tag :a,
+                   :content ["Products"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-navbar-item ", :data-icon "none"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:href "http://m.ikea.com/au/en/stores/"},
+                   :tag :a,
+                   :content ["Stores"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-navbar-item", :id "ikea-nav-li"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:id "footer-login", :style "display: none"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs
+                     {:id "login1",
+                      :href "/au/en/login/",
+                      :rel "nofollow",
+                      :data-ajax "false"},
+                     :tag :a,
+                     :content ["Log in"]}
+                    "\r\n"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:id "footer-logout", :style "display: none"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs {:id "logged-in-as"},
+                     :tag :span,
+                     :content nil}
+                    "\r\n"
+                    {:type :element,
+                     :attrs {:class "header-logout"},
+                     :tag :div,
+                     :content
+                     ["\r\n"
+                      {:type :element,
+                       :attrs
+                       {:style "cursor:pointer;",
+                        :onclick "Login.logout();return false;",
+                        :data-ajax "false",
+                        :rel "nofollow"},
+                       :tag :a,
+                       :content ["Log out"]}
+                      "\r\n"]}
+                    "\r\n"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:class "ui-headerbar-more-item",
+                  :data-role "headerbar-more-item"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:data-role "popupbutton", :id "buttonMenu"},
+                   :tag :a,
+                   :content
+                   [{:type :element,
+                     :attrs {:class "ikea-navbar-more-image"},
+                     :tag :div,
+                     :content nil}]}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:data-role "selectlist", :id "more-item-popup"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs
+               {:data-role "listview",
+                :data-theme "d",
+                :id "ikea-more-list"},
+               :tag :ul,
+               :content ["\r\n"]}
+              "\r\n"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :comment, :content [" page content"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-breadcrumbs", :itemprop "breadcrumb"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:onclick "history.back();", :href "#"},
+             :tag :a,
+             :content ["Back"]}
+            {:type :element,
+             :attrs {:class "ikea-breadcrumb-divider-slash"},
+             :tag :span,
+             :content [" / "]}
+            "Package measurement ...\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:data-role "content"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:data-role "listview",
+              :data-inset "true",
+              :data-theme "d"},
+             :tag :ul,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "productRow"},
+               :tag :li,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-product-list-wrapper"},
+                 :tag :div,
+                 :content
+                 ["\n"
+                  {:type :element,
+                   :attrs {:class "ikea-product-pricetag-imageCell"},
+                   :tag :div,
+                   :content
+                   ["\n"
+                    {:type :element,
+                     :attrs
+                     {:class "ikea-product-img",
+                      :align "left",
+                      :src
+                      "http://www.ikea.com/PIAimages/0252390_PE391192_S2.JPG",
+                      :alt "BILLY, Height extension unit, black-brown"},
+                     :tag :img,
+                     :content nil}
+                    "\n"]}
+                  "\n"
+                  {:type :element,
+                   :attrs {:class "ikea-product-pricetag-cell"},
+                   :tag :div,
+                   :content
+                   ["\n"
+                    {:type :element,
+                     :attrs {:class "ikea-product-tag"},
+                     :tag :div,
+                     :content
+                     ["\n"
+                      {:type :element,
+                       :attrs
+                       {:class "ikea-product-pricetag-name",
+                        :itemprop "itemListElement"},
+                       :tag :div,
+                       :content ["BILLY"]}
+                      "\n"
+                      {:type :element,
+                       :attrs {:class "ikea-product-pricetag-desc"},
+                       :tag :span,
+                       :content
+                       ["Height extension unit,"
+                        {:type :element,
+                         :attrs nil,
+                         :tag :span,
+                         :content
+                         ["\n"
+                          {:type :element,
+                           :attrs {:class "ikea-product-pricetag-desc"},
+                           :tag :span,
+                           :content
+                           [{:type :element,
+                             :attrs nil,
+                             :tag :nobr,
+                             :content ["80x28x35 cm"]}]}
+                          "\n"
+                          {:type :element,
+                           :attrs {:class "ikea-product-pricetag-price"},
+                           :tag :div,
+                           :content
+                           [{:type :element,
+                             :attrs {:class "inline"},
+                             :tag :span,
+                             :content ["$40"]}]}
+                          "\n"]}]}]}
+                    "\n"
+                    {:type :element,
+                     :attrs {:class "ikeaproduct-package-count"},
+                     :tag :span,
+                     :content ["\n1 packages in total\n"]}
+                    "\n"]}
+                  "\n"]}
+                "\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs {:class "product-measurements"},
+               :tag :li,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-measurements"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs nil,
+                   :tag :h2,
+                   :content ["BILLY"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs nil,
+                   :tag :p,
+                   :content
+                   ["\r\nHeight extension unit"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nArticle Number: 902.638.55"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\n"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "ikea-package-multiple-disclaimer"},
+                   :tag :div,
+                   :content [" Article consists of several packages"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "ikea-measurements-table-row"},
+                   :tag :div,
+                   :content
+                   ["\r\nWidth:\r\n37 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nHeight:\r\n6 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nLength:\r\n78 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nWeight:\r\n7.84 kg\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\n"
+                    {:type :element,
+                     :attrs {:class "measure-quantity"},
+                     :tag :div,
+                     :content [" 1 packages "]}
+                    "\r\n"]}]}]}
+              "\r\n"]}]}
+          "\r\n"
+          "\r\n"]}
+        "\r\n"
+        {:type :comment, :content [" /page content"]}
+        "\r\n"
+        {:type :comment, :content [" common footer"]}
+        "\r\n"
+        {:type :element,
+         :attrs {:class "ikea-footer"},
+         :tag :div,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs {:class "ikea-right-link-new"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:onclick "$.mobile.silentScroll(1);"},
+             :tag :a,
+             :content ["Go to top"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-divider"},
+           :tag :div,
+           :content nil}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-label"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["Follow us on"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://www.facebook.com/ikea.au",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Facebook"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://www.pinterest.com/IKEA_Australia/",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Pinterest"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "http://everyday.ikea.com/",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Tumblr"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://instagram.com/ikea_australia",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Instagram"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "http://www.youtube.com/user/IKEAAUSTRALIA",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["YouTube"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["IKEA Catalogue App"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://itunes.apple.com/au/app/ikea-catalogue/id386592716?mt=8"},
+                 :tag :a,
+                 :content ["iOS"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://play.google.com/store/apps/details?id=com.ikea.catalogue.android"},
+                 :tag :a,
+                 :content ["Android"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["IKEA Store App"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://itunes.apple.com/au/app/ikea-store/id976577934?mt=8",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["iOS"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://play.google.com/store/apps/details?id=com.ikea.kompis&hl=en",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Android"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-full-site"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:href "http://www.ikea.com/au/en/?preferedui=desktop",
+              :data-location "footer",
+              :onclick "return waMLS.appendIRWURL(this)"},
+             :tag :a,
+             :content ["Full site"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-copyright"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-legal"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "ikea-footer-label"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-footer-label-inner"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:class "linksRow"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href "http://www.ikea.com.au/privacy"},
+                     :tag :a,
+                     :content ["Privacy policy"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href "http://www.ikea.com.au/cookies"},
+                     :tag :a,
+                     :content ["Cookies privacy"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href
+                      "http://www.ikea.com/au/en/about_ikea/newsroom/product_recalls"},
+                     :tag :a,
+                     :content ["Product recalls"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href
+                      "http://m.ikea.com/au/en/pages/customer_service/contact/"},
+                     :tag :a,
+                     :content ["Contact us"]}
+                    "\r\n"]}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n© Inter IKEA Systems B.V. 1999-2015\r\n"]}
+          "\r\n"]}
+        "\r\n"
+        "\r\n"
+        {:type :element,
+         :attrs nil,
+         :tag :noscript,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs {:style "display: none;"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:src
+              "https://smetrics.ikea.com/b/ss/ikeaallmlsnojavascriptprod/5/?c8=au&v7=au&pageName=nojavascript",
+              :alt "",
+              :width "5",
+              :height "5"},
+             :tag :img,
+             :content nil}
+            "\r\n"]}
+          "\r\n"]}
+        "\r\n"
+        "\r\n"]}]}]}
+  )
+
+(def multi
+
+  {:type :document,
+   :content
+   [{:type :document-type,
+     :attrs {:name "html", :publicid "", :systemid ""}}
+    {:type :element,
+     :attrs nil,
+     :tag :html,
+     :content
+     [{:type :element,
+       :attrs nil,
+       :tag :head,
+       :content
+       ["\r\n"
+        {:type :comment, :content [" config "]}
+        "\r\n"
+        {:type :element,
+         :attrs {:charset "utf-8"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "viewport",
+          :id "viewport",
+          :content
+          "width=device-width,minimum-scale=1.0,maximum-scale=1.0,initial-scale=1.0"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs {:name "format-detection", :content "telephone=no"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "title",
+          :content "HEMNES - Package measurement and weight - IKEA"},
+         :tag :meta,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs {:name "ROBOTS", :content "NOINDEX, FOLLOW"},
+         :tag :meta,
+         :content nil}
+        "\n"
+        {:type :comment, :content [" libraries "]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "stylesheet",
+          :href "/irmw-resources/external/jquery.mobile-1.2.0.min.css"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :src "/irmw-resources/external/jquery-1.8.2.min.js"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :src
+          "/irmw-resources/external/jquery.mobile.custom-1.2.0.min.js"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :comment, :content [" custom stylesheets and scripts "]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "stylesheet",
+          :href "/irmw-resources/css/ikea.mobile.min.css?v=5.20"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:type "text/javascript",
+          :charset "utf-8",
+          :src "/irmw-resources/js/ikea.mobile.min.js?v=5.20"},
+         :tag :script,
+         :content nil}
+        "\r\n"
+        {:type :comment, :content [" page specifc scripts "]}
+        "\r\n"
+        {:type :element,
+         :attrs nil,
+         :tag :script,
+         :content
+         ["\r\n(function($){\r\nfunction loadSettings(){\r\n//Initialize dynamic configuration.\r\n$.mobile.retailUnit = 'au';\r\n$.mobile.languageCode = 'en';\r\n$.mobile.loadingMessage = \"Loading\";\r\n$.mobile.defaultShoppingBagName = \"Shopping list\";\r\n$.mobile.offlineLoadError = \"The page you tried to go to can\\'t be shown right now. You can still view your shopping list.\";\r\n$.mobile.loggedInAsMessage =\"Logged in as {1}\";\r\n$.mobile.currencySettings = {symbol: \"$\", decimalSymbol: \".\", positiveFormat: \"%s%n\", negativeFormat: \"-%s%n\", groupSymbol: \",\", fractionDigits: \"2\", showFractionDigitsOnInteger: \"false\", integerSymbol: \"\", currencyCode: \"AUD\"};\r\n$.mobile.showLocalPrices = false;\r\n$.mobile.baseUrl = '/au/en/';\r\n$.mobile.isProduction = true;\r\n$.mobile.cookieDomain = '.ikea.com';\r\n$.mobile.fullSiteUrl = 'www.ikea.com';\r\n$.mobile.secureFullSiteUrl = 'secure.ikea.com';\r\n$.mobile.enableTealium =false;\r\n}\r\nloadSettings();\r\n}(jQuery));\r\n"]}
+        "\r\n"
+        {:type :element,
+         :attrs {:id "txtHtmlTitle"},
+         :tag :title,
+         :content ["HEMNES - Package measurement and weight - IKEA"]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "shortcut icon",
+          :type "image/ico",
+          :href "/irmw-resources/img/favicon.ico"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :href
+          "/irmw-resources/img/apple-touch-icon-57x57-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "114x114",
+          :href
+          "/irmw-resources/img/apple-touch-icon-114x114-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "144x144",
+          :href
+          "/irmw-resources/img/apple-touch-icon-144x144-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "32x32",
+          :href
+          "/irmw-resources/img/apple-touch-icon-32x32-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "57x57",
+          :href
+          "/irmw-resources/img/apple-touch-icon-57x57-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:rel "apple-touch-icon-precomposed",
+          :sizes "72x72",
+          :href
+          "/irmw-resources/img/apple-touch-icon-72x72-precomposed.png"},
+         :tag :link,
+         :content nil}
+        "\r\n"
+        {:type :comment,
+         :content [" For Windows Phone (only 144x144 used)"]}
+        "\r\n"
+        {:type :element,
+         :attrs
+         {:name "msapplication-TileImage",
+          :content
+          "/irmw-resources/img/apple-touch-icon-144x144-precomposed.png"},
+         :tag :meta,
+         :content nil}
+        "\r\n"]}
+      "\r\n"
+      {:type :element,
+       :attrs {:itemscope "", :itemtype "http://schema.org/WebPage"},
+       :tag :body,
+       :content
+       ["\r\n"
+        {:type :element,
+         :attrs
+         {:data-role "page",
+          :data-theme "d",
+          :id "mls1449292401974",
+          :data-dom-cache "false"},
+         :tag :div,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs
+           {:data-role "header",
+            :id "ikea-homeheader",
+            :data-backbtn "false"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:id "ikea-home-topbar"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:id "ikea-topbar-logocell"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:tabindex "-1", :href "/au/en/", :rel "external"},
+                 :tag :a,
+                 :content
+                 [{:type :element,
+                   :attrs {:class "ikea-logo", :title ""},
+                   :tag :span,
+                   :content nil}]}
+                "\r\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs {:id "ikea-topbar-iconcell"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:id "search-icon",
+                  :onclick
+                  "$(this).parent().siblings('#ikea-topbar-searchfield').first().searchdecorate('toggle');"},
+                 :tag :div,
+                 :content nil}
+                "\r\n"
+                {:type :element,
+                 :attrs {:id "list-icon"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs
+                   {:href "http://m.ikea.com/au/en/shoppinglist/",
+                    :rel "external"},
+                   :tag :a,
+                   :content nil}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "list-icon"},
+                   :tag :div,
+                   :content nil}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs
+               {:data-role "searchdecorate",
+                :id "ikea-topbar-searchfield"},
+               :tag :div,
+               :content nil}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs
+             {:id "ikea-navbar",
+              :data-role "headerbar",
+              :data-morelistid "#ikea-more-list"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs nil,
+               :tag :ul,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs
+                 {:class "ikea-navbar-item ikea-navbar-item-active",
+                  :data-icon "none"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs
+                   {:href "http://m.ikea.com/au/en/catalog/functional/"},
+                   :tag :a,
+                   :content ["Products"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-navbar-item ", :data-icon "none"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:href "http://m.ikea.com/au/en/stores/"},
+                   :tag :a,
+                   :content ["Stores"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-navbar-item", :id "ikea-nav-li"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:id "footer-login", :style "display: none"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs
+                     {:id "login1",
+                      :href "/au/en/login/",
+                      :rel "nofollow",
+                      :data-ajax "false"},
+                     :tag :a,
+                     :content ["Log in"]}
+                    "\r\n"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:id "footer-logout", :style "display: none"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs {:id "logged-in-as"},
+                     :tag :span,
+                     :content nil}
+                    "\r\n"
+                    {:type :element,
+                     :attrs {:class "header-logout"},
+                     :tag :div,
+                     :content
+                     ["\r\n"
+                      {:type :element,
+                       :attrs
+                       {:style "cursor:pointer;",
+                        :onclick "Login.logout();return false;",
+                        :data-ajax "false",
+                        :rel "nofollow"},
+                       :tag :a,
+                       :content ["Log out"]}
+                      "\r\n"]}
+                    "\r\n"]}
+                  "\r\n"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:class "ui-headerbar-more-item",
+                  :data-role "headerbar-more-item"},
+                 :tag :li,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:data-role "popupbutton", :id "buttonMenu"},
+                   :tag :a,
+                   :content
+                   [{:type :element,
+                     :attrs {:class "ikea-navbar-more-image"},
+                     :tag :div,
+                     :content nil}]}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:data-role "selectlist", :id "more-item-popup"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs
+               {:data-role "listview",
+                :data-theme "d",
+                :id "ikea-more-list"},
+               :tag :ul,
+               :content ["\r\n"]}
+              "\r\n"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :comment, :content [" page content"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-breadcrumbs", :itemprop "breadcrumb"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:onclick "history.back();", :href "#"},
+             :tag :a,
+             :content ["Back"]}
+            {:type :element,
+             :attrs {:class "ikea-breadcrumb-divider-slash"},
+             :tag :span,
+             :content [" / "]}
+            "Package measurement ...\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:data-role "content"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:data-role "listview",
+              :data-inset "true",
+              :data-theme "d"},
+             :tag :ul,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "productRow"},
+               :tag :li,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-product-list-wrapper"},
+                 :tag :div,
+                 :content
+                 ["\n"
+                  {:type :element,
+                   :attrs {:class "ikea-product-pricetag-imageCell"},
+                   :tag :div,
+                   :content
+                   ["\n"
+                    {:type :element,
+                     :attrs
+                     {:class "ikea-product-img",
+                      :align "left",
+                      :src
+                      "http://www.ikea.com/PIAimages/0137908_PE296720_S2.JPG",
+                      :alt
+                      "HEMNES, Cabinet with panel/glass-door, black-brown"},
+                     :tag :img,
+                     :content nil}
+                    "\n"]}
+                  "\n"
+                  {:type :element,
+                   :attrs {:class "ikea-product-pricetag-cell"},
+                   :tag :div,
+                   :content
+                   ["\n"
+                    {:type :element,
+                     :attrs {:class "ikea-product-tag"},
+                     :tag :div,
+                     :content
+                     ["\n"
+                      {:type :element,
+                       :attrs
+                       {:class "ikea-product-pricetag-name",
+                        :itemprop "itemListElement"},
+                       :tag :div,
+                       :content ["HEMNES"]}
+                      "\n"
+                      {:type :element,
+                       :attrs {:class "ikea-product-pricetag-desc"},
+                       :tag :span,
+                       :content
+                       ["Cabinet with panel/glass-door,"
+                        {:type :element,
+                         :attrs nil,
+                         :tag :span,
+                         :content
+                         ["\n"
+                          {:type :element,
+                           :attrs {:class "ikea-product-pricetag-desc"},
+                           :tag :span,
+                           :content
+                           [{:type :element,
+                             :attrs nil,
+                             :tag :nobr,
+                             :content ["90x197 cm"]}]}
+                          "\n"
+                          {:type :element,
+                           :attrs {:class "ikea-product-pricetag-price"},
+                           :tag :div,
+                           :content
+                           [{:type :element,
+                             :attrs {:class "inline"},
+                             :tag :span,
+                             :content ["$399"]}]}
+                          "\n"]}]}]}
+                    "\n"
+                    {:type :element,
+                     :attrs {:class "ikeaproduct-package-count"},
+                     :tag :span,
+                     :content ["\n2 packages in total\n"]}
+                    "\n"]}
+                  "\n"]}
+                "\n"]}
+              "\r\n"
+              {:type :element,
+               :attrs {:class "product-measurements"},
+               :tag :li,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-measurements"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs nil,
+                   :tag :h2,
+                   :content ["HEMNES"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs nil,
+                   :tag :p,
+                   :content
+                   ["\r\nCabinet with panel/glass-door"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nArticle Number: 202.271.25"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\n"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "ikea-package-multiple-disclaimer"},
+                   :tag :div,
+                   :content [" Article consists of several packages"]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "ikea-measurements-table-row"},
+                   :tag :div,
+                   :content
+                   ["\r\nWidth:\r\n38 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nHeight:\r\n14 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nLength:\r\n100 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nWeight:\r\n20.00 kg\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\n"
+                    {:type :element,
+                     :attrs {:class "measure-quantity"},
+                     :tag :div,
+                     :content [" 1 packages "]}
+                    " "]}
+                  "\r\n"
+                  {:type :element,
+                   :attrs {:class "ikea-measurements-table-row-child"},
+                   :tag :div,
+                   :content
+                   ["\r\nWidth:\r\n45 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nHeight:\r\n9 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nLength:\r\n199 cm\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\nWeight:\r\n23.00 kg\r\n"
+                    {:type :element, :attrs nil, :tag :br, :content nil}
+                    "\r\n"
+                    {:type :element,
+                     :attrs {:class "measure-quantity"},
+                     :tag :div,
+                     :content [" 1 packages "]}
+                    "\r\n"]}]}]}
+              "\r\n"]}]}
+          "\r\n"
+          "\r\n"]}
+        "\r\n"
+        {:type :comment, :content [" /page content"]}
+        "\r\n"
+        {:type :comment, :content [" common footer"]}
+        "\r\n"
+        {:type :element,
+         :attrs {:class "ikea-footer"},
+         :tag :div,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs {:class "ikea-right-link-new"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:onclick "$.mobile.silentScroll(1);"},
+             :tag :a,
+             :content ["Go to top"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-divider"},
+           :tag :div,
+           :content nil}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-label"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["Follow us on"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://www.facebook.com/ikea.au",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Facebook"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://www.pinterest.com/IKEA_Australia/",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Pinterest"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "http://everyday.ikea.com/",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Tumblr"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "https://instagram.com/ikea_australia",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Instagram"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href "http://www.youtube.com/user/IKEAAUSTRALIA",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["YouTube"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["IKEA Catalogue App"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://itunes.apple.com/au/app/ikea-catalogue/id386592716?mt=8"},
+                 :tag :a,
+                 :content ["iOS"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://play.google.com/store/apps/details?id=com.ikea.catalogue.android"},
+                 :tag :a,
+                 :content ["Android"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-label-inner"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "linksRow"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs nil,
+                 :tag :label,
+                 :content ["IKEA Store App"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://itunes.apple.com/au/app/ikea-store/id976577934?mt=8",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["iOS"]}
+                "\r\n"
+                {:type :element, :attrs nil, :tag :span, :content ["|"]}
+                "\r\n"
+                {:type :element,
+                 :attrs
+                 {:data-ajax "false",
+                  :href
+                  "https://play.google.com/store/apps/details?id=com.ikea.kompis&hl=en",
+                  :target "_blank"},
+                 :tag :a,
+                 :content ["Android"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-full-site"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:href "http://www.ikea.com/au/en/?preferedui=desktop",
+              :data-location "footer",
+              :onclick "return waMLS.appendIRWURL(this)"},
+             :tag :a,
+             :content ["Full site"]}
+            "\r\n"]}
+          "\r\n"
+          {:type :element,
+           :attrs {:class "ikea-footer-copyright"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs {:class "ikea-footer-legal"},
+             :tag :div,
+             :content
+             ["\r\n"
+              {:type :element,
+               :attrs {:class "ikea-footer-label"},
+               :tag :div,
+               :content
+               ["\r\n"
+                {:type :element,
+                 :attrs {:class "ikea-footer-label-inner"},
+                 :tag :div,
+                 :content
+                 ["\r\n"
+                  {:type :element,
+                   :attrs {:class "linksRow"},
+                   :tag :div,
+                   :content
+                   ["\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href "http://www.ikea.com.au/privacy"},
+                     :tag :a,
+                     :content ["Privacy policy"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href "http://www.ikea.com.au/cookies"},
+                     :tag :a,
+                     :content ["Cookies privacy"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href
+                      "http://www.ikea.com/au/en/about_ikea/newsroom/product_recalls"},
+                     :tag :a,
+                     :content ["Product recalls"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs nil,
+                     :tag :span,
+                     :content ["|"]}
+                    "\r\n"
+                    {:type :element,
+                     :attrs
+                     {:data-ajax "false",
+                      :href
+                      "http://m.ikea.com/au/en/pages/customer_service/contact/"},
+                     :tag :a,
+                     :content ["Contact us"]}
+                    "\r\n"]}
+                  "\r\n"]}
+                "\r\n"]}
+              "\r\n"]}
+            "\r\n© Inter IKEA Systems B.V. 1999-2015\r\n"]}
+          "\r\n"]}
+        "\r\n"
+        "\r\n"
+        {:type :element,
+         :attrs nil,
+         :tag :noscript,
+         :content
+         ["\r\n"
+          {:type :element,
+           :attrs {:style "display: none;"},
+           :tag :div,
+           :content
+           ["\r\n"
+            {:type :element,
+             :attrs
+             {:src
+              "https://smetrics.ikea.com/b/ss/ikeaallmlsnojavascriptprod/5/?c8=au&v7=au&pageName=nojavascript",
+              :alt "",
+              :width "5",
+              :height "5"},
+             :tag :img,
+             :content nil}
+            "\r\n"]}
+          "\r\n"]}
+        "\r\n"
+        "\r\n"]}]}]}
+  ))


add links to mentions

add links to mentions

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starcity-properties/odin.git/'


break shader code into seperate namespace

error: pathspec 'src/splash/shaders.cljs' did not match any file(s) known to git

--- src/splash/shaders.cljs
+++ src/splash/shaders.cljs
@@ -0,0 +1,107 @@
+(ns ^:figwheel-always splash.shaders
+    (:require [infinitelives.pixi.canvas :as canv]
+              [infinitelives.pixi.resources :as resources]
+              [infinitelives.pixi.sprite :as sprite]
+              [infinitelives.utils.events :as events]
+              [infinitelives.utils.console :refer [log]]
+              [infinitelives.pixi.texture :as texture]
+              [infinitelives.pixi.font :as font]
+              [infinitelives.utils.math :as math]
+              [infinitelives.utils.sound :as sound]
+              ;[splash.macros :refer-macros [ignore]]
+              [cljs.core.async :refer [<!]]
+              [PIXI])
+    (:require-macros [cljs.core.async.macros :refer [go]]
+                     [splash.macros :as macros]
+                     ))
+
+;;
+;; Horizontal Hue colour bars moving vertically
+;;
+(defn make-colour-bars []
+  (let [f
+        (PIXI/AbstractFilter.
+         #js [
+              "
+precision mediump float;
+varying vec2 vTextureCoord;
+varying vec4 vColor;
+uniform sampler2D uSampler;
+uniform float time;
+
+// hue,saturation,value -> red,green,blue
+vec3 hsv2rgb(vec3 c)
+{
+    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+
+// shader mainline
+void main( void ) {
+    vec4 col = texture2D(uSampler, vTextureCoord);
+
+    float r=col.r;
+    float g=col.g;
+    float b=col.b;
+
+    // is our hue magenta?
+    bool mag = ((r-b)<0.1) && r>0.1 && g<0.1;
+
+    if(mag)
+    {
+         gl_FragColor = vec4(
+             hsv2rgb(
+                 vec3(4.0 * vTextureCoord.y + (-0.02 * time),
+                      1.0,
+                      1.0)),
+             1.0) * r;
+    }
+    else
+    {
+        gl_FragColor = col;
+    }
+}
+"]
+         #js {"time" #js {"type" "1f" "value" 0.0}})]
+    (go
+      (loop [frame 0]
+        (<! (events/next-frame))
+        (set! (.-uniforms.time.value f) (float frame))
+        (recur (inc frame))))
+    f))
+
+;;
+;; Wave scroller
+;;
+(defn make-wavy []
+  (let [f
+        (PIXI/AbstractFilter.
+         #js [
+              "
+precision mediump float;
+varying vec2 vTextureCoord;
+varying vec4 vColor;
+uniform sampler2D uSampler;
+uniform float time;
+
+// shader mainline
+void main( void ) {
+    float x=vTextureCoord.x;
+    float y=vTextureCoord.y;
+    vec4 col = texture2D(uSampler,
+                         vec2(x,
+                              y - 0.05 * sin(0.1 * time + 10.0 * x))
+                         //vec2(x,y)
+                         );
+
+    gl_FragColor = col;
+}
+"]
+         #js {"time" #js {"type" "1f" "value" 0.0}})]
+    (go
+      (loop [frame 0]
+        (<! (events/next-frame))
+        (set! (.-uniforms.time.value f) (float frame))
+        (recur (inc frame))))
+    f))


Add subscription for selected files and validation for next step


latest

latest

error: pathspec 'script/figwheel.clj' did not match any file(s) known to git

--- script/figwheel.clj
+++ script/figwheel.clj
@@ -0,0 +1,17 @@
+(require '[figwheel-sidecar.repl :as r]
+  '[figwheel-sidecar.repl-api :as ra])
+
+(ra/start-figwheel!
+  {:figwheel-options {}
+   :build-ids ["dev"]
+   :all-builds
+   [{:id "dev"
+     :figwheel true
+     :source-paths ["src"]
+     :compiler {:main 'ui-check.core
+                :asset-path "js"
+                :output-to "resources/public/js/main.js"
+                :output-dir "resources/public/js"
+                :verbose true}}]})
+
+(ra/cljs-repl)


temporarily fork core.async so that local installs are not necessary

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/aamedina/foundation-app.git/'


Fill in blanks for mail configuration

Fill in blanks for mail configuration

--- src/clojars/web/user.clj
+++ src/clojars/web/user.clj
@@ -116,11 +116,11 @@
 (defn send-mail [to subject message]
   (let [{:keys [hostname username password port ssl from]} (config/config :mail)
         mail (doto (SimpleEmail.)
-               (.setHostName hostname)
-               (.setSslSmtpPort (str port))
-               (.setSmtpPort port)
-               (.setSSL ssl)
-               (.setFrom from "Clojars")
+               (.setHostName (or hostname "localhost"))
+               (.setSslSmtpPort (str (or port 25)))
+               (.setSmtpPort (or port 25))
+               (.setSSL (or ssl false))
+               (.setFrom (or from "noreply@clojars.org") "Clojars")
                (.addTo to)
                (.setSubject subject)
                (.setMsg message))]


Add another example code

Add another example code

error: pathspec 'examples/example/core.clj' did not match any file(s) known to git

--- examples/example/core.clj
+++ examples/example/core.clj
@@ -0,0 +1,19 @@
+(ns example.core
+  (:require [example.lib :as lib]))
+
+(defn collatz [n]
+  (lazy-seq
+    (cons n
+          (if (lib/even? n)
+            (collatz (/ n 2))
+            (collatz (+ (* 3 n) 1))))))
+
+(defmacro let1 [name expr & body]
+  `(let [~name ~expr]
+     ~@body))
+
+(def x 42)
+
+(defn f []
+  (let1 x (* x x)
+    (+ x x)))


Remove unused fn util/send-notifications

Nothing is using this any more.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/democracyworks/election-notification-works.git/'


Added failing test for self link

Added failing test for self link

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/physion/ovation-webapi.git/'


Keep the local names

Keep the local names

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/shaunstanislaus/cljs2go.git/'


fix when and to or

fix when and to or


Removed logging.

Removed logging.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/inchingforward/eon.git/'


add a metrics ns

add a metrics ns

error: pathspec 'src/replify/metrics.clj' did not match any file(s) known to git

--- src/replify/metrics.clj
+++ src/replify/metrics.clj
@@ -0,0 +1,30 @@
+(ns replify.metrics
+  (:require
+   [clojure.string :as str]
+   [clojure.java.io :as io]
+   [clojure.edn :as edn]
+   [me.raynes.fs :as fs])
+  (:import
+   clojure.lang.DynamicClassLoader
+   (java.net URL URLClassLoader)))
+
+(defn ls [dir]
+  (filter #(.isFile %) (fs/list-dir dir)))
+
+(defn lc [fname]
+  (with-open [rdr (io/reader fname)]
+    [(fs/name fname)
+     (count (line-seq rdr))]))
+
+(defn stats []
+  (let [lines (map #(lc %) (list-files "."))
+        total-lines (reduce + (map second lines))
+        pages (let [max (quot total-lines 54)
+                    min (quot total-lines 62)]
+                [min max])]
+    (println "Line Count")
+    (println lines)
+    (println "Total Lines")
+    (println total-lines)
+    (println "Pages (min, max)")
+    (println pages)))


Implemented the Nth Concat problem

Implemented the Nth Concat problem

error: pathspec 'src/clojush/problems/software/benchmarks_v2/nth_concat.clj' did not match any file(s) known to git

--- src/clojush/problems/software/benchmarks_v2/nth_concat.clj
+++ src/clojush/problems/software/benchmarks_v2/nth_concat.clj
@@ -0,0 +1,229 @@
+;; nth_concat.clj
+;; Tom Helmuth, thelmuth@hamilton.edu
+;;
+;; Note that this problem is harder for clojush because string_nth finds the
+;; integer mod the size of the string to get the index.
+;;
+;; Problem inspired by Hamilton College CS 101 lab problem
+
+(ns clojush.problems.software.benchmarks-v2.nth-concat
+  (:use clojush.pushgp.pushgp
+        [clojush pushstate interpreter random util globals]
+        clojush.instructions.tag)
+  (:require [clojure.math.numeric-tower :as nt]))
+
+; Atom generators
+(def atom-generators
+  (make-proportional-atom-generators
+   (concat
+    (registered-for-stacks [:vector_string :string :char :integer :boolean :exec])
+    (list (tag-instruction-erc [:vector_string :string :char :integer :boolean :exec] 1000) ; tags
+          (tagged-instruction-erc 1000)))
+   (list 'in1 'in2) ; inputs
+   (list []
+         ""
+         0
+         1
+         ) ; constants
+   {:proportion-inputs 0.15
+    :proportion-constants 0.05}))
+
+(def nth-concat-chars (map char (range 32 127)))
+
+(defn nth-concat-random-string
+  "Creates a string for this problem of length len"
+  [len]
+  (apply str (repeatedly len #(lrand-nth nth-concat-chars))))
+
+(defn rand-int-range
+  "Returns an integer between start (inclusive) and end (exclusive)"
+  [start end]
+  (+ start (lrand-int (- end start))))
+
+(defn nth-concat-input-all-have-n
+  "Creates a vector of strings of length len and an index n in [0,19].
+   This version makes sure all strings have length > n, so that all
+   strings will contribute to answer"
+  [len]
+  (let [n (lrand-int 20)]
+    (vector (vec (repeatedly len #(nth-concat-random-string
+                                   (rand-int-range (inc n) 21))))
+            n)))
+
+(defn nth-concat-input-some-shorter-than-n
+  "Creates a vector of strings of length len and an index n in [0,20].
+   Note that if n = 20, it will always be outside of the indices of the strings.
+   This version allows strings of any length [0,20], so that some will not
+   have index n in them."
+  [len]
+  (let [n (lrand-int 21)]
+    (vector (vec (repeatedly len #(nth-concat-random-string
+                                   (lrand-int 21))))
+            n)))
+
+;; A list of data domains. Each domain is a vector containing
+;; a "set" of inputs and two integers representing how many cases from the set
+;; should be used as training and testing cases respectively. Each "set" of
+;; inputs is either a list or a function that, when called, will create a
+;; random element of the set.
+(def data-domains
+  [[(list [[] 5]
+          [[] 20]
+          [["arbitrary string"] 8]
+          [["01234567890123456789"] 19]
+          [["a" "b" "c" "d" "e" "f" "g"] 0]
+          [["12" "34" "56" "78" "90"] 1]
+          [["12ab" "34cd" "56ef" "78gh" "90ij"] 1]
+          [["hi" "there" "world" "is" "nice" "!"] 0]
+          [["POIUYTREWQLKJHGFDSAM"
+            "zxcvbnmasdfghjklqwer"
+            "~!@#$%^&*()_+-=[];'."
+            "0987654321poiuytNBVC"] 19]
+          [["cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY"
+            "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD"] 2])
+    10 0] ; inputs where n is within the bounds of every string in the vector
+   [(list [["friend"] 10]
+          [["01234567890123456789"] 20]
+          [["its in" "out"] 3]
+          [["too short" "this one is longer"] 16]
+          [["one long string here" "a bit shorter" "also pretty long"] 14]
+          [["G" "" "O" "" "A" "" "T" ""] 0]
+          [["hi" "there" "world" "is" "nice" "!"] 1]
+          [["hi" "there" "world" "is" "nice" "!"] 2]
+          [["hi" "there" "world" "is" "nice" "!"] 3]
+          [["hi" "there" "world" "is" "nice" "!"] 4]
+          [["hi" "there" "world" "is" "nice" "!"] 5]
+          [["hi" "there" "world" "is" "nice" "!"] 20]
+          [["" "1" "23" "456" "789q" "werty" "uiop[]" "asdfghj" "kl;'zxcv" "bnm,./<>?"] 0]
+          [["" "1" "23" "456" "789q" "werty" "uiop[]" "asdfghj" "kl;'zxcv" "bnm,./<>?"] 5]
+          [["" "1" "23" "456" "789q" "werty" "uiop[]" "asdfghj" "kl;'zxcv" "bnm,./<>?"] 8]
+          [["" "1" "23" "456" "789q" "werty" "uiop[]" "asdfghj" "kl;'zxcv" "bnm,./<>?"] 15]
+          [["POIUYTREWQLKJHGFDSAM"
+            "zxcvbnmasdfghjklqwer"
+            "~!@#$%^&*()_+-=[];'."
+            "0987654321poiuytNBVC"] 20]
+          [["cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY" "cat" "SUNNY"
+            "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD" "dog" "HAROLD"] 3]
+          [(vec (repeat 20 "")) 12]
+          [["" "456" "" "" "" "321" "" "" "train" "" "      " "QUACK" "" "" "" "" "ken" ""] 1]
+          )
+    20 0] ; inputs where n is outside the bounds of some strings
+   [(fn [] (nth-concat-input-all-have-n (rand-int-range 1 21))) 50 1000] ; all indices are in bounds
+   [(fn [] (nth-concat-input-some-shorter-than-n (rand-int-range 1 21))) 120 1000]] ; some indices may be out of bounds
+)
+
+;;Can make test data like this:
+(comment (test-and-train-data-from-domains data-domains))
+
+(defn solve-nth-concat
+  "Solves the problem given the input."
+  [[vec-of-str n]]
+  (apply str
+         (map (fn [string]
+                (if (< n (count string))
+                  (nth string n)
+                  ""))
+              vec-of-str)))
+
+; Helper function for error function
+(defn test-cases
+  "Takes a sequence of inputs and gives IO test cases of the form
+   [[input1 input2] output]."
+  [inputs]
+  (map (fn [in]
+         (vector in
+                 (solve-nth-concat in)))
+       inputs))
+
+(defn make-error-function-from-cases
+  "Creates and returns the error function based on the train/test cases."
+  [train-cases test-cases]
+  (fn the-actual-error-function
+    ([individual]
+     (the-actual-error-function individual :train))
+    ([individual data-cases] ;; data-cases should be :train or :test
+     (the-actual-error-function individual data-cases false))
+    ([individual data-cases print-outputs]
+     (let [behavior (atom '())
+           errors (doall
+                   (for [[[input1 input2] correct-output] (case data-cases
+                                                            :train train-cases
+                                                            :test test-cases
+                                                            [])]
+                     (let [final-state (run-push (:program individual)
+                                                 (->> (make-push-state)
+                                                      (push-item input2 :input)
+                                                      (push-item input1 :input)))
+                           result (top-item :string final-state)]
+                       (when print-outputs
+                         (println (format "| Correct output: %s\n| Program output: %s\n" (str correct-output) (str result))))
+                         ; Record the behavior
+                       (swap! behavior conj result)
+                         ; Error is Levenshtein distance
+                       (if (string? result)
+                         (levenshtein-distance correct-output (str result))
+                         1000000) ; penalty for no return value
+                       )))]
+       (if (= data-cases :train)
+         (assoc individual :behaviors @behavior :errors errors)
+         (assoc individual :test-errors errors))))))
+
+(defn get-train-and-test
+  "Returns the train and test cases."
+  [data-domains]
+  (map test-cases
+       (test-and-train-data-from-domains data-domains)))
+
+; Define train and test cases
+(def train-and-test-cases
+  (get-train-and-test data-domains))
+
+(defn initial-report
+  [argmap]
+  (println "Train and test cases:")
+  (doseq [[i case] (map vector (range) (first train-and-test-cases))]
+    (println (format "Train Case: %3d | Input/Output: %s" i (str case))))
+  (doseq [[i case] (map vector (range) (second train-and-test-cases))]
+    (println (format "Test Case: %3d | Input/Output: %s" i (str case))))
+  (println ";;******************************"))
+
+(defn custom-report
+  "Custom generational report."
+  [best population generation error-function report-simplifications]
+  (let [best-test-errors (:test-errors (error-function best :test))
+        best-total-test-error (apply +' best-test-errors)]
+    (println ";;******************************")
+    (printf ";; -*- Find Pair problem report - generation %s\n" generation) (flush)
+    (println "Test total error for best:" best-total-test-error)
+    (println (format "Test mean error for best: %.5f" (double (/ best-total-test-error (count best-test-errors)))))
+    (when (zero? (:total-error best))
+      (doseq [[i error] (map vector
+                             (range)
+                             best-test-errors)]
+        (println (format "Test Case  %3d | Error: %s" i (str error)))))
+    (println ";;------------------------------")
+    (println "Outputs of best individual on training cases:")
+    (error-function best :train true)
+    (println ";;******************************"))) ;; To do validation, could have this function return an altered best individual
+       ;; with total-error > 0 if it had error of zero on train but not on validation
+       ;; set. Would need a third category of data cases, or a defined split of training cases.
+
+
+; Define the argmap
+(def argmap
+  {:error-function (make-error-function-from-cases (first train-and-test-cases)
+                                                   (second train-and-test-cases))
+   :atom-generators atom-generators
+   :max-points 2000
+   :max-genome-size-in-initial-program 250
+   :evalpush-limit 2000
+   :population-size 1000
+   :max-generations 300
+   :parent-selection :lexicase
+   :genetic-operator-probabilities {:uniform-addition-and-deletion 1.0}
+   :uniform-addition-and-deletion-rate 0.09
+   :problem-specific-report custom-report
+   :problem-specific-initial-report initial-report
+   :report-simplifications 0
+   :final-report-simplifications 5000
+   :max-error 1000000})


Intro tasks for Clojure.

Intro tasks for Clojure.

error: pathspec 'clojure-functional/src/test/clojure/fp/test/intro.clj' did not match any file(s) known to git

--- clojure-functional/src/test/clojure/fp/test/intro.clj
+++ clojure-functional/src/test/clojure/fp/test/intro.clj
@@ -0,0 +1,106 @@
+(ns fp.test.intro
+  (:use [clojure.test]))
+
+; Placeholders do not touch.
+(defn __ [& args] false)
+(def ___ '())
+
+
+(deftest very-basic-types
+  (is (= 1 1))
+  (is (true? true))
+  (is (= (str \a \b \c) "abc"))
+  ;; Use vector literal
+  (is (= '(1 2 3) [1 2 3]))
+  ;; Use list literal
+  (is (= [1 2 3] '(1 2 3)))
+  ;; Use list function
+  (is (= [1 2 3] (list 1 2 3)))
+  ;; Use vector function
+  (is (= [1 2 3] (vector 1 2 3))))
+
+
+(deftest use-conjoin
+  (are [x y] (= x y)
+    (conj [1] 2) [1 2]
+    (conj [1 2] 3 4) [1 2 3 4]
+    (conj '(2 1) 3 4) '(4 3 2 1)
+    (conj #{2 1} 3 4) #{1 2 3 4}))
+
+;; Use (doc conj) for info
+
+
+(deftest how-to-count-stuff
+  (are [x y] (= x y)
+       5 (count '(1 2 3 4 5))
+       4 (count [1 2 3 4])
+       10 (count (range 10))
+       4 (count {:a 1, :b 2, :c 3, :d 4})
+       8 (count "En banan")))
+
+
+(deftest how-to-find-length-of-something
+  (let [f count]
+    (are [x y] (= x y)
+      (f '(1 2 3 3 1)) 5
+      (f "Hello World") 11
+      (f [[1 2] [3 4] [5 6]]) 3
+      (f '(13)) 1
+      (f '(:a :b :c))) 3))
+
+
+(deftest using-if
+  (are [x y] (= x y)
+       (if (> 1 0)
+         true
+         false)
+       true
+
+       (if (> 3 2 1)
+         true
+         false)
+       true
+
+       (if (> 3 2 3 1)
+         true
+         false)
+       false))
+
+
+(deftest dealing-with-lists
+  (are [x y] (= x y)
+       (first '(1 2 3 4))  1
+       (last '(1 2 3 4 5)) 5
+       (take 2 '(1 2 3 4)) '(1 2)))
+
+(deftest operating-on-lists
+  (are [x y] (= x y)
+       (apply + [1 2 3 4]) 10
+       (apply - [4 3 2 1]) -2
+       (apply * [1 2 3 4]) 24
+       ))
+
+
+(deftest how-to-filter-out-the-stuff-you-want
+  (are [x y] (= x y)
+    (filter odd? '(1 2 3 4 5)) '(1 3 5)
+    (filter even? '(1 2 3 4 5)) '(2 4)))
+
+
+(deftest define-a-function-that-checks-length
+  (let [long? (fn [str len] (> (count str) len))]
+    (are [x y] (= x y)
+         (long? "long string" 5) true
+         (long? "short" 5) false
+         (long? nil 2) false
+         )))
+
+
+(deftest use-map-to-double-all-numbers-in-a-sequence
+  (let [double (fn [i] (* 2 i))]
+    (are [x y] (= x y)
+         (map double '(1 2 3)) '(2 4 6)
+         (map double '(5 10 15)) '(10 20 30))))
+
+
+(run-tests)


Create persistence.clj

Create persistence.clj

error: pathspec 'src/iyye/persistence.clj' did not match any file(s) known to git

--- src/iyye/persistence.clj
+++ src/iyye/persistence.clj
@@ -0,0 +1,58 @@
+ ; Iyye - AI agent
+; Copyright (C) 2016-2017  Sasha Yumzya
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU Affero General Public License as
+; published by the Free Software Foundation, either version 3 of the
+; License, or (at your option) any later version.
+
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU Affero General Public License for more details.
+
+; You should have received a copy of the GNU Affero General Public License
+; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+(ns iyye.persistence
+   (:require
+     [clojure.tools.logging :as log]
+     [extras.mongowrapper :as mongo]))
+
+(defn local-time-to-string []
+  (let [ date (java.util.Date.)]  (.format (java.text.SimpleDateFormat. "MM_dd_yyyy_HH_mm_ss") date)))
+
+(def iyye-name (ref ()))
+(def iyye-main "iyye_main")
+
+(defn iyye-exists? [name]
+  (let [entry (mongo/get-entry iyye-main "iyye_list" {:name name})]
+    (if (first entry)  true false)))
+
+(defn add-iyye [name]
+  (if (iyye-exists? name)
+    false
+    (mongo/add-entry iyye-main "iyye_list" {:name name :time (local-time-to-string)} )))
+
+(defn list-iyye []
+  (log/info "nothing list")
+  (let [entries (mongo/get-entry iyye-main "iyye_list" {})]
+    (if entries (map :name entries)  false)))
+
+(defn set-current-iyye [name]
+  (dorun
+    [(add-iyye name)
+    (println "iyye name is : " name)
+    (dosync (ref-set iyye-name name))]))
+
+(defn load-current-iyye [name]
+  (println "iyye name is : " name)
+  (dosync (ref-set iyye-name name)))
+
+(defn record-day [day-num]
+  (mongo/add-entry iyye-main (str "days_" @iyye-name) {:number day-num :time (local-time-to-string)}))
+(defn record-night [night-num]
+  (mongo/add-entry iyye-main (str "nights_" @iyye-name) {:number night-num :time (local-time-to-string)}))
+
+(defn load-days []
+  (mongo/get-count iyye-main (str "days_" @iyye-name) {}))


Handled titles with very long words

Handled titles with very long words

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/hudren/ebook-novel.git/'


pep-395 wip

pep-395 wip

error: pathspec 'src/pep_395/wip.clj' did not match any file(s) known to git

--- src/pep_395/wip.clj
+++ src/pep_395/wip.clj
@@ -0,0 +1,79 @@
+(ns pep-395.wip)
+
+(Math/PI) ; 3.141592653589793
+(type (Math/PI)) ; java.lang.Double
+
+(def pi Math/PI)
+(defn sin [n] (Math/sin n))
+(defn acos [n] (Math/acos n))
+
+(acos (/ 4 5)) ; radians 0.6435011087932843
+(acos (/ 3 5)) ; radians 0.9272952180016123
+
+(* (acos (/ 4 5)) (/ 180 pi)) ; degrees 36.86989764584401
+(* (acos (/ 3 5)) (/ 180 pi)) ; degrees 53.13010235415599
+
+
+(import javax.swing.JFrame)
+(import javax.swing.JPanel)
+(import java.awt.Color)
+(import java.awt.Graphics)
+(import java.awt.Color)
+
+(defn render [#^Graphics g w h]
+  (doto g
+    ; http://ethanschoonover.com/solarized
+    (.setColor (Color. 0 43 54))
+    (.fillRect 0 0 w h)
+    (.setColor (Color. 203 75 22))
+    (.setStroke (java.awt.BasicStroke. 10))
+    (.drawLine 0 0 w h)))
+
+(defn create-panel []
+  (proxy [JPanel] []
+    (paintComponent [g]
+      (proxy-super paintComponent g)
+      (time (render g (. this getWidth) (. this getHeight))))))
+
+(doto
+  (JFrame. "pep-395")
+  (.add (create-panel))
+  (.setSize 640 400)
+  (.setVisible true))
+
+;;;;;; Let's draw something
+(defn draw-branch [#^Graphics g x y angle depth]
+  (. g setStroke (java.awt.BasicStroke. 10))
+  (. g drawLine x y x 70)) ; TODO how can I be related to h and w?
+
+(defn render [#^Graphics g w h]
+  (doto g
+    ; http://ethanschoonover.com/solarized
+    (.setColor (Color. 0 43 54))
+    (.fillRect 0 0 w h)
+    (.setColor (Color. 203 75 22)))
+  (draw-branch g (/ w 2) h 0 1))
+
+
+;  (let [init-length (/ (min w h) 5),
+;        branch-angle (* 10 (/ w h)),
+;        max-depth 12]
+;    (draw-tree g 0.0 (/ w 2) h init-length branch-angle max-depth)))
+
+(defn panel []
+  "Create a panel with a customised render"
+  (proxy [JPanel] []
+    (paintComponent [g]
+      (proxy-super paintComponent g)
+      ;(System/gc)
+      (time (render g (. this getWidth) (. this getHeight))))))
+
+(doto
+  (JFrame. "pep-395")
+  (.add (panel))
+  (.setSize 640 400)
+  (.setVisible true))
+
+;;;
+
+; TODO Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)


Probak konpondu

Probak konpondu

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/lnmnd/magnet.zer.git/'


ensure blank labels are clickable

ensure blank labels are clickable

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/wilkerlucio/youtube-looper.git/'


@donnitazen -main function

@donnitazen -main function

error: pathspec 'sscom/src/sscom/core.clj' did not match any file(s) known to git

--- sscom/src/sscom/core.clj
+++ sscom/src/sscom/core.clj
@@ -0,0 +1,6 @@
+(ns sscom.core
+  (:require [sscom.system :as system]))
+
+(defn -main [& args]
+  (system/init)
+  (system/start))


bumped utils version

bumped utils version

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ChallenHB/samsara.git/'


Add auth related events namespace.

Add auth related events namespace.

error: pathspec 'src/uxbox/data/auth.cljs' did not match any file(s) known to git

--- src/uxbox/data/auth.cljs
+++ src/uxbox/data/auth.cljs
@@ -0,0 +1,65 @@
+(ns uxbox.data.auth
+  (:require [beicon.core :as rx]
+            [promesa.core :as p]
+            [uxbox.repo :as rp]
+            [uxbox.rstore :as rs]
+            [uxbox.router :as r]
+            [uxbox.state :as st]
+            [uxbox.schema :as sc]
+            [uxbox.time :as time]))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Schemas
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(def ^:const +login-schema+
+  {:username [sc/required sc/string]
+   :password [sc/required sc/string]})
+
+(def ^:const +user-schema+
+  {:username [sc/required sc/string]
+   :email [sc/required sc/email]
+   :photo [sc/required sc/string]
+   :fullname [sc/required sc/string]})
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Events
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn- login-success
+  [{:keys [full photo username email] :as params}]
+  (sc/validate! +user-schema+ params)
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (assoc state :auth params))
+
+    rs/EffectEvent
+    (-apply-effect [_ state]
+      (r/go :dashboard/projects))))
+
+(defn login
+  [{:keys [username password] :as params}]
+  (sc/validate! +login-schema+ params)
+  (letfn [(on-error [err]
+            (rx/of (login-success {})))
+          (on-success [value]
+            (rx/of (login-success value)))]
+    (reify
+      rs/WatchEvent
+      (-apply-watch [_ state]
+        (->> (rp/do :login params)
+             (rx/flat-map on-success)
+             (rx/catch on-error))))))
+
+(defn logout
+  []
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (assoc state :auth nil))
+
+    rs/WatchEvent
+    (-apply-watch [_ state]
+      (rx/of (r/navigate :auth/login)))))


adding back in the :target attribute on query column metadata which went missing at some point and causes FKs to not be linkable in explore any more.

--- src/metabase/driver/query_processor.clj
+++ src/metabase/driver/query_processor.clj
@@ -5,7 +5,7 @@
             [korma.core :refer :all]
             [metabase.db :refer :all]
             [metabase.driver.interface :as i]
-            [metabase.models.field :refer [Field field->fk-table]]
+            [metabase.models.field :refer [Field field->fk-table field->fk-field]]
             [metabase.util :as u]))

 (declare add-implicit-breakout-order-by
@@ -253,6 +253,7 @@
                           :table_id table-id :name [in (set column-names)])
                      (map (fn [{:keys [name] :as column}]                                         ; build map of column-name -> column
                             {name (-> (select-keys column [:id :table_id :name :description :base_type :special_type])
+                                      (assoc :target (field->fk-field column))
                                       (assoc :extra_info (if-let [fk-table (field->fk-table column)]
                                                            {:target_table_id (:id fk-table)}
                                                            {})))}))


Extract mapper function

Extract mapper function

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/sojoner/clj-kstream-string-long-window-aggregate.git/'


create generic `application/update` event

accepts a map of params, passes them to a graphql mutation, and queries only the
values that were changed and passes them into the app db. use a multimethod to
allow each step to define where the resultant data goes.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/starcity-properties/odin.git/'


Add stub for raindrops

Add stub for raindrops

error: pathspec 'exercises/raindrops/src/raindrops.clj' did not match any file(s) known to git

--- exercises/raindrops/src/raindrops.clj
+++ exercises/raindrops/src/raindrops.clj
@@ -0,0 +1,5 @@
+(ns raindrops)
+
+(defn convert [] ;; <- Arglist goes here
+      ;; your code goes here
+      )