diff --git a/cypress/e2e/example9001-moves.cy.js b/cypress/e2e/example9001-moves.cy.js
index 17e1c4b..53e896e 100644
--- a/cypress/e2e/example9001-moves.cy.js
+++ b/cypress/e2e/example9001-moves.cy.js
@@ -20,11 +20,15 @@ describe('Example 9001: moves', () => {
})
it('test1: e2 - e4', () => {
- cy.get('#test1move1Btn').click()
+ cy.get('#test1StartBtn').click()
+ .get('#test1Container').should('be.visible')
+ .get('#test1move1Btn').should('be.visible')
+ .get('#test1move1Btn').click()
.window().then(win => {
assert.exists(win.test1move)
assert.isTrue(isPromise(win.test1move))
})
+ .get('#test1results').should('be.visible')
.get('#test1move1finished').should('be.visible')
.window().then(win => {
assert.exists(win.board1)
@@ -34,7 +38,8 @@ describe('Example 9001: moves', () => {
})
it('test2: multiple moves', () => {
- cy.get('#test2move1Btn').click()
+ cy.get('#test2StartBtn').click()
+ .get('#test2move1Btn').click()
.window().then(win => {
assert.exists(win.test2move1)
assert.exists(win.test2move2)
@@ -48,7 +53,8 @@ describe('Example 9001: moves', () => {
})
it('test3: set position callback', () => {
- cy.get('#test3step1Btn').click()
+ cy.get('#test3StartBtn').click()
+ .get('#test3step1Btn').click()
.window().then(win => {
assert.exists(win.test3step1)
assert.isTrue(isPromise(win.test3step1))
@@ -61,7 +67,8 @@ describe('Example 9001: moves', () => {
})
it('test4: set position Promise', () => {
- cy.get('#test4step1Btn').click()
+ cy.get('#test4StartBtn').click()
+ .get('#test4step1Btn').click()
.get('#test4step1finished').should('not.exist')
// wait for animation to finish ...
.get('#test4step1finished').should('be.visible')
@@ -75,8 +82,10 @@ describe('Example 9001: moves', () => {
})
it('test5: orientation', () => {
+ cy.get('#test5StartBtn').click()
+
// step1: add Arrows
- cy.get('#test5step1Btn').click()
+ .get('#test5step1Btn').click()
.get('#myBoard .arrow-bc3c7').should('have.length', 3)
.get('#myBoard .circle-a0266').should('have.length', 0)
.get('#myBoard .item-18a5b').should('have.length', 3)
diff --git a/cypress/e2e/example9005-config.cy.js b/cypress/e2e/example9005-config.cy.js
new file mode 100644
index 0000000..d19cc66
--- /dev/null
+++ b/cypress/e2e/example9005-config.cy.js
@@ -0,0 +1,37 @@
+describe('Example 9005: config', () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:3232/examples/9005.html')
+ .get('#myBoard .piece-349f8').should('have.length', 32)
+ .window().then((win) => {
+ assert.exists(win.board1)
+ // test that a few of the API methods exist
+ assert.isFunction(win.board1.position)
+ assert.isFunction(win.board1.move)
+ assert.isFunction(win.board1.addArrow)
+
+ // we should be in the start position
+ assert.equal(win.board1.position('map').size, 32)
+ assert.equal(win.board1.fen(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
+ })
+ })
+
+ it('change config values at runtime', () => {
+ cy.get('#setConfig1Btn').click()
+ .window().then(win => {
+ assert.equal(win.board1.orientation(), 'black')
+ })
+ .get('#setKingPawnEndgameBtn').click()
+ .get('#myBoard .piece-349f8').should('have.length', 3)
+ .get('#onChangeTarget').then($div => {
+ const innerText = $div.text()
+ assert.equal(innerText, 'change1')
+ })
+ .get('#setConfig2Btn').click()
+ .get('#setRuyLopezBtn').click()
+ .get('#myBoard .piece-349f8').should('have.length', 32)
+ .get('#onChangeTarget').then($div => {
+ const innerText = $div.text()
+ assert.equal(innerText, 'change2')
+ })
+ })
+})
diff --git a/examples/3009-circles.example b/examples/3009-circles.example
index b8623c8..7bd0b66 100644
--- a/examples/3009-circles.example
+++ b/examples/3009-circles.example
@@ -38,7 +38,7 @@ const board = Chessboard2('myBoard', 'start')
let circle1Id = null
let circle2Id = null
let circle3Id = null
-let circle4Id = null
+const circle4Id = null
let circle5Id = null
attachEvent('addCircle1Btn', 'click', () => {
diff --git a/examples/9001-move-test.example b/examples/9001-move-test.example
index 5de295d..cb5e517 100644
--- a/examples/9001-move-test.example
+++ b/examples/9001-move-test.example
@@ -8,41 +8,43 @@ Move Pieces Test
This file exists for Cypress testing
===== HTML
-
-
-
- test1: e2 - e4
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
test2: multiple moves
-
-
-
+
test3: set position callback
-
-
-
+
test4: set position promise
-
-
-
+
test5: orientation
@@ -51,17 +53,41 @@ This file exists for Cypress testing
-
-
===== JS
-const board1 = Chessboard2('myBoard', 'start')
+const board1 = Chessboard2('myBoard', 'start')
window.board1 = board1
+function byId (id) {
+ return document.getElementById(id)
+}
+
function appendHtml (id, html) {
- const el = document.getElementById(id)
+ const el = byId(id)
el.innerHTML = el.innerHTML + html
}
+function hideEl (id) {
+ byId(id).style.display = 'none'
+}
+
+function hideAllSections () {
+ hideEl('test1Container')
+ hideEl('test2Container')
+ hideEl('test3Container')
+ hideEl('test4Container')
+ hideEl('test5Container')
+}
+
+function showSection (id) {
+ byId(id).style.display = ''
+}
+
+attachEvent('test1StartBtn', 'click', () => { hideAllSections(); showSection('test1Container') })
+attachEvent('test2StartBtn', 'click', () => { hideAllSections(); showSection('test2Container') })
+attachEvent('test3StartBtn', 'click', () => { hideAllSections(); showSection('test3Container') })
+attachEvent('test4StartBtn', 'click', () => { hideAllSections(); showSection('test4Container') })
+attachEvent('test5StartBtn', 'click', () => { hideAllSections(); showSection('test5Container') })
+
// -----------------------------------------------------------------------------
// Test 1
diff --git a/examples/9004-draggable-test.example b/examples/9004-draggable-test.example
index d70c397..358530d 100644
--- a/examples/9004-draggable-test.example
+++ b/examples/9004-draggable-test.example
@@ -13,6 +13,6 @@ This file exists for testing
===== JS
const config = {
draggable: true,
- position: "start"
+ position: 'start'
}
-const board1 = Chessboard2("myBoard", config)
+const board1 = Chessboard2('myBoard', config)
diff --git a/examples/9005-config-test.example b/examples/9005-config-test.example
new file mode 100644
index 0000000..38a4991
--- /dev/null
+++ b/examples/9005-config-test.example
@@ -0,0 +1,81 @@
+===== id
+9005
+
+===== Name
+Config Test
+
+===== DescriptionMD
+This file exists for testing config settings.
+
+===== HTML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+===== JS
+const board1 = Chessboard2('myBoard', 'start')
+window.board1 = board1
+
+const config2 = {
+ onChange: onChange2
+}
+
+function byId (id) {
+ return document.getElementById(id)
+}
+
+function appendHtml (id, html) {
+ const el = byId(id)
+ el.innerHTML = el.innerHTML + html
+}
+
+// -----------------------------------------------------------------------------
+// Test 1
+
+function onChange1 () {
+ byId('onChangeTarget').innerHTML = 'change1'
+}
+
+function onChange2 () {
+ byId('onChangeTarget').innerHTML = 'change2'
+}
+
+attachEvent('setConfig1Btn', 'click', () => {
+ // should warn that "banana" is not a valid value for "orientation"
+ board1.config('orientation', 'banana')
+ // should warn-log that "foo" is not a valid config property
+ board1.config('foo', 'bar')
+
+ board1.config('orientation', 'black')
+ board1.config('onChange', onChange1)
+})
+
+attachEvent('setConfig2Btn', 'click', () => {
+ board1.setConfig(config2)
+})
+
+attachEvent('setStartBtn', 'click', () => {
+ board1.start()
+})
+
+attachEvent('setRuyLopezBtn', 'click', () => {
+ board1.position('r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R')
+})
+
+attachEvent('setKingPawnEndgameBtn', 'click', () => {
+ board1.position('8/8/8/3pk3/8/4K3/8/8')
+})
diff --git a/scripts/website.js b/scripts/website.js
index 5503acb..62521a8 100755
--- a/scripts/website.js
+++ b/scripts/website.js
@@ -19,7 +19,7 @@ const cmReader = new commonmark.Parser()
const cmWriter = new commonmark.HtmlRenderer()
// toggle development version
-const useLocalDevFiles = false
+const useLocalDevFiles = true
const jsCDNScript = ''
// const esmCDNScript = ''
const cssCDNLink = ''
diff --git a/src-cljs/com/oakmac/chessboard2/api.cljs b/src-cljs/com/oakmac/chessboard2/api.cljs
index 37fc632..a329449 100644
--- a/src-cljs/com/oakmac/chessboard2/api.cljs
+++ b/src-cljs/com/oakmac/chessboard2/api.cljs
@@ -1,20 +1,33 @@
(ns com.oakmac.chessboard2.api
"Functions that represent the CLJS API for Chessboard2"
(:require
+ [clojure.string :as str]
[com.oakmac.chessboard2.animations :refer [animation->dom-op calculate-animations]]
- [com.oakmac.chessboard2.constants :refer [animate-speed-strings->times]]
+ [com.oakmac.chessboard2.config :as config]
+ [com.oakmac.chessboard2.constants :refer [animate-speed-strings->times start-position]]
[com.oakmac.chessboard2.dom-ops :as dom-ops]
[com.oakmac.chessboard2.feature-flags :as flags]
[com.oakmac.chessboard2.html :as html]
[com.oakmac.chessboard2.util.arrows :as arrow-util]
[com.oakmac.chessboard2.util.dom :as dom-util :refer [get-element set-inner-html! set-style-prop!]]
+ [com.oakmac.chessboard2.util.fen :refer [fen->position valid-fen?]]
[com.oakmac.chessboard2.util.ids :refer [random-id]]
[com.oakmac.chessboard2.util.logging :refer [warn-log]]
[com.oakmac.chessboard2.util.moves :refer [apply-move-to-position]]
- [com.oakmac.chessboard2.util.predicates :refer [arrow-item? valid-square? valid-position?]]
+ [com.oakmac.chessboard2.util.predicates :refer [arrow-item? fen-string? start-string? valid-square? valid-position?]]
[com.oakmac.chessboard2.util.squares :refer [square->dimensions]]
[goog.object :as gobj]))
+;; TODO: move this to a util namespace
+; (defn coerce-to-position-map
+; "Does it's best to coerce p into a position map if possible"
+; [p]
+; (cond
+; (start-string? p) start-position
+; (valid-fen? p) (fen->position p)
+; (valid-position? p) p
+; :else nil))
+
(defn get-items-by-type
"Returns a map of Items on the board"
[board-state type-str]
@@ -376,3 +389,28 @@
(reset! board-state nil))
;; return null
nil)
+
+(defn update-config!
+ "Update the board config with new values."
+ [board-state new-config]
+ (let [;; do not allow them to update the position via this method
+ cfg2 (dissoc new-config :position)
+ validated-config (reduce
+ (fn [cfg3 [prop val]]
+ (if-not (contains? config/valid-config-keys prop)
+ ;; Google Closure adds these keys to Objects for some reason ¯\_(ツ)_/¯
+ ;; do not log and confuse the end user
+ (do (when-not (str/starts-with? (name prop) "closure_uid")
+ (warn-log "Invalid config property:" (name prop)))
+ cfg3)
+ (let [validation-fn (get-in config/config-props [prop :valid-fn])
+ valid-value? (validation-fn val)]
+ (if-not valid-value?
+ (do (warn-log (str "Invalid value for config property \"" (name prop) "\": "
+ val))
+ cfg3)
+ (assoc cfg3 prop val)))))
+ {}
+ cfg2)]
+ (swap! board-state merge validated-config)
+ nil))
diff --git a/src-cljs/com/oakmac/chessboard2/config.cljs b/src-cljs/com/oakmac/chessboard2/config.cljs
index 7f199b2..772b055 100644
--- a/src-cljs/com/oakmac/chessboard2/config.cljs
+++ b/src-cljs/com/oakmac/chessboard2/config.cljs
@@ -3,6 +3,7 @@
[com.oakmac.chessboard2.constants :refer [animate-speed-strings animate-speed-strings->times start-position]]
[com.oakmac.chessboard2.util.data-transforms :refer [clj->js-map js-map->clj]]
[com.oakmac.chessboard2.util.fen :refer [fen->position position->fen valid-fen?]]
+ [com.oakmac.chessboard2.util.logging :refer [warn-log]]
[com.oakmac.chessboard2.util.predicates :refer [fen-string?
map-string?
start-string?
@@ -86,6 +87,11 @@
(def valid-config-keys
(set (keys config-props)))
+(def valid-config-strings
+ (->> valid-config-keys
+ (map name)
+ set))
+
;; TODO: Good candidate for unit tests
(defn merge-config
[their-config]
@@ -98,3 +104,8 @@
(assoc new-config config-key default-val))))
{}
config-props))
+
+(defn state->config
+ "Given the board-state, return the public config object"
+ [board-state]
+ (select-keys board-state valid-config-keys))
diff --git a/src-cljs/com/oakmac/chessboard2/core.cljs b/src-cljs/com/oakmac/chessboard2/core.cljs
index c249726..d13124f 100644
--- a/src-cljs/com/oakmac/chessboard2/core.cljs
+++ b/src-cljs/com/oakmac/chessboard2/core.cljs
@@ -14,6 +14,7 @@
[com.oakmac.chessboard2.util.dom :as dom-util :refer [add-class! append-html! remove-class! remove-element!]]
[com.oakmac.chessboard2.util.fen :refer [fen->position valid-fen?]]
[com.oakmac.chessboard2.util.ids :refer [random-id]]
+ [com.oakmac.chessboard2.util.lang :refer [atom?]]
[com.oakmac.chessboard2.util.logging :refer [error-log warn-log]]
[com.oakmac.chessboard2.util.moves :refer [move->map]]
[com.oakmac.chessboard2.util.pieces :refer [random-piece-id]]
@@ -703,7 +704,6 @@
squares-el (dom-util/get-element squares-selector)]
(remove-class! squares-el css/orientation-black)
(add-class! squares-el css/orientation-white)
- (swap! board assoc :orientation "white")
(draw-items-instant! board)))
(defn set-black-orientation!
@@ -712,7 +712,6 @@
squares-el (dom-util/get-element squares-selector)]
(remove-class! squares-el css/orientation-white)
(add-class! squares-el css/orientation-black)
- (swap! board assoc :orientation "black")
(draw-items-instant! board)))
(defn orientation
@@ -721,15 +720,15 @@
([board arg]
(let [lc-arg (safe-lower-case arg)]
(cond
- (= lc-arg "white") (do (set-white-orientation! board)
+ (= lc-arg "white") (do (swap! board assoc :orientation "white")
"white")
- (= lc-arg "black") (do (set-black-orientation! board)
+ (= lc-arg "black") (do (swap! board assoc :orientation "black")
"black")
(= lc-arg "flip") (do (swap! board update :orientation toggle-orientation)
(let [new-orientation (:orientation @board)]
(if (= new-orientation "white")
- (set-white-orientation! board)
- (set-black-orientation! board))
+ (swap! board assoc :orientation "white")
+ (swap! board assoc :orientation "black"))
new-orientation))
:else (:orientation @board)))))
@@ -836,9 +835,13 @@
; (def default-animate-speed-ms 2500)
(defn board-state-change
- [_key _atom old-state new-state]
+ [_key board-atom old-state new-state]
(when new-state
- ;; FIXME: board orientation
+ ;; board orientation
+ (when-not (= (:orientation old-state) (:orientation new-state))
+ (if (= "white" (:orientation new-state))
+ (set-white-orientation! board-atom)
+ (set-black-orientation! board-atom)))
;; FIXME: coordinate config change
;; show / hide coordinates
(when-not (= (:show-coords? old-state) (:show-coords? new-state))
@@ -912,11 +915,9 @@
"getCircles" (partial js-get-circles board-state)
"removeCircle" (partial js-remove-circle board-state)
- ;; FIXME: implement these
- ; https://github.com/oakmac/chessboard2/issues/7
- ; "config" #()
- ; "getConfig" #()
- ; "setConfig" #()
+ "config" (partial js-api/config board-state)
+ "getConfig" (partial js-api/get-config board-state)
+ "setConfig" (partial js-api/set-config board-state)
;; FIXME: allow adding custom items
;; https://github.com/oakmac/chessboard2/issues/9
diff --git a/src-cljs/com/oakmac/chessboard2/js_api.cljs b/src-cljs/com/oakmac/chessboard2/js_api.cljs
index 201662f..39e342a 100644
--- a/src-cljs/com/oakmac/chessboard2/js_api.cljs
+++ b/src-cljs/com/oakmac/chessboard2/js_api.cljs
@@ -273,3 +273,39 @@
(if (= 1 (count moves))
(clj->js (first moves))
(clj->js moves))))
+
+(defn get-config
+ "Returns the current board config as a JS Object"
+ [board-state]
+ (clj->js (config/state->config @board-state)))
+
+;; TODO: would be nice to unit test the warning logs here via mocks
+(defn set-config
+ "Set the config."
+ [board-state arg1 arg2]
+ (cond
+ ;; set a single config value
+ (contains? config/valid-config-strings arg1)
+ (do (api/update-config! board-state {(keyword arg1) arg2})
+ (get-config board-state))
+
+ ;; set multiple values via object
+ (goog/isObject arg1)
+ (do (api/update-config! board-state (js->clj arg1 :keywordize-keys true))
+ (get-config board-state))
+
+ :else (warn-log "Invalid args passed to setConfig():" arg1 arg2)))
+
+(defn config
+ "Get or Set the board config"
+ [board-state]
+ (let [js-args (array)]
+ (copy-arguments js-args)
+ (.shift js-args)
+ (let [arg1 (aget js-args 0)
+ arg2 (aget js-args 1)]
+ (cond
+ (not arg1) (get-config board-state)
+ (string? arg1) (set-config board-state arg1 arg2)
+ (goog/isObject arg1) (set-config board-state arg1 nil)
+ :else (warn-log "Invalid args passed to config():" arg1 arg2)))))
diff --git a/src-cljs/com/oakmac/chessboard2/util/lang.cljs b/src-cljs/com/oakmac/chessboard2/util/lang.cljs
new file mode 100644
index 0000000..180098a
--- /dev/null
+++ b/src-cljs/com/oakmac/chessboard2/util/lang.cljs
@@ -0,0 +1,7 @@
+(ns com.oakmac.chessboard2.util.lang)
+
+;; TODO: candidate for removal
+;; TODO: could use unit tests
+(defn atom?
+ [a]
+ (satisfies? IAtom a))