;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; CLOG - The Common Lisp Omnificent GUI ;;;; ;;;; (c) David Botton ;;;; ;;;; ;;;; ;;;; clog-base.lisp ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cl:in-package :clog) ;;; Implements the base object clog-obj that encapsulates the communication ;;; between the browser and the lisp code for each object in the HTML DOM. ;;; This includes properties, methods and events. Each clog-obj also has ;;; methods to retrieve connection-data (data that is associated with the ;;; current page regardless of object or thread of execution is lisp). (pushnew :clog *features*) (defvar *connection-cache* nil "Dynamic variable containing optional cache. Every thread has its own context and therefore its own copy of this variable when dynamically bound. As a result no thread protection is needed to access. To use dynamically bind the *connection-cache* and set it to (list :cache) turn on caching. By default this is off its main use is during initial setup of complex pages. (private) See macro with-connection-cache.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Implementation - clog-obj ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defclass clog-obj () ((connection-id :reader connection-id :initarg :connection-id) (html-id :reader html-id :initarg :html-id) (parent :accessor parent :initform nil)) (:documentation "CLOG objects (clog-obj) encapsulate the connection between lisp and an HTML DOM element.")) ;;;;;;;;;;;;;;;;;; ;; print-object ;; ;;;;;;;;;;;;;;;;;; (defmethod print-object ((obj clog-obj) stream) (print-unreadable-object (obj stream :type t) (with-slots (connection-id html-id) obj (format stream "connection-id: ~a html-id: ~a" connection-id html-id)))) ;;;;;;;;;;;;;;;;;;; ;; create-parent ;; ;;;;;;;;;;;;;;;;;;; (defgeneric parent (clog-obj) (:documentation "Returns the clog-obj of the obj that was used as creation parent if was set or nil. This is not per se the parent in the DOM.")) ;;;;;;;;;;;;;;;;;;; ;; connection-id ;; ;;;;;;;;;;;;;;;;;;; (defgeneric connection-id (clog-obj) (:documentation "Reader for connection-id slot. (Private)")) ;;;;;;;;;;;;; ;; html-id ;; ;;;;;;;;;;;;; (defgeneric html-id (clog-obj) (:documentation "Internal html-id of clog-obj. (Internal)")) ;;;;;;;;;;;;;;;;;;; ;; 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 CLOG-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)))) ;;;;;;;;;;;;;;;;;;; ;; cached-excute ;; ;;;;;;;;;;;;;;;;;;; (defun cached-execute (connection-id script) "Excute SCRIPT on browser, if connection cache enabled use. (Private)" (if *connection-cache* (push script *connection-cache*) (clog-connection:execute connection-id script))) ;;;;;;;;;;;;;;;; ;; js-execute ;; ;;;;;;;;;;;;;;;; (defgeneric js-execute (clog-obj script) (:documentation "Execute JavaScript SCRIPT on browser. CLOG-OBJ is used to obtain the connection the script should run on. Result is discarded, return CLOG-OBJ. (Internal)")) (defmethod js-execute ((obj clog-obj) script) (cached-execute (connection-id obj) script) obj) ;;;;;;;;;;;;;; ;; js-query ;; ;;;;;;;;;;;;;; (defgeneric js-query (clog-obj script &key default-answer) (:documentation "Execute JavaScript SCRIPT on browser and return result. CLOG-OBJ us used to obtain the connection the script should run on. (Internal)")) (defmethod js-query ((obj clog-obj) script &key (default-answer nil)) (flush-connection-cache obj) (clog-connection:query (connection-id obj) script :default-answer default-answer)) ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; with-connection-cache ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro with-connection-cache ((clog-obj) &body body) "Caches writes to the connection-id of CLOG-OBJ until flushed with FLUSH-CONNECTION-CACHE or a query is made." `(let ((*connection-cache* (list :cache))) ,@body (clog:flush-connection-cache ,clog-obj))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; flush-connection-cache ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun flush-connection-cache (clog-obj) "Flush connection cache if on CLOG-OBJ is located on." (when *connection-cache* (let ((big (make-string-output-stream))) (write-string "(function(){" big) (dolist (script (reverse *connection-cache*)) (unless (eq script :cache) (write-string script big) (write-char #\; big))) (write-string "})()" big) (clog-connection:execute (connection-id clog-obj) (get-output-stream-string big))) (setf *connection-cache* (list :cache)))) ;;;;;;;;;;;;; ;; execute ;; ;;;;;;;;;;;;; (defgeneric execute (clog-obj method) (:documentation "Execute the JavaScript METHOD on OBJ. Returns CLOG-OBJ. see JQUERY-EXECUTE (Internal)")) (defmethod execute ((obj clog-obj) method) (js-execute obj (format nil "~A.~A" (script-id obj) method))) ;;;;;;;;;;; ;; query ;; ;;;;;;;;;;; (defgeneric query (clog-obj method &key default-answer) (:documentation "Execute the JavaScript query METHOD on OBJ and return result or if time out DEFAULT-ANSWER. see JQUERY-QUERY (Internal)")) (defmethod query ((obj clog-obj) method &key (default-answer nil)) (js-query obj (format nil "~A.~A" (script-id obj) method) :default-answer default-answer)) ;;;;;;;;;;;;;;;;;;;;;;; ;; bind-event-script ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric bind-event-script (clog-obj event call-back) (:documentation "Create the code client side for EVENT CALL-BACK. (Private)")) (defmethod bind-event-script ((obj clog-obj) 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.currentTarget.getBoundingClientRect().left + e.currentTarget.scrollLeft) + ':' + (e.clientY - e.currentTarget.getBoundingClientRect().top + e.currentTarget.scrollTop) + ':' + e.screenX + ':' + e.screenY + ':' + e.which + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + e.clientX + ':' + e.clientY + ':' + e.pageX + ':' + e.pageY" "JavaScript to collect mouse event data from browser.") ;; 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. (defparameter mouse-event-script-for-window "+ e.clientX + ':' + e.clientY + ':' + e.screenX + ':' + e.screenY + ':' + e.which + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + e.clientX + ':' + e.clientY + ':' + e.pageX + ':' + e.pageY" "JavaScript to collect mouse event data from browser.") (defun parse-mouse-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :mouse :x (js-to-integer (nth 0 f)) :y (js-to-integer (nth 1 f)) :screen-x (js-to-integer (nth 2 f)) :screen-y (js-to-integer (nth 3 f)) :which-button (js-to-integer (nth 4 f)) :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)) :client-x (js-to-integer (nth 9 f)) :client-Y (js-to-integer (nth 10 f)) :page-x (js-to-integer (nth 11 f)) :page-Y (js-to-integer (nth 12 f))))) ;;;;;;;;;;;;;;;;;;;;;;; ;; parse-touch-event ;; ;;;;;;;;;;;;;;;;;;;;;;; (defparameter touch-event-script "+ (e.touches[0].clientX - e.touches[0].target.getBoundingClientRect().left + e.touches[0].target.scrollLeft) + ':' + (e.touches[0].clientY - e.touches[0].target.getBoundingClientRect().top + e.touches[0].target.scrollTop) + ':' + e.touches[0].screenX + ':' + e.touches[0].screenY + ':' + e.touches.length + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + e.touches[0].clientX + ':' + e.touches[0].clientY + ':' + e.touches[0].pageX + ':' + e.touches[0].pageY" "JavaScript to collect touch event data from browser.") (defun parse-touch-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :touch :x (js-to-integer (nth 0 f)) :y (js-to-integer (nth 1 f)) :screen-x (js-to-integer (nth 2 f)) :screen-y (js-to-integer (nth 3 f)) :number-fingers (js-to-integer (nth 4 f)) :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)) :client-x (js-to-integer (nth 9 f)) :client-Y (js-to-integer (nth 10 f)) :page-x (js-to-integer (nth 11 f)) :page-Y (js-to-integer (nth 12 f))))) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; parse-pointer-event ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter pointer-event-script "+ (e.clientX - e.currentTarget.getBoundingClientRect().left + e.currentTarget.scrollLeft) + ':' + (e.clientY - e.currentTarget.getBoundingClientRect().top + e.currentTarget.scrollTop) + ':' + e.screenX + ':' + e.screenY + ':' + e.which + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + e.clientX + ':' + e.clientY + ':' + e.pageX + ':' + e.pageY" "JavaScript to collect pointer event data from browser.") (defun parse-pointer-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :pointer :x (js-to-integer (nth 0 f)) :y (js-to-integer (nth 1 f)) :screen-x (js-to-integer (nth 2 f)) :screen-y (js-to-integer (nth 3 f)) :which-button (js-to-integer (nth 4 f)) :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)) :client-x (js-to-integer (nth 9 f)) :client-Y (js-to-integer (nth 10 f)) :page-x (js-to-integer (nth 11 f)) :page-Y (js-to-integer (nth 12 f))))) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; parse-keyboard-event ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter keyboard-event-script "+ e.key.charCodeAt(0) + ':' + e.charCode + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + (e.key == ':' ? 'colon' : e.key)" "JavaScript to collect keyboard event data from browser.") (defun parse-keyboard-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :keyboard :key-code (js-to-integer (nth 0 f)) :char-code (js-to-integer (nth 1 f)) :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)) :key (if (equal (nth 6 f) "colon") ":" (nth 6 f))))) ;;;;;;;;;;;;;;;;;;;;;; ;; parse-drop-event ;; ;;;;;;;;;;;;;;;;;;;;;; (defparameter drop-event-script "+ (e.clientX - e.currentTarget.getBoundingClientRect().left + e.currentTarget.scrollLeft) + ':' + (e.clientY - e.currentTarget.getBoundingClientRect().top + e.currentTarget.scrollTop) + ':' + e.which + ':' + e.altKey + ':' + e.ctrlKey + ':' + e.shiftKey + ':' + e.metaKey + ':' + encodeURIComponent(e.originalEvent.dataTransfer.getData('~A'))" "JavaScript to collect drop event data from browser.") (defun parse-drop-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :drop :x (js-to-integer (nth 0 f)) :y (js-to-integer (nth 1 f)) :which-button (js-to-integer (nth 2 f)) :alt-key (js-true-p (nth 3 f)) :ctrl-key (js-true-p (nth 4 f)) :shift-key (js-true-p (nth 5 f)) :meta-key (js-true-p (nth 6 f)) :drag-data (quri:url-decode (or (nth 7 f) ""))))) ;;;;;;;;;;;;;;;;;;;;;;; ;; parse-wheel-event ;; ;;;;;;;;;;;;;;;;;;;;;;; (defparameter wheel-event-script "+ e.originalEvent.deltaX + ':' + e.originalEvent.deltaY + ':' + e.originalEvent.deltaZ + ':' + e.originalEvent.deltaMode" "JavaScript to collect wheel event data from browser.") (defun parse-wheel-event (data) (let ((f (ppcre:split ":" data))) (list :event-type :wheel :delta-x (js-to-float (nth 0 f)) :delta-y (js-to-float (nth 1 f)) :delta-z (js-to-float (nth 2 f)) :delta-mode (js-to-integer (nth 3 f))))) ;;;;;;;;;;;;;;; ;; set-event ;; ;;;;;;;;;;;;;;; (defgeneric set-event (clog-obj event handler &key call-back-script pre-eval eval-script post-eval cancel-event one-time) (:documentation "Create the low-level hook for incoming events. (Private)")) (defmethod set-event ((obj clog-obj) event handler &key (call-back-script "") (pre-eval "") (eval-script "") (post-eval "") (cancel-event nil) (one-time nil)) (let ((hook (format nil "~A:~A" (html-id obj) event)) (cd (connection-data obj))) (if cd (cond (handler (bind-event-script obj event (format nil "~A~Aws.send('E:~A '~A)~A~@[~A~]~@[~A~]" pre-eval eval-script hook call-back-script post-eval (when one-time (format nil "; ~A.off('~A')" (jquery obj) event)) (when cancel-event "; return false"))) (setf (gethash hook cd) handler)) (t (unbind-event-script obj event) (remhash hook cd))) (format t "Attempt to set event on non-existant connection.~%")))) ;;;;;;;;;;;;;; ;; property ;; ;;;;;;;;;;;;;; (defgeneric property (clog-obj property-name &key default-answer) (:documentation "Get/Setf html property.")) (defmethod property ((obj clog-obj) property-name &key (default-answer nil)) (jquery-query obj (format nil "prop('~A')" property-name) :default-answer default-answer)) (defgeneric (setf property) (value clog-obj property-name) (:documentation "Set html property.")) (defmethod (setf property) (value (obj clog-obj) property-name) (jquery-execute obj (format nil "prop('~A','~A')" property-name (escape-string value))) value) ;;;;;;;;;;;; ;; height ;; ;;;;;;;;;;;; (defgeneric height (clog-obj) (:documentation "Get/Setf html height in pixels.")) (defmethod height ((obj clog-obj)) (js-to-integer (jquery-query obj "height()"))) (defgeneric (setf height) (value clog-obj) (:documentation "Set height VALUE for CLOG-OBJ")) (defmethod (setf height) (value (obj clog-obj)) (jquery-execute obj (format nil "height('~A')" (escape-string value))) value) ;;;;;;;;;;; ;; width ;; ;;;;;;;;;;; (defgeneric width (clog-obj) (:documentation "Get/Setf html width in pixels.")) (defmethod width ((obj clog-obj)) (js-to-integer (jquery-query obj "width()"))) (defgeneric (setf width) (value clog-obj) (:documentation "Set width VALUE for CLOG-OBJ")) (defmethod (setf width) (value (obj clog-obj)) (jquery-execute obj (format nil "width('~A')" (escape-string value))) value) ;;;;;;;;;;; ;; 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 blur ((obj clog-obj)) (jquery-execute obj "blur()")) ;;;;;;;;;;;; ;; validp ;; ;;;;;;;;;;;; (defgeneric validp (clog-obj) (:documentation "Returns true if connection is valid on this CLOG-OBJ.")) (defmethod validp ((obj clog-obj)) (clog-connection:validp (connection-id obj))) ;;;;;;;;;;;;;;;;;;;;; ;; connection-data ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-data (clog-obj) (:documentation "Get connection-data that is associated with clog-obj's connection that will persist regardless of thread calling. The event hooks are stored in this string based hash in the format of: \"html-id:event-name\" => #'event-handler. clog-* keys are reserved for internal use of clog. The following default keys are set: \"clog-body\" clog-body of this connection, see CONNECTION-BODY \"clog-path\" html path used, see CONNECTION-PATH \"clog-sync\" mutex used for syncing events, see CONNECTION-SYNC")) (defmethod connection-data ((obj clog-obj)) (clog-connection:get-connection-data (connection-id obj))) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; connection-data-item ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-data-item (clog-obj item-name) (:documentation "Get/Setf item-name from connection-data.")) (defmethod connection-data-item ((obj clog-obj) item-name) (ignore-errors (gethash item-name (connection-data obj)))) (defgeneric (setf connection-data-item) (value clog-obj item-name) (:documentation "Set connection-data item-name with value.")) (defmethod (setf connection-data-item) (value (obj clog-obj) item-name) (ignore-errors (setf (gethash item-name (connection-data obj)) value)) value) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; remove-connection-data-item ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric remove-connection-data-item (clog-obj item-name) (:documentation "Remove item-name from connection-data.")) (defmethod remove-connection-data-item ((obj clog-obj) item-name) (ignore-errors (remhash item-name (connection-data obj)))) ;;;;;;;;;;;;;;;;;;;;; ;; connection-body ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-body (clog-obj) (:documentation "Get connection's clog-body.")) (defmethod connection-body (clog-obj) (connection-data-item clog-obj "clog-body")) ;;;;;;;;;;;;;;;;;;;;; ;; connection-path ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-path (clog-obj) (:documentation "Get the HTML passed used to make the connection.")) (defmethod connection-path (clog-obj) (connection-data-item clog-obj "clog-path")) ;;;;;;;;;;;;;;;;;;;;; ;; connection-sync ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric connection-sync (clog-obj) (:documentation "Get connection's clog-sync for optional syncing events.")) (defmethod connection-sync (clog-obj) (connection-data-item clog-obj "clog-sync")) ;;;;;;;;;;;;;;;;;;;;; ;; with-sync-event ;; ;;;;;;;;;;;;;;;;;;;;; (defmacro with-sync-event ((clog-obj) &body body) "Place at start of event to serialize access to the event. All events in an application share per connection the same queue of serialized events." `(bordeaux-threads:with-lock-held (,`(connection-sync ,clog-obj)) ,@body)) ;;;;;;;;;;;;;;;;;; ;; set-on-event ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-event (clog-obj event-name handler &key cancel-event one-time) (:documentation "Set a HANDLER for EVENT-NAME on CLOG-OBJ. If handler is nil unbind all event handlers. (Internal)")) (defmethod set-on-event ((obj clog-obj) event-name handler &key (cancel-event nil) (one-time nil)) (set-event obj event-name (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :cancel-event cancel-event :one-time one-time)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-event-with-data ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-event-with-data (clog-obj event-name handler &key cancel-event one-time) (:documentation "Set a HANDLER for EVENT-NAME on CLOG-OBJ. If handler is nil unbind all event handlers. Handler is called with a data option passed from javascript calling the jQuery custom event mechanism .trigger('event_name', data) (Internal)")) (defmethod set-on-event-with-data ((obj clog-obj) event-name handler &key (cancel-event nil) (one-time nil)) (set-event obj event-name (when handler (lambda (data) (funcall handler obj data))) :call-back-script "+data" :cancel-event cancel-event :one-time one-time)) ;;;;;;;;;;;;;;;;;;; ;; 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. In most modern browser this only works on the clog-window object, unless a ResizeObserver is put in place, so one is installed except for clog-window making this event functional.")) (defmethod set-on-resize ((obj clog-obj) handler) (set-on-event obj "resize" handler :cancel-event t) (unless (typep obj 'clog-window) (js-execute obj (format nil "new ResizeObserver(() => { ~A.trigger('resize'); }).observe(~A)" (jquery obj) (script-id obj))))) ;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "focus" handler)) ;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "blur" handler)) ;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "change" handler)) ;;;;;;;;;;;;;;;;;; ;; set-on-input ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-input (clog-obj on-input-handler) (:documentation "Set the ON-INPUT-HANDLER for CLOG-OBJ. If ON-INPUT-HANDLER is nil unbind the event.")) (defmethod set-on-input ((obj clog-obj) handler) (set-on-event obj "input" handler)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-drag-start ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-drag-start (clog-obj on-drag-start-handler &key drag-data drag-type) (:documentation "Set the ON-DRAG-START-HANDLER for CLOG-OBJ. If ON-DRAG-START-HANDLER is nil unbind the event.")) (defmethod set-on-drag-start ((obj clog-obj) handler &key (drag-data "") (drag-type "text/plain")) (set-event obj "dragstart" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :eval-script (format nil "e.originalEvent.dataTransfer.setData('~A','~A'); " drag-type drag-data))) ;;;;;;;;;;;;;;;;; ;; set-on-drag ;; ;;;;;;;;;;;;;;;;; (defgeneric set-on-drag (clog-obj on-drag-handler) (:documentation "Set the ON-DRAG-HANDLER for CLOG-OBJ. If ON-DRAG-HANDLER is nil unbind the event.")) (defmethod set-on-drag ((obj clog-obj) handler) (set-on-event obj "drag" handler)) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-drag-end ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-drag-end (clog-obj on-drag-end-handler) (:documentation "Set the ON-DRAG-END-HANDLER for CLOG-OBJ. If ON-DRAG-END-HANDLER is nil unbind the event.")) (defmethod set-on-drag-end ((obj clog-obj) handler) (set-on-event obj "dragend" handler)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-drag-enter ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-drag-enter (clog-obj on-drag-enter-handler) (:documentation "Set the ON-DRAG-ENTER-HANDLER for CLOG-OBJ. If ON-DRAG-ENTER-HANDLER is nil unbind the event.")) (defmethod set-on-drag-enter ((obj clog-obj) handler) (set-on-event obj "dragenter" handler)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-drag-leave ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-drag-leave (clog-obj on-drag-leave-handler) (:documentation "Set the ON-DRAG-LEAVE-HANDLER for CLOG-OBJ. If ON-DRAG-LEAVE-HANDLER is nil unbind the event.")) (defmethod set-on-drag-leave ((obj clog-obj) handler) (set-on-event obj "dragleave" handler)) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-drag-over ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-drag-over (clog-obj on-drag-over-handler) (:documentation "Set the ON-DRAG-OVER-HANDLER for CLOG-OBJ. If ON-DRAG-OVER-HANDLER is nil unbind the event.")) (defmethod set-on-drag-over ((obj clog-obj) handler) (set-event obj "dragover" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :cancel-event t :eval-script "e.preventDefault(); ")) ;;;;;;;;;;;;;;;;; ;; set-on-drop ;; ;;;;;;;;;;;;;;;;; (defgeneric set-on-drop (clog-obj on-drop-handler &key drag-type) (:documentation "Set the ON-DROP-HANDLER for CLOG-OBJ. If ON-DROP-HANDLER is nil unbind the event.")) (defmethod set-on-drop ((obj clog-obj) handler &key (drag-type "text/plain")) (set-event obj "drop" (when handler (lambda (data) (funcall handler obj (parse-drop-event data)))) :call-back-script (format nil drop-event-script drag-type) :eval-script "e.preventDefault(); " :cancel-event t)) ;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "focusin" handler)) ;;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "focusout" handler)) ;;;;;;;;;;;;;;;;;; ;; 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. This event is activated by using reset on a form. If this event is bound, you must call the form reset manually.")) (defmethod set-on-reset ((obj clog-obj) handler) (set-event obj "reset" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :cancel-event t)) ;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "search" handler)) ;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "select" handler)) ;;;;;;;;;;;;;;;;;;; ;; 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. This event is activated by using submit on a form. If this event is bound, you must call the (submit clog-form) manually if wish the form action to be run. See CLOG-Form SUBMIT for more details.")) (defmethod set-on-submit ((obj clog-obj) handler) (set-event obj "submit" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :cancel-event t)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-context-menu ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-context-menu (clog-obj on-context-menu-handler &key one-time) (: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. If :ONE-TIME unbind event on right click.")) (defmethod set-on-context-menu ((obj clog-obj) handler &key one-time) (set-event obj "contextmenu" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :one-time one-time :cancel-event t)) ;;;;;;;;;;;;;;;;;; ;; set-on-click ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-click (clog-obj on-click-handler &key one-time cancel-event) (: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. If :ONE-TIME unbind event on click.")) (defmethod set-on-click ((obj clog-obj) handler &key one-time cancel-event) (set-event obj "click" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :one-time one-time :cancel-event cancel-event)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-double-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-double-click (clog-obj on-double-click-handler &key one-time cancel-event) (: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) handler &key one-time cancel-event) (set-event obj "dblclick" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))) :one-time one-time :cancel-event cancel-event)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-click ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-click (clog-obj on-mouse-click-handler &key one-time cancel-event) (: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) handler &key one-time cancel-event) (set-event obj "click" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-double-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-double-click (clog-obj on-mouse-double-click-handler &key one-time cancel-event) (: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) handler &key one-time cancel-event) (set-event obj "dblclick" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-right-click ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-right-click (clog-obj on-mouse-right-click-handler &key one-time cancel-event) (: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) handler &key one-time (cancel-event t)) (set-event obj "contextmenu" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script) :cancel-event cancel-event)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "mouseenter" handler)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "mouseleave" handler)) ;;;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "mouseover" handler)) ;;;;;;;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "mouseout" handler)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-down ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-down (clog-obj on-mouse-down-handler &key one-time cancel-event) (:documentation "Set the ON-MOUSE-DOWN-HANDLER for CLOG-OBJ. If ON-MOUSE-DOWN-HANDLER is nil unbind the event. If cancel-event is true event does not bubble.")) (defmethod set-on-mouse-down ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "mousedown" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-up ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-up (clog-obj on-mouse-up-handler &key one-time cancel-event) (: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) handler &key (one-time nil) (cancel-event nil)) (set-event obj "mouseup" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script))) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-mouse-move ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-mouse-move (clog-obj on-mouse-move-handler &key one-time cancel-event) (: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) handler &key (one-time nil) (cancel-event nil)) (set-event obj "mousemove" (when handler (lambda (data) (funcall handler obj (parse-mouse-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script (if (typep obj 'clog-window) mouse-event-script-for-window mouse-event-script))) ;;;;;;;;;;;;;;;;;; ;; set-on-wheel ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-wheel (clog-obj on-wheel-handler &key one-time cancel-event) (:documentation "Set the ON-WHEEL-HANDLER for CLOG-OBJ. If ON-WHEEL-HANDLER is nil unbind the event.")) (defmethod set-on-wheel ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "wheel" (when handler (lambda (data) (funcall handler obj (parse-wheel-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script wheel-event-script)) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-enter ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-enter (clog-obj on-pointer-enter-handler) (:documentation "Set the ON-POINTER-ENTER-HANDLER for CLOG-OBJ. If ON-POINTER-ENTER-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-enter ((obj clog-obj) handler) (set-on-event obj "pointerenter" handler)) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-leave ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-leave (clog-obj on-pointer-leave-handler) (:documentation "Set the ON-POINTER-LEAVE-HANDLER for CLOG-OBJ. If ON-POINTER-LEAVE-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-leave ((obj clog-obj) handler) (set-on-event obj "pointerleave" handler)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-over ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-over (clog-obj on-pointer-over-handler) (:documentation "Set the ON-POINTER-OVER-HANDLER for CLOG-OBJ. If ON-POINTER-OVER-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-over ((obj clog-obj) handler) (set-on-event obj "pointerover" handler)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-out ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-out (clog-obj on-pointer-out-handler) (:documentation "Set the ON-POINTER-OUT-HANDLER for CLOG-OBJ. If ON-POINTER-OUT-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-out ((obj clog-obj) handler) (set-on-event obj "pointerout" handler)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-down ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-down (clog-obj on-pointer-down-handler &key capture-pointer one-time cancel-event) (:documentation "Set the ON-POINTER-DOWN-HANDLER for CLOG-OBJ. If ON-POINTER-DOWN-HANDLER is nil unbind the event. If cancel event is t the even does not bubble.")) (defmethod set-on-pointer-down ((obj clog-obj) handler &key (capture-pointer nil) (one-time nil) (cancel-event nil)) (set-event obj "pointerdown" (when handler (lambda (data) (funcall handler obj (parse-pointer-event data)))) :post-eval (if capture-pointer (format nil "; ~A.setPointerCapture(e.pointerId)" (script-id obj)) "") :one-time one-time :cancel-event cancel-event :call-back-script pointer-event-script)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-up ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-up (clog-obj on-pointer-up-handler &key one-time cancel-event) (:documentation "Set the ON-POINTER-UP-HANDLER for CLOG-OBJ. If ON-POINTER-UP-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-up ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "pointerup" (when handler (lambda (data) (funcall handler obj (parse-pointer-event data)))) :post-eval (format nil "; ~A.releasePointerCapture(e.pointerId)" (script-id obj)) :one-time one-time :cancel-event cancel-event :call-back-script pointer-event-script)) ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-cancel ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-cancel (clog-obj on-pointer-cancel-handler &key one-time cancel-event) (:documentation "Set the ON-POINTER-CANCEL-HANDLER for CLOG-OBJ. If ON-POINTER-CANCEL-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-cancel ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "pointercancel" (when handler (lambda (data) (funcall handler obj (parse-pointer-event data)))) :post-eval (format nil "; ~A.releasePointerCapture(e.pointerId)" (script-id obj)) :one-time one-time :cancel-event cancel-event :call-back-script pointer-event-script)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-pointer-move ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-pointer-move (clog-obj on-pointer-move-handler &key one-time cancel-event) (:documentation "Set the ON-POINTER-MOVE-HANDLER for CLOG-OBJ. If ON-POINTER-MOVE-HANDLER is nil unbind the event.")) (defmethod set-on-pointer-move ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "pointermove" (when handler (lambda (data) (funcall handler obj (parse-pointer-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script pointer-event-script)) ;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-touch-start ;; ;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-touch-start (clog-obj on-touch-start-handler &key one-time cancel-event) (:documentation "Set the ON-TOUCH-START-HANDLER for CLOG-OBJ. If ON-TOUCH-START-HANDLER is nil unbind the event.")) (defmethod set-on-touch-start ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "touchstart" (when handler (lambda (data) (funcall handler obj (parse-touch-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script touch-event-script)) ;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-touch-move ;; ;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-touch-move (clog-obj on-touch-move-handler &key one-time cancel-event) (:documentation "Set the ON-TOUCH-MOVE-HANDLER for CLOG-OBJ. If ON-TOUCH-MOVE-HANDLER is nil unbind the event.")) (defmethod set-on-touch-move ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "touchmove" (when handler (lambda (data) (funcall handler obj (parse-touch-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script touch-event-script)) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-touch-end ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-touch-end (clog-obj on-touch-end-handler &key one-time cancel-event) (:documentation "Set the ON-TOUCH-END-HANDLER for CLOG-OBJ. If ON-TOUCH-END-HANDLER is nil unbind the event.")) (defmethod set-on-touch-end ((obj clog-obj) handler &key (one-time nil) (cancel-event nil)) (set-event obj "touchend" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj '(:event-type :touch)))) :one-time one-time :cancel-event cancel-event)) ;;;;;;;;;;;;;;;;;;;;;;;;; ;; set-on-touch-cancel ;; ;;;;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-touch-cancel (clog-obj on-touch-cancel-handler) (:documentation "Set the ON-TOUCH-CANCEL-HANDLER for CLOG-OBJ. If ON-TOUCH-CANCEL-HANDLER is nil unbind the event.")) (defmethod set-on-touch-cancel ((obj clog-obj) handler) (set-event obj "touchcancel" (when handler (lambda (data) (declare (ignore data)) (funcall handler obj))))) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-character ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-character (clog-obj on-character-handler &key one-time disable-default) (:documentation "Set the ON-CHARACTER-HANDLER for CLOG-OBJ. If ON-CHARACTER-HANDLER is nil unbind the event. If disable-default is t default key bindings in browser will not occur. Setting this event to nil will unbind on-key-press also.")) (defmethod set-on-character ((obj clog-obj) handler &key (one-time nil) (disable-default nil)) (set-event obj "keypress" (when handler (lambda (data) (let ((f (parse-keyboard-event data))) (funcall handler obj (code-char (getf f :char-code)))))) :one-time one-time :cancel-event disable-default :call-back-script keyboard-event-script)) ;;;;;;;;;;;;;;;;;;;;; ;; set-on-key-down ;; ;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-down (clog-obj on-key-down-handler &key one-time disable-default) (:documentation "Set the ON-KEY-DOWN-HANDLER for CLOG-OBJ. If disable-default is t default key bindings in browser will not occur. If ON-KEY-DOWN-HANDLER is nil unbind the event.")) (defmethod set-on-key-down ((obj clog-obj) handler &key (one-time nil) (disable-default nil)) (set-event obj "keydown" (when handler (lambda (data) (funcall handler obj (parse-keyboard-event data)))) :one-time one-time :cancel-event disable-default :call-back-script keyboard-event-script)) ;;;;;;;;;;;;;;;;;;; ;; set-on-key-up ;; ;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-up (clog-obj on-key-up-handler &key one-time cancel-event) (: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) handler &key one-time cancel-event) (set-event obj "keyup" (when handler (lambda (data) (funcall handler obj (parse-keyboard-event data)))) :one-time one-time :cancel-event cancel-event :call-back-script keyboard-event-script)) ;;;;;;;;;;;;;;;;;;;;;; ;; set-on-key-press ;; ;;;;;;;;;;;;;;;;;;;;;; (defgeneric set-on-key-press (clog-obj on-key-press-handler &key one-time disable-default) (: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) handler &key (one-time nil) (disable-default nil)) (set-event obj "keypress" (when handler (lambda (data) (funcall handler obj (parse-keyboard-event data)))) :one-time one-time :cancel-event disable-default :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) handler) (set-on-event obj "copy" handler)) ;;;;;;;;;;;;;;;; ;; 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) handler) (set-on-event obj "cut" handler)) ;;;;;;;;;;;;;;;;;; ;; set-on-paste ;; ;;;;;;;;;;;;;;;;;; (defgeneric set-on-paste (clog-obj 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) handler) (set-on-event obj "paste" handler))