A whole host of changes rescued from the alcoholic laptop.
authorDrew Crampsie <drewc@tech.coop>
Tue, 18 Oct 2005 02:19:00 +0000 (19:19 -0700)
committerDrew Crampsie <drewc@tech.coop>
Tue, 18 Oct 2005 02:19:00 +0000 (19:19 -0700)
darcs-hash:20051018021900-5417e-94b6d14699126e5fa2004f548cbcbbed6ed9362c.gz

lisp-on-lines.asd
src/components/ajax.lisp
src/lisp-on-lines.lisp
src/mewa/mewa.lisp
src/mewa/packages.lisp
src/mewa/presentations.lisp
src/mewa/slot-presentations.lisp
src/packages.lisp
wwwroot/prototype-1.3.1.js [deleted file]

index fbb7a5b..90d76a0 100644 (file)
@@ -32,4 +32,5 @@
                :pathname "src/components/"
                 :components ((:file "range-list")
                             (:file "ajax"))))
                :pathname "src/components/"
                 :components ((:file "range-list")
                             (:file "ajax"))))
+  :serial t
 :depends-on (:meta-model :mewa :split-sequence))
 :depends-on (:meta-model :mewa :split-sequence))
index 7552c41..9f5d5e3 100644 (file)
@@ -165,8 +165,6 @@ but here's what i use."
        :class "auto-complete-list-item"
        (funcall (render auto-complete) value))))))
 
        :class "auto-complete-list-item"
        (funcall (render auto-complete) value))))))
 
-
-
 (defcomponent fkey-auto-complete (auto-complete)
   ())
 
 (defcomponent fkey-auto-complete (auto-complete)
   ())
 
@@ -206,9 +204,11 @@ but here's what i use."
          
 (defaction revert-foreign-slot ((slot ajax-foreign-key-slot-presentation))
   (setf (lol::value (live-search slot)) nil)
          
 (defaction revert-foreign-slot ((slot ajax-foreign-key-slot-presentation))
   (setf (lol::value (live-search slot)) nil)
-  (mewa::sync-foreign-instance slot (original-value slot)))
+  (when (original-value slot)
+  (mewa::sync-foreign-instance slot (original-value slot))))
 
 (defmethod present-slot :around ((slot ajax-foreign-key-slot-presentation) instance)
 
 (defmethod present-slot :around ((slot ajax-foreign-key-slot-presentation) instance)
+
   (let ((foreign-instance 
         (if (lol::value (live-search slot))
             (lol::value (live-search slot))
   (let ((foreign-instance 
         (if (lol::value (live-search slot))
             (lol::value (live-search slot))
index 8a9a88b..465ec5f 100644 (file)
@@ -10,7 +10,7 @@
 ;;;; ** Initialisation
 ;;;; The following macros are used to initialise a set of database tables as LoL objects.
 (eval-when (:compile-toplevel :load-toplevel :execute)
 ;;;; ** Initialisation
 ;;;; The following macros are used to initialise a set of database tables as LoL objects.
 (eval-when (:compile-toplevel :load-toplevel :execute)
-  (defun generate-initialize-lol-for-table (table)
+  (defun generate-define-view-for-table (table)
     "
 Generates a form that, when evaluated, initialises the given table as an lol object.
 This involves creating a meta-model, a clsql view-class, and the setting up the default attributes for a mewa presentation"
     "
 Generates a form that, when evaluated, initialises the given table as an lol object.
 This involves creating a meta-model, a clsql view-class, and the setting up the default attributes for a mewa presentation"
@@ -19,19 +19,53 @@ This involves creating a meta-model, a clsql view-class, and the setting up the
       (def-view-class-from-table ,table)
       (set-default-attributes (quote ,(meta-model::sql->sym table))))))
     
       (def-view-class-from-table ,table)
       (set-default-attributes (quote ,(meta-model::sql->sym table))))))
     
-(defmacro initialize-lol-for-table (&rest tables)
+(defmacro define-view-for-table (&rest tables)
   " expand to a form which initialises TABLES for use with LOL"
   `(progn
   " expand to a form which initialises TABLES for use with LOL"
   `(progn
-    ,@(loop for tbl in tables collect (generate-initialize-lol-for-table tbl))
+    ,@(loop for tbl in tables collect (generate-define-view-for-table tbl))
     (values)))
 
     (values)))
 
-(defmacro initialize-lol-for-database ()
+(defmacro define-views-for-database ()
   "expands to init-i-f-t using the listing of tables provided by meta-model"
   "expands to init-i-f-t using the listing of tables provided by meta-model"
-  `(initialize-lol-for-table ,@(meta-model::list-tables)))
+  `(define-view-for-table ,@(meta-model::list-tables)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defun %make-view (object type &rest attributes-and-args)
+    (let ((attributes (car attributes-and-args))
+         (args (cdr attributes-and-args)))
+      `(mewa:make-presentation
+       ,object
+       :type ,type
+       :initargs
+       '(,@ (when attributes
+              `(:attributes ,attributes))) 
+       ,@args)))) 
+
+(defmethod make-view (object &rest args &key (type :viewer) (attributes nil)
+                     &allow-other-keys )
+  (apply #'make-presentation (cdr (%make-view object type (cons attributes args)))))
+
+(defmacro present-view ((object &optional (type :viewer))
+                       &body attributes-and-args)
+  `(present ,(%make-view object type attributes-and-args)))
+
+
+(defmacro call-view ((object &optional (type :viewer) (component 'self component-supplied-p))
+                    &body attributes-and-args)
+  
+  
+  `(ucw:call-component
+    ,component
+    ,(%make-view object type attributes-and-args)))
+
+(defmethod slot-view ((self mewa) slot-name)
+  (mewa::find-attribute-slot self slot-name))
+
+(defmethod present-slot-view ((self mewa) slot-name &optional (instance (instance self)))
+  (present-slot (slot-view self slot-name) instance))
+
 
 
-;;;; * AJAX stuff 
 
 
-;;;; TODO: This search stuff should probably me refactored elsewhere
 
 (defmethod find-slots-of-type (model &key (type 'string)
                              (types '((string)) types-supplied-p))
 
 (defmethod find-slots-of-type (model &key (type 'string)
                              (types '((string)) types-supplied-p))
index e620130..255ce57 100644 (file)
@@ -2,19 +2,6 @@
  
 (defparameter *default-type* :ucw)
 
  
 (defparameter *default-type* :ucw)
 
-;;; maps meta-model slot-types to slot-presentation
-(defparameter *slot-type-map*
-  '(boolean   mewa-boolean
-    string    mewa-string
-    number    mewa-currency
-    integer   mewa-integer
-    currency  mewa-currency
-    clsql:generalized-boolean mewa-boolean))
-
-;;; an alist of model-class-name . attributes
-;;; should really be a hash-table.
-(defvar *attribute-map* (list)) 
-
 ;;; some utilities for merging plists
 
 (defun plist-nunion (new-props plist)
 ;;; some utilities for merging plists
 
 (defun plist-nunion (new-props plist)
   "Non-destructive version of plist-nunion"
                   (plist-nunion new-props (copy-list plist)))
 
   "Non-destructive version of plist-nunion"
                   (plist-nunion new-props (copy-list plist)))
 
-(defun gen-ptype (type)
-  (or (getf *slot-type-map* type) type))
-
-(defun gen-presentation-slots (instance)
-  (mapcar #'(lambda (x) (gen-pslot (cadr x) 
-                                  (string (car x)) 
-                                  (car x))) 
-         (meta-model:list-slot-types instance)))
-
-
-(defun gen-pslot (type label slot-name)
-  (copy-list `(,(gen-ptype type) 
-              :label ,label
-              :slot-name ,slot-name))) 
-
-(defun gen-presentation-args (instance args)
-  (declare (ignore instance))
-  (if args args nil))
 
 
+;;; an alist of model-class-name . attributes
+;;; should really be a hash-table.
+(defvar *attribute-map* (list))
 
 (defun find-or-create-attributes (class-name)
   "return an exisiting class attribute map or create one. 
 
 (defun find-or-create-attributes (class-name)
   "return an exisiting class attribute map or create one. 
@@ -122,9 +94,75 @@ attributes is an alist keyed on the attribute name."
     ,@(loop for model in models
            collect `(perform-define-attributes (quote ,model) (quote ,attribute-definitions)))
   (mapcar #'find-class-attributes (quote ,models ))))
     ,@(loop for model in models
            collect `(perform-define-attributes (quote ,model) (quote ,attribute-definitions)))
   (mapcar #'find-class-attributes (quote ,models ))))
+
+(defun find-presentation-attributes (model)
+  (remove nil (mapcar #'(lambda (att)
+             (when (keywordp (car att))
+               att))
+         (cdr (find-class-attributes model)))))
+
+
+;;;; ** Default Attributes
+
+
+;;;; The default mewa class contains the types use as defaults.
+;;;; maps meta-model slot-types to slot-presentation
+
+(defvar *default-attributes-class-name* 'default)
+
+(define-attributes (default)
+  (boolean mewa-boolean)
+  (string mewa-string)
+  (number mewa-currency)
+  (integer   mewa-integer)
+  (currency  mewa-currency)
+  (clsql:generalized-boolean mewa-boolean)
+  (foreign-key foreign-key)
+  (:viewer mewa-viewer)
+  (:editor mewa-editor)
+  (:creator mewa-creator)
+  (:one-line mewa-one-line-presentation)
+  (:listing mewa-list-presentation :global-properties (:editablep nil) :editablep t)
+  (:search-model mewa-object-presentation))
+
   
   
+(defun find-default-presentation-attributes ()
+  (if (eql *default-attributes-class-name* 'default)
+      (find-presentation-attributes 'default)
+      (remove-duplicates (append
+                         (find-presentation-attributes 'default)
+                         (find-presentation-attributes
+                          *default-attributes-class-name*)))))
+
+
+(defmacro with-default-attributes ((model-name) &body body)
+  `(let ((*default-attributes-class-name* ',model-name))
+    ,@body))
+
+(defun gen-ptype (type)
+  (let ((type (if (consp type) (car type) type)))
+  (or (second (find-attribute *default-attributes-class-name* type))
+      (second (find-attribute 'default type))
+      type)))
+
+(defun gen-presentation-slots (instance)
+  (mapcar #'(lambda (x) (gen-pslot (cadr x) 
+                                  (string (car x)) 
+                                  (car x))) 
+         (meta-model:list-slot-types instance)))
+
 
 
-(defmethod default-attributes ((model t))
+(defun gen-pslot (type label slot-name)
+  (copy-list `(,(gen-ptype type) 
+              :label ,label
+              :slot-name ,slot-name))) 
+
+(defun gen-presentation-args (instance args)
+  (declare (ignore instance))
+  (if args args nil))
+
+
+(defmethod find-default-attributes ((model t))
   "return the default attributes for a given model using the meta-model's meta-data"
   (append (mapcar #'(lambda (s) 
                      (cons (car s) 
   "return the default attributes for a given model using the meta-model's meta-data"
   (append (mapcar #'(lambda (s) 
                      (cons (car s) 
@@ -140,14 +178,15 @@ attributes is an alist keyed on the attribute name."
                                        (make-presentation 
                                         ,model 
                                         :type :one-line)))))
                                        (make-presentation 
                                         ,model 
                                         :type :one-line)))))
-                 (meta-model:list-has-many model))))
+                 (meta-model:list-has-many model))
+         (find-default-presentation-attributes)))
 
 (defmethod set-default-attributes ((model t))
   "Set the default attributes for MODEL"
   (clear-class-attributes model)
   (mapcar #'(lambda (x) 
              (setf (find-attribute model (car x)) (cdr x)))
 
 (defmethod set-default-attributes ((model t))
   "Set the default attributes for MODEL"
   (clear-class-attributes model)
   (mapcar #'(lambda (x) 
              (setf (find-attribute model (car x)) (cdr x)))
-         (default-attributes model)))
+         (find-default-attributes model)))
 
 
 (defgeneric attributes-getter (model))
 
 
 (defgeneric attributes-getter (model))
@@ -255,7 +294,7 @@ attributes is an alist keyed on the attribute name."
                          (car (second attribute))
                          (second attribute))
                      *presentation-slot-type-mapping*) 
                          (car (second attribute))
                          (second attribute))
                      *presentation-slot-type-mapping*) 
-            (error  "Can't find slot type for ~A in ~A" attribute *presentation-slot-type-mapping* ))))
+            (error  "Can't find slot type for ~A in ~A" attribute self ))))
                
     (cons (first attribute) (apply #'make-instance 
                                   class-name
                
     (cons (first attribute) (apply #'make-instance 
                                   class-name
@@ -279,7 +318,7 @@ attributes is an alist keyed on the attribute name."
   
 (defmethod make-presentation ((object t) &key (type :viewer) (initargs nil))
   (let* ((p (make-instance 'mewa-object-presentation))
   
 (defmethod make-presentation ((object t) &key (type :viewer) (initargs nil))
   (let* ((p (make-instance 'mewa-object-presentation))
-        (a (progn (setf (slot-value p 'ucw::instance) object)
+        (a (progn (setf (slot-value p 'instance) object)
                   (initialize-slots p) 
                   (assoc type (find-all-attributes p))))
         (i (apply #'make-instance (or (second a)
                   (initialize-slots p) 
                   (assoc type (find-all-attributes p))))
         (i (apply #'make-instance (or (second a)
@@ -322,7 +361,6 @@ attributes is an alist keyed on the attribute name."
   (call-next-method)
   (render-on res (slot-value self 'body)))
 
   (call-next-method)
   (render-on res (slot-value self 'body)))
 
-
 (defmethod instance-is-stored-p ((instance clsql:standard-db-object))
   (slot-value instance 'clsql-sys::view-database))
 
 (defmethod instance-is-stored-p ((instance clsql:standard-db-object))
   (slot-value instance 'clsql-sys::view-database))
 
@@ -341,26 +379,33 @@ attributes is an alist keyed on the attribute name."
   (setf (modifiedp self) nil)
   (answer self))
 
   (setf (modifiedp self) nil)
   (answer self))
 
+(defmethod confirm-sync-instance ((self mewa))
+  nil)
 
 (defaction ensure-instance-sync ((self mewa))
   (when (modifiedp self)
 
 (defaction ensure-instance-sync ((self mewa))
   (when (modifiedp self)
-    (let ((message (format nil "Record has been modified, Do you wish to save the changes?")))
-      (case (call 'about-dialog
-                  :body (make-presentation (instance self) 
-                                          :type :viewer)
-                 :message message
-                 :options '((:save . "Save changes to Database")
-                            (:cancel . "Cancel all changes")))
-       (:cancel
-        (cancel-save-instance self))
-       (:save 
-        (save-instance self))))))
+    (if nil
+       (let ((message (format nil "Record has been modified, Do you wish to save the changes?")))
+         (case (call 'about-dialog
+                     :body (make-presentation (instance self) 
+                                              :type :viewer)
+                     :message message
+                     :options '((:save . "Save changes to Database")
+                                (:cancel . "Cancel all changes")))
+           (:cancel
+            (cancel-save-instance self))
+           (:save 
+            (save-instance self))))
+       (save-instance self))))
+
+(defaction sync-and-answer ((self mewa))
+  (ensure-instance-sync self)
+  (answer (instance self)))
 
 (defaction ok ((self mewa) &optional arg)
   "Returns the component if it has not been modified. if it has been, prompt user to save or cancel"
   ;(declare (ignore arg))
 
 (defaction ok ((self mewa) &optional arg)
   "Returns the component if it has not been modified. if it has been, prompt user to save or cancel"
   ;(declare (ignore arg))
-  (meta-model::sync-instance (instance self))
-  (answer (instance self)))
+  (sync-and-answer self))
 
 (defmethod (setf presentation-slot-value) :around (value (slot slot-presentation) instance)
   (let* ((old (prog1 
 
 (defmethod (setf presentation-slot-value) :around (value (slot slot-presentation) instance)
   (let* ((old (prog1 
@@ -373,20 +418,7 @@ attributes is an alist keyed on the attribute name."
       (setf (modifiedp self) instance
            (modifications self)  (append (list new old value slot instance) (modifications self)))))))
 
       (setf (modifiedp self) instance
            (modifications self)  (append (list new old value slot instance) (modifications self)))))))
 
-;;;; * Finally set up some defaults
-
-(setf (find-attribute t :viewer) 
-      '(mewa-object-presentation :global-properties (:editablep nil))
-      (find-attribute t :editor)
-      '(mewa-object-presentation :global-properties (:editablep t))
-      (find-attribute t :creator)
-      '(mewa-object-presentation :global-properties (:editablep t))
-      (find-attribute t :one-line)
-      '(mewa-one-line-presentation)
-      (find-attribute t :listing)
-      '(mewa-list-presentation :global-properties (:editablep nil) :editablep t)
-      (find-attribute t :search-model)
-      '(mewa-object-presentation))
+
 
 
 
 
 
 
index 76681d4..ebdf210 100644 (file)
@@ -3,6 +3,10 @@
   (:export 
    :mewa 
    :editablep
   (:export 
    :mewa 
    :editablep
+   
+   ;object presentations
+   :present
+   :foreign-key-slot-presentation
    :mewa-object-presentation 
    :mewa-one-line-presentation
    :mewa-list-presentation
    :mewa-object-presentation 
    :mewa-one-line-presentation
    :mewa-list-presentation
    :defslot-presentation
    :slot-presentation
    :mewa-slot-presentation
    :defslot-presentation
    :slot-presentation
    :mewa-slot-presentation
-   :foreign-key-slot-presentation
+
+   :present-slot
+   :presentation-slot-value
+   
+
    :mewa-relation-slot-presentation
    :has-a-slot-presentation
    :has-a
    :mewa-relation-slot-presentation
    :has-a-slot-presentation
    :has-a
    :has-very-many-slot-presentation
    :has-very-many
    :slot-name
    :has-very-many-slot-presentation
    :has-very-many
    :slot-name
+
+   ;attributes
    :define-attributes
    :define-attributes
-   :find-attribute 
+   :with-default-attributes
+   :find-attribute
+   :find-attribute-slot
    :set-default-attributes 
    :make-presentation 
    :call-presentation 
    :set-default-attributes 
    :make-presentation 
    :call-presentation 
@@ -35,6 +47,7 @@
    :find-class-attributes 
    :default-attributes 
    :ok
    :find-class-attributes 
    :default-attributes 
    :ok
+   :instance
    :edit-instance
    :save-instance
    :cancel-save-instance
    :edit-instance
    :save-instance
    :cancel-save-instance
index ab7c27b..82f18e3 100644 (file)
@@ -1,20 +1,47 @@
 (in-package :mewa)
 
 (in-package :mewa)
 
+
+
+ (defun split-list (n list)  
+  (loop for cons on list
+        by #'(lambda (x) (nthcdr n x))
+        if (< 0 n)
+        collect (loop for atom in cons
+                      repeat n
+                      collect atom)
+        else return nil))
+
 (defaction edit-instance ((self mewa))
   (call-presentation (instance self) :type :editor))
 
 ;;;one-line objects
 (defcomponent mewa-one-line-presentation (mewa one-line-presentation)
   ()
 (defaction edit-instance ((self mewa))
   (call-presentation (instance self) :type :editor))
 
 ;;;one-line objects
 (defcomponent mewa-one-line-presentation (mewa one-line-presentation)
   ()
-  (:default-initargs :attributes-getter #'one-line-attributes-getter))
+  (:default-initargs
+   :attributes-getter #'one-line-attributes-getter
+   :global-properties '(:editablep nil)))
 
 (defmethod one-line-attributes-getter ((self mewa))
 
 (defmethod one-line-attributes-getter ((self mewa))
-  (or (meta-model:list-keys (instance self))))
+  (or (meta-model::find-slots-of-type (instance self))
+      (meta-model::list-keys (instance self))))
 
 ;;;objects
 (defcomponent mewa-object-presentation (mewa object-presentation) 
   ((instance :accessor instance :initarg :instance :initform nil)))
 
 
 ;;;objects
 (defcomponent mewa-object-presentation (mewa object-presentation) 
   ((instance :accessor instance :initarg :instance :initform nil)))
 
+(defcomponent mewa-viewer (mewa-object-presentation)
+  ()
+  (:default-initargs
+   :global-properties '(:editablep nil)))
+
+(defcomponent mewa-editor (mewa-object-presentation)
+  ()
+  (:default-initargs
+   :global-properties '(:editablep t)))
+
+(defcomponent mewa-creator (mewa-editor)
+  ())
+
 (defmethod present ((pres mewa-object-presentation))
   (<:table :class (css-class pres)
     (dolist (slot (slots pres))
 (defmethod present ((pres mewa-object-presentation))
   (<:table :class (css-class pres)
     (dolist (slot (slots pres))
index ef86dec..82893a5 100644 (file)
@@ -14,9 +14,9 @@
 ;;;; ** Textarea Slot Presentation
 
 (defslot-presentation text-slot-presentation ()
 ;;;; ** Textarea Slot Presentation
 
 (defslot-presentation text-slot-presentation ()
-  ((rows :initarg :rows :accessor rows :initform nil)
-   (columns :initarg :columns :accessor columns :initform nil)
-   (html-contentp :initarg :escape-html-p :accessor escape-html-p :initform nil))
+  ((rows :initarg :rows :accessor rows :initform 25)
+   (columns :initarg :columns :accessor columns :initform 40)
+   (escape-html-p :initarg :escape-html-p :accessor escape-html-p :initform nil))
   (:type-name text))
 
 (defmethod present-slot ((slot text-slot-presentation) instance)
   (:type-name text))
 
 (defmethod present-slot ((slot text-slot-presentation) instance)
@@ -25,8 +25,9 @@
                     :rows (rows slot)
                     :cols (columns slot))
       (if (escape-html-p slot)
                     :rows (rows slot)
                     :cols (columns slot))
       (if (escape-html-p slot)
+         (<:as-html (presentation-slot-value slot instance))
          (<:as-is (presentation-slot-value slot instance))
          (<:as-is (presentation-slot-value slot instance))
-         (<:as-html (presentation-slot-value slot instance)))))
+         )))
 
 
 (defcomponent mewa-slot-presentation ()
 
 
 (defcomponent mewa-slot-presentation ()
@@ -67,7 +68,8 @@ When T, only the default value for primary keys and the joins are updated.")
   currency)
 
 (defslot-presentation clsql-wall-time-slot-presentation (mewa-relation-slot-presentation)
   currency)
 
 (defslot-presentation clsql-wall-time-slot-presentation (mewa-relation-slot-presentation)
-       ()
+       ((input-id :accessor input-id :initform (arnesi:random-string 10 arnesi:+ascii-alphabet+))
+       (trigger-id :accessor trigger-id :initform (arnesi:random-string 10 arnesi:+ascii-alphabet+)))
        (:type-name clsql-sys:wall-time))
 
 (defmethod presentation-slot-value ((slot clsql-wall-time-slot-presentation) instance)
        (:type-name clsql-sys:wall-time))
 
 (defmethod presentation-slot-value ((slot clsql-wall-time-slot-presentation) instance)
@@ -88,18 +90,19 @@ When T, only the default value for primary keys and the joins are updated.")
   (concatenate 'string (call-next-method) "  (m/d/y)"))
 
 (defmethod present-slot ((slot clsql-wall-time-slot-presentation) instance)
   (concatenate 'string (call-next-method) "  (m/d/y)"))
 
 (defmethod present-slot ((slot clsql-wall-time-slot-presentation) instance)
-  (let ((date (presentation-slot-value slot instance))
-       (input-id (string (gensym))))
+  (let ((date (presentation-slot-value slot instance)))
     (if (and date (not (editablep slot)))
        (<:as-html date))
     (when (editablep slot)
     (if (and date (not (editablep slot)))
        (<:as-html date))
     (when (editablep slot)
-      (<ucw:input :accessor (presentation-slot-value slot instance) :id input-id)
+      (<ucw:input :accessor (presentation-slot-value slot instance) :id (input-id slot) :style "display:inline")
+      (<:button :id (trigger-id slot) (<:as-html "[...]"))
       (<:script :type "text/javascript" 
                (<:as-is (format nil " 
       (<:script :type "text/javascript" 
                (<:as-is (format nil " 
-      Calendar.setup({
-        inputField     :    \"~a\",
-        ifFormat       :    \"%m/%d/%Y\",
-      });" input-id))))))
+      
+Calendar.setup({
+ inputField     :    \"~a\",
+ button         :    \"~a\",
+ ifFormat       :    \"%m/%d/%Y\" });" (input-id slot) (trigger-id slot)))))))
 
 (defslot-presentation  mewa-relation-slot-presentation (mewa-slot-presentation slot-presentation)
   ((foreign-instance :accessor foreign-instance)
 
 (defslot-presentation  mewa-relation-slot-presentation (mewa-slot-presentation slot-presentation)
   ((foreign-instance :accessor foreign-instance)
@@ -231,17 +234,37 @@ When T, only the default value for primary keys and the joins are updated.")
 (defmethod present-slot ((slot has-many-slot-presentation) instance)
   (when (slot-boundp slot 'ucw::place)
     (<ucw:submit :action (add-to-has-many slot instance) :value (add-new-label slot)))
 (defmethod present-slot ((slot has-many-slot-presentation) instance)
   (when (slot-boundp slot 'ucw::place)
     (<ucw:submit :action (add-to-has-many slot instance) :value (add-new-label slot)))
-  (let ((i (get-foreign-instances slot instance)))
-       
-    (<:ul 
-     (dolist (s i)
-       (let ((s s))
-        (setf (foreign-instance slot) s)
-         (when (slot-boundp slot 'ucw::place)
-           (<ucw:a :action (view-instance slot s :initargs `(:global-properties ,(list :linkedp t :editablep nil)))
-                (<:li   (setf (linkedp slot) nil)
-                        (present-relation slot instance)))))))))
-
+  (let* ((i (get-foreign-instances slot instance))
+        (presentation (and i (make-presentation (car  i) :type :one-line))))
+    (when i
+      (flet ((linker (i string)
+              (<ucw:a
+               :action (view-instance slot i
+                                      :initargs
+                                      `(:global-properties ,
+                                        (list
+                                         :linkedp t
+                                         :editablep nil)))
+               (<:as-html string))))
+       (<:table
+        (<:tr
+         (<:th)                        ;empty col for (view) link
+         (dolist (s (slots presentation))
+           (<:th (<:as-html  (label s)))))
+        (dolist (s i)
+          (let ((s s))
+            (setf (foreign-instance slot) s)
+            (when (slot-boundp slot 'ucw::place)
+              (<:tr
+               (<:td (linker s " (view) "))
+               (dolist (p (slots (make-presentation s :type :one-line
+                                                    :initargs
+                                                    '(:global-properties
+                                                      (:editablep nil)))))
+                 (<:td              
+                               
+                  (present-slot p s))))))))))))
+                      
 
 (defmethod get-foreign-instances ((slot has-many-slot-presentation) instance)
   (slot-value instance (slot-name slot)))
 
 (defmethod get-foreign-instances ((slot has-many-slot-presentation) instance)
   (slot-value instance (slot-name slot)))
index 0633ae4..1db3466 100644 (file)
@@ -3,9 +3,15 @@
   (:nicknames :lol)
   (:export 
    ;;;; LoL 
   (:nicknames :lol)
   (:export 
    ;;;; LoL 
-   :initialize-lol-for-table
-   :initialize-lol-for-database
+   :define-view-for-table
+   :define-views-for-database
 
 
+   ;;;;a wrapper for calling make-presentation
+   :call-view
+   :present-view
+   :slot-view
+   :present-slot-view
+   :make-view
    ;;;; Ajax
    :auto-complete
    :call-auto-complete
    ;;;; Ajax
    :auto-complete
    :call-auto-complete
    :mewa ;the superclass of all mewa-presentations
    :make-presentation
    :call-presentation
    :mewa ;the superclass of all mewa-presentations
    :make-presentation
    :call-presentation
+
    ;;attributes
    :attributes
    ;;attributes
    :attributes
+   :define-attributes
+   :with-default-attributes
    :set-default-attributes
    :set-attribute
    :find-attribute
    :set-default-attributes
    :set-attribute
    :find-attribute
@@ -25,6 +34,8 @@
    :define-attributes
 
    ;; presentation objects
    :define-attributes
 
    ;; presentation objects
+   :present
+   :instance
    :mewa-object-presentation
    :mewa-one-line-presentation
    :mewa-list-presentation
    :mewa-object-presentation
    :mewa-one-line-presentation
    :mewa-list-presentation
@@ -34,6 +45,7 @@
    :slot-name
    :mewa-relation-slot-presentation
    :has-many-slot-presentation
    :slot-name
    :mewa-relation-slot-presentation
    :has-many-slot-presentation
+   :present-slot
    ;; CRUD
    :instance-is-stored-p
 
    ;; CRUD
    :instance-is-stored-p
 
diff --git a/wwwroot/prototype-1.3.1.js b/wwwroot/prototype-1.3.1.js
deleted file mode 100644 (file)
index 5740b6c..0000000
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*  Prototype JavaScript framework, version 1.3.1
- *  (c) 2005 Sam Stephenson <sam@conio.net>
- *
- *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
- *  against the source tree, available from the Prototype darcs repository. 
- *
- *  Prototype is freely distributable under the terms of an MIT-style license.
- *
- *  For details, see the Prototype web site: http://prototype.conio.net/
- *
-/*--------------------------------------------------------------------------*/
-
-var Prototype = {
-  Version: '1.3.1',
-  emptyFunction: function() {}
-}
-
-var Class = {
-  create: function() {
-    return function() { 
-      this.initialize.apply(this, arguments);
-    }
-  }
-}
-
-var Abstract = new Object();
-
-Object.extend = function(destination, source) {
-  for (property in source) {
-    destination[property] = source[property];
-  }
-  return destination;
-}
-
-Object.prototype.extend = function(object) {
-  return Object.extend.apply(this, [this, object]);
-}
-
-Function.prototype.bind = function(object) {
-  var __method = this;
-  return function() {
-    __method.apply(object, arguments);
-  }
-}
-
-Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
-  return function(event) {
-    __method.call(object, event || window.event);
-  }
-}
-
-Number.prototype.toColorPart = function() {
-  var digits = this.toString(16);
-  if (this < 16) return '0' + digits;
-  return digits;
-}
-
-var Try = {
-  these: function() {
-    var returnValue;
-
-    for (var i = 0; i < arguments.length; i++) {
-      var lambda = arguments[i];
-      try {
-        returnValue = lambda();
-        break;
-      } catch (e) {}
-    }
-
-    return returnValue;
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var PeriodicalExecuter = Class.create();
-PeriodicalExecuter.prototype = {
-  initialize: function(callback, frequency) {
-    this.callback = callback;
-    this.frequency = frequency;
-    this.currentlyExecuting = false;
-
-    this.registerCallback();
-  },
-
-  registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
-  },
-
-  onTimerEvent: function() {
-    if (!this.currentlyExecuting) {
-      try { 
-        this.currentlyExecuting = true;
-        this.callback(); 
-      } finally { 
-        this.currentlyExecuting = false;
-      }
-    }
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-function $() {
-  var elements = new Array();
-
-  for (var i = 0; i < arguments.length; i++) {
-    var element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-
-    if (arguments.length == 1) 
-      return element;
-
-    elements.push(element);
-  }
-
-  return elements;
-}
-
-if (!Array.prototype.push) {
-  Array.prototype.push = function() {
-               var startLength = this.length;
-               for (var i = 0; i < arguments.length; i++)
-      this[startLength + i] = arguments[i];
-         return this.length;
-  }
-}
-
-if (!Function.prototype.apply) {
-  // Based on code from http://www.youngpup.net/
-  Function.prototype.apply = function(object, parameters) {
-    var parameterStrings = new Array();
-    if (!object)     object = window;
-    if (!parameters) parameters = new Array();
-    
-    for (var i = 0; i < parameters.length; i++)
-      parameterStrings[i] = 'parameters[' + i + ']';
-    
-    object.__apply__ = this;
-    var result = eval('object.__apply__(' + 
-      parameterStrings.join(', ') + ')');
-    object.__apply__ = null;
-    
-    return result;
-  }
-}
-
-String.prototype.extend({
-  stripTags: function() {
-    return this.replace(/<\/?[^>]+>/gi, '');
-  },
-
-  escapeHTML: function() {
-    var div = document.createElement('div');
-    var text = document.createTextNode(this);
-    div.appendChild(text);
-    return div.innerHTML;
-  },
-
-  unescapeHTML: function() {
-    var div = document.createElement('div');
-    div.innerHTML = this.stripTags();
-    return div.childNodes[0].nodeValue;
-  }
-});
-
-var Ajax = {
-  getTransport: function() {
-    return Try.these(
-      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
-      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
-      function() {return new XMLHttpRequest()}
-    ) || false;
-  }
-}
-
-Ajax.Base = function() {};
-Ajax.Base.prototype = {
-  setOptions: function(options) {
-    this.options = {
-      method:       'post',
-      asynchronous: true,
-      parameters:   ''
-    }.extend(options || {});
-  },
-
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0 
-        || (this.transport.status >= 200 && this.transport.status < 300);
-  },
-
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
-  }
-}
-
-Ajax.Request = Class.create();
-Ajax.Request.Events = 
-  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-Ajax.Request.prototype = (new Ajax.Base()).extend({
-  initialize: function(url, options) {
-    this.transport = Ajax.getTransport();
-    this.setOptions(options);
-    this.request(url);
-  },
-
-  request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length > 0) parameters += '&_=';
-
-    try {
-      if (this.options.method == 'get')
-        url += '?' + parameters;
-
-      this.transport.open(this.options.method, url,
-        this.options.asynchronous);
-
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
-
-      this.setRequestHeaders();
-
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
-
-    } catch (e) {
-    }
-  },
-
-  setRequestHeaders: function() {
-    var requestHeaders = 
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version];
-
-    if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', 
-        'application/x-www-form-urlencoded');
-
-      /* Force "Connection: close" for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651. 
-       */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
-    }
-
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
-    for (var i = 0; i < requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
-  },
-
-  onStateChange: function() {
-    var readyState = this.transport.readyState;
-    if (readyState != 1)
-      this.respondToReadyState(this.transport.readyState);
-  },
-
-  respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
-
-    if (event == 'Complete')
-      (this.options['on' + this.transport.status]
-       || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
-       || Prototype.emptyFunction)(this.transport);
-
-    (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
-
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
-      this.transport.onreadystatechange = Prototype.emptyFunction;
-  }
-});
-
-Ajax.Updater = Class.create();
-Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
-
-Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
-  initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $(container.success) : $(container),
-      failure: container.failure ? $(container.failure) :
-        (container.success ? null : $(container))
-    }
-
-    this.transport = Ajax.getTransport();
-    this.setOptions(options);
-
-    var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function() {
-      this.updateContent();
-      onComplete(this.transport);
-    }).bind(this);
-
-    this.request(url);
-  },
-
-  updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
-
-    var match    = new RegExp(Ajax.Updater.ScriptFragment, 'img');
-    var response = this.transport.responseText.replace(match, '');
-    var scripts  = this.transport.responseText.match(match);
-
-    if (receiver) {
-      if (this.options.insertion) {
-        new this.options.insertion(receiver, response);
-      } else {
-        receiver.innerHTML = response;
-      }
-    }
-
-    if (this.responseIsSuccess()) {
-      if (this.onComplete)
-        setTimeout((function() {this.onComplete(
-          this.transport)}).bind(this), 10);
-    }
-
-    if (this.options.evalScripts && scripts) {
-      match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
-      setTimeout((function() {
-        for (var i = 0; i < scripts.length; i++)
-          eval(scripts[i].match(match)[1]);
-      }).bind(this), 10);
-    }
-  }
-});
-
-Ajax.PeriodicalUpdater = Class.create();
-Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
-  initialize: function(container, url, options) {
-    this.setOptions(options);
-    this.onComplete = this.options.onComplete;
-
-    this.frequency = (this.options.frequency || 2);
-    this.decay = 1;
-
-    this.updater = {};
-    this.container = container;
-    this.url = url;
-
-    this.start();
-  },
-
-  start: function() {
-    this.options.onComplete = this.updateComplete.bind(this);
-    this.onTimerEvent();
-  },
-
-  stop: function() {
-    this.updater.onComplete = undefined;
-    clearTimeout(this.timer);
-    (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
-  },
-
-  updateComplete: function(request) {
-    if (this.options.decay) {
-      this.decay = (request.responseText == this.lastText ? 
-        this.decay * this.options.decay : 1);
-
-      this.lastText = request.responseText;
-    }
-    this.timer = setTimeout(this.onTimerEvent.bind(this), 
-      this.decay * this.frequency * 1000);
-  },
-
-  onTimerEvent: function() {
-    this.updater = new Ajax.Updater(this.container, this.url, this.options);
-  }
-});
-
-document.getElementsByClassName = function(className) {
-  var children = document.getElementsByTagName('*') || document.all;
-  var elements = new Array();
-  
-  for (var i = 0; i < children.length; i++) {
-    var child = children[i];
-    var classNames = child.className.split(' ');
-    for (var j = 0; j < classNames.length; j++) {
-      if (classNames[j] == className) {
-        elements.push(child);
-        break;
-      }
-    }
-  }
-  
-  return elements;
-}
-
-/*--------------------------------------------------------------------------*/
-
-if (!window.Element) {
-  var Element = new Object();
-}
-
-Object.extend(Element, {
-  toggle: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 
-        (element.style.display == 'none' ? '' : 'none');
-    }
-  },
-
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
-    }
-  },
-
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
-    }
-  },
-
-  remove: function(element) {
-    element = $(element);
-    element.parentNode.removeChild(element);
-  },
-   
-  getHeight: function(element) {
-    element = $(element);
-    return element.offsetHeight; 
-  },
-
-  hasClassName: function(element, className) {
-    element = $(element);
-    if (!element)
-      return;
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] == className)
-        return true;
-    }
-    return false;
-  },
-
-  addClassName: function(element, className) {
-    element = $(element);
-    Element.removeClassName(element, className);
-    element.className += ' ' + className;
-  },
-
-  removeClassName: function(element, className) {
-    element = $(element);
-    if (!element)
-      return;
-    var newClassName = '';
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] != className) {
-        if (i > 0)
-          newClassName += ' ';
-        newClassName += a[i];
-      }
-    }
-    element.className = newClassName;
-  },
-  
-  // removes whitespace-only text node children
-  cleanWhitespace: function(element) {
-    var element = $(element);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
-      if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 
-        Element.remove(node);
-    }
-  }
-});
-
-var Toggle = new Object();
-Toggle.display = Element.toggle;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.Insertion = function(adjacency) {
-  this.adjacency = adjacency;
-}
-
-Abstract.Insertion.prototype = {
-  initialize: function(element, content) {
-    this.element = $(element);
-    this.content = content;
-    
-    if (this.adjacency && this.element.insertAdjacentHTML) {
-      this.element.insertAdjacentHTML(this.adjacency, this.content);
-    } else {
-      this.range = this.element.ownerDocument.createRange();
-      if (this.initializeRange) this.initializeRange();
-      this.fragment = this.range.createContextualFragment(this.content);
-      this.insertContent();
-    }
-  }
-}
-
-var Insertion = new Object();
-
-Insertion.Before = Class.create();
-Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
-  initializeRange: function() {
-    this.range.setStartBefore(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, this.element);
-  }
-});
-
-Insertion.Top = Class.create();
-Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(true);
-  },
-  
-  insertContent: function() {  
-    this.element.insertBefore(this.fragment, this.element.firstChild);
-  }
-});
-
-Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.appendChild(this.fragment);
-  }
-});
-
-Insertion.After = Class.create();
-Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
-  initializeRange: function() {
-    this.range.setStartAfter(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, 
-      this.element.nextSibling);
-  }
-});
-
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $(arguments[i]).value = '';
-  },
-
-  focus: function(element) {
-    $(element).focus();
-  },
-  
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
-    return true;
-  },
-  
-  select: function(element) {
-    $(element).select();
-  },
-   
-  activate: function(element) {
-    $(element).focus();
-    $(element).select();
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
-    
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
-    }
-    
-    return queryComponents.join('&');
-  },
-  
-  getElements: function(form) {
-    var form = $(form);
-    var elements = new Array();
-
-    for (tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
-    }
-    return elements;
-  },
-  
-  getInputs: function(form, typeName, name) {
-    var form = $(form);
-    var inputs = form.getElementsByTagName('input');
-    
-    if (!typeName && !name)
-      return inputs;
-      
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
-      var input = inputs[i];
-      if ((typeName && input.type != typeName) ||
-          (name && input.name != name)) 
-        continue;
-      matchingInputs.push(input);
-    }
-
-    return matchingInputs;
-  },
-
-  disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.blur();
-      element.disabled = 'true';
-    }
-  },
-
-  enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.disabled = '';
-    }
-  },
-
-  focusFirstElement: function(form) {
-    var form = $(form);
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      if (element.type != 'hidden' && !element.disabled) {
-        Field.activate(element);
-        break;
-      }
-    }
-  },
-
-  reset: function(form) {
-    $(form).reset();
-  }
-}
-
-Form.Element = {
-  serialize: function(element) {
-    var element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter)
-      return encodeURIComponent(parameter[0]) + '=' + 
-        encodeURIComponent(parameter[1]);                   
-  },
-  
-  getValue: function(element) {
-    var element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter) 
-      return parameter[1];
-  }
-}
-
-Form.Element.Serializers = {
-  input: function(element) {
-    switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
-      case 'checkbox':  
-      case 'radio':
-        return Form.Element.Serializers.inputSelector(element);
-    }
-    return false;
-  },
-
-  inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
-  },
-
-  textarea: function(element) {
-    return [element.name, element.value];
-  },
-
-  select: function(element) {
-    var value = '';
-    if (element.type == 'select-one') {
-      var index = element.selectedIndex;
-      if (index >= 0)
-        value = element.options[index].value || element.options[index].text;
-    } else {
-      value = new Array();
-      for (var i = 0; i < element.length; i++) {
-        var opt = element.options[i];
-        if (opt.selected)
-          value.push(opt.value || opt.text);
-      }
-    }
-    return [element.name, value];
-  }
-}
-
-/*--------------------------------------------------------------------------*/
-
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.TimedObserver = function() {}
-Abstract.TimedObserver.prototype = {
-  initialize: function(element, frequency, callback) {
-    this.frequency = frequency;
-    this.element   = $(element);
-    this.callback  = callback;
-    
-    this.lastValue = this.getValue();
-    this.registerCallback();
-  },
-  
-  registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
-  },
-  
-  onTimerEvent: function() {
-    var value = this.getValue();
-    if (this.lastValue != value) {
-      this.callback(this.element, value);
-      this.lastValue = value;
-    }
-  }
-}
-
-Form.Element.Observer = Class.create();
-Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
-  getValue: function() {
-    return Form.Element.getValue(this.element);
-  }
-});
-
-Form.Observer = Class.create();
-Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
-  getValue: function() {
-    return Form.serialize(this.element);
-  }
-});
-
-/*--------------------------------------------------------------------------*/
-
-Abstract.EventObserver = function() {}
-Abstract.EventObserver.prototype = {
-  initialize: function(element, callback) {
-    this.element  = $(element);
-    this.callback = callback;
-    
-    this.lastValue = this.getValue();
-    if (this.element.tagName.toLowerCase() == 'form')
-      this.registerFormCallbacks();
-    else
-      this.registerCallback(this.element);
-  },
-  
-  onElementEvent: function() {
-    var value = this.getValue();
-    if (this.lastValue != value) {
-      this.callback(this.element, value);
-      this.lastValue = value;
-    }
-  },
-  
-  registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
-  },
-  
-  registerCallback: function(element) {
-    if (element.type) {
-      switch (element.type.toLowerCase()) {
-        case 'checkbox':  
-        case 'radio':
-          element.target = this;
-          element.prev_onclick = element.onclick || Prototype.emptyFunction;
-          element.onclick = function() {
-            this.prev_onclick(); 
-            this.target.onElementEvent();
-          }
-          break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
-          element.target = this;
-          element.prev_onchange = element.onchange || Prototype.emptyFunction;
-          element.onchange = function() {
-            this.prev_onchange(); 
-            this.target.onElementEvent();
-          }
-          break;
-      }
-    }    
-  }
-}
-
-Form.Element.EventObserver = Class.create();
-Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
-  getValue: function() {
-    return Form.Element.getValue(this.element);
-  }
-});
-
-Form.EventObserver = Class.create();
-Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
-  getValue: function() {
-    return Form.serialize(this.element);
-  }
-});
-
-
-if (!window.Event) {
-  var Event = new Object();
-}
-
-Object.extend(Event, {
-  KEY_BACKSPACE: 8,
-  KEY_TAB:       9,
-  KEY_RETURN:   13,
-  KEY_ESC:      27,
-  KEY_LEFT:     37,
-  KEY_UP:       38,
-  KEY_RIGHT:    39,
-  KEY_DOWN:     40,
-  KEY_DELETE:   46,
-
-  element: function(event) {
-    return event.target || event.srcElement;
-  },
-
-  isLeftClick: function(event) {
-    return (((event.which) && (event.which == 1)) ||
-            ((event.button) && (event.button == 1)));
-  },
-
-  pointerX: function(event) {
-    return event.pageX || (event.clientX + 
-      (document.documentElement.scrollLeft || document.body.scrollLeft));
-  },
-
-  pointerY: function(event) {
-    return event.pageY || (event.clientY + 
-      (document.documentElement.scrollTop || document.body.scrollTop));
-  },
-
-  stop: function(event) {
-    if (event.preventDefault) { 
-      event.preventDefault(); 
-      event.stopPropagation(); 
-    } else {
-      event.returnValue = false;
-    }
-  },
-
-  // find the first node with the given tagName, starting from the
-  // node the event was triggered on; traverses the DOM upwards
-  findElement: function(event, tagName) {
-    var element = Event.element(event);
-    while (element.parentNode && (!element.tagName ||
-        (element.tagName.toUpperCase() != tagName.toUpperCase())))
-      element = element.parentNode;
-    return element;
-  },
-
-  observers: false,
-  
-  _observeAndCache: function(element, name, observer, useCapture) {
-    if (!this.observers) this.observers = [];
-    if (element.addEventListener) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.addEventListener(name, observer, useCapture);
-    } else if (element.attachEvent) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.attachEvent('on' + name, observer);
-    }
-  },
-  
-  unloadCache: function() {
-    if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
-      Event.stopObserving.apply(this, Event.observers[i]);
-      Event.observers[i][0] = null;
-    }
-    Event.observers = false;
-  },
-
-  observe: function(element, name, observer, useCapture) {
-    var element = $(element);
-    useCapture = useCapture || false;
-    
-    if (name == 'keypress' &&
-        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
-        || element.attachEvent))
-      name = 'keydown';
-    
-    this._observeAndCache(element, name, observer, useCapture);
-  },
-
-  stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
-    useCapture = useCapture || false;
-    
-    if (name == 'keypress' &&
-        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
-        || element.detachEvent))
-      name = 'keydown';
-    
-    if (element.removeEventListener) {
-      element.removeEventListener(name, observer, useCapture);
-    } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
-    }
-  }
-});
-
-/* prevent memory leaks in IE */
-Event.observe(window, 'unload', Event.unloadCache, false);
-
-var Position = {
-
-  // set to true if needed, warning: firefox performance problems
-  // NOT neeeded for page scrolling, only if draggable contained in
-  // scrollable elements
-  includeScrollOffsets: false, 
-
-  // must be called before calling withinIncludingScrolloffset, every time the
-  // page is scrolled
-  prepare: function() {
-    this.deltaX =  window.pageXOffset 
-                || document.documentElement.scrollLeft 
-                || document.body.scrollLeft 
-                || 0;
-    this.deltaY =  window.pageYOffset 
-                || document.documentElement.scrollTop 
-                || document.body.scrollTop 
-                || 0;
-  },
-
-  realOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.scrollTop  || 0;
-      valueL += element.scrollLeft || 0; 
-      element = element.parentNode;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  cumulativeOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  // caches x/y coordinate pair to use with overlap
-  within: function(element, x, y) {
-    if (this.includeScrollOffsets)
-      return this.withinIncludingScrolloffsets(element, x, y);
-    this.xcomp = x;
-    this.ycomp = y;
-    this.offset = this.cumulativeOffset(element);
-
-    return (y >= this.offset[1] &&
-            y <  this.offset[1] + element.offsetHeight &&
-            x >= this.offset[0] && 
-            x <  this.offset[0] + element.offsetWidth);
-  },
-
-  withinIncludingScrolloffsets: function(element, x, y) {
-    var offsetcache = this.realOffset(element);
-
-    this.xcomp = x + offsetcache[0] - this.deltaX;
-    this.ycomp = y + offsetcache[1] - this.deltaY;
-    this.offset = this.cumulativeOffset(element);
-
-    return (this.ycomp >= this.offset[1] &&
-            this.ycomp <  this.offset[1] + element.offsetHeight &&
-            this.xcomp >= this.offset[0] && 
-            this.xcomp <  this.offset[0] + element.offsetWidth);
-  },
-
-  // within must be called directly before
-  overlap: function(mode, element) {  
-    if (!mode) return 0;  
-    if (mode == 'vertical') 
-      return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 
-        element.offsetHeight;
-    if (mode == 'horizontal')
-      return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 
-        element.offsetWidth;
-  },
-
-  clone: function(source, target) {
-    source = $(source);
-    target = $(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
-  }
-}