From d5e996b3f1e6f25053a3b13f661ab34697085c5c Mon Sep 17 00:00:00 2001 From: Drew Crampsie Date: Mon, 17 Oct 2005 19:19:00 -0700 Subject: [PATCH 1/1] A whole host of changes rescued from the alcoholic laptop. darcs-hash:20051018021900-5417e-94b6d14699126e5fa2004f548cbcbbed6ed9362c.gz --- lisp-on-lines.asd | 1 + src/components/ajax.lisp | 6 +- src/lisp-on-lines.lisp | 48 +- src/mewa/mewa.lisp | 160 +++-- src/mewa/packages.lisp | 17 +- src/mewa/presentations.lisp | 31 +- src/mewa/slot-presentations.lisp | 69 +- src/packages.lisp | 16 +- wwwroot/prototype-1.3.1.js | 1038 ------------------------------ 9 files changed, 245 insertions(+), 1141 deletions(-) delete mode 100644 wwwroot/prototype-1.3.1.js diff --git a/lisp-on-lines.asd b/lisp-on-lines.asd index fbb7a5b..90d76a0 100644 --- a/lisp-on-lines.asd +++ b/lisp-on-lines.asd @@ -32,4 +32,5 @@ :pathname "src/components/" :components ((:file "range-list") (:file "ajax")))) + :serial t :depends-on (:meta-model :mewa :split-sequence)) diff --git a/src/components/ajax.lisp b/src/components/ajax.lisp index 7552c41..9f5d5e3 100644 --- a/src/components/ajax.lisp +++ b/src/components/ajax.lisp @@ -165,8 +165,6 @@ but here's what i use." :class "auto-complete-list-item" (funcall (render auto-complete) value)))))) - - (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) - (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) + (let ((foreign-instance (if (lol::value (live-search slot)) (lol::value (live-search slot)) diff --git a/src/lisp-on-lines.lisp b/src/lisp-on-lines.lisp index 8a9a88b..465ec5f 100644 --- a/src/lisp-on-lines.lisp +++ b/src/lisp-on-lines.lisp @@ -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) - (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" @@ -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)))))) -(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 - ,@(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))) -(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" - `(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)) diff --git a/src/mewa/mewa.lisp b/src/mewa/mewa.lisp index e620130..255ce57 100644 --- a/src/mewa/mewa.lisp +++ b/src/mewa/mewa.lisp @@ -2,19 +2,6 @@ (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) @@ -26,25 +13,10 @@ "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. @@ -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 )))) + +(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) @@ -140,14 +178,15 @@ attributes is an alist keyed on the attribute name." (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))) - (default-attributes model))) + (find-default-attributes 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*) - (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 @@ -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)) - (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) @@ -322,7 +361,6 @@ attributes is an alist keyed on the attribute name." (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)) @@ -341,26 +379,33 @@ attributes is an alist keyed on the attribute name." (setf (modifiedp self) nil) (answer self)) +(defmethod confirm-sync-instance ((self mewa)) + nil) (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)) - (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 @@ -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))))))) -;;;; * 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)) + diff --git a/src/mewa/packages.lisp b/src/mewa/packages.lisp index 76681d4..ebdf210 100644 --- a/src/mewa/packages.lisp +++ b/src/mewa/packages.lisp @@ -3,6 +3,10 @@ (:export :mewa :editablep + + ;object presentations + :present + :foreign-key-slot-presentation :mewa-object-presentation :mewa-one-line-presentation :mewa-list-presentation @@ -12,7 +16,11 @@ :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 @@ -21,8 +29,12 @@ :has-very-many-slot-presentation :has-very-many :slot-name + + ;attributes :define-attributes - :find-attribute + :with-default-attributes + :find-attribute + :find-attribute-slot :set-default-attributes :make-presentation :call-presentation @@ -35,6 +47,7 @@ :find-class-attributes :default-attributes :ok + :instance :edit-instance :save-instance :cancel-save-instance diff --git a/src/mewa/presentations.lisp b/src/mewa/presentations.lisp index ab7c27b..82f18e3 100644 --- a/src/mewa/presentations.lisp +++ b/src/mewa/presentations.lisp @@ -1,20 +1,47 @@ (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) () - (: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)) - (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))) +(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)) diff --git a/src/mewa/slot-presentations.lisp b/src/mewa/slot-presentations.lisp index ef86dec..82893a5 100644 --- a/src/mewa/slot-presentations.lisp +++ b/src/mewa/slot-presentations.lisp @@ -14,9 +14,9 @@ ;;;; ** 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) @@ -25,8 +25,9 @@ :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-html (presentation-slot-value slot instance))))) + ))) (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) - () + ((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) @@ -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) - (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) - ( - * - * 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 = '(?:)((\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'; - } -} -- 2.20.1