diff --git a/README.md b/README.md index 6c889f4..63db9b3 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ Tutorial Summary - 11-tutorial.lisp - Attaching to existing HTML - 12-tutorial.lisp - Running a website in CLOG (routing) - 13-tutorial/ - Flying Solo - A minimalist CLOG project +- 14-tutorial.lisp - Local (persistent) and Session client side storage Demo Summary diff --git a/clog-location.lisp b/clog-location.lisp index 35df562..ccbd0da 100644 --- a/clog-location.lisp +++ b/clog-location.lisp @@ -174,10 +174,10 @@ ;; reload ;; ;;;;;;;;;;;; -(defgeneric reload (clog-window) +(defgeneric reload (clog-location) (:documentation "Reload browser window.")) -(defmethod reload ((obj clog-window)) +(defmethod reload ((obj clog-location)) (execute obj "reload()")) ;;;;;;;;;;;;;;;;; diff --git a/clog-window.lisp b/clog-window.lisp index 3f085a9..54ea8ab 100644 --- a/clog-window.lisp +++ b/clog-window.lisp @@ -468,8 +468,6 @@ If ON-ORIENTATION-CHANGE-HANDLER is nil unbind the event.")) ;; set-on-storage ;; ;;;;;;;;;;;;;;;;;;;; -;; need to change to use a true on-storage event - (defparameter storage-event-script "+ encodeURIComponent(e.originalEvent.key) + ':' + encodeURIComponent(e.originalEvent.oldValue) + ':' + @@ -478,13 +476,13 @@ If ON-ORIENTATION-CHANGE-HANDLER is nil unbind the event.")) (defun parse-storage-event (data) (let ((f (ppcre:split ":" data))) (list - :key-value (quri:url-decode (nth 0 f)) + :key (quri:url-decode (nth 0 f)) :old-value (quri:url-decode (nth 1 f)) - :new-value (quri:url-decode (nth 2 f))))) + :value (quri:url-decode (nth 2 f))))) (defgeneric set-on-storage (clog-window on-storage-handler) - (:documentation "Set the ON-STORAGE-HANDLER for CLOG-OBJ. If -ON-STORAGE-HANDLER is nil unbind the event.")) + (:documentation "Set the ON-STORAGE-HANDLER for CLOG-OBJ. The +on-storage event is fired for changes to :local storage keys.")) (defmethod set-on-storage ((obj clog-window) handler) (set-event obj "storage" @@ -492,3 +490,60 @@ ON-STORAGE-HANDLER is nil unbind the event.")) (lambda (data) (funcall handler obj (parse-storage-event data)))) :call-back-script storage-event-script)) + +;;;;;;;;;;;;;;;;;;;; +;; storage-length ;; +;;;;;;;;;;;;;;;;;;;; + +(deftype storage-type '(member local session)) + +(defgeneric storage-length (clog-window storage-type) + (:documentation "Number of entries in browser STORAGE-TYPE. +(local = persistant or session)")) + +(defmethod storage-length ((obj clog-window) storage-type) + (parse-integer (query obj "~(~a~)Storage.length" storage-type))) + +;;;;;;;;;;;;;;;;; +;; storage-key ;; +;;;;;;;;;;;;;;;;; + +(defgeneric storage-key (clog-window storage-type key-num) + (:documentation "Return the key for entry number KEY-NUM in browser +STORAGE-TYPE. (local = persistant or session)")) + +(defmethod storage-key ((obj clog-window) storage-type key-num) + (query obj (format nil "~(~a~)Storage.key(~A)" storage-type key-num))) + +;;;;;;;;;;;;;;;;;;;; +;; storage-remove ;; +;;;;;;;;;;;;;;;;;;;; + +(defgeneric storage-remove (clog-window storage-type key-name) + (:documentation "Remove the storage key and value in browser +STORAGE-TYPE. (local = persistant or session)")) + +(defmethod storage-remove ((obj clog-window) storage-type key-name) + (execute obj (format nil "~(~a~)Storage.removeItem(~A)" storage-type key-name))) + +;;;;;;;;;;;;;;;;;;;;; +;; storage-element ;; +;;;;;;;;;;;;;;;;;;;;; + +(defgeneric storage-element (clog-window storage-type key-name) + (:documentation "Get/Setf storage-element on browser client.")) + +(defmethod storage-element ((obj clog-window) storage-type key-name) + (query obj (format nil "~(~a~)Storage.getItem('~A')" + storage-type + (escape-string key-name)))) + +(defgeneric set-storage-element (clog-window storage-type key-name value) + (:documentation "Set storage-element.")) + +(defmethod set-storage-element ((obj clog-window) storage-type key-name value) + (execute obj (format nil "~(~a~)Storage.setItem('~A','~A')" + storage-type + (escape-string key-name) + (escape-string value)))) +(defsetf storage-element set-storage-element) diff --git a/clog.asd b/clog.asd index 536d1cd..2da0df0 100644 --- a/clog.asd +++ b/clog.asd @@ -5,7 +5,7 @@ :author "David Botton " :license "BSD" - :version "0.1.1" + :version "0.2.0" :serial t :depends-on (#:clack #:websocket-driver #:alexandria #:hunchentoot #:cl-ppcre #:bordeaux-threads #:trivial-open-browser diff --git a/clog.lisp b/clog.lisp index 720dd6c..9093b77 100644 --- a/clog.lisp +++ b/clog.lisp @@ -471,7 +471,11 @@ embedded in a native template application.)" (scroll-to generic-function) (close-window generic-function) (close-connection generic-function) - + (storage-length generic-function) + (storage-key generic-function) + (storage-remove generic-function) + (storage-element generic-function) + "CLOG-Window - Events" (set-on-abort generic-function) (set-on-error generic-function) @@ -537,6 +541,7 @@ embedded in a native template application.)" (export 'make-markup) (defun make-markup () (load "clog.lisp") + (load "clog-docs.lisp") (load "clog-base.lisp") (load "clog-element.lisp") (load "clog-element-common.lisp") @@ -554,6 +559,7 @@ embedded in a native template application.)" (export 'make-html) (defun make-html () (load "clog.lisp") + (load "clog-docs.lisp") (load "clog-base.lisp") (load "clog-element.lisp") (load "clog-element-common.lisp") diff --git a/tutorial/14-tutorial.lisp b/tutorial/14-tutorial.lisp new file mode 100644 index 0000000..2396835 --- /dev/null +++ b/tutorial/14-tutorial.lisp @@ -0,0 +1,56 @@ +(defpackage #:clog-user + (:use #:cl #:clog) + (:export start-tutorial)) + +(in-package :clog-user) + +(defun on-new-window (body) + (set-on-click (create-button body :content "Set Local Key") + (lambda (obj) + (setf (storage-element (window body) :local "my-local-key") + (get-universal-time)) + (reload (location body)))) + + (set-on-click (create-button body :content "Set Session Key") + (lambda (obj) + (setf (storage-element (window body) :session "my-session-key") + (get-universal-time)) + (reload (location body)))) + + (set-on-storage (window body) + (lambda (obj data) + (create-div body :content + (format nil "
~A : ~A => ~A
" + (getf data ':key) + (getf data ':old-value) + (getf data ':value))))) + + + (create-div body :content (format nil + "

Local Storage vs Session Storage

+

+The value of local storage persists in browser cache even after browser closed. +If you reset this page the session storage key will remain the same, but openning +in another window or tab will be a new session but if came from a click from this +window the session keys are copied first to the new window.

+
+Another Window = Different Session
+
+
+Local Storage key: ~A := ~A
+
+Session Storage key: ~A := ~A
+
+Changes made to a local key will fire an event and print below:
" + "my-local-key" + (storage-element (window body) :local "my-local-key") + "my-session-key" + (storage-element (window body) :session "my-session-key"))) + + (run body)) + +(defun start-tutorial () + "Start turtorial." + + (initialize #'on-new-window) + (open-browser)) diff --git a/tutorial/README.md b/tutorial/README.md index 95ded1d..1a07031 100644 --- a/tutorial/README.md +++ b/tutorial/README.md @@ -46,3 +46,4 @@ Tutorial Summary - 11-tutorial.lisp - Attaching to existing HTML - 12-tutorial.lisp - Running a website in CLOG (routing) - 13-tutorial/ - Flying Solo - A minimalist CLOG project +- 14-tutorial.lisp - Local (persistent) and Session client side storage