Now working correctly with publish/subscribe model
This commit is contained in:
parent
3ebc2612fa
commit
b3c624eb5c
13
project.clj
13
project.clj
|
@ -5,7 +5,8 @@
|
||||||
[reagent "0.6.0"]
|
[reagent "0.6.0"]
|
||||||
[re-frame "0.9.4"]
|
[re-frame "0.9.4"]
|
||||||
[org.clojure/core.async "0.2.391"]
|
[org.clojure/core.async "0.2.391"]
|
||||||
[re-com "2.0.0"]]
|
[re-com "2.0.0"]
|
||||||
|
[org.webjars.bower/snap.svg "0.4.1"]]
|
||||||
|
|
||||||
:plugins [[lein-cljsbuild "1.1.4"]]
|
:plugins [[lein-cljsbuild "1.1.4"]]
|
||||||
|
|
||||||
|
@ -20,9 +21,7 @@
|
||||||
:profiles
|
:profiles
|
||||||
{:dev
|
{:dev
|
||||||
{:dependencies [[binaryage/devtools "0.8.2"]]
|
{:dependencies [[binaryage/devtools "0.8.2"]]
|
||||||
|
:plugins [[lein-figwheel "0.5.9"]]}}
|
||||||
:plugins [[lein-figwheel "0.5.9"]]
|
|
||||||
}}
|
|
||||||
|
|
||||||
:cljsbuild
|
:cljsbuild
|
||||||
{:builds
|
{:builds
|
||||||
|
@ -44,8 +43,4 @@
|
||||||
:output-to "resources/public/js/compiled/app.js"
|
:output-to "resources/public/js/compiled/app.js"
|
||||||
:optimizations :advanced
|
:optimizations :advanced
|
||||||
:closure-defines {goog.DEBUG false}
|
:closure-defines {goog.DEBUG false}
|
||||||
:pretty-print false}}
|
:pretty-print false}}]})
|
||||||
|
|
||||||
|
|
||||||
]}
|
|
||||||
)
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
|
||||||
<script src="js/compiled/app.js"></script>
|
<script src="js/compiled/app.js"></script>
|
||||||
<script>swinging_needle_meter.core.init();</script>
|
<script>swinging_needle_meter.core.init();</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
(ns swinging-needle-meter.db)
|
|
||||||
|
|
||||||
(def default-db
|
|
||||||
{:name "re-frame"})
|
|
|
@ -1,10 +1,57 @@
|
||||||
(ns swinging-needle-meter.events
|
(ns swinging-needle-meter.events
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[swinging-needle-meter.db :as db]))
|
[swinging-needle-meter.state :as state]))
|
||||||
|
|
||||||
;;; This file is unchanged (except this line) from the leiningen recom template
|
|
||||||
|
|
||||||
|
;; Reset.
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
:initialize-db
|
:initialize-db
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
db/default-db))
|
state/default-state))
|
||||||
|
|
||||||
|
;; The clock ticked.
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:timer
|
||||||
|
(fn [db _]
|
||||||
|
db))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-value
|
||||||
|
(fn [db [x value]]
|
||||||
|
(js/console.log (str :set-value " " x " " value))
|
||||||
|
(assoc db :value value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-setpoint
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :setpoint value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-gradations
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :gradations value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-size
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :size value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-min-value
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :min-val value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-max-value
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :max-val value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-warning-value
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :warn-val value)))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:set-unit
|
||||||
|
(fn [db [_ value]]
|
||||||
|
(assoc db :unit value)))
|
||||||
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
(ns swinging-needle-meter.pen-trace-meter
|
|
||||||
(:require [re-com.core :refer [h-box v-box box gap line label title slider checkbox p]]
|
|
||||||
[re-com.box :refer [flex-child-style]]
|
|
||||||
[re-com.util :refer [deref-or-value]]
|
|
||||||
[re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]]
|
|
||||||
[reagent.core :as reagent]))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;;;
|
|
||||||
;;;; pen-trace-meter: an experiment in animating SVG from re-frame.
|
|
||||||
;;;; Draws heavily on re-com..
|
|
||||||
;;;;
|
|
||||||
;;;; This program is free software; you can redistribute it and/or
|
|
||||||
;;;; modify it under the terms of the GNU General Public License
|
|
||||||
;;;; as published by the Free Software Foundation; either version 2
|
|
||||||
;;;; 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 General Public License for more details.
|
|
||||||
;;;;
|
|
||||||
;;;; You should have received a copy of the GNU General Public License
|
|
||||||
;;;; along with this program; if not, write to the Free Software
|
|
||||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
;;;; USA.
|
|
||||||
;;;;
|
|
||||||
;;;; Copyright (C) 2014 Simon Brooke
|
|
||||||
;;;;
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;; ------------------------------------------------------------------------------------
|
|
||||||
;; Component: pen-trace-meter
|
|
||||||
;; ------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(defn pen-trace-meter
|
|
||||||
[& {:keys [model width height min-value max-value]
|
|
||||||
:or {width "100%"
|
|
||||||
height "100%"
|
|
||||||
min-value 0
|
|
||||||
max-value 100
|
|
||||||
}}]
|
|
||||||
)
|
|
14
src/cljs/swinging_needle_meter/state.cljs
Normal file
14
src/cljs/swinging_needle_meter/state.cljs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
(ns ^{:doc "Client state."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
swinging-needle-meter.state)
|
||||||
|
|
||||||
|
(def default-state
|
||||||
|
{:timer (js/Date.)
|
||||||
|
:value 60
|
||||||
|
:setpoint 75
|
||||||
|
:gradations 5
|
||||||
|
:size 70
|
||||||
|
:min-val 0
|
||||||
|
:max-val 100
|
||||||
|
:warn-val 80
|
||||||
|
:unit "Mw"})
|
|
@ -2,9 +2,53 @@
|
||||||
(:require-macros [reagent.ratom :refer [reaction]])
|
(:require-macros [reagent.ratom :refer [reaction]])
|
||||||
(:require [re-frame.core :as re-frame]))
|
(:require [re-frame.core :as re-frame]))
|
||||||
|
|
||||||
;;; This file is unchanged (except this line) from the leiningen recom template
|
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:name
|
:name
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(:name db)))
|
(:name db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:value
|
||||||
|
(fn [db]
|
||||||
|
(:value db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:setpoint
|
||||||
|
(fn [db]
|
||||||
|
(:setpoint db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:unit
|
||||||
|
(fn [db]
|
||||||
|
(:unit db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:min-val
|
||||||
|
(fn [db]
|
||||||
|
(:min-val db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:max-val
|
||||||
|
(fn [db]
|
||||||
|
(:max-val db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:gradations
|
||||||
|
(fn [db]
|
||||||
|
(:gradations db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:warn-val
|
||||||
|
(fn [db]
|
||||||
|
(:warn-val db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:size
|
||||||
|
(fn [db]
|
||||||
|
(:size db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:timer
|
||||||
|
(fn [db]
|
||||||
|
(:timer db)))
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
[re-com.box :refer [flex-child-style]]
|
[re-com.box :refer [flex-child-style]]
|
||||||
[re-com.util :refer [deref-or-value]]
|
[re-com.util :refer [deref-or-value]]
|
||||||
[re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]]
|
[re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]]
|
||||||
[reagent.core :as reagent]))
|
[reagent.core :as reagent]
|
||||||
|
[swinging-needle-meter.utils :refer [abs]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
;;;;
|
||||||
|
@ -82,11 +83,6 @@
|
||||||
{:name :attr :required false :type "HTML attr map"
|
{:name :attr :required false :type "HTML attr map"
|
||||||
:validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}])
|
:validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}])
|
||||||
|
|
||||||
|
|
||||||
(defn abs
|
|
||||||
"Return the absolute value of the (numeric) argument."
|
|
||||||
[n] (max n (- n)))
|
|
||||||
|
|
||||||
;; the constant 140 represents the full sweep of the needle
|
;; the constant 140 represents the full sweep of the needle
|
||||||
;; from the left end of the scale to right end, in degrees.
|
;; from the left end of the scale to right end, in degrees.
|
||||||
(def full-scale-deflection 140)
|
(def full-scale-deflection 140)
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
(ns swinging-needle-meter.utils
|
(ns swinging-needle-meter.utils
|
||||||
(:require [re-com.core :refer [h-box v-box box gap title line label hyperlink-href align-style]]))
|
(:require [re-com.core :refer [h-box v-box box gap title line label hyperlink-href align-style]]))
|
||||||
|
|
||||||
;;;; This file is just stolen wholesale from re-demo in the re-com package;
|
;;;; This file is mostly stolen wholesale from re-demo in the re-com package;
|
||||||
;;;; I claim no credit for it.
|
;;;; I claim no credit for it.
|
||||||
|
|
||||||
|
|
||||||
|
(defn abs
|
||||||
|
"Return the absolute value of the (numeric) argument."
|
||||||
|
[n] (max n (- n)))
|
||||||
|
|
||||||
|
|
||||||
(defn github-hyperlink
|
(defn github-hyperlink
|
||||||
"given a label and a relative path, return a component which hyperlinks to the GitHub URL in a new tab"
|
"given a label and a relative path, return a component which hyperlinks to the GitHub URL in a new tab"
|
||||||
[label src-path]
|
[label src-path]
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
(ns swinging-needle-meter.views
|
(ns swinging-needle-meter.views
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as rf]
|
||||||
[re-com.core :refer [h-box v-box box gap line label title progress-bar slider checkbox p single-dropdown]]
|
[re-com.core :refer [h-box v-box box gap line label title progress-bar slider checkbox p single-dropdown]]
|
||||||
[re-com.util :refer [deref-or-value]]
|
[re-com.util :refer [deref-or-value]]
|
||||||
[swinging-needle-meter.swinging-needle-meter :refer [swinging-needle-meter swinging-needle-args-desc]]
|
[swinging-needle-meter.swinging-needle-meter :refer [swinging-needle-meter swinging-needle-args-desc]]
|
||||||
[swinging-needle-meter.utils :refer [panel-title title2 args-table github-hyperlink status-text]]
|
[swinging-needle-meter.utils :refer [panel-title title2 args-table github-hyperlink status-text]]
|
||||||
[reagent.core :as reagent]))
|
[reagent.core :as reagent]
|
||||||
|
[swinging-needle-meter.utils :refer [abs]]))
|
||||||
|
|
||||||
;; ------------------------------------------------------------------------------------
|
;; ------------------------------------------------------------------------------------
|
||||||
;; Demo: swinging-needle-meter
|
;; Demo: swinging-needle-meter
|
||||||
;; ------------------------------------------------------------------------------------
|
;; ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(defn dispatch-timer-event
|
||||||
|
[]
|
||||||
|
(let [now (js/Date.)]
|
||||||
|
(rf/dispatch [:timer now]))) ;; <-- dispatch used
|
||||||
|
|
||||||
|
;; call the dispatching function every tenth of a second
|
||||||
|
(defonce do-timer (js/setInterval dispatch-timer-event 100))
|
||||||
|
|
||||||
|
|
||||||
(defn swinging-needle-demo
|
(defn swinging-needle-demo
|
||||||
[]
|
[]
|
||||||
(let [value (reagent/atom 60)
|
(let [unit @(rf/subscribe [:unit])
|
||||||
setpoint (reagent/atom 75)
|
min-val @(rf/subscribe [:min-val])
|
||||||
gradations (reagent/atom 5)
|
max-val @(rf/subscribe [:max-val])
|
||||||
size (reagent/atom 70)
|
warn-val @(rf/subscribe [:warn-val])
|
||||||
min-val (reagent/atom 0)
|
gradations @(rf/subscribe [:gradations])
|
||||||
max-val (reagent/atom 100)
|
size @(rf/subscribe [:size])]
|
||||||
warn-val (reagent/atom 80)
|
|
||||||
unit (reagent/atom "Mw")]
|
|
||||||
(fn
|
(fn
|
||||||
[]
|
[]
|
||||||
[v-box
|
[v-box
|
||||||
|
@ -61,48 +69,48 @@
|
||||||
[v-box
|
[v-box
|
||||||
:gap "20px"
|
:gap "20px"
|
||||||
:children [[swinging-needle-meter
|
:children [[swinging-needle-meter
|
||||||
:model value
|
:model @(rf/subscribe [:value])
|
||||||
:setpoint setpoint
|
:setpoint @(rf/subscribe [:setpoint])
|
||||||
:unit (deref-or-value unit)
|
:unit @(rf/subscribe [:unit])
|
||||||
:min-value (deref-or-value min-val)
|
:min-value @(rf/subscribe [:min-val])
|
||||||
:warn-value (deref-or-value warn-val)
|
:warn-value @(rf/subscribe [:warn-val])
|
||||||
:max-value (deref-or-value max-val)
|
:max-value @(rf/subscribe [:max-val])
|
||||||
:tolerance 2
|
:tolerance 2
|
||||||
:alarm-class "snm-warning"
|
:alarm-class "snm-warning"
|
||||||
:gradations (deref-or-value gradations)
|
:gradations @(rf/subscribe [:gradations])
|
||||||
:height (int (* (deref-or-value size) 6))
|
:height (int (* @(rf/subscribe [:size]) 6))
|
||||||
:width (int (* (deref-or-value size) 10))]
|
:width (int (* @(rf/subscribe [:size]) 10))]
|
||||||
[title :level :level3 :label "Parameters"]
|
[title :level :level3 :label "Parameters"]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":model"]]
|
:children [[box :align :start :child [:code ":model"]]
|
||||||
[slider
|
[slider
|
||||||
:model value
|
:model @(rf/subscribe [:value])
|
||||||
:min -100
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! value %)]
|
:on-change #(rf/dispatch [:set-value %])]
|
||||||
[label :label @value]]]
|
[label :label @(rf/subscribe [:value])]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":setpoint"]]
|
:children [[box :align :start :child [:code ":setpoint"]]
|
||||||
[slider
|
[slider
|
||||||
:model setpoint
|
:model @(rf/subscribe [:setpoint])
|
||||||
:min -100
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! setpoint %)]
|
:on-change #(rf/dispatch [:set-setpoint %])]
|
||||||
[label :label @setpoint]]]
|
[label :label @(rf/subscribe [:setpoint])]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":min-val"]]
|
:children [[box :align :start :child [:code ":min-val"]]
|
||||||
[slider
|
[slider
|
||||||
:model min-val
|
:model @(rf/subscribe [:min-val])
|
||||||
:min -100
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! min-val %)]
|
:on-change #(rf/dispatch [:set-min-value %])]
|
||||||
[label :label @min-val]]]
|
[label :label @(rf/subscribe [:min-val])]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":max-val"]]
|
:children [[box :align :start :child [:code ":max-val"]]
|
||||||
|
@ -111,8 +119,8 @@
|
||||||
:min -100
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! max-val %)]
|
:on-change #(rf/dispatch [:set-max-value %])]
|
||||||
[label :label @max-val]]]
|
[label :label max-val]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":warn-val"]]
|
:children [[box :align :start :child [:code ":warn-val"]]
|
||||||
|
@ -121,8 +129,8 @@
|
||||||
:min -100
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! warn-val %)]
|
:on-change #(rf/dispatch [:set-warning-value %])]
|
||||||
[label :label @warn-val]]]
|
[label :label warn-val]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":gradations"]]
|
:children [[box :align :start :child [:code ":gradations"]]
|
||||||
|
@ -131,19 +139,19 @@
|
||||||
:min 0
|
:min 0
|
||||||
:max 10
|
:max 10
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! gradations %)]
|
:on-change #(rf/dispatch [:set-gradations %])]
|
||||||
[label :label @gradations]]]
|
[label :label gradations]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":unit"]]
|
:children [[box :align :start :child [:code ":unit"]]
|
||||||
[single-dropdown
|
[single-dropdown
|
||||||
:model unit
|
:model @(rf/subscribe [:unit])
|
||||||
:choices [{:id "Mw" :label "Megawatts" :group "Electrical"}
|
:choices [{:id "Mw" :label "Megawatts" :group "Electrical"}
|
||||||
{:id "M/s" :label "Metres per second" :group "Motion"}
|
{:id "M/s" :label "Metres per second" :group "Motion"}
|
||||||
{:id "F/f" :label "Furlongs per fortnight" :group "Motion"}
|
{:id "F/f" :label "Furlongs per fortnight" :group "Motion"}
|
||||||
{:id "°C" :label "Degrees Celsius" :group "Temperature"}]
|
{:id "°C" :label "Degrees Celsius" :group "Temperature"}]
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! unit %)]]]
|
:on-change #(rf/dispatch [:set-unit %])]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":size"]]
|
:children [[box :align :start :child [:code ":size"]]
|
||||||
|
@ -152,8 +160,8 @@
|
||||||
:min 25
|
:min 25
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! size %)]
|
:on-change #(rf/dispatch [:set-size %])]
|
||||||
[label :label @size]]]
|
[label :label size]]]
|
||||||
]]]]]]]])))
|
]]]]]]]])))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue