;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; CLOG - The Common Lisp Omnificent GUI ;;;; ;;;; (c) 2020-2021 David Botton ;;;; ;;;; License BSD 3 Clause ;;;; ;;;; ;;;; ;;;; clog-base.lisp ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cl:in-package :clog) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Implementation - clog-obj ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defclass clog-obj () ((connection-id :reader connection-id :initarg :connection-id) (html-id :reader html-id :initarg :html-id)) (:documentation "CLOG objects (clog-obj) encapsulate the connection between lisp and the HTML DOM element.")) (defgeneric connection-id (clog-obj) (:documentation "Reader for connection-id slot. (Private)")) (defgeneric html-id (clog-obj) (:documentation "Reader for html-id slot. (Private)")) ;;;;;;;;;;;;;;;;;;; ;; make-clog-obj ;; ;;;;;;;;;;;;;;;;;;; (defun make-clog-obj (connection-id html-id) "Construct a new clog-obj. (Private)" (make-instance 'clog-obj :connection-id connection-id :html-id html-id)) ;;;;;;;;;;;;;;; ;; script-id ;; ;;;;;;;;;;;;;;; (defgeneric script-id (clog-obj) (:documentation "Return the script id for OBJ based on the html-id set during attachment. (Private)")) (defmethod script-id ((obj clog-obj)) (if (eql (html-id obj) 0) "'body'" (format nil "clog['~A']" (html-id obj)))) ;;;;;;;;;;;; ;; jquery ;; ;;;;;;;;;;;; (defgeneric jquery (clog-obj) (:documentation "Return the jquery accessor for OBJ. (Private)")) (defmethod jquery ((obj clog-obj)) (format nil "$(~A)" (script-id obj))) ;;;;;;;;;;;;;;;;;;;; ;; jquery-execute ;; ;;;;;;;;;;;;;;;;;;;; (defgeneric jquery-execute (clog-obj method) (:documentation "Execute the jquery METHOD on OBJ. Result is dicarded. (Private)")) (defmethod jquery-execute ((obj clog-obj) method) (cc:execute (connection-id obj) (format nil "~A.~A" (jquery obj) method))) ;;;;;;;;;;;;;;;;;; ;; jquery-query ;; ;;;;;;;;;;;;;;;;;; (defgeneric jquery-query (clog-obj method) (:documentation "Execute the jquery METHOD on OBJ and return result. (Private)")) (defmethod jquery-query ((obj clog-obj) method) (cc:query (connection-id obj) (format nil "~A.~A" (jquery obj) method))) ;;;;;;;;;;;;;;;;;;;;;;; ;; bind-event-script ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric bind-event-script (clog-obj event call-back &key cancel-event) (:documentation "Create the code client side for EVENT CALL-BACK. (Private)")) (defmethod bind-event-script ((obj clog-obj) event call-back &key (cancel-event nil)) (if cancel-event (jquery-execute obj (format nil "on('~A',function (e, data){~A});return false" event call-back)) (jquery-execute obj (format nil "on('~A',function (e, data){~A})" event call-back)))) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; unbind-event-script ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric unbind-event-script (clog-obj event) (:documentation "Remove the client call back code for EVENT. (Private)")) (defmethod unbind-event-script ((obj clog-obj) event) (jquery-execute obj (format nil "off(~A)" event))) ;;;;;;;;;;;;;;;;;;;;;;; ;; parse-mouse-event ;; ;;;;;;;;;;;;;;;;;;;;;;; (defparameter mouse-event-script "+ (e.clientX - e.target.getBoundingClientRect().left) + ':' + (e.clientY - e.target.getBoundingClientRect().top) + ':' + e.screenX + ':' + e.screenY + ':' + e.which + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey") ;; e.buttons would be better but not supported currently outside ;; of firefox and would always return 0 on Mac so using e.which. ;; The use of offsetLeft and offsetTop is to correct the X and Y ;; to the actual X,Y of the target. (defun parse-mouse-event (data) (let ((f (ppcre:split ":" data))) (list :x (parse-integer (nth 0 f) :junk-allowed t) :y (parse-integer (nth 1 f) :junk-allowed t) :screen-y (parse-integer (nth 2 f) :junk-allowed t) :screen-x (parse-integer (nth 3 f) :junk-allowed t) :which-button (parse-integer (nth 4 f) :junk-allowed t) :alt-key (js-true-p (nth 5 f)) :ctrl-key (js-true-p (nth 6 f)) :shift-key (js-true-p (nth 7 f)) :meta-key (js-true-p (nth 8 f))))) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; parse-keyboard-event ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter keyboard-event-script "+ e.keyCode + ':' + e.charCode + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey") (defun parse-keyboard-event (data) (let ((f (ppcre:split ":" data))) (list :key-code (parse-integer (nth 0 f) :junk-allowed t) :char-code (parse-integer (nth 1 f) :junk-allowed t) :alt-key (js-true-p (nth 2 f)) :ctrl-key (js-true-p (nth 3 f)) :shift-key (js-true-p (nth 4 f)) :meta-key (js-true-p (nth 5 f))))) ;;;;;;;;;;;;;;; ;; set-event ;; ;;;;;;;;;;;;;;; (defgeneric set-event (clog-obj event handler &key call-back-script) (:documentation "Create the hood for incoming events. (Private)")) (defmethod set-event ((obj clog-obj) event handler &key (call-back-script "")) ;; meeds mutex (let ((hook (format nil "~A:~A" (html-id obj) event))) (cond (handler (bind-event-script obj event (format nil "ws.send('E:~A-'~A)" hook call-back-script)) (setf (gethash hook (connection-data obj)) handler)) (t (unbind-event-script obj event) (remhash hook (connection-data obj)))))) ;;;;;;;;;;;;;; ;; property ;; ;;;;;;;;;;;;;; (defgeneric property (clog-obj property-name) (:documentation "Get/Setf html property. (eg. draggable)")) (defmethod property ((obj clog-obj) property-name) (jquery-query obj (format nil "prop('~A')" property-name))) (defgeneric set-property (clog-obj property-name value) (:documentation "Set html property.")) (defmethod set-property ((obj clog-obj) property-name value) (jquery-execute obj (format nil "prop('~A','~A')" property-name value))) (defsetf property set-property) ;;;;;;;;;;; ;; style ;; ;;;;;;;;;;; (defgeneric style (clog-obj style-name) (:documentation "Get/Setf css style.")) (defmethod style ((obj clog-obj) style-name) (jquery-query obj (format nil "css('~A')" style-name))) (defgeneric set-style (clog-obj style-name value) (:documentation "Set css style.")) (defmethod set-style ((obj clog-obj) style-name value) (jquery-execute obj (format nil "css('~A','~A')" style-name value))) (defsetf style set-style) ;;;;;;;;;;;;;;; ;; attribute ;; ;;;;;;;;;;;;;;; (defgeneric attribute (clog-obj attribute-name) (:documentation "Get/Setf html tag attribute. (eg. src on img tag)")) (defmethod attribute ((obj clog-obj) attribute-name) (jquery-query obj (format nil "attr('~A')" attribute-name))) (defgeneric set-attribute (clog-obj attribute-name value) (:documentation "Set html tag attribute.")) (defmethod set-attribute ((obj clog-obj) attribute-name value) (jquery-execute obj (format nil "attr('~A','~A')" attribute-name value))) (defsetf attribute set-attribute) ;;;;;;;;;;;; ;; height ;; ;;;;;;;;;;;; (defgeneric height (clog-obj) (:documentation "Get/Setf html height in pixels.")) (defmethod height ((obj clog-obj)) (jquery-query obj "height()")) (defgeneric set-height (clog-obj value) (:documentation "Set height VALUE for CLOG-OBJ")) (defmethod set-height ((obj clog-obj) value) (jquery-execute obj (format nil "height('~A')" value))) (defsetf height set-height) ;;;;;;;;;;; ;; width ;; ;;;;;;;;;;; (defgeneric width (clog-obj) (:documentation "Get/Setf html width in pixels.")) (defmethod width ((obj clog-obj)) (jquery-query obj "width()")) (defgeneric set-width (clog-obj value) (:documentation "Set width VALUE for CLOG-OBJ")) (defmethod set-width ((obj clog-obj) value) (jquery-execute obj (format nil "width('~A')" value))) (defsetf width set-width) ;;;;;;;;;;; ;; focus ;; ;;;;;;;;;;; (defgeneric focus (clog-obj) (:documentation "Focus on CLOG-OBJ")) (defmethod focus ((obj clog-obj)) (jquery-execute obj "focus()")) ;;;;;;;;;; ;; blur ;; ;;;;;;;;;; (defgeneric blur (clog-obj) (:documentation "Remove focus from CLOG-OBJ")) (defmethod focus ((obj clog-obj)) (jquery-execute obj "blur()")) ;;;;;;;;;;;;;;;;;; ;; create-child ;; ;;;;;;;;;;;;;;;;;; (defgeneric create-child (clog-obj html &key auto-place) (:documentation "Create a new CLOG-OBJ from HTML element as child of OBJ and if :AUTO-PLACE (default t) place-inside-bottom-of OBJ")) (defmethod create-child ((obj clog-obj) html &key (auto-place t)) (let ((child (create-with-html (connection-id obj) html))) (if auto-place (place-inside-bottom-of obj child) child))) ;;;;;;;;;;;;;;;;;;;;; ;; attach-as-child ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric attach-as-child (clog-obj html-id) (:documentation "Create a new CLOG-OBJ and attach an existing element with HTML-ID. The HTML-ID must be unique.")) (defmethod attach-as-child ((obj clog-obj) html-id) (cc:execute (connection-id obj) (format nil "clog['~A']=$('#~A')" html-id html-id)) (make-clog-obj (connection-id obj) html-id)) ;;;;;;;;;;;; ;; validp ;; ;;;;;;;;;;;; (defgeneric validp (clog-obj) (:documentation "Returns true of connection is valid on this CLOG-OBJ.")) (defmethod validp ((obj clog-obj)) (cc:validp (connection-id obj))) ;;;;;;;;;;;;;;;;;;;;; ;; connection-data ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-data (clog-obj) (:documentation "Get connection-data that is associated with clog-obj that will persist regardless of thread. The event hooks are stored in this string based hash in the format of: \"html-id:event-name\" => event-handler.")) (defmethod connection-data ((obj clog-obj)) (cc:get-connection-data (connection-id obj))) ;;;;;;;;;;;;;;;;; ;; place-after ;; ;;;;;;;;;;;;;;;;; (defgeneric place-after (clog-obj next-obj) (:documentation "Places NEXT-OBJ after CLOG-OBJ in DOM")) (defmethod place-after ((obj clog-obj) next-obj) (jquery-execute obj (format nil "after(~A)" (script-id next-obj))) next-obj) ;;;;;;;;;;;;;;;;;; ;; place-before ;; ;;;;;;;;;;;;;;;;;; (defgeneric place-before (clog-obj next-obj) (:documentation "Places NEXT-OBJ before CLOG-OBJ in DOM")) (defmethod place-before ((obj clog-obj) next-obj) (jquery-execute obj (format nil "before(~A)" (script-id next-obj))) next-obj) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; place-inside-top-of ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric place-inside-top-of (clog-obj next-obj) (:documentation "Places NEXT-OBJ inside top of CLOG-OBJ in DOM")) (defmethod place-inside-top-of ((obj clog-obj) next-obj) (jquery-execute obj (format nil "prepend(~A)" (script-id next-obj))) next-obj) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; place-inside-bottom-of ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric place-inside-bottom-of (clog-obj next-obj) (:documentation "Places NEXT-OBJ inside bottom of CLOG-OBJ in DOM")) (defmethod place-inside-bottom-of ((obj clog-obj) next-obj) (jquery-execute obj (format nil "append(~A)" (script-id next-obj))) next-obj) ;;;;;;;;;;;;;;;;;;; ;; set-on-resize ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-resize (clog-obj on-resize-handler) (:documentation "Set the ON-RESIZE-HANDLER for CLOG-OBJ. If ON-RESIZE-HANDLER is nil unbind the event.")) (defmethod set-on-resize ((obj clog-obj) on-resize-handler) (let ((on-resize on-resize-handler)) (set-event obj "resize" (lambda (data) (declare (ignore data)) (funcall on-resize))))) ;;;;;;;;;;;;;;;;;; ;; set-on-focus ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-focus (clog-obj on-focus-handler) (:documentation "Set the ON-FOCUS-HANDLER for CLOG-OBJ. If ON-FOCUS-HANDLER is nil unbind the event.")) (defmethod set-on-focus ((obj clog-obj) on-focus-handler) (let ((on-focus on-focus-handler)) (set-event obj "focus" (lambda (data) (declare (ignore data)) (funcall on-focus))))) ;;;;;;;;;;;;;;;;; ;; set-on-blur ;; ;;;;;;;;;;;;;;;;; (defgeneric set-on-blur (clog-obj on-blur-handler) (:documentation "Set the ON-BLUR-HANDLER for CLOG-OBJ. If ON-BLUR-HANDLER is nil unbind the event.")) (defmethod set-on-blur ((obj clog-obj) on-blur-handler) (let ((on-blur on-blur-handler)) (set-event obj "blur" (lambda (data) (declare (ignore data)) (funcall on-blur))))) ;;;;;;;;;;;;;;;;;;; ;; Set-on-change ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-change (clog-obj on-change-handler) (:documentation "Set the ON-CHANGE-HANDLER for CLOG-OBJ. If ON-CHANGE-HANDLER is nil unbind the event.")) (defmethod set-on-change ((obj clog-obj) on-change-handler) (let ((on-change on-change-handler)) (set-event obj "change" (lambda (data) (declare (ignore data)) (funcall on-change))))) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-focus-in ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-focus-in (clog-obj on-focus-in-handler) (:documentation "Set the ON-FOCUS-IN-HANDLER for CLOG-OBJ. If ON-FOCUS-IN-HANDLER is nil unbind the event.")) (defmethod set-on-focus-in ((obj clog-obj) on-focus-in-handler) (let ((on-focus-in on-focus-in-handler)) (set-event obj "focusin" (lambda (data) (declare (ignore data)) (funcall on-focus-in))))) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-focus-out ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-focus-out (clog-obj on-focus-out-handler) (:documentation "Set the ON-FOCUS-OUT-HANDLER for CLOG-OBJ. If ON-FOCUS-OUT-HANDLER is nil unbind the event.")) (defmethod set-on-focus-out ((obj clog-obj) on-focus-out-handler) (let ((on-focus-out on-focus-out-handler)) (set-event obj "focusout" (lambda (data) (declare (ignore data)) (funcall on-focus-out))))) ;;;;;;;;;;;;;;;;;; ;; set-on-reset ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-reset (clog-obj on-reset-handler) (:documentation "Set the ON-RESET-HANDLER for CLOG-OBJ. If ON-RESET-HANDLER is nil unbind the event.")) (defmethod set-on-reset ((obj clog-obj) on-reset-handler) (let ((on-reset on-reset-handler)) (set-event obj "reset" (lambda (data) (declare (ignore data)) (funcall on-reset))))) ;;;;;;;;;;;;;;;;;;; ;; set-on-search ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-search (clog-obj on-search-handler) (:documentation "Set the ON-SEARCH-HANDLER for CLOG-OBJ. If ON-SEARCH-HANDLER is nil unbind the event.")) (defmethod set-on-search ((obj clog-obj) on-search-handler) (let ((on-search on-search-handler)) (set-event obj "search" (lambda (data) (declare (ignore data)) (funcall on-search))))) ;;;;;;;;;;;;;;;;;;; ;; set-on-select ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-select (clog-obj on-select-handler) (:documentation "Set the ON-SELECT-HANDLER for CLOG-OBJ. If ON-SELECT-HANDLER is nil unbind the event.")) (defmethod set-on-select ((obj clog-obj) on-select-handler) (let ((on-select on-select-handler)) (set-event obj "select" (lambda (data) (declare (ignore data)) (funcall on-select))))) ;;;;;;;;;;;;;;;;;;; ;; set-on-submit ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-submit (clog-obj on-submit-handler) (:documentation "Set the ON-SUBMIT-HANDLER for CLOG-OBJ. If ON-SUBMIT-HANDLER is nil unbind the event.")) (defmethod set-on-submit ((obj clog-obj) on-submit-handler) (let ((on-submit on-submit-handler)) (set-event obj "submit" (lambda (data) (declare (ignore data)) (funcall on-submit))))) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-context-menu ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-context-menu (clog-obj on-context-menu-handler) (:documentation "Set the ON-CONTEXT-MENU-HANDLER for CLOG-OBJ. If ON-CONTEXT-MENU-HANDLER is nil unbind the event. Setting on-mouse-right-click will replace this handler.")) (defmethod set-on-context-menu ((obj clog-obj) on-context-menu-handler) (let ((on-context-menu on-context-menu-handler)) (set-event obj "contextmenu" (lambda (data) (declare (ignore data)) (funcall on-context-menu))))) ;;;;;;;;;;;;;;;;;; ;; set-on-click ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-click (clog-obj on-click-handler) (:documentation "Set the ON-CLICK-HANDLER for CLOG-OBJ. If ON-CLICK-HANDLER is nil unbind the event. Setting this event will replace an on-mouse click if set.")) (defmethod set-on-click ((obj clog-obj) on-click-handler) (let ((on-click on-click-handler)) (set-event obj "click" (lambda (data) (declare (ignore data)) (funcall on-click))))) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-double-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-double-click (clog-obj on-double-click-handler) (:documentation "Set the ON-DOUBLE-CLICK-HANDLER for CLOG-OBJ. If ON-DOUBLE-CLICK-HANDLER is nil unbind the event. Setting the on-mouse-double-click event will replace this handler.")) (defmethod set-on-double-click ((obj clog-obj) on-double-click-handler) (let ((on-double-click on-double-click-handler)) (set-event obj "dblclick" (lambda (data) (declare (ignore data)) (funcall on-double-click))))) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-click ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-click (clog-obj on-mouse-click-handler) (:documentation "Set the ON-MOUSE-CLICK-HANDLER for CLOG-OBJ. If ON-MOUSE-CLICK-HANDLER is nil unbind the event. Setting this event will replace on an on-click event.")) (defmethod set-on-mouse-click ((obj clog-obj) on-mouse-click-handler) (let ((on-mouse-click on-mouse-click-handler)) (set-event obj "click" (lambda (data) (funcall on-mouse-click (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-double-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-double-click (clog-obj on-mouse-double-click-handler) (:documentation "Set the ON-MOUSE-DOUBLE-CLICK-HANDLER for CLOG-OBJ. If ON-MOUSE-DOUBLE-CLICK-HANDLER is nil unbind the event. Setting this event will replace on an on-double-click event.")) (defmethod set-on-mouse-double-click ((obj clog-obj) on-mouse-double-click-handler) (let ((on-mouse-double-click on-mouse-double-click-handler)) (set-event obj "dblclick" (lambda (data) (funcall on-mouse-double-click (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-right-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-right-click (clog-obj on-mouse-right-click-handler) (:documentation "Set the ON-MOUSE-RIGHT-CLICK-HANDLER for CLOG-OBJ. If ON-MOUSE-RIGHT-CLICK-HANDLER is nil unbind the event. Setting this event will replace on an on-context-menu event.")) (defmethod set-on-mouse-right-click ((obj clog-obj) on-mouse-right-click-handler) (let ((on-mouse-right-click on-mouse-right-click-handler)) (set-event obj "contextmenu" (lambda (data) (funcall on-mouse-right-click (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-enter ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-enter (clog-obj on-mouse-enter-handler) (:documentation "Set the ON-MOUSE-ENTER-HANDLER for CLOG-OBJ. If ON-MOUSE-ENTER-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-enter ((obj clog-obj) on-mouse-enter-handler) (let ((on-mouse-enter on-mouse-enter-handler)) (set-event obj "mouseenter" (lambda (data) (declare (ignore data)) (funcall on-mouse-enter))))) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-leave ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-leave (clog-obj on-mouse-leave-handler) (:documentation "Set the ON-MOUSE-LEAVE-HANDLER for CLOG-OBJ. If ON-MOUSE-LEAVE-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-leave ((obj clog-obj) on-mouse-leave-handler) (let ((on-mouse-leave on-mouse-leave-handler)) (set-event obj "mouseleave" (lambda (data) (declare (ignore data)) (funcall on-mouse-leave))))) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-over ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-over (clog-obj on-mouse-over-handler) (:documentation "Set the ON-MOUSE-OVER-HANDLER for CLOG-OBJ. If ON-MOUSE-OVER-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-over ((obj clog-obj) on-mouse-over-handler) (let ((on-mouse-over on-mouse-over-handler)) (set-event obj "mouseover" (lambda (data) (declare (ignore data)) (funcall on-mouse-over))))) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-out ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-out (clog-obj on-mouse-out-handler) (:documentation "Set the ON-MOUSE-OUT-HANDLER for CLOG-OBJ. If ON-MOUSE-OUT-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-out ((obj clog-obj) on-mouse-out-handler) (let ((on-mouse-out on-mouse-out-handler)) (set-event obj "mouseout" (lambda (data) (declare (ignore data)) (funcall on-mouse-out))))) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-down ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-down (clog-obj on-mouse-down-handler) (:documentation "Set the ON-MOUSE-DOWN-HANDLER for CLOG-OBJ. If ON-MOUSE-DOWN-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-down ((obj clog-obj) on-mouse-down-handler) (let ((on-mouse-down on-mouse-down-handler)) (set-event obj "mousedown" (lambda (data) (funcall on-mouse-down (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-up ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-up (clog-obj on-mouse-up-handler) (:documentation "Set the ON-MOUSE-UP-HANDLER for CLOG-OBJ. If ON-MOUSE-UP-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-up ((obj clog-obj) on-mouse-up-handler) (let ((on-mouse-up on-mouse-up-handler)) (set-event obj "mouseup" (lambda (data) (funcall on-mouse-up (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-move ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-move (clog-obj on-mouse-move-handler) (:documentation "Set the ON-MOUSE-MOVE-HANDLER for CLOG-OBJ. If ON-MOUSE-MOVE-HANDLER is nil unbind the event.")) (defmethod set-on-mouse-move ((obj clog-obj) on-mouse-move-handler) (let ((on-mouse-move on-mouse-move-handler)) (set-event obj "mousemove" (lambda (data) (funcall on-mouse-move (parse-mouse-event data))) :call-back-script mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-character ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-character (clog-obj on-character-handler) (:documentation "Set the ON-CHARACTER-HANDLER for CLOG-OBJ. If ON-CHARACTER-HANDLER is nil unbind the event. Setting this event will replace a on-key-press")) (defmethod set-on-character ((obj clog-obj) on-character-handler) (let ((on-character on-character-handler)) (set-event obj "keypress" (lambda (data) (let ((f (parse-keyboard-event data))) (funcall on-character (code-char (getf f ':char-code))))) :call-back-script keyboard-event-script))) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-key-down ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-down (clog-obj on-key-down-handler) (:documentation "Set the ON-KEY-DOWN-HANDLER for CLOG-OBJ. If ON-KEY-DOWN-HANDLER is nil unbind the event.")) (defmethod set-on-key-down ((obj clog-obj) on-key-down-handler) (let ((on-key-down on-key-down-handler)) (set-event obj "keydown" (lambda (data) (funcall on-key-down (parse-keyboard-event data))) :call-back-script keyboard-event-script))) ;;;;;;;;;;;;;;;;;;; ;; set-on-key-up ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-up (clog-obj on-key-up-handler) (:documentation "Set the ON-KEY-UP-HANDLER for CLOG-OBJ. If ON-KEY-UP-HANDLER is nil unbind the event.")) (defmethod set-on-key-up ((obj clog-obj) on-key-up-handler) (let ((on-key-up on-key-up-handler)) (set-event obj "keyup" (lambda (data) (funcall on-key-up (parse-keyboard-event data))) :call-back-script keyboard-event-script))) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-key-press ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-press (clog-obj on-key-press-handler) (:documentation "Set the ON-KEY-PRESS-HANDLER for CLOG-OBJ. If ON-KEY-PRESS-HANDLER is nil unbind the event.")) (defmethod set-on-key-press ((obj clog-obj) on-key-press-handler) (let ((on-key-press on-key-press-handler)) (set-event obj "keypress" (lambda (data) (funcall on-key-press (parse-keyboard-event data))) :call-back-script keyboard-event-script))) ;;;;;;;;;;;;;;;;; ;; set-on-copy ;; ;;;;;;;;;;;;;;;;; (defgeneric set-on-copy (clog-obj on-copy-handler) (:documentation "Set the ON-COPY-HANDLER for CLOG-OBJ. If ON-COPY-HANDLER is nil unbind the event.")) (defmethod set-on-copy ((obj clog-obj) on-copy-handler) (let ((on-copy on-copy-handler)) (set-event obj "copy" (lambda (data) (declare (ignore data)) (funcall on-copy))))) ;;;;;;;;;;;;;;;; ;; set-on-cut ;; ;;;;;;;;;;;;;;;; (defgeneric set-on-cut (clog-obj on-cut-handler) (:documentation "Set the ON-CUT-HANDLER for CLOG-OBJ. If ON-CUT-HANDLER is nil unbind the event.")) (defmethod set-on-cut ((obj clog-obj) on-cut-handler) (let ((on-cut on-cut-handler)) (set-event obj "cut" (lambda (data) (declare (ignore data)) (funcall on-cut))))) ;;;;;;;;;;;;;;;;;; ;; set-on-paste ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-paste (clog-obj on-paste-handler) (:documentation "Set the ON-PASTE-HANDLER for CLOG-OBJ. If ON-PASTE-HANDLER is nil unbind the event.")) (defmethod set-on-paste ((obj clog-obj) on-paste-handler) (let ((on-paste on-paste-handler)) (set-event obj "paste" (lambda (data) (declare (ignore data)) (funcall on-paste))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Implementation - CLOG Low Level ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;; ;; attach ;; ;;;;;;;;;;;; (defun attach (connection-id html-id) "Create a new clog-obj and attach an existing element with HTML-ID on CONNECTION-ID to it and then return it. The HTML-ID must be unique. (private)" (cc:execute connection-id (format nil "clog['~A']=$('#~A')" html-id html-id)) (make-clog-obj connection-id html-id)) ;;;;;;;;;;;;;;;;;;;;;; ;; create-with-html ;; ;;;;;;;;;;;;;;;;;;;;;; (defun create-with-html (connection-id html) "Create a new clog-obj and attach it to HTML on CONNECTION-ID. There must be a single outer block that will be set to an internal id. The returned clog-obj requires placement or will not be visible, ie. place-after, etc. (private)" (let ((web-id (cc:generate-id))) (cc:execute connection-id (format nil "clog['~A']=$(\"~A\"); clog['~A'].first().prop('id','~A')" web-id html web-id web-id)) (make-clog-obj connection-id web-id)))