From bknr at bknr.net Sat Nov 1 10:06:29 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 01 Nov 2008 11:06:29 +0100 Subject: [bknr-cvs] hans changed trunk/projects/quickhoney/website/ Message-ID: Revision: 4024 Author: hans URL: http://bknr.net/trac/changeset/4024 Revive full image display. A trunk/projects/quickhoney/website/static/image-full.html U trunk/projects/quickhoney/website/static/javascript.js D trunk/projects/quickhoney/website/templates/image-full.xml Added: trunk/projects/quickhoney/website/static/image-full.html =================================================================== --- trunk/projects/quickhoney/website/static/image-full.html (rev 0) +++ trunk/projects/quickhoney/website/static/image-full.html 2008-11-01 10:06:28 UTC (rev 4024) @@ -0,0 +1,26 @@ + + + + + + + QuickHoney image + + + + +
+ + + +
+ + + Modified: trunk/projects/quickhoney/website/static/javascript.js =================================================================== --- trunk/projects/quickhoney/website/static/javascript.js 2008-10-30 16:12:57 UTC (rev 4023) +++ trunk/projects/quickhoney/website/static/javascript.js 2008-11-01 10:06:28 UTC (rev 4024) @@ -1200,7 +1200,7 @@ } function enlarge() { - var detail_window = open("image-full#" + encodeURI(current_image.name), + var detail_window = open("/static/image-full.html#" + encodeURI(current_image.name), "_new", "width=" + current_image.width + ",height=" + current_image.height + ",status=no,toolbar=no,menubar=no"); Deleted: trunk/projects/quickhoney/website/templates/image-full.xml =================================================================== --- trunk/projects/quickhoney/website/templates/image-full.xml 2008-10-30 16:12:57 UTC (rev 4023) +++ trunk/projects/quickhoney/website/templates/image-full.xml 2008-11-01 10:06:28 UTC (rev 4024) @@ -1,29 +0,0 @@ - - - - - - - QuickHoney image - - - - -
- - - -
- - - From bknr at bknr.net Mon Nov 10 08:20:25 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 09:20:25 +0100 Subject: [bknr-cvs] hans changed buildbot/master/master.cfg Message-ID: Revision: 4025 Author: hans URL: http://bknr.net/trac/changeset/4025 Remove linux builder for now. U buildbot/master/master.cfg Modified: buildbot/master/master.cfg =================================================================== --- buildbot/master/master.cfg 2008-11-01 10:06:28 UTC (rev 4024) +++ buildbot/master/master.cfg 2008-11-10 08:20:25 UTC (rev 4025) @@ -7,7 +7,6 @@ from buildbot.buildslave import BuildSlave c['slaves'] = [BuildSlave("paracetamol", "bknrbu1ld", max_builds=1), BuildSlave("netzhansa.com", "bknrbu1ld", max_builds=1), - BuildSlave("lisp.homelinux.net", "bknrbu1ld", max_builds=1), BuildSlave("test.createrainforest.org", "bknrbu1ld", max_builds=1), BuildSlave("harald.headcraft.de", "bknrbu1ld", max_builds=1)] @@ -29,7 +28,6 @@ builderNames=["bknr-fbsd-ccl-amd64", "bknr-fbsd-sbcl-amd64", "bknr-fbsd-sbcl-i386", - "bknr-linux-sbcl-i386", "bknr-darwin-ccl-ppc", #"bknr-linux-cmucl-i386", #"bknr-fbsd-cmucl-i386", @@ -71,11 +69,6 @@ 'builddir': "bknr-fbsd-sbcl-i386", 'factory': make_bknr_factory("trunk", "sbcl"), }, - {'name': "bknr-linux-sbcl-i386", - 'slavename': "lisp.homelinux.net", - 'builddir': "bknr-linux-sbcl-i386", - 'factory': make_bknr_factory("trunk", "sbcl"), - }, {'name': "bknr-darwin-ccl-ppc", 'slavename': "harald.headcraft.de", 'builddir': "bknr-darwin-ccl-ppc", From bknr at bknr.net Mon Nov 10 08:25:02 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 09:25:02 +0100 Subject: [bknr-cvs] hans changed trunk/bknr/ Message-ID: Revision: 4026 Author: hans URL: http://bknr.net/trac/changeset/4026 resolve conflicts with alexandria U trunk/bknr/datastore/src/data/package.lisp U trunk/bknr/datastore/src/utils/package.lisp U trunk/bknr/datastore/src/utils/utils.lisp U trunk/bknr/modules/text/package.lisp U trunk/bknr/web/src/packages.lisp Modified: trunk/bknr/datastore/src/data/package.lisp =================================================================== --- trunk/bknr/datastore/src/data/package.lisp 2008-11-10 08:20:25 UTC (rev 4025) +++ trunk/bknr/datastore/src/data/package.lisp 2008-11-10 08:25:02 UTC (rev 4026) @@ -3,10 +3,11 @@ (defpackage :bknr.datastore (:use :cl :bknr.utils :cl-interpol :cl-ppcre :bknr.indices :bknr.statistics - :closer-mop ) + :closer-mop :alexandria) #+cmu (:shadowing-import-from :common-lisp #:subtypep #:typep) (:shadowing-import-from :cl-interpol quote-meta-chars) + (:shadowing-import-from :bknr.indices array-index) #| (:shadow :cl #:get-internal-run-time #:get-internal-real-time #:get-universal-time #:sleep) |# (:export #:*store-debug* #:*store* Modified: trunk/bknr/datastore/src/utils/package.lisp =================================================================== --- trunk/bknr/datastore/src/utils/package.lisp 2008-11-10 08:20:25 UTC (rev 4025) +++ trunk/bknr/datastore/src/utils/package.lisp 2008-11-10 08:25:02 UTC (rev 4026) @@ -35,8 +35,6 @@ #:parse-time ;; filesystem functions - #:copy-stream - #:copy-file #:move-file #:directory-empty-p #:subdir-p Modified: trunk/bknr/datastore/src/utils/utils.lisp =================================================================== --- trunk/bknr/datastore/src/utils/utils.lisp 2008-11-10 08:20:25 UTC (rev 4025) +++ trunk/bknr/datastore/src/utils/utils.lisp 2008-11-10 08:25:02 UTC (rev 4026) @@ -163,19 +163,6 @@ 0 (length (pathname-directory dir))) (pathname-directory dir))))) -(defun copy-file (source target &key (overwrite t)) - (let ((buffer (make-array 4096 :element-type '(unsigned-byte 8))) - (read-count 0)) - (with-open-file (in source :direction :input - :element-type '(unsigned-byte 8)) - (with-open-file (out target :direction :output - :element-type '(unsigned-byte 8) - :if-exists (if overwrite :overwrite :error) :if-does-not-exist :create) - (loop - (setf read-count (read-sequence buffer in)) - (write-sequence buffer out :end read-count) - (when (< read-count 4096) (return))))))) - (defun move-file (file1 file2) #+(or allegro openmcl) (rename-file file1 file2) @@ -186,19 +173,6 @@ (sb-unix:unix-rename (namestring file1) (namestring file2))) -(defun copy-stream (in out &optional (element-type '(unsigned-byte 8))) - "Copy everything from in to out" - (let* ((buffer-size 4096) - (buffer (make-array buffer-size :element-type element-type))) - (labels ((read-chunks () - (let ((size (read-sequence buffer in))) - (if (< size buffer-size) - (write-sequence buffer out :start 0 :end size) - (progn - (write-sequence buffer out) - (read-chunks)))))) - (read-chunks)))) - (defun make-temporary-pathname (&key (defaults nil) (name "tmp")) (loop for file = (make-pathname :name (format nil "~A-~A-~A" name Modified: trunk/bknr/modules/text/package.lisp =================================================================== --- trunk/bknr/modules/text/package.lisp 2008-11-10 08:20:25 UTC (rev 4025) +++ trunk/bknr/modules/text/package.lisp 2008-11-10 08:25:02 UTC (rev 4026) @@ -18,7 +18,6 @@ :alexandria) (:shadowing-import-from :bknr.indices array-index) (:shadowing-import-from :cl-interpol quote-meta-chars) - (:shadowing-import-from :alexandria #:copy-stream #:copy-file) (:export ;; billboards #:list-billboards-page Modified: trunk/bknr/web/src/packages.lisp =================================================================== --- trunk/bknr/web/src/packages.lisp 2008-11-10 08:20:25 UTC (rev 4025) +++ trunk/bknr/web/src/packages.lisp 2008-11-10 08:25:02 UTC (rev 4026) @@ -164,7 +164,6 @@ (:shadowing-import-from :cl-interpol #:quote-meta-chars) (:shadowing-import-from :hunchentoot #:host) (:shadowing-import-from :alexandria #:array-index) - (:shadowing-import-from :alexandria #:copy-stream #:copy-file) (:export #:*user* #:with-http-request #:with-http-body @@ -405,7 +404,6 @@ :bknr.user) (:shadowing-import-from :cl-interpol #:quote-meta-chars) (:shadowing-import-from :bknr.indices #:array-index) - (:shadowing-import-from :alexandria #:copy-stream #:copy-file) (:export #:imageproc #:define-imageproc-handler #:image-handler ; plain images only From bknr at bknr.net Mon Nov 10 08:27:59 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 09:27:59 +0100 Subject: [bknr-cvs] hans changed trunk/projects/quickhoney/src/ Message-ID: Revision: 4027 Author: hans URL: http://bknr.net/trac/changeset/4027 work on shopping system U trunk/projects/quickhoney/src/packages.lisp U trunk/projects/quickhoney/src/quickhoney.asd U trunk/projects/quickhoney/src/shop.lisp Modified: trunk/projects/quickhoney/src/packages.lisp =================================================================== --- trunk/projects/quickhoney/src/packages.lisp 2008-11-10 08:25:02 UTC (rev 4026) +++ trunk/projects/quickhoney/src/packages.lisp 2008-11-10 08:27:59 UTC (rev 4027) @@ -21,7 +21,16 @@ (:use :cl :bknr.datastore :bknr.indices - :bknr.user)) + :bknr.user) + (:export #:download-product + #:emailable-product + #:mailable-product + #:product-stock-count + #:shopping-cart + #:put-to-cart + #:insufficient-inventory + #:product-already-in-shopping-cart + #:fulfill)) (defpackage :quickhoney (:use :cl Modified: trunk/projects/quickhoney/src/quickhoney.asd =================================================================== --- trunk/projects/quickhoney/src/quickhoney.asd 2008-11-10 08:25:02 UTC (rev 4026) +++ trunk/projects/quickhoney/src/quickhoney.asd 2008-11-10 08:27:59 UTC (rev 4027) @@ -24,7 +24,8 @@ :bknr.web :bknr.datastore :bknr.modules - :cl-gd) + :cl-gd + :unit-test) :components ((:file "packages") (:file "config" :depends-on ("packages")) Modified: trunk/projects/quickhoney/src/shop.lisp =================================================================== --- trunk/projects/quickhoney/src/shop.lisp 2008-11-10 08:25:02 UTC (rev 4026) +++ trunk/projects/quickhoney/src/shop.lisp 2008-11-10 08:27:59 UTC (rev 4027) @@ -28,7 +28,6 @@ :update :type string :index-type string-unique-index - :index-reader product-with-name :documentation "Short name of the product, must be unique, should be identifier") (description @@ -41,10 +40,13 @@ (:documentation "Sell PRODUCT, adjusting the stock count if needed. Returns the product sold.")) -(defgeneric stock-count (product) +(defgeneric product-stock-count (product) (:documentation "Return the number of instances of PRODUCT available, or NIL if the - product can be sold in infinite amounts.")) + product can be sold in infinite amounts.") + (:method (product) + "By default, assume infinite supply" + nil)) (define-persistent-class download-product (product) () @@ -54,9 +56,6 @@ address. Once paid, the system makes the product available to the customer for download.")) -(defmethod stock-count ((product download-product)) - nil) - (define-persistent-class emailable-product (product) () (:documentation @@ -65,20 +64,40 @@ be supplied by the customer. Once paid, the system sends the order to the store personnel for fulfillment.")) -(defmethod stock-count ((product emailable-product)) - nil) - (define-persistent-class mailable-product (product) ((stock-count :update :type integer - :accessor stock-count + :accessor product-stock-count :documentation - "Number of instances of this product that are available to be sold.")) + "Number of instances of this product that are + available to be sold, including reserved amounts in + shopping carts.") + (reserved-count :update + :type integer + :initform 0)) (:documentation "A product that is sent to the customer by regular mail \(i.e. a t-shirt or poster). Once paid, the system sends the order to the store personell for fulfillment.")) +(defgeneric available-p (product count) + (:documentation "Return a true value if COUNT units of PRODUCT are + currently available. Should be called with the store guard locked.") + (:method (product count) + (or (null (product-stock-count product)) + (<= count (product-stock-count product))))) + +(defmethod product-stock-count ((product mailable-product)) + "The available stock count for a mailable product is reduced by the reserved count and returned." + (- (slot-value product 'stock-count) + (mailable-product-reserved-count product))) + +(defmethod (setf product-stock-count) (newval (product mailable-product)) + "The available sock count for a mailable product is set to NEWVAL." + (when (< newval (mailable-product-reserved-count product)) + (error "cannot reduce the available stock count below the reserved count")) + (setf (slot-value product 'stock-count) newval)) + (define-persistent-class shipping-address () ((country :read)) (:documentation @@ -99,9 +118,28 @@ "List of shipping addresses with the preferred address being the CAR of the list."))) +(define-persistent-class number-generator () + ((name :read + :type symbol + :initform (error "cannot make number-generator instance without name") + :index-type string-unique-index + :index-reader number-generator-with-name) + (next :update + :type integer + :initarg :next + :initform 1))) + +(defun get-next-number (name) + (with-transaction (:get-next-number) + (let* ((number-generator (or (number-generator-with-name name) + (make-instance 'number-generator :name name))) + (number (number-generator-next number-generator))) + (incf (number-generator-next number-generator)) + number))) + (define-persistent-class order () ((number :read - :initform (make-order-number)) + :initform (get-next-number 'orders)) (customer :read) (items :update))) @@ -112,6 +150,161 @@ (define-persistent-class invoice () ((number :read - :initform (make-invoice-number)) + :initform (get-next-number 'invoices)) (items :update))) +(define-persistent-class lease () + ((product :read + :initform (error "missing :product initarg to lease creation") + :documentation "product that has been leased") + (count :read + :initform (error "missing :count initarg to lease creation") + :documentation "number of units of product held by this lease") + (fulfilled :update + :initform nil + :documentation "Set to a true value when the lease has + been fulfilled. Used during lease descruction in order + to determine whether to return the leased inventory to + the product stock.")) + (:documentation "Instance representing a lease for a product.")) + +(defgeneric update-reserved-stock (product count) + (:documentation "Update the reserved counter of PRODUCT by COUNT units") + (:method (product count) + (declare (ignore product count)))) + +(defgeneric note-sale (product count) + (:documentation "Update the stock count of the PRODUCT by COUNT + units after a sale has been done") + (:method (product count) + (declare (ignore product count)))) + +(defmethod initialize-instance :after ((lease lease) &key) + (update-reserved-stock (lease-product lease) (lease-count lease))) + +(defmethod destroy-object :before ((lease lease)) + (unless (lease-fulfilled lease) + (update-reserved-stock (lease-product lease) (- (lease-count lease))))) + +(defmethod update-reserved-stock ((product mailable-product) count) + (incf (mailable-product-reserved-count product) count)) + +(defmethod note-sale ((product mailable-product) count) + (decf (slot-value product 'stock-count) count) + (update-reserved-stock product (- count))) + +(define-persistent-class shopping-cart () + ((leases :update + :initform nil) + (expires :read + :initform (error "missing :expires initarg to shopping cart creation") + :documentation "universal time at which this shopping cart expires")) + (:documentation "Represents the intent to buy goods, in the form of LEASE objects")) + +(defmethod destroy-object :before ((shopping-cart shopping-cart)) + (mapc #'delete-object (shopping-cart-leases shopping-cart))) + +(define-condition insufficient-inventory (error) + ((product :initarg :product + :reader product) + (requested :initarg :requested + :reader requested) + (available :initarg :available + :reader available)) + (:report (lambda (c stream) + (format stream "Insufficient inventory for product ~A - Requested ~A, but~[~; only~]~:* ~A available" + (product c) (requested c) (available c)) + c))) + +(define-condition product-already-in-shopping-cart (error) + ((product :initarg :product + :reader product)) + (:report (lambda (c stream) + (format stream "Product ~A is already in shopping cart" + (product c))))) + +(defun put-to-shopping-cart (count product shopping-cart) + "Reserve COUNT units of PRODUCT, signalling a INSUFFICIENT-INVENTORY + error if not enough inventory of PRODUCT is available. Returns a + LEASE object." + (with-store-guard () + (unless (available-p product count) + (error 'insufficient-inventory + :product product + :requested count + :available (product-stock-count product))) + (when (find product (shopping-cart-leases shopping-cart) + :key #'lease-product) + (error 'product-already-in-shopping-cart + :product product)) + (with-transaction (:make-lease) + (push (make-instance 'lease + :product product + :count count) + (shopping-cart-leases shopping-cart))))) + +(defun fulfill (shopping-cart) + "Fulfill the given shopping cart." + (with-transaction (:fulfill) + (dolist (lease (shopping-cart-leases shopping-cart)) + (let ((product (lease-product lease)) + (count (lease-count lease))) + (setf (lease-fulfilled lease) t) + (note-sale product count))) + (delete-object shopping-cart))) + +;;; TESTING + +(defmacro with-temporary-directory ((pathname) &body body) + `(let ((,pathname (pathname (format nil "/tmp/store-test-~A/" (sb-posix:getpid))))) + (asdf:run-shell-command "rm -rf ~A" ,pathname) + (prog1 + (progn , at body) + (asdf:run-shell-command "rm -rf ~A" ,pathname)))) + +(defun do-with-test-store (thunk) + (when (and (boundp '*store*) *store*) + (warn "closing open store *store* to run tests") + (close-store)) + (with-temporary-directory (store-directory) + (make-instance 'mp-store + :subsystems (list (make-instance 'store-object-subsystem)) + :directory store-directory) + (funcall thunk) + (close-store))) + +(defmacro with-test-store (() &body body) + `(do-with-test-store (lambda () , at body))) + +(unit-test:deftest :shop "lease and cart tests" + (with-test-store () + (let* ((t-shirt (make-instance 'mailable-product :name 't-shirt :stock-count 10)) + (file (make-instance 'download-product :name 'file)) + (shopping-cart (make-instance 'shopping-cart :expires (+ (* 60 10) (get-universal-time))))) + (unit-test:test-equal 10 (product-stock-count t-shirt)) + (put-to-shopping-cart 10 t-shirt shopping-cart) + (unit-test:test-equal 0 (product-stock-count t-shirt)) + (unit-test:test-assert (product-stock-count t-shirt)) + (with-transaction (:add-to-inventory) + (incf (slot-value t-shirt 'stock-count) 10)) + (put-to-shopping-cart 5 t-shirt shopping-cart) + (unit-test:test-equal 5 (product-stock-count t-shirt)) + (delete-object shopping-cart) + (unit-test:test-equal 20 (product-stock-count t-shirt)) + (setf shopping-cart (make-instance 'shopping-cart :expires (+ (* 60 10) (get-universal-time)))) + (put-to-shopping-cart 5 t-shirt shopping-cart) + (put-to-shopping-cart 500 file shopping-cart) + (unit-test:test-equal 15 (product-stock-count t-shirt))))) + +(unit-test:deftest :shop "fulfill test" + (with-test-store () + (let* ((t-shirt (make-instance 'mailable-product :name 't-shirt :stock-count 10)) + (file (make-instance 'download-product :name 'file)) + (shopping-cart (make-instance 'shopping-cart :expires (+ (* 60 10) (get-universal-time))))) + (put-to-shopping-cart 3 t-shirt shopping-cart) + (put-to-shopping-cart 7 file shopping-cart) + (fulfill shopping-cart) + (unit-test:test-equal 7 (product-stock-count t-shirt)) + (unit-test:test-equal 0 (length (class-instances 'shopping-cart))) + (unit-test:test-equal 0 (length (class-instances 'lease))) + (unit-test:test-equal 0 (mailable-product-reserved-count t-shirt))))) \ No newline at end of file From bknr at bknr.net Mon Nov 10 08:39:46 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 09:39:46 +0100 Subject: [bknr-cvs] hans changed trunk/bknr/web/src/web/tags.lisp Message-ID: Revision: 4028 Author: hans URL: http://bknr.net/trac/changeset/4028 Comment out code to avoid a "deleting unreachable code" warning U trunk/bknr/web/src/web/tags.lisp Modified: trunk/bknr/web/src/web/tags.lisp =================================================================== --- trunk/bknr/web/src/web/tags.lisp 2008-11-10 08:27:59 UTC (rev 4027) +++ trunk/bknr/web/src/web/tags.lisp 2008-11-10 08:39:46 UTC (rev 4028) @@ -114,6 +114,7 @@ (defun user-template-prefix () (error 'wtf) + #+(or) (user-preference (bknr-session-user) :template-path-prefix "file:///Volumes/web/template/")) ;; xxx new templater From bknr at bknr.net Mon Nov 10 09:38:22 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 10:38:22 +0100 Subject: [bknr-cvs] hans changed trunk/projects/quickhoney/src/handlers.lisp Message-ID: Revision: 4029 Author: hans URL: http://bknr.net/trac/changeset/4029 Improve image quality for uploaded jpg images. U trunk/projects/quickhoney/src/handlers.lisp Modified: trunk/projects/quickhoney/src/handlers.lisp =================================================================== --- trunk/projects/quickhoney/src/handlers.lisp 2008-11-10 08:39:46 UTC (rev 4028) +++ trunk/projects/quickhoney/src/handlers.lisp 2008-11-10 09:38:22 UTC (rev 4029) @@ -303,7 +303,8 @@ (defun maybe-convert-to-palette (&optional (image cl-gd:*default-image*)) (when (and (cl-gd:true-color-p image) (<= (count-colors-used image) 256)) - (cl-gd:true-color-to-palette :image image))) + (cl-gd:true-color-to-palette :image image) + t)) (defmethod handle ((handler upload-image-handler)) (with-query-params (client spider-keywords) @@ -322,7 +323,8 @@ :class-name 'quickhoney-image :keywords (cons :upload (image-keywords-from-request-parameters)) :initargs (list :owner (bknr-session-user) - :cat-sub (mapcar #'make-keyword-from-string (decoded-handler-path handler)) + :cat-sub (mapcar #'make-keyword-from-string + (decoded-handler-path handler)) :client client :spider-keywords spider-keywords)))) (with-http-response () @@ -367,47 +369,49 @@ (defmethod handle ((handler upload-news-handler)) (with-query-params (title text) - (let ((uploaded-file (request-uploaded-file "image-file"))) + (let ((uploaded-file (or (request-uploaded-file "image-file")))) + (unless uploaded-file + (error "no file uploaded")) (handler-case - (progn - (unless uploaded-file - (error "no file uploaded")) - (with-image-from-upload (uploaded-image uploaded-file) - (maybe-convert-to-palette uploaded-image) - (when (> (cl-gd:image-width uploaded-image) +news-image-width+) - (let* ((scaled-height (floor (* (/ +news-image-width+ (cl-gd:image-width uploaded-image)) - (cl-gd:image-height uploaded-image)))) - (scaled-image (cl-gd:create-image +news-image-width+ scaled-height (cl-gd:true-color-p uploaded-image)))) - (cl-gd:copy-image uploaded-image scaled-image - 0 0 0 0 - (cl-gd:image-width uploaded-image) (cl-gd:image-height uploaded-image) - :resample t :resize t - :dest-width +news-image-width+ :dest-height scaled-height) - (cl-gd:destroy-image uploaded-image) - (setf uploaded-image scaled-image))) - (let* ((name (normalize-news-title title)) - (item (make-store-image :name name - :image uploaded-image - :type (if (cl-gd:true-color-p uploaded-image) :jpg :png) - :class-name 'quickhoney-news-item - :keywords (list :upload) - :initargs (list :cat-sub (list :news) - :title title - :text text - :owner (bknr-session-user))))) - (declare (ignore item)) ; for now - (twitter:update-status (bknr-session-user) - (format nil "Posted news item: http://quickhoney.com/news/~A" name)) - (with-http-response () - (with-http-body () - (html (:html - (:head - (:title "News article created") - ((:script :type "text/javascript" :language "JavaScript") - "function done() { window.opener.reload_news(); window.close(); }")) - (:body - (:p "News article created") - (:p ((:a :href "javascript:done()") "ok")))))))))) + (with-image-from-upload (uploaded-image uploaded-file) + (let* ((processed (when (> (cl-gd:image-width uploaded-image) +news-image-width+) + (let* ((scaled-height (floor (* (/ +news-image-width+ (cl-gd:image-width uploaded-image)) + (cl-gd:image-height uploaded-image)))) + (scaled-image (cl-gd:create-image +news-image-width+ scaled-height + (cl-gd:true-color-p uploaded-image)))) + (cl-gd:copy-image uploaded-image scaled-image + 0 0 0 0 + (cl-gd:image-width uploaded-image) (cl-gd:image-height uploaded-image) + :resample t :resize t + :dest-width +news-image-width+ :dest-height scaled-height) + (cl-gd:destroy-image uploaded-image) + (setf uploaded-image scaled-image)) + t)) + (name (normalize-news-title title)) + (args (list :name name + :type (if (cl-gd:true-color-p uploaded-image) :jpg :png) + :class-name 'quickhoney-news-item + :keywords (list :upload) + :initargs (list :cat-sub (list :news) + :title title + :text text + :owner (bknr-session-user)))) + (item (if processed + (apply #'make-store-image :image uploaded-image args) + (apply #'import-image (upload-pathname uploaded-file) args)))) + (declare (ignore item)) ; for now + (twitter:update-status (bknr-session-user) + (format nil "Posted news item: http://quickhoney.com/news/~A" name)) + (with-http-response () + (with-http-body () + (html (:html + (:head + (:title "News article created") + ((:script :type "text/javascript" :language "JavaScript") + "function done() { window.opener.reload_news(); window.close(); }")) + (:body + (:p "News article created") + (:p ((:a :href "javascript:done()") "ok"))))))))) (error (e) (with-http-response () (with-http-body () From bknr at bknr.net Mon Nov 10 09:39:19 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 10:39:19 +0100 Subject: [bknr-cvs] hans changed trunk/bknr/web/src/images/image.lisp Message-ID: Revision: 4030 Author: hans URL: http://bknr.net/trac/changeset/4030 Always create jpeg images with maximum quality. U trunk/bknr/web/src/images/image.lisp Modified: trunk/bknr/web/src/images/image.lisp =================================================================== --- trunk/bknr/web/src/images/image.lisp 2008-11-10 09:38:22 UTC (rev 4029) +++ trunk/bknr/web/src/images/image.lisp 2008-11-10 09:39:19 UTC (rev 4030) @@ -82,9 +82,12 @@ initargs))) (ensure-directories-exist (blob-pathname store-image)) (ignore-errors (delete-file (blob-pathname store-image))) - (cl-gd:write-image-to-file (blob-pathname store-image) - :image image - :type type) + (apply #'cl-gd:write-image-to-file + (blob-pathname store-image) + :image image + :type type + (when (eq type :jpg) + (list :quality 95))) store-image)) (defmacro with-store-image-from-id ((var id) &rest body) From bknr at bknr.net Mon Nov 10 10:05:14 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 11:05:14 +0100 Subject: [bknr-cvs] hans changed trunk/thirdparty/cl-gd-0.5.6/colors.lisp Message-ID: Revision: 4031 Author: hans URL: http://bknr.net/trac/changeset/4031 make save-alpha-p and (setf save-alpha-p) orthogonal U trunk/thirdparty/cl-gd-0.5.6/colors.lisp Modified: trunk/thirdparty/cl-gd-0.5.6/colors.lisp =================================================================== --- trunk/thirdparty/cl-gd-0.5.6/colors.lisp 2008-11-10 09:39:19 UTC (rev 4030) +++ trunk/thirdparty/cl-gd-0.5.6/colors.lisp 2008-11-10 10:05:14 UTC (rev 4031) @@ -200,7 +200,7 @@ (check-type image image) (not (zerop (get-slot-value (img image) 'gd-image 'save-alpha-flag)))) -(defun (setf save-alpha-p) (save &key (image *default-image*)) +(defun (setf save-alpha-p) (save &optional (image *default-image*)) "Determines whether PNG images will be saved with full alpha channel information." (check-type image image) From bknr at bknr.net Mon Nov 10 10:07:00 2008 From: bknr at bknr.net (BKNR Commits) Date: Mon, 10 Nov 2008 11:07:00 +0100 Subject: [bknr-cvs] hans changed trunk/thirdparty/cl-gd-0.5.6/colors.lisp Message-ID: Revision: 4032 Author: hans URL: http://bknr.net/trac/changeset/4032 revert previous change U trunk/thirdparty/cl-gd-0.5.6/colors.lisp Modified: trunk/thirdparty/cl-gd-0.5.6/colors.lisp =================================================================== --- trunk/thirdparty/cl-gd-0.5.6/colors.lisp 2008-11-10 10:05:14 UTC (rev 4031) +++ trunk/thirdparty/cl-gd-0.5.6/colors.lisp 2008-11-10 10:07:00 UTC (rev 4032) @@ -200,7 +200,7 @@ (check-type image image) (not (zerop (get-slot-value (img image) 'gd-image 'save-alpha-flag)))) -(defun (setf save-alpha-p) (save &optional (image *default-image*)) +(defun (setf save-alpha-p) (save &key (image *default-image*)) "Determines whether PNG images will be saved with full alpha channel information." (check-type image image) From bknr at bknr.net Tue Nov 11 13:27:30 2008 From: bknr at bknr.net (BKNR Commits) Date: Tue, 11 Nov 2008 14:27:30 +0100 Subject: [bknr-cvs] hans changed trunk/projects/quickhoney/src/ Message-ID: Revision: 4033 Author: hans URL: http://bknr.net/trac/changeset/4033 add simple json parser A trunk/projects/quickhoney/src/json-parser.lisp U trunk/projects/quickhoney/src/json.lisp U trunk/projects/quickhoney/src/packages.lisp Added: trunk/projects/quickhoney/src/json-parser.lisp =================================================================== --- trunk/projects/quickhoney/src/json-parser.lisp (rev 0) +++ trunk/projects/quickhoney/src/json-parser.lisp 2008-11-11 13:27:30 UTC (rev 4033) @@ -0,0 +1,145 @@ +(in-package :json-parser) + +(defconstant +default-string-length+ 20 + "Default length of strings that are created while reading json input.") + +(defvar *parse-object-key-fn* #'identity + "Function to call to convert a key string in a JSON array to a key in the CL hash produced.") + +(defun make-adjustable-string () + "Return an adjustable empty string, usable as a buffer for parsing strings and numbers." + (make-array +default-string-length+ + :adjustable t :fill-pointer 0 :element-type 'character)) + +(defun parse-number (input) + ;; would be + ;; (cl-ppcre:scan-to-strings "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+|)(?:[eE][-+]?[0-9]+|)" buffer) + ;; but we want to operate on streams + (let ((buffer (make-adjustable-string))) + (loop + while (position (peek-char nil input nil) ".0123456789+-Ee") + do (vector-push-extend (read-char input) buffer)) + (values (read-from-string buffer)))) + +(defun parse-string (input) + (let ((output (make-adjustable-string))) + (labels ((outc (c) + (vector-push-extend c output)) + (next () + (read-char input)) + (peek () + (peek-char nil input))) + (next) + (loop + (cond + ((eql (peek) #\") + (next) + (return-from parse-string output)) + ((eql (peek) #\\) + (next) + (ecase (next) + (#\" (outc #\")) + (#\\ (outc #\\)) + (#\/ (outc #\/)) + (#\b (outc #\Backspace)) + (#\f (outc #\Page)) + (#\n (outc #\Newline)) + (#\r (outc #\Return)) + (#\t (outc #\Tab)) + (#\u (outc (code-char (let ((buffer (make-string 4))) + (read-sequence buffer input) + (parse-integer buffer :radix 16))))))) + (t + (outc (next)))))))) + +(defun whitespace-p (char) + (member char '(#\Space #\Newline #\Tab #\Linefeed))) + +(defun skip-whitespace (input) + (loop + while (and (listen input) + (whitespace-p (peek-char nil input))) + do (read-char input))) + +(defun peek-char-skipping-whitespace (input &optional (eof-error-p t)) + (skip-whitespace input) + (peek-char nil input eof-error-p)) + +(defun parse-constant (input) + (destructuring-bind (expected-string return-value) + (find (peek-char nil input nil) + '(("true" t) + ("false" nil) + ("null" nil)) + :key (lambda (entry) (aref (car entry) 0)) + :test #'eql) + (loop + for char across expected-string + unless (eql (read-char input nil) char) + do (error "invalid constant")) + return-value)) + +(define-condition cannot-convert-key (error) + ((key-string :initarg :key-string + :reader key-string)) + (:report (lambda (c stream) + (format stream "cannot convert key ~S used in JSON object to hash table key" + (key-string c))))) + +(defun parse-object (input) + (let ((return-value (make-hash-table :test #'equal))) + (read-char input) + (loop + (when (eql (peek-char-skipping-whitespace input) + #\}) + (return)) + (skip-whitespace input) + (setf (gethash (prog1 + (let ((key-string (parse-string input))) + (or (funcall *parse-object-key-fn* key-string) + (error 'cannot-convert-key :key-string key-string))) + (skip-whitespace input) + (unless (eql #\: (read-char input)) + (error 'expected-colon)) + (skip-whitespace input)) + return-value) + (parse input)) + (ecase (peek-char-skipping-whitespace input) + (#\, (read-char input)) + (#\} nil))) + (read-char input) + return-value)) + +(defconstant +initial-array-size+ 20 + "Initial size of JSON arrays read, they will grow as needed.") + +(defun parse-array (input) + (let ((return-value (make-array +initial-array-size+ :adjustable t :fill-pointer 0))) + (read-char input) + (loop + (when (eql (peek-char-skipping-whitespace input) + #\]) + (return)) + (vector-push-extend (parse input) return-value) + (ecase (peek-char-skipping-whitespace input) + (#\, (read-char input)) + (#\] nil))) + (read-char input) + return-value)) + +(defgeneric parse (input) + (:method ((input stream)) + (ecase (peek-char-skipping-whitespace input) + (#\" + (parse-string input)) + ((#\- #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) + (parse-number input)) + (#\{ + (parse-object input)) + (#\[ + (parse-array input)) + ((#\t #\f #\n) + (parse-constant input)))) + (:method ((input string)) + (parse (make-string-input-stream input)))) + Modified: trunk/projects/quickhoney/src/json.lisp =================================================================== --- trunk/projects/quickhoney/src/json.lisp 2008-11-10 10:07:00 UTC (rev 4032) +++ trunk/projects/quickhoney/src/json.lisp 2008-11-11 13:27:30 UTC (rev 4033) @@ -65,3 +65,4 @@ (with-json-output-to-string () (with-json-object () , at body)))) + Modified: trunk/projects/quickhoney/src/packages.lisp =================================================================== --- trunk/projects/quickhoney/src/packages.lisp 2008-11-10 10:07:00 UTC (rev 4032) +++ trunk/projects/quickhoney/src/packages.lisp 2008-11-11 13:27:30 UTC (rev 4033) @@ -87,4 +87,9 @@ (defpackage :twitter (:use :cl :bknr.datastore) - (:export #:update-status)) \ No newline at end of file + (:export #:update-status)) + +(defpackage :json-parser + (:use :cl) + (:export #:parse + #:*parse-object-key-fn*)) \ No newline at end of file From bknr at bknr.net Wed Nov 12 09:09:32 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 10:09:32 +0100 Subject: [bknr-cvs] hans changed trunk/thirdparty/cl-json/ Message-ID: Revision: 4034 Author: hans URL: http://bknr.net/trac/changeset/4034 remove cl-json D trunk/thirdparty/cl-json/ From bknr at bknr.net Wed Nov 12 10:08:24 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 11:08:24 +0100 Subject: [bknr-cvs] hans changed trunk/ Message-ID: Revision: 4035 Author: hans URL: http://bknr.net/trac/changeset/4035 Save work before extracting JSON library into a separate package. U trunk/bknr/datastore/src/bknr.datastore.asd U trunk/bknr/datastore/src/data/json.lisp U trunk/bknr/web/src/bknr.web.asd U trunk/bknr/web/src/packages.lisp U trunk/bknr/web/src/web/web-utils.lisp U trunk/projects/bos/web/poi-handlers.lisp U trunk/projects/quickhoney/src/handlers.lisp U trunk/projects/quickhoney/src/json-parser.lisp U trunk/projects/quickhoney/src/json.lisp U trunk/projects/quickhoney/src/packages.lisp U trunk/projects/quickhoney/website/static/index.css U trunk/projects/quickhoney/website/static/javascript.js U trunk/projects/quickhoney/website/templates/index.xml U trunk/projects/scrabble/src/scrabble.asd Change set too large, please see URL above From bknr at bknr.net Wed Nov 12 10:12:10 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 11:12:10 +0100 Subject: [bknr-cvs] hans changed trunk/ Message-ID: Revision: 4036 Author: hans URL: http://bknr.net/trac/changeset/4036 Add new libraries/ subdirectory to contain self-contained libraries. D trunk/ccl-profiler/ D trunk/clixdoc/ A trunk/libraries/ A trunk/libraries/ccl-profiler/ A trunk/libraries/clixdoc/ A trunk/libraries/xhtmlgen/ D trunk/xhtmlgen/ Copied: trunk/libraries/ccl-profiler (from rev 4023, trunk/ccl-profiler) Copied: trunk/libraries/clixdoc (from rev 4023, trunk/clixdoc) Copied: trunk/libraries/xhtmlgen (from rev 4023, trunk/xhtmlgen) From bknr at bknr.net Wed Nov 12 10:27:20 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 11:27:20 +0100 Subject: [bknr-cvs] hans changed trunk/ Message-ID: Revision: 4037 Author: hans URL: http://bknr.net/trac/changeset/4037 Add new YASON library. A trunk/libraries/yason/ A trunk/libraries/yason/encode.lisp A trunk/libraries/yason/package.lisp A trunk/libraries/yason/parse.lisp A trunk/libraries/yason/yason.asd D trunk/projects/quickhoney/src/json-parser.lisp D trunk/projects/quickhoney/src/json.lisp U trunk/projects/quickhoney/src/packages.lisp U trunk/projects/quickhoney/src/quickhoney.asd Change set too large, please see URL above From bknr at bknr.net Wed Nov 12 11:00:34 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 12:00:34 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/ Message-ID: Revision: 4038 Author: hans URL: http://bknr.net/trac/changeset/4038 Add license, copyright notes. A trunk/libraries/yason/LICENSE U trunk/libraries/yason/encode.lisp U trunk/libraries/yason/package.lisp U trunk/libraries/yason/parse.lisp U trunk/libraries/yason/yason.asd Added: trunk/libraries/yason/LICENSE =================================================================== --- trunk/libraries/yason/LICENSE (rev 0) +++ trunk/libraries/yason/LICENSE 2008-11-12 11:00:34 UTC (rev 4038) @@ -0,0 +1,30 @@ +Copyright (c) 2008 Hans H??bner +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name BKNR nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Modified: trunk/libraries/yason/encode.lisp =================================================================== --- trunk/libraries/yason/encode.lisp 2008-11-12 10:27:20 UTC (rev 4037) +++ trunk/libraries/yason/encode.lisp 2008-11-12 11:00:34 UTC (rev 4038) @@ -1,3 +1,10 @@ +;; This file is part of yason, a Common Lisp JSON parser/encoder +;; +;; Copyright (c) 2008 Hans H??bner +;; All rights reserved. +;; +;; Please see the file LICENSE in the distribution. + (in-package :yason) (defvar *json-output*) Modified: trunk/libraries/yason/package.lisp =================================================================== --- trunk/libraries/yason/package.lisp 2008-11-12 10:27:20 UTC (rev 4037) +++ trunk/libraries/yason/package.lisp 2008-11-12 11:00:34 UTC (rev 4038) @@ -1,3 +1,10 @@ +;; This file is part of yason, a Common Lisp JSON parser/encoder +;; +;; Copyright (c) 2008 Hans H??bner +;; All rights reserved. +;; +;; Please see the file LICENSE in the distribution. + (defpackage :yason (:use :cl) @@ -5,7 +12,6 @@ (:nicknames :json) (:export - ;; Parser #:parse #:*parse-object-key-fn* Modified: trunk/libraries/yason/parse.lisp =================================================================== --- trunk/libraries/yason/parse.lisp 2008-11-12 10:27:20 UTC (rev 4037) +++ trunk/libraries/yason/parse.lisp 2008-11-12 11:00:34 UTC (rev 4038) @@ -1,3 +1,10 @@ +;; This file is part of yason, a Common Lisp JSON parser/encoder +;; +;; Copyright (c) 2008 Hans H??bner +;; All rights reserved. +;; +;; Please see the file LICENSE in the distribution. + (in-package :yason) (defconstant +default-string-length+ 20 Modified: trunk/libraries/yason/yason.asd =================================================================== --- trunk/libraries/yason/yason.asd 2008-11-12 10:27:20 UTC (rev 4037) +++ trunk/libraries/yason/yason.asd 2008-11-12 11:00:34 UTC (rev 4038) @@ -1,3 +1,10 @@ +;; This file is part of yason, a Common Lisp JSON parser/encoder +;; +;; Copyright (c) 2008 Hans H?bner +;; All rights reserved. +;; +;; Please see the file LICENSE in the distribution. + ;;;; -*- Mode: LISP -*- (in-package :cl-user) @@ -8,9 +15,9 @@ (in-package :yason.system) (defsystem :yason - :name "YASON" + :name "yason" :author "Hans Huebner " - :version "0" + :version "1" :maintainer "Hans Huebner " :licence "BSD" :description "JSON parser/encoder" From bknr at bknr.net Wed Nov 12 11:00:57 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 12:00:57 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/package.lisp Message-ID: Revision: 4039 Author: hans URL: http://bknr.net/trac/changeset/4039 fix indentation U trunk/libraries/yason/package.lisp Modified: trunk/libraries/yason/package.lisp =================================================================== --- trunk/libraries/yason/package.lisp 2008-11-12 11:00:34 UTC (rev 4038) +++ trunk/libraries/yason/package.lisp 2008-11-12 11:00:57 UTC (rev 4039) @@ -13,8 +13,8 @@ (:export ;; Parser - #:parse - #:*parse-object-key-fn* + #:parse + #:*parse-object-key-fn* ;; Basic encoder interface #:encode From bknr at bknr.net Wed Nov 12 11:19:32 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 12 Nov 2008 12:19:32 +0100 Subject: [bknr-cvs] hans changed trunk/ Message-ID: Revision: 4040 Author: hans URL: http://bknr.net/trac/changeset/4040 Fixes to make Quickhoney work with yason. Yason fixes to bknr-web. Make clixdoc compile. U trunk/bknr/web/src/web/web-utils.lisp U trunk/libraries/clixdoc/clixdoc.asd U trunk/libraries/clixdoc/edi-docutil.lisp U trunk/libraries/clixdoc/make-doc.lisp U trunk/projects/quickhoney/src/handlers.lisp Modified: trunk/bknr/web/src/web/web-utils.lisp =================================================================== --- trunk/bknr/web/src/web/web-utils.lisp 2008-11-12 11:00:57 UTC (rev 4039) +++ trunk/bknr/web/src/web/web-utils.lisp 2008-11-12 11:19:32 UTC (rev 4040) @@ -240,6 +240,6 @@ (defmacro with-json-response (() &body body) `(with-http-response (:content-type "application/json") - (with-output-to-string () + (json:with-output-to-string* () (json:with-object () , at body)))) \ No newline at end of file Modified: trunk/libraries/clixdoc/clixdoc.asd =================================================================== --- trunk/libraries/clixdoc/clixdoc.asd 2008-11-12 11:00:57 UTC (rev 4039) +++ trunk/libraries/clixdoc/clixdoc.asd 2008-11-12 11:19:32 UTC (rev 4040) @@ -3,7 +3,6 @@ :depends-on (:cxml :swank :cl-ppcre) :serial t :components ((:file "packages") - (:file "specials") (:file "edi-docutil") (:file "check-doc") (:file "make-doc"))) \ No newline at end of file Modified: trunk/libraries/clixdoc/edi-docutil.lisp =================================================================== --- trunk/libraries/clixdoc/edi-docutil.lisp 2008-11-12 11:00:57 UTC (rev 4039) +++ trunk/libraries/clixdoc/edi-docutil.lisp 2008-11-12 11:19:32 UTC (rev 4040) @@ -32,6 +32,11 @@ (in-package "CLIXDOC") +(defvar *maybe-skip-methods-p* nil + "This is the default value for the :MAYBE-SKIP-METHODS-P keyword +argument of CREATE-TEMPLATE and its initial value is NIL. It is also +used internally.") + ;;; For the purpose of this file, an "entry" is a list of four or five ;;; symbols - a name, a keyword for the kind of the entry, a lambda ;;; list (for functions and macros), a documentation string, and Modified: trunk/libraries/clixdoc/make-doc.lisp =================================================================== --- trunk/libraries/clixdoc/make-doc.lisp 2008-11-12 11:00:57 UTC (rev 4039) +++ trunk/libraries/clixdoc/make-doc.lisp 2008-11-12 11:19:32 UTC (rev 4040) @@ -1,7 +1,8 @@ (in-package "CLIXDOC") +#+(or) (defun make-doc (package &optional (output *standard-output*)) (with-xml-output (make-character-stream-sink *output*) (with-namespace ("clix" "http://bknr.net/clixdoc") - (with-namespace ( \ No newline at end of file + (with-namespace ())))) \ No newline at end of file Modified: trunk/projects/quickhoney/src/handlers.lisp =================================================================== --- trunk/projects/quickhoney/src/handlers.lisp 2008-11-12 11:00:57 UTC (rev 4039) +++ trunk/projects/quickhoney/src/handlers.lisp 2008-11-12 11:19:32 UTC (rev 4040) @@ -45,34 +45,39 @@ () (:default-initargs :query-function #'store-image-with-name)) +(defmethod json:encode ((object symbol) stream) + (json:encode (string-downcase (symbol-name object)) + stream)) + (defmethod image-to-json ((image quickhoney-image)) (json:with-object () - (encode-object-element "class" (string-downcase (cl-ppcre:regex-replace "^QUICKHONEY-" - (symbol-name (class-name (class-of image))) - ""))) - (encode-object-element "name" (store-image-name image)) + (json:encode-object-element "class" + (string-downcase (cl-ppcre:regex-replace "^QUICKHONEY-" + (symbol-name (class-name (class-of image))) + ""))) + (json:encode-object-element "name" (store-image-name image)) (when (quickhoney-image-category image) - (encode-object-element "category" (quickhoney-image-category image)) + (json:encode-object-element "category" (quickhoney-image-category image)) (when (quickhoney-image-subcategory image) - (encode-object-element "subcategory" (quickhoney-image-subcategory image)))) - (encode-object-element "id" (store-object-id image)) - (encode-object-element "type" (image-content-type (blob-mime-type image))) - (encode-object-element "width" (store-image-width image)) - (encode-object-element "height" (store-image-height image)) - (encode-object-element "client" (or (quickhoney-image-client image) "")) + (json:encode-object-element "subcategory" (quickhoney-image-subcategory image)))) + (json:encode-object-element "id" (store-object-id image)) + (json:encode-object-element "type" (image-content-type (blob-mime-type image))) + (json:encode-object-element "width" (store-image-width image)) + (json:encode-object-element "height" (store-image-height image)) + (json:encode-object-element "client" (or (quickhoney-image-client image) "")) (when (typep image 'quickhoney-animation-image) - (encode-object-element "animation_type" + (json:encode-object-element "animation_type" (image-content-type (blob-mime-type (quickhoney-animation-image-animation image))))) (when (quickhoney-image-spider-keywords image) - (encode-object-element "spider_keywords" (quickhoney-image-spider-keywords image))) - (with-object-element ("keywords") + (json:encode-object-element "spider_keywords" (quickhoney-image-spider-keywords image))) + (json:with-object-element ("keywords") (json:with-object () (dolist (keyword (intersection *editable-keywords* (store-image-keywords image))) - (encode-object-element (string-downcase (symbol-name keyword)) t)))))) + (json:encode-object-element (string-downcase (symbol-name keyword)) t)))))) (defmethod handle-object ((handler json-image-info-handler) image) - (json:with-response () - (with-object-element ("image") + (with-json-response () + (json:with-object-element ("image") (image-to-json image)))) (defclass json-image-query-handler (object-handler quickhoney-image-dependent-handler) @@ -91,14 +96,14 @@ (json:with-array () (dolist (row (page-rows page)) (json:with-array () - (encode-array-element (row-cell-width row)) - (encode-array-element (row-cell-height row)) + (json:encode-array-element (row-cell-width row)) + (json:encode-array-element (row-cell-height row)) (dolist (image (row-images row)) (image-to-json image)))))))) (defmethod handle-object ((handler json-image-query-handler) images) - (json:with-response () - (with-object-element ("queryResult") + (with-json-response () + (json:with-object-element ("queryResult") (with-query-params (layout) (layout-to-json (make-instance (case (make-keyword-from-string layout) (:smallworld 'quickhoney-name-layout) @@ -109,40 +114,40 @@ ()) (defmethod handle ((handler json-login-handler)) - (json:with-response () - (encode-object-element "admin" (admin-p (bknr-session-user))) + (with-json-response () + (json:encode-object-element "admin" (admin-p (bknr-session-user))) (when (and (anonymous-p (bknr-session-user)) (query-param "__username")) - (encode-object-element "login_failed" t)) - (encode-object-element "login" (user-login (bknr-session-user))))) + (json:encode-object-element "login_failed" t)) + (json:encode-object-element "login" (user-login (bknr-session-user))))) (defclass json-logout-handler (page-handler) ()) (defmethod handle ((handler json-logout-handler)) (setf (session-value 'bknr-session) nil) - (json:with-response () - (encode-object-element "logged_out" t))) + (with-json-response () + (json:encode-object-element "logged_out" t))) (defclass json-clients-handler (page-handler) ()) (defmethod handle ((handler json-clients-handler)) - (json:with-response () - (with-object-element ("clients") + (with-json-response () + (json:with-object-element ("clients") (json:with-array () (dolist (client (sort (remove "" (all-clients) :test #'equal) #'string-lessp)) - (encode-array-element client)))))) + (json:encode-array-element client)))))) (defclass json-edit-image-handler (admin-only-handler edit-object-handler) () (:default-initargs :object-class 'quickhoney-image)) (defmethod handle-object-form ((handler json-edit-image-handler) action image) - (json:with-response () - (encode-object-element "result" "error") - (encode-object-element "message" (format nil "; invalid action ~A or invalid object ~A~%" action image)))) + (with-json-response () + (json:encode-object-element "result" "error") + (json:encode-object-element "message" (format nil "; invalid action ~A or invalid object ~A~%" action image)))) (defun image-keywords-from-request-parameters () (let (retval) @@ -159,14 +164,14 @@ (store-image-keywords image) (append (set-difference (store-image-keywords image) *editable-keywords*) (image-keywords-from-request-parameters))))) (setf *last-image-upload-timestamp* (get-universal-time)) - (json:with-response () - (encode-object-element "result" "edited"))) + (with-json-response () + (json:encode-object-element "result" "edited"))) (defmethod handle-object-form ((handler json-edit-image-handler) (action (eql :delete)) (image quickhoney-image)) (delete-object image) (setf *last-image-upload-timestamp* (get-universal-time)) - (json:with-response () - (encode-object-element "result" "deleted"))) + (with-json-response () + (json:encode-object-element "result" "deleted"))) (defclass json-edit-news-item-handler (json-edit-image-handler) () @@ -178,8 +183,8 @@ (setf (quickhoney-news-item-title item) title (quickhoney-news-item-text item) text))) (setf *last-image-upload-timestamp* (get-universal-time)) - (json:with-response () - (encode-object-element "result" "edited"))) + (with-json-response () + (json:encode-object-element "result" "edited"))) (defclass digg-image-handler (object-handler) () @@ -264,13 +269,13 @@ collect image))))) (defmethod handle ((handler json-buttons-handler)) - (json:with-response () - (with-object-element ("buttons") + (with-json-response () + (json:with-object-element ("buttons") (json:with-object () (loop for (category subcategories-string) on (decoded-handler-path handler) by #'cddr do (dolist (subcategory (split "," subcategories-string)) - (with-object-element ((format nil "~(~A/~A~)" category subcategory)) + (json:with-object-element ((format nil "~(~A/~A~)" category subcategory)) (json:with-array () ;; For each subcategory, an array of buttons is ;; generated. The first element of the array is @@ -286,10 +291,10 @@ (or (preproduced-buttons category subcategory) (newest-images category subcategory) (warn "No images for ~A ~A found" category subcategory)) - (encode-array-element type) + (json:encode-array-element type) (dolist (image (or images (list (store-image-with-name "button-dummy")))) - (encode-array-element (store-object-id image))))))))))))) + (json:encode-array-element (store-object-id image))))))))))))) (defclass upload-image-handler (admin-only-handler prefix-handler) ()) @@ -536,30 +541,30 @@ ; do nothing ) (:method :before ((item store-object)) - (encode-object-element "id" (store-object-id item))) + (json:encode-object-element "id" (store-object-id item))) (:method :before ((image quickhoney-image)) (when (owned-object-owner image) - (encode-object-element "owner" (user-login (owned-object-owner image)))) - (encode-object-element "date" (format-date-time (blob-timestamp image) :vms-style t :show-time nil)) - (encode-object-element "name" (store-image-name image))) + (json:encode-object-element "owner" (user-login (owned-object-owner image)))) + (json:encode-object-element "date" (format-date-time (blob-timestamp image) :vms-style t :show-time nil)) + (json:encode-object-element "name" (store-image-name image))) (:method ((image quickhoney-image)) - (encode-object-element "type" "upload") - (encode-object-element "category" (quickhoney-image-category image)) - (encode-object-element "subcategory" (quickhoney-image-subcategory image)) - (with-object-element ("keywords") + (json:encode-object-element "type" "upload") + (json:encode-object-element "category" (quickhoney-image-category image)) + (json:encode-object-element "subcategory" (quickhoney-image-subcategory image)) + (json:with-object-element ("keywords") (json:with-array () (dolist (keyword (store-image-keywords image)) - (encode-array-element (string-downcase (symbol-name keyword))))))) + (json:encode-array-element (string-downcase (symbol-name keyword))))))) (:method ((item quickhoney-news-item)) - (encode-object-element "type" "news") - (encode-object-element "title" (quickhoney-news-item-title item)) - (encode-object-element "text" (quickhoney-news-item-text item)) - (encode-object-element "width" (store-image-width item)) - (encode-object-element "height" (store-image-height item)))) + (json:encode-object-element "type" "news") + (json:encode-object-element "title" (quickhoney-news-item-title item)) + (json:encode-object-element "text" (quickhoney-news-item-text item)) + (json:encode-object-element "width" (store-image-width item)) + (json:encode-object-element "height" (store-image-height item)))) (defun json-encode-news-items (items) - (json:with-response () - (with-object-element ("items") + (with-json-response () + (json:with-object-element ("items") (json:with-array () (dolist (item items) (json:with-object () @@ -576,8 +581,8 @@ (:default-initargs :object-class 'rss-channel :query-function #'find-rss-channel)) (defmethod handle-object ((handler json-news-archive-handler) (channel rss-channel)) - (json:with-response () - (with-object-element ("months") + (with-json-response () + (json:with-object-element ("months") (json:with-array () (dolist (month (sort (rss-channel-archived-months channel) (lambda (a b) @@ -585,8 +590,8 @@ (> (second a) (second b)) (> (first a) (first b)))))) (json:with-array () - (encode-array-element (first month)) - (encode-array-element (second month)))))))) + (json:encode-array-element (first month)) + (json:encode-array-element (second month)))))))) (defclass shutdown-handler (admin-only-handler page-handler) ()) From bknr at bknr.net Thu Nov 13 22:23:07 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 13 Nov 2008 23:23:07 +0100 Subject: [bknr-cvs] hans changed trunk/thirdparty/cffi/uffi-compat/uffi Message-ID: Revision: 4041 Author: hans URL: http://bknr.net/trac/changeset/4041 rename file as it confuses buildbot (?) A trunk/thirdparty/cffi/uffi-compat/uffi-compat.asd D trunk/thirdparty/cffi/uffi-compat/uffi.asd Copied: trunk/thirdparty/cffi/uffi-compat/uffi-compat.asd (from rev 4030, trunk/thirdparty/cffi/uffi-compat/uffi.asd) =================================================================== --- trunk/thirdparty/cffi/uffi-compat/uffi-compat.asd (rev 0) +++ trunk/thirdparty/cffi/uffi-compat/uffi-compat.asd 2008-11-13 22:23:06 UTC (rev 4041) @@ -0,0 +1,3 @@ +;;;; uffi.asd -*- Mode: Lisp -*- + +(defsystem uffi :depends-on (cffi-uffi-compat)) Deleted: trunk/thirdparty/cffi/uffi-compat/uffi.asd =================================================================== --- trunk/thirdparty/cffi/uffi-compat/uffi.asd 2008-11-12 11:19:32 UTC (rev 4040) +++ trunk/thirdparty/cffi/uffi-compat/uffi.asd 2008-11-13 22:23:06 UTC (rev 4041) @@ -1,3 +0,0 @@ -;;;; uffi.asd -*- Mode: Lisp -*- - -(defsystem uffi :depends-on (cffi-uffi-compat)) From bknr at bknr.net Thu Nov 13 23:02:28 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 00:02:28 +0100 Subject: [bknr-cvs] hans changed trunk/build.lisp Message-ID: Revision: 4042 Author: hans URL: http://bknr.net/trac/changeset/4042 Remove cl-json from buildbot build list U trunk/build.lisp Modified: trunk/build.lisp =================================================================== --- trunk/build.lisp 2008-11-13 22:23:06 UTC (rev 4041) +++ trunk/build.lisp 2008-11-13 23:02:28 UTC (rev 4042) @@ -77,8 +77,6 @@ :html-match.test :cl-gd-test :cl-ppcre-test - :cl-json - :cl-json.test :kmrcl-tests :flexi-streams :flexi-streams-test From bknr at bknr.net Thu Nov 13 23:26:06 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 00:26:06 +0100 Subject: [bknr-cvs] hans changed trunk/build.lisp Message-ID: Revision: 4043 Author: hans URL: http://bknr.net/trac/changeset/4043 Remove cl-json test invocation. U trunk/build.lisp Modified: trunk/build.lisp =================================================================== --- trunk/build.lisp 2008-11-13 23:02:28 UTC (rev 4042) +++ trunk/build.lisp 2008-11-13 23:26:06 UTC (rev 4043) @@ -204,6 +204,5 @@ (progn #+(or) (fiveam-run-no-failures-p :it.bese.FiveAM) (warn "skipping :it.bese.FiveAM tests") t) - (fiveam-run-no-failures-p 'json-test::json) )) From bknr at bknr.net Fri Nov 14 08:11:17 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 09:11:17 +0100 Subject: [bknr-cvs] hans changed trunk/build.lisp Message-ID: Revision: 4044 Author: hans URL: http://bknr.net/trac/changeset/4044 add yason system to list of loaded systems in build.lisp U trunk/build.lisp Modified: trunk/build.lisp =================================================================== --- trunk/build.lisp 2008-11-13 23:26:06 UTC (rev 4043) +++ trunk/build.lisp 2008-11-14 08:11:16 UTC (rev 4044) @@ -75,6 +75,7 @@ :bknr.skip-list.test :html-match :html-match.test + :yason :cl-gd-test :cl-ppcre-test :kmrcl-tests From bknr at bknr.net Fri Nov 14 08:14:17 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 09:14:17 +0100 Subject: [bknr-cvs] hans changed trunk/projects/quickhoney/src/shop.lisp Message-ID: Revision: 4045 Author: hans URL: http://bknr.net/trac/changeset/4045 add ccl support for shop test U trunk/projects/quickhoney/src/shop.lisp Modified: trunk/projects/quickhoney/src/shop.lisp =================================================================== --- trunk/projects/quickhoney/src/shop.lisp 2008-11-14 08:11:16 UTC (rev 4044) +++ trunk/projects/quickhoney/src/shop.lisp 2008-11-14 08:14:17 UTC (rev 4045) @@ -255,6 +255,14 @@ ;;; TESTING +(defun getpid () + #+openmcl + (ccl::getpid) + #+sbcl + (sb-posix:getpid) + #+(not (or sbcl openmcl)) + (random 10000)) + (defmacro with-temporary-directory ((pathname) &body body) `(let ((,pathname (pathname (format nil "/tmp/store-test-~A/" (sb-posix:getpid))))) (asdf:run-shell-command "rm -rf ~A" ,pathname) From bknr at bknr.net Fri Nov 14 12:28:00 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 13:28:00 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/m2/mail-generator.lisp Message-ID: Revision: 4046 Author: hans URL: http://bknr.net/trac/changeset/4046 Correct error in data type assumption. U trunk/projects/bos/m2/mail-generator.lisp Modified: trunk/projects/bos/m2/mail-generator.lisp =================================================================== --- trunk/projects/bos/m2/mail-generator.lisp 2008-11-14 08:14:17 UTC (rev 4045) +++ trunk/projects/bos/m2/mail-generator.lisp 2008-11-14 12:28:00 UTC (rev 4046) @@ -290,6 +290,7 @@ (defun mail-backoffice-sponsor-data (contract numsqm country email name address language request-params) (let* ((contract-id (store-object-id contract)) + (numsqm (if (stringp numsqm) (parse-integer numsqm) numsqm)) (parts (list (make-html-part (format nil " @@ -324,7 +325,7 @@ (store-object-id contract) (store-object-id (contract-sponsor contract)) numsqm - (* 3 (parse-integer numsqm))) + (* 3 numsqm)) :name name :address address :email email))))) From bknr at bknr.net Fri Nov 14 12:37:03 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 13:37:03 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/ Message-ID: Revision: 4047 Author: hans URL: http://bknr.net/trac/changeset/4047 checkpoint work U trunk/libraries/clixdoc/clixdoc.asd U trunk/libraries/clixdoc/clixdoc.xsl U trunk/libraries/clixdoc/edi-docutil.lisp U trunk/libraries/clixdoc/make-doc.lisp A trunk/libraries/yason/doc.xml U trunk/libraries/yason/encode.lisp U trunk/libraries/yason/package.lisp U trunk/libraries/yason/parse.lisp Change set too large, please see URL above From bknr at bknr.net Fri Nov 14 13:05:00 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 14 Nov 2008 14:05:00 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/ Message-ID: Revision: 4048 Author: hans URL: http://bknr.net/trac/changeset/4048 Documentation updates. U trunk/libraries/clixdoc/clixdoc.xsl U trunk/libraries/yason/doc.xml Modified: trunk/libraries/clixdoc/clixdoc.xsl =================================================================== --- trunk/libraries/clixdoc/clixdoc.xsl 2008-11-14 12:37:03 UTC (rev 4047) +++ trunk/libraries/clixdoc/clixdoc.xsl 2008-11-14 13:05:00 UTC (rev 4048) @@ -45,7 +45,7 @@ <xsl:value-of select="clix:title"/> - + +

YASON - A JSON encoder/decoder for Common Lisp

+ + + +

Abstract

+
+ YASON is a Common Lisp library for encoding and decoding data in + the JSON interchange format. + JSON is used in AJAX applications as a lightweight alternative + to XML. YASON has the sole purpose of encoding and decoding + data and does not impose any object model on the Common Lisp + application that uses it. +
+ +

Contents

+
    +
  1. Introduction
  2. +
  3. Installation
  4. +
  5. Mapping between JSON and CL datatypes
  6. +
  7. +Parsing JSON data
    1. Parser dictionary
    +
  8. +
  9. +Encoding JSON data
      +
    1. Encoding a JSON DOM
    2. +
    3. Encoding JSON in streaming mode
    4. +
    5. Application specific encoders
    6. +
    +
  10. +
  11. Symbol index
  12. +
  13. License
  14. +
  15. Acknowledgements
  16. +
+ +

Introduction

+ JSON is an established alternative + to XML as a data interchange format for web applications. YASON + implements reading and writing of JSON formatted data in Common + Lisp. It does not attempt to provide a mapping between CLOS + objects and YASON, but can be used to implement such mappings. + + +

Installation

+

+ YASON has its permanent home + at common-lisp.net. + It can be obtained either by downloading + the release + tarball or by checking out the current development version + from its subversion repository: +

svn co svn://bknr.net/svn/trunk/libraries/yason/
+

+

+ YASON is written in ANSI Common Lisp and does not depend on + other libraries. +

+

+ YASON lives in the :yason package and creates a package nickname + :json. Applications will not normally :use this + package, but rather use qualified names to access YASON's + symbols. For that reason, YASON's symbols do not contain the + string "JSON" themselves. See below for usage samples. +

+ + +

Mapping between JSON and CL datatypes

+ By default, YASON performs the following mappings between JSON and + CL datatypes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JSON

datatype
CL

datatype
Notes
objecthash-table

:test??#'equal
+ Keys are strings by default, + see *parse-object-key-fn* +
arraylist + Can be changed to read to vectors, + see *parse-json-arrays-as-vectors* +
stringstring + JSON escape characters are recognized upon reading. + Upon writing, known escape characters are used, but + non-ASCII Unicode characters are written as is. +
numbernumber + Parsed with READ, printed with PRINC. This is not a + faithful implementation of the specification. +
truetCan be changed to read as TRUE, see *parse-json-booleans-as-symbols*
falsenilCan be changed to read as FALSE, see *parse-json-booleans-as-symbols*
nullnil
+ + +

Parsing JSON data

+

+ JSON data is always completely parsed into an equivalent + in-memory representation. Upon reading, some translations are + performed by default to make it easier for the Common Lisp + program to work with the data; see mapping + for details. If desired, the parser can be configured to + preserve the full semantics of the JSON data read. +

+ + For example + +
CL-USER> (defvar *json-string* "[{\"foo\":1,\"bar\":[7,8,9]},2,3,4,[5,6,7],true,null]")
+*JSON-STRING*
+CL-USER> (let* ((result (json:parse *json-string*)))
+           (print result)
+           (alexandria:hash-table-plist (first result)))
+
+(#<HASH-TABLE :TEST EQUAL :COUNT 2 {5A4420F1}> 2 3 4 (5 6 7) T NIL) 
+("bar" (7 8 9) "foo" 1)
+CL-USER> (defun maybe-convert-to-keyword (js-name)
+           (or (find-symbol (string-upcase js-name) :keyword)
+               js-name))
+MAYBE-CONVERT-TO-KEYWORD
+CL-USER> :FOO ; intern the :FOO keyword
+:FOO
+CL-USER> (let* ((json:*parse-json-arrays-as-vectors* t)
+                (json:*parse-json-booleans-as-symbols* t)
+                (json:*parse-object-key-fn* #'maybe-convert-to-string)
+                (result (json:parse *json-string*)))
+           (print result)
+           (alexandria:hash-table-plist (aref result 0)))
+
+#(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59B4EAD1}> 2 3 4 #(5 6 7) YASON:TRUE NIL) 
+("bar" #(7 8 9) :FOO 1)
+ +

+ The second example modifies the parser's behaviour so that JSON + arrays are read as CL vectors, JSON booleans will be read as the + symbols TRUE and FALSE and JSON object keys will be looked up in + the :keyword package. Interning strings coming from an + external source is not recommended practice. +

+ +

Parser dictionary

+

[Generic function]
parse input + => + object

+ Parse input, which needs to be a string + or a stream, as JSON. Returns the lisp representation of the + JSON structure parsed. +

+ +

+ [Special variable]
*parse-json-arrays-as-vectors*

+ If set to a true value, JSON arrays will be parsed as vectors, + not as lists. +

+ +

+ [Special variable]
*parse-json-booleans-as-symbols*

+ If set to a true value, JSON booleans will be read as the + symbols TRUE and FALSE, not as T and NIL, respectively. +

+ +

+ [Special variable]
*parse-object-key-fn*

+ Function to call to convert a key string in a JSON array to a + key in the CL hash produced. +

+ + + +

Encoding JSON data

+ YASON provides for two distinct modes to encode JSON data: + Applications can either create an in memory representation of the + data to be serialized, then have YASON convert it to JSON in one + go, or they can use a set of macros to serialze the JSON data + element-by-element, thereby having fine grained control over the + layout of the generated data. + +

Encoding a JSON DOM

+

+ In this mode, an in-memory structure is encoded in JSON format. + The structure must consist of objects that are serializable + using the ENCODE function. YASON defines a + number of encoders for standard data types + (see MAPPING), but the application can + define additional methods, e.g. for encoding CLOS objects. +

+ For example: +
CL-USER> (json:encode 
+          (list (alexandria:plist-hash-table
+                 '("foo" 1 "bar" (7 8 9))
+                 :test #'equal)
+                2 3 4
+                '(5 6 7)
+                t nil)
+          *standard-output*)
+[{"foo":1,"bar":[7,8,9]},2,3,4,[5,6,7],true,null]
+(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59942D21}> 2 3 4 (5 6 7) T NIL)
+ +

DOM encoder dictionary

+

[Generic function]
encode object &optional stream + => + object

+ Encode object + to stream in JSON format. May be + specialized by applications to perform specific + rendering. stream defaults to + *STANDARD-OUTPUT*. +

+ + + +

Encoding JSON in streaming mode

+

+ In this mode, the JSON structure is generated in a stream. + The application makes explicit calls to the encoding library + in order to generate the JSON structure. It provides for more + control over the generated output, and can be used to generate + arbitary JSON without requiring that there exists a directly + matching Lisp datastructure. The streaming API uses + the encode function, so it is possible to + intermix the two. See app-encoders for + an example. +

+ For example: +
CL-USER> (json:with-output (*standard-output*)
+           (json:with-array ()
+             (dotimes (i 3)
+               (json:encode-array-element i))))
+[0,1,2]
+NIL
+CL-USER> (json:with-output (*standard-output*)
+           (json:with-object ()
+             (json:encode-object-element "hello" "hu hu")
+             (json:with-object-element ("harr")
+               (json:with-array ()
+                 (dotimes (i 3)
+                   (json:encode-array-element i))))))
+{"hello":"hu hu","harr":[0,1,2]}
+NIL
+ +

Streaming encoder dictionary

+

[Macro]
with-output (stream) &body body + => + result*

+ Set up a JSON streaming encoder context + on stream, then + evaluate body. +

+ +

[Macro]
with-output-to-string* () &body body + => + result*

+ Set up a JSON streaming encoder context, then + evaluate body. Return a string with the + generated JSON output. +

+ +

+ [Condition type]
no-json-output-context

+ This condition is signalled when one of the stream encoding + function is used outside the dynamic context of a + WITH-OUTPUT or + WITH-OUTPUT-TO-STRING* body. +

+ +

[Macro]
with-array () &body body + => + result*

+ Open a JSON array, then run body. Inside + the body, ENCODE-ARRAY-ELEMENT must be + called to encode elements to the opened array. Must be called + within an existing JSON encoder context, see + WITH-OUTPUT and + WITH-OUTPUT-TO-STRING*. +

+ +

[Function]
encode-array-element object + => + object

+ Encode object as next array element to + the last JSON array opened + with WITH-ARRAY in the dynamic + context. object is encoded using the + ENCODE generic function, so it must be of + a type for which an ENCODE method is + defined. +

+ +

[Macro]
with-object () &body body + => + result*

+ Open a JSON object, then run body. Inside the body, + ENCODE-OBJECT-ELEMENT + or WITH-OBJECT-ELEMENT must be called to + encode elements to the object. Must be called within an + existing JSON encoder context, + see WITH-OUTPUT + and WITH-OUTPUT-TO-STRING*. +

+ +

[Macro]
with-object-element (key) &body body + => + result*

+ Open a new encoding context to encode a JSON object + element. key is the key of the element. + The value will be whatever body + serializes to the current JSON output context using one of the + stream encoding functions. This can be used to stream out + nested object structures. +

+ +

[Function]
encode-object-element key value + => + value

+ Encode key and value + as object element to the last JSON object opened + with WITH-OBJECT in the dynamic + context. key + and value are encoded using + the ENCODE generic function, so they both + must be of a type for which an ENCODE + method is defined. +

+ + + +

Application specific encoders

+ + Suppose your application uses structs to represent its data, and + you want to encode such structs using JSON in order to send it + to a client application. Suppose further that your structs also + include internal information that you do not want to send. Here + is some code that illustrates how one could implement a + serialization function: + +
CL-USER> (defstruct user name age password)
+USER
+CL-USER> (defmethod json:encode ((user user) &optional (stream *standard-output*))
+           (json:with-output (stream)
+             (json:with-object ()
+               (json:encode-object-element "name" (user-name user))
+               (json:encode-object-element "age" (user-age user)))))
+#<STANDARD-METHOD YASON:ENCODE (USER) {5B40A591}>
+CL-USER> (json:encode (list (make-user :name "horst" :age 27 :password "puppy")
+                            (make-user :name "uschi" :age 28 :password "kitten")))
+[{"name":"horst","age":27},{"name":"uschi","age":28}]
+(#S(USER :NAME "horst" :AGE 27 :PASSWORD "puppy")
+ #S(USER :NAME "uschi" :AGE 28 :PASSWORD "kitten"))
+ + As you can see, the streaming API and the DOM encoder can be + used together. ENCODE invokes itself + recursively, so any application defined method will be called + while encoding in-memory objects as appropriate. + + + + +

Symbol index

+ + + +

License

+
Copyright (c) 2008 Hans H??bner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  - Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+  - Neither the name BKNR nor the names of its contributors may be
+    used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ + +

Acknowledgements

+ Thanks go to Edi Weitz for being a great inspiration. This + documentation as been generated with a hacked-up version of + his DOCUMENTATION-TEMPLATE + software. + + + From bknr at bknr.net Sat Nov 15 10:10:54 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 11:10:54 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/ Message-ID: Revision: 4049 Author: hans URL: http://bknr.net/trac/changeset/4049 release 0.1 U trunk/libraries/yason/doc.xml U trunk/libraries/yason/encode.lisp U trunk/libraries/yason/package.lisp U trunk/libraries/yason/parse.lisp Change set too large, please see URL above From bknr at bknr.net Sat Nov 15 10:40:14 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 11:40:14 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/doc.xml Message-ID: Revision: 4055 Author: hans URL: http://bknr.net/trac/changeset/4055 add author email address U trunk/libraries/yason/doc.xml Modified: trunk/libraries/yason/doc.xml =================================================================== --- trunk/libraries/yason/doc.xml 2008-11-15 10:37:50 UTC (rev 4054) +++ trunk/libraries/yason/doc.xml 2008-11-15 10:40:14 UTC (rev 4055) @@ -43,7 +43,9 @@

You may also check out the current development version from its subversion repository: -

svn co svn://bknr.net/svn/trunk/libraries/yason/
+
svn co svn://bknr.net/svn/trunk/libraries/yason/
If + you have suggestions regarding YASON, please email me + at hans.huebner at gmail.com.

YASON is written in ANSI Common Lisp and does not depend on From bknr at bknr.net Sat Nov 15 10:42:29 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 11:42:29 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/index.html Message-ID: Revision: 4056 Author: hans URL: http://bknr.net/trac/changeset/4056 regenerated HTML documentation U trunk/libraries/yason/index.html Modified: trunk/libraries/yason/index.html =================================================================== --- trunk/libraries/yason/index.html 2008-11-15 10:40:14 UTC (rev 4055) +++ trunk/libraries/yason/index.html 2008-11-15 10:42:29 UTC (rev 4056) @@ -71,14 +71,16 @@

YASON has its permanent home at common-lisp.net. - It can be obtained either by downloading + It can be obtained by downloading the release - tarball. The current release is 0.1. + tarball. The current release is 0.1.

You may also check out the current development version from its subversion repository: -

svn co svn://bknr.net/svn/trunk/libraries/yason/
+
svn co svn://bknr.net/svn/trunk/libraries/yason/
If + you have suggestions regarding YASON, please email me + at hans.huebner at gmail.com.

YASON is written in ANSI Common Lisp and does not depend on From bknr at bknr.net Sat Nov 15 11:05:41 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:05:41 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/doc.xml Message-ID: Revision: 4057 Author: hans URL: http://bknr.net/trac/changeset/4057 Refer to CL-JSON in the documentation. U trunk/libraries/yason/doc.xml Modified: trunk/libraries/yason/doc.xml =================================================================== --- trunk/libraries/yason/doc.xml 2008-11-15 10:42:29 UTC (rev 4056) +++ trunk/libraries/yason/doc.xml 2008-11-15 11:05:41 UTC (rev 4057) @@ -25,11 +25,21 @@ - JSON is an established alternative - to XML as a data interchange format for web applications. YASON - implements reading and writing of JSON formatted data in Common - Lisp. It does not attempt to provide a mapping between CLOS - objects and YASON, but can be used to implement such mappings. +

+ JSON is an established alternative + to XML as a data interchange format for web applications. YASON + implements reading and writing of JSON formatted data in Common + Lisp. It does not attempt to provide a mapping between CLOS + objects and YASON, but can be used to implement such mappings. +

+

+ CL-JSON is + another Common Lisp package that can be used to work with JSON + encoded data. It takes a more integrated approach, providing + for library internal mappings between JSON objects and CLOS + objects. YASON was created as a lightweight, documented + alternative with a minimalistic approach and extensibilty. +

From bknr at bknr.net Sat Nov 15 11:12:43 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:12:43 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4058 Author: hans URL: http://bknr.net/trac/changeset/4058 release yason-0.1 A releases/yason/0.1/ Copied: releases/yason/0.1 (from rev 4057, trunk/libraries/yason) From bknr at bknr.net Sat Nov 15 11:13:52 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:13:52 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/yason/ Message-ID: Revision: 4059 Author: hans URL: http://bknr.net/trac/changeset/4059 release yason-0.1 A releases/yason/0.1/yason/ Copied: releases/yason/0.1/yason (from rev 4058, trunk/libraries/yason) From bknr at bknr.net Sat Nov 15 11:16:07 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:16:07 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4060 Author: hans URL: http://bknr.net/trac/changeset/4060 remove previous yason-0.1 from repository D releases/yason/0.1/ From bknr at bknr.net Sat Nov 15 11:16:16 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:16:16 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4061 Author: hans URL: http://bknr.net/trac/changeset/4061 release yason-0.1 A releases/yason/0.1/ Copied: releases/yason/0.1 (from rev 4060, trunk/libraries/yason) From bknr at bknr.net Sat Nov 15 11:20:52 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:20:52 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4062 Author: hans URL: http://bknr.net/trac/changeset/4062 remove previous yason-0.1 from repository D releases/yason/0.1/ From bknr at bknr.net Sat Nov 15 11:21:01 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:21:01 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4063 Author: hans URL: http://bknr.net/trac/changeset/4063 release yason-0.1 A releases/yason/0.1/ Copied: releases/yason/0.1 (from rev 4062, trunk/libraries/yason) From bknr at bknr.net Sat Nov 15 11:22:18 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:22:18 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4064 Author: hans URL: http://bknr.net/trac/changeset/4064 remove previous yason-0.1 from repository D releases/yason/0.1/ From bknr at bknr.net Sat Nov 15 11:22:28 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:22:28 +0100 Subject: [bknr-cvs] hans changed releases/yason/0.1/ Message-ID: Revision: 4065 Author: hans URL: http://bknr.net/trac/changeset/4065 release yason-0.1 A releases/yason/0.1/ Copied: releases/yason/0.1 (from rev 4064, trunk/libraries/yason) From bknr at bknr.net Sat Nov 15 11:37:30 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 12:37:30 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/index.html Message-ID: Revision: 4066 Author: hans URL: http://bknr.net/trac/changeset/4066 Remove index.html from repository, as it is re-generated by the release script. D trunk/libraries/yason/index.html Deleted: trunk/libraries/yason/index.html =================================================================== --- trunk/libraries/yason/index.html 2008-11-15 11:22:27 UTC (rev 4065) +++ trunk/libraries/yason/index.html 2008-11-15 11:37:30 UTC (rev 4066) @@ -1,482 +0,0 @@ - - -YASON - A JSON encoder/decoder for Common Lisp - -

YASON - A JSON encoder/decoder for Common Lisp

- - - -

Abstract

-
- YASON is a Common Lisp library for encoding and decoding data in - the JSON interchange format. - JSON is used in AJAX applications as a lightweight alternative - to XML. YASON has the sole purpose of encoding and decoding - data and does not impose any object model on the Common Lisp - application that uses it. -
- -

Contents

-
    -
  1. Introduction
  2. -
  3. Download and Installation
  4. -
  5. Mapping between JSON and CL datatypes
  6. -
  7. -Parsing JSON data
    1. Parser dictionary
    -
  8. -
  9. -Encoding JSON data
      -
    1. Encoding a JSON DOM
    2. -
    3. Encoding JSON in streaming mode
    4. -
    5. Application specific encoders
    6. -
    -
  10. -
  11. Symbol index
  12. -
  13. License
  14. -
  15. Acknowledgements
  16. -
- -

Introduction

- JSON is an established alternative - to XML as a data interchange format for web applications. YASON - implements reading and writing of JSON formatted data in Common - Lisp. It does not attempt to provide a mapping between CLOS - objects and YASON, but can be used to implement such mappings. - - -

Download and Installation

-

- YASON has its permanent home - at common-lisp.net. - It can be obtained by downloading - the release - tarball. The current release is 0.1. -

-

- You may also check out the current development version from its - subversion repository: -

svn co svn://bknr.net/svn/trunk/libraries/yason/
If - you have suggestions regarding YASON, please email me - at hans.huebner at gmail.com. -

-

- YASON is written in ANSI Common Lisp and does not depend on - other libraries. -

-

- YASON lives in the :yason package and creates a package nickname - :json. Applications will not normally :use this - package, but rather use qualified names to access YASON's - symbols. For that reason, YASON's symbols do not contain the - string "JSON" themselves. See below for usage samples. -

- - -

Mapping between JSON and CL datatypes

- By default, YASON performs the following mappings between JSON and - CL datatypes: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JSON

datatype
CL

datatype
Notes
objecthash-table

:test??#'equal
- Keys are strings by default, - see *parse-object-key-fn* -
arraylist - Can be changed to read to vectors, - see *parse-json-arrays-as-vectors* -
stringstring - JSON escape characters are recognized upon reading. - Upon writing, known escape characters are used, but - non-ASCII Unicode characters are written as is. -
numbernumber - Parsed with READ, printed with PRINC. This is not a - faithful implementation of the specification. -
truetCan be changed to read as TRUE, see *parse-json-booleans-as-symbols*
falsenilCan be changed to read as FALSE, see *parse-json-booleans-as-symbols*
nullnil
- - -

Parsing JSON data

-

- JSON data is always completely parsed into an equivalent - in-memory representation. Upon reading, some translations are - performed by default to make it easier for the Common Lisp - program to work with the data; see mapping - for details. If desired, the parser can be configured to - preserve the full semantics of the JSON data read. -

- - For example - -
CL-USER> (defvar *json-string* "[{\"foo\":1,\"bar\":[7,8,9]},2,3,4,[5,6,7],true,null]")
-*JSON-STRING*
-CL-USER> (let* ((result (json:parse *json-string*)))
-           (print result)
-           (alexandria:hash-table-plist (first result)))
-
-(#<HASH-TABLE :TEST EQUAL :COUNT 2 {5A4420F1}> 2 3 4 (5 6 7) T NIL) 
-("bar" (7 8 9) "foo" 1)
-CL-USER> (defun maybe-convert-to-keyword (js-name)
-           (or (find-symbol (string-upcase js-name) :keyword)
-               js-name))
-MAYBE-CONVERT-TO-KEYWORD
-CL-USER> :FOO ; intern the :FOO keyword
-:FOO
-CL-USER> (let* ((json:*parse-json-arrays-as-vectors* t)
-                (json:*parse-json-booleans-as-symbols* t)
-                (json:*parse-object-key-fn* #'maybe-convert-to-string)
-                (result (json:parse *json-string*)))
-           (print result)
-           (alexandria:hash-table-plist (aref result 0)))
-
-#(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59B4EAD1}> 2 3 4 #(5 6 7) YASON:TRUE NIL) 
-("bar" #(7 8 9) :FOO 1)
- -

- The second example modifies the parser's behaviour so that JSON - arrays are read as CL vectors, JSON booleans will be read as the - symbols TRUE and FALSE and JSON object keys will be looked up in - the :keyword package. Interning strings coming from an - external source is not recommended practice. -

- -

Parser dictionary

-

[Generic function]
parse input - => - object

- Parse input, which needs to be a string - or a stream, as JSON. Returns the lisp representation of the - JSON structure parsed. -

- -

- [Special variable]
*parse-json-arrays-as-vectors*

- If set to a true value, JSON arrays will be parsed as vectors, - not as lists. -

- -

- [Special variable]
*parse-json-booleans-as-symbols*

- If set to a true value, JSON booleans will be read as the - symbols TRUE and FALSE, not as T and NIL, respectively. -

- -

- [Special variable]
*parse-object-key-fn*

- Function to call to convert a key string in a JSON array to a - key in the CL hash produced. -

- - - -

Encoding JSON data

- YASON provides for two distinct modes to encode JSON data: - Applications can either create an in memory representation of the - data to be serialized, then have YASON convert it to JSON in one - go, or they can use a set of macros to serialze the JSON data - element-by-element, thereby having fine grained control over the - layout of the generated data. - -

Encoding a JSON DOM

-

- In this mode, an in-memory structure is encoded in JSON format. - The structure must consist of objects that are serializable - using the ENCODE function. YASON defines a - number of encoders for standard data types - (see MAPPING), but the application can - define additional methods, e.g. for encoding CLOS objects. -

- For example: -
CL-USER> (json:encode 
-          (list (alexandria:plist-hash-table
-                 '("foo" 1 "bar" (7 8 9))
-                 :test #'equal)
-                2 3 4
-                '(5 6 7)
-                t nil)
-          *standard-output*)
-[{"foo":1,"bar":[7,8,9]},2,3,4,[5,6,7],true,null]
-(#<HASH-TABLE :TEST EQUAL :COUNT 2 {59942D21}> 2 3 4 (5 6 7) T NIL)
- -

DOM encoder dictionary

-

[Generic function]
encode object &optional stream - => - object

- Encode object - to stream in JSON format. May be - specialized by applications to perform specific - rendering. stream defaults to - *STANDARD-OUTPUT*. -

- - - -

Encoding JSON in streaming mode

-

- In this mode, the JSON structure is generated in a stream. - The application makes explicit calls to the encoding library - in order to generate the JSON structure. It provides for more - control over the generated output, and can be used to generate - arbitary JSON without requiring that there exists a directly - matching Lisp datastructure. The streaming API uses - the encode function, so it is possible to - intermix the two. See app-encoders for - an example. -

- For example: -
CL-USER> (json:with-output (*standard-output*)
-           (json:with-array ()
-             (dotimes (i 3)
-               (json:encode-array-element i))))
-[0,1,2]
-NIL
-CL-USER> (json:with-output (*standard-output*)
-           (json:with-object ()
-             (json:encode-object-element "hello" "hu hu")
-             (json:with-object-element ("harr")
-               (json:with-array ()
-                 (dotimes (i 3)
-                   (json:encode-array-element i))))))
-{"hello":"hu hu","harr":[0,1,2]}
-NIL
- -

Streaming encoder dictionary

-

[Macro]
with-output (stream) &body body - => - result*

- Set up a JSON streaming encoder context - on stream, then - evaluate body. -

- -

[Macro]
with-output-to-string* () &body body - => - result*

- Set up a JSON streaming encoder context, then - evaluate body. Return a string with the - generated JSON output. -

- -

- [Condition type]
no-json-output-context

- This condition is signalled when one of the stream encoding - function is used outside the dynamic context of a - WITH-OUTPUT or - WITH-OUTPUT-TO-STRING* body. -

- -

[Macro]
with-array () &body body - => - result*

- Open a JSON array, then run body. Inside - the body, ENCODE-ARRAY-ELEMENT must be - called to encode elements to the opened array. Must be called - within an existing JSON encoder context, see - WITH-OUTPUT and - WITH-OUTPUT-TO-STRING*. -

- -

[Function]
encode-array-element object - => - object

- Encode object as next array element to - the last JSON array opened - with WITH-ARRAY in the dynamic - context. object is encoded using the - ENCODE generic function, so it must be of - a type for which an ENCODE method is - defined. -

- -

[Macro]
with-object () &body body - => - result*

- Open a JSON object, then run body. Inside the body, - ENCODE-OBJECT-ELEMENT - or WITH-OBJECT-ELEMENT must be called to - encode elements to the object. Must be called within an - existing JSON encoder context, - see WITH-OUTPUT - and WITH-OUTPUT-TO-STRING*. -

- -

[Macro]
with-object-element (key) &body body - => - result*

- Open a new encoding context to encode a JSON object - element. key is the key of the element. - The value will be whatever body - serializes to the current JSON output context using one of the - stream encoding functions. This can be used to stream out - nested object structures. -

- -

[Function]
encode-object-element key value - => - value

- Encode key and value - as object element to the last JSON object opened - with WITH-OBJECT in the dynamic - context. key - and value are encoded using - the ENCODE generic function, so they both - must be of a type for which an ENCODE - method is defined. -

- - - -

Application specific encoders

- - Suppose your application uses structs to represent its data, and - you want to encode such structs using JSON in order to send it - to a client application. Suppose further that your structs also - include internal information that you do not want to send. Here - is some code that illustrates how one could implement a - serialization function: - -
CL-USER> (defstruct user name age password)
-USER
-CL-USER> (defmethod json:encode ((user user) &optional (stream *standard-output*))
-           (json:with-output (stream)
-             (json:with-object ()
-               (json:encode-object-element "name" (user-name user))
-               (json:encode-object-element "age" (user-age user)))))
-#<STANDARD-METHOD YASON:ENCODE (USER) {5B40A591}>
-CL-USER> (json:encode (list (make-user :name "horst" :age 27 :password "puppy")
-                            (make-user :name "uschi" :age 28 :password "kitten")))
-[{"name":"horst","age":27},{"name":"uschi","age":28}]
-(#S(USER :NAME "horst" :AGE 27 :PASSWORD "puppy")
- #S(USER :NAME "uschi" :AGE 28 :PASSWORD "kitten"))
- - As you can see, the streaming API and the DOM encoder can be - used together. ENCODE invokes itself - recursively, so any application defined method will be called - while encoding in-memory objects as appropriate. - - - - -

Symbol index

- - - -

License

-
Copyright (c) 2008 Hans H??bner
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-  - Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-
-  - Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in
-    the documentation and/or other materials provided with the
-    distribution.
-
-  - Neither the name BKNR nor the names of its contributors may be
-    used to endorse or promote products derived from this software
-    without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- - -

Acknowledgements

- Thanks go to Edi Weitz for being a great inspiration. This - documentation as been generated with a hacked-up version of - his DOCUMENTATION-TEMPLATE - software. Thanks to David Lichteblau for coining YASON's name. - - - From bknr at bknr.net Sat Nov 15 12:06:55 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 13:06:55 +0100 Subject: [bknr-cvs] hans changed trunk/bknr/datastore/src/data/json.lisp Message-ID: Revision: 4067 Author: hans URL: http://bknr.net/trac/changeset/4067 Make STREAM argument in json:encode method optional. U trunk/bknr/datastore/src/data/json.lisp Modified: trunk/bknr/datastore/src/data/json.lisp =================================================================== --- trunk/bknr/datastore/src/data/json.lisp 2008-11-15 11:37:30 UTC (rev 4066) +++ trunk/bknr/datastore/src/data/json.lisp 2008-11-15 12:06:55 UTC (rev 4067) @@ -6,7 +6,7 @@ `(let ((*ignore-slots* (append *ignore-slots* ,slots))) , at body)) -(defmethod json:encode ((object store-object) stream) +(defmethod json:encode ((object store-object) &optional (stream *standard-output*)) (json:with-output (stream) (json:with-object () (dolist (slotdef (closer-mop:class-slots (class-of object))) From bknr at bknr.net Sat Nov 15 12:56:45 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 13:56:45 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/release.sh Message-ID: Revision: 4068 Author: hans URL: http://bknr.net/trac/changeset/4068 commit release shell script A trunk/libraries/release.sh Added: trunk/libraries/release.sh =================================================================== --- trunk/libraries/release.sh (rev 0) +++ trunk/libraries/release.sh 2008-11-15 12:56:45 UTC (rev 4068) @@ -0,0 +1,35 @@ +#!/bin/sh + +set -e + +package=$1 +release=$2 + +if [ "$release" = "" ] +then + echo usage: $0 '' '' 1>&2 + exit 1 +fi + +packagerel=$1-$2 +webhost=common-lisp.net +webdir=/project/$package/public_html + +repo=`svn info yason | grep "Repository Root" | awk -F': ' '{print $2}'` + +set -x +svn status $package +svn rm -m "remove previous $packagerel from repository" $repo/releases/$package/$release +svn cp -m "release $packagerel" $repo/trunk/libraries/$package $repo/releases/$package/$release +svn co $repo/releases/$package/$release $packagerel + +if [ -f $packagerel/doc.xml ] +then + xsltproc clixdoc/clixdoc.xsl $packagerel/doc.xml > $packagerel/index.html +fi + +tar --exclude .svn --exclude '*.fasl' --exclude '*.log' \ + -cvzf $packagerel.tar.gz $packagerel/* + +scp $packagerel/index.html $packagerel.tar.gz ${webhost}:${webdir} +ssh $webhost "cd $webdir ; rm -f $package.tar.gz ; ln -s $packagerel.tar.gz $package.tar.gz" From bknr at bknr.net Sat Nov 15 13:22:42 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 15 Nov 2008 14:22:42 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/clixdoc/make-doc.lisp Message-ID: Revision: 4069 Author: hans URL: http://bknr.net/trac/changeset/4069 make clix-description work for things other than symbols (i.e. setf-functions) U trunk/libraries/clixdoc/make-doc.lisp Modified: trunk/libraries/clixdoc/make-doc.lisp =================================================================== --- trunk/libraries/clixdoc/make-doc.lisp 2008-11-15 12:56:45 UTC (rev 4068) +++ trunk/libraries/clixdoc/make-doc.lisp 2008-11-15 13:22:41 UTC (rev 4069) @@ -24,7 +24,10 @@ ((find name args :test #'string-equal) (with-clix-element ("arg") (text (string-downcase name)))) - ((find name *current-doc-entries* :key #'first :test #'string-equal) + ((find-if (lambda (doc-entry) + (when (symbolp doc-entry) + (string-equal name doc-entry))) + *current-doc-entries*) (with-clix-element ("ref") (text name))) (t From bknr at bknr.net Thu Nov 20 16:04:55 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 17:04:55 +0100 Subject: [bknr-cvs] hans changed deployed/ Message-ID: Revision: 4070 Author: hans URL: http://bknr.net/trac/changeset/4070 Make directory for deployed versions. A deployed/ From bknr at bknr.net Thu Nov 20 16:13:02 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 17:13:02 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4071 Author: hans URL: http://bknr.net/trac/changeset/4071 Deployed version of the elw/lisp-ecoop application A deployed/lisp-ecoop/ Copied: deployed/lisp-ecoop (from rev 2319, trunk) From bknr at bknr.net Thu Nov 20 16:31:20 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 17:31:20 +0100 Subject: [bknr-cvs] hans changed trunk/thirdparty/emacs/vc-svn.el Message-ID: Revision: 4072 Author: hans URL: http://bknr.net/trac/changeset/4072 Make svn mode more tolerant when the output of svn status is not quite what it expects. U trunk/thirdparty/emacs/vc-svn.el Modified: trunk/thirdparty/emacs/vc-svn.el =================================================================== --- trunk/thirdparty/emacs/vc-svn.el 2008-11-20 16:13:02 UTC (rev 4071) +++ trunk/thirdparty/emacs/vc-svn.el 2008-11-20 16:31:19 UTC (rev 4072) @@ -184,7 +184,8 @@ (match-string 3))) ((looking-at "^I +") nil) ;; An ignored file ((looking-at " \\{40\\}") nil) ;; A file that is not in the wc nor svn? - (t (error "Couldn't parse output from `svn status -v'"))))) + (t (warn "Couldn't parse output from `svn status -v'") + nil)))) (defun vc-svn-parse-state-only () From bknr at bknr.net Thu Nov 20 19:17:08 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:17:08 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4073 Author: hans URL: http://bknr.net/trac/changeset/4073 Remove, copied wrong version. D deployed/lisp-ecoop/ From bknr at bknr.net Thu Nov 20 19:17:51 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:17:51 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4074 Author: hans URL: http://bknr.net/trac/changeset/4074 deployed version branch, second try --his line, and those below, will be ignored-- A svn+ssh://bknr.net/svn/deployed/lisp-ecoop A deployed/lisp-ecoop/ Copied: deployed/lisp-ecoop (from rev 3314, trunk) From bknr at bknr.net Thu Nov 20 19:24:29 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:24:29 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4075 Author: hans URL: http://bknr.net/trac/changeset/4075 try yet again D deployed/lisp-ecoop/ From bknr at bknr.net Thu Nov 20 19:25:11 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:25:11 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4076 Author: hans URL: http://bknr.net/trac/changeset/4076 Copy last version that Didier changed. A deployed/lisp-ecoop/ Copied: deployed/lisp-ecoop (from rev 3392, trunk) From bknr at bknr.net Thu Nov 20 19:37:44 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:37:44 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4077 Author: hans URL: http://bknr.net/trac/changeset/4077 re-branch D deployed/lisp-ecoop/ From bknr at bknr.net Thu Nov 20 19:38:42 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 20:38:42 +0100 Subject: [bknr-cvs] hans changed deployed/lisp-ecoop/ Message-ID: Revision: 4078 Author: hans URL: http://bknr.net/trac/changeset/4078 re-branch part 2 A deployed/lisp-ecoop/ Copied: deployed/lisp-ecoop (from rev 3475, trunk) From bknr at bknr.net Thu Nov 20 20:04:58 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 20 Nov 2008 21:04:58 +0100 Subject: [bknr-cvs] hans changed deployed/bos/ Message-ID: Revision: 4079 Author: hans URL: http://bknr.net/trac/changeset/4079 deployed version of bos application. A deployed/bos/ Copied: deployed/bos (from rev 4016, trunk) From bknr at bknr.net Tue Nov 25 10:35:25 2008 From: bknr at bknr.net (BKNR Commits) Date: Tue, 25 Nov 2008 11:35:25 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/templates/ Message-ID: Revision: 4080 Author: hans URL: http://bknr.net/trac/changeset/4080 Textaenderungen, Willie ist nicht mehr Vorsitzender von BOS. U trunk/projects/bos/payment-website/templates/de/headline2.xml U trunk/projects/bos/payment-website/templates/en/headline2.xml Modified: trunk/projects/bos/payment-website/templates/de/headline2.xml =================================================================== --- trunk/projects/bos/payment-website/templates/de/headline2.xml 2008-11-20 20:04:58 UTC (rev 4079) +++ trunk/projects/bos/payment-website/templates/de/headline2.xml 2008-11-25 10:35:24 UTC (rev 4080) @@ -1,87 +1,94 @@ + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + template="toplevel_main" + title="REGENWALD f??r SAMBOJA LESTARI" + xmlns="http://www.w3.org/1999/xhtml" + xmlns:bknr="http://bknr.net" + xmlns:bos="http://headcraft.de/bos" + > -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Der Schl??ssel zum Erfolg - die lokale Bev??lkerung - - - - - zur??ck - -
- - -
- Der Naturschutz und die Bed??rfnisse der Bev??lkerung d??rfen sich nicht im Wege stehen. -

- Das Naturreservat Samboja Lestari bietet der lokalen Bev??lkerung ein gesichertes Einkommen, Gesundheit - und Bildung. Die Menschen werden in alle Phasen des Projektes mit einbezogen. Landwirtschaft, Baumschule, - Kompostproduktion, Aufforstung, Anpflanzung, Forschung und Aufbau der Infrastruktur bieten sichere Arbeitspl??tze. -
-
-
- "Auf diese Weise wird den Menschen eine Alternative geboten, und sie brauchen den Wald nicht mehr zu roden. So k??nnen - wir der Welt zeigen, dass Natur und Menschen zusammenleben k??nnen und einander nicht auszuschlie??en brauchen." -
- sagt Dr. Willie Smits, Vorsitzender von BOS Indonesien. -
-
-
-
-
-
- Die Sicherheit des Naturschutzreservates ist durch die Akzeptanz der indonesischen Bev??lkerung gew??hrleistet. - Der Erfolg des Projektes garantiert einen besseren Lebensstandard und umgekehrt. -
-
-
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Der Schl??ssel zum Erfolg - die lokale Bev??lkerung + + + + + zur??ck + +
+ + +
+ Der Naturschutz und die Bed??rfnisse der Bev??lkerung d??rfen + sich nicht im Wege stehen.

Das Naturreservat + Samboja Lestari bietet der lokalen Bev??lkerung ein + gesichertes Einkommen, Gesundheit und Bildung. Die + Menschen werden in alle Phasen des Projektes mit + einbezogen. Landwirtschaft, Baumschule, Kompostproduktion, + Aufforstung, Anpflanzung, Forschung und Aufbau der + Infrastruktur bieten sichere Arbeitspl??tze.
+
+
+ "Auf diese Weise wird den Menschen eine Alternative + geboten, und sie brauchen den Wald nicht mehr zu + roden. So k??nnen wir der Welt zeigen, dass Natur und + Menschen zusammenleben k??nnen und einander nicht + auszuschlie??en brauchen." + +
sagt Dr. Willie Smits, Gr??nder der BOS Foundation + in Indonesien.
+
+
+
+
+
+ Die Sicherheit des Naturschutzreservates ist durch die + Akzeptanz der indonesischen Bev??lkerung gew??hrleistet. + Der Erfolg des Projektes garantiert einen besseren + Lebensstandard und umgekehrt. +
+
+
Modified: trunk/projects/bos/payment-website/templates/en/headline2.xml =================================================================== --- trunk/projects/bos/payment-website/templates/en/headline2.xml 2008-11-20 20:04:58 UTC (rev 4079) +++ trunk/projects/bos/payment-website/templates/en/headline2.xml 2008-11-25 10:35:24 UTC (rev 4080) @@ -1,83 +1,86 @@ + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + template="toplevel_main" + title="RAINFOREST for SAMBOJA LESTARI" + xmlns="http://www.w3.org/1999/xhtml" + xmlns:bknr="http://bknr.net" + xmlns:bos="http://headcraft.de/bos" + > -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - The key to success - the local population - - - - - back - -
- - -
- Nature conservation and the needs of the population must not be in conflict with each other. -

- The nature reserve Samboja Lestari offers a safe income, health and education to the local population. The people will be included into all phases of the project. Agriculture, nursery, production of compost, reforestation and the creation of infrastructure provide safe workplaces. -
-
-
- "This offers an alternative to the people, so that they don't have to clear the forest any more. Thus we can show the world, that nature and humans are able to coexist and don't have to exclude each other." -
- says Dr. Willie Smits, chairman of BOS Indonesia. -
-
-
-
-
-
- The safety of the nature reserve is guaranteed by the acceptance through the Indonesian population. The success of the project guarantees a better standard of living and vice versa. -
-
-
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + The key to success - the local population + + + + + back + +
+ + +
+ Nature conservation and the needs of the population must not be in conflict with each other. +

+ The nature reserve Samboja Lestari offers a safe income, health and education to the local population. The people will be included into all phases of the project. Agriculture, nursery, production of compost, reforestation and the creation of infrastructure provide safe workplaces. +
+
+
+ + "This offers an alternative to the people, so that they + don't have to clear the forest any more. Thus we can show + the world, that nature and humans are able to coexist and + don't have to exclude each other." +
says Dr. Willie Smits, founder of BOS + Indonesia.
+
+
+
+
+
+ The safety of the nature reserve is guaranteed by the acceptance through the Indonesian population. The success of the project guarantees a better standard of living and vice versa. +
+
+
From bknr at bknr.net Tue Nov 25 12:51:47 2008 From: bknr at bknr.net (BKNR Commits) Date: Tue, 25 Nov 2008 13:51:47 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/templates/de/verschenken.xml Message-ID: Revision: 4081 Author: hans URL: http://bknr.net/trac/changeset/4081 Correct typo. U trunk/projects/bos/payment-website/templates/de/verschenken.xml Modified: trunk/projects/bos/payment-website/templates/de/verschenken.xml =================================================================== --- trunk/projects/bos/payment-website/templates/de/verschenken.xml 2008-11-25 10:35:24 UTC (rev 4080) +++ trunk/projects/bos/payment-website/templates/de/verschenken.xml 2008-11-25 12:51:47 UTC (rev 4081) @@ -1,39 +1,41 @@ + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -

+ template="toplevel_extra" + title="REGENWALD f??r SAMBOJA LESTARI - Verschenken" + xmlns="http://www.w3.org/1999/xhtml" + xmlns:bknr="http://bknr.net" + xmlns:bos="http://headcraft.de/bos" + > +

-

Suchen Sie ein sinnvolles Geschenk?

+

Suchen Sie ein sinnvolles Geschenk?

-

Verschenken Sie Quadratmeter wiederentstehenden Regenwaldes!

+

Verschenken Sie Quadratmeter wiederentstehenden Regenwaldes!

-

-Der/Die Beschenkte erh??lt eine attraktive Urkunde und pers??nliche -Zugangscodes, um "seine/ihre" Quadratmeter in Borneo leicht orten zu -k??nnen. Zudem kann er/sie den Quadratmetern einen Gru??text anh??ngen, -der f??r jeden Besucher einsehbar ist. -

-

-Auch Sie erhalten die gleichen Zugangscodes und k??nnen z.B. einen -Geburtstagsgru?? "auf" den verschenkten Quadratmetern -hinterlassen. -

-

-Setzen Sie das H??ckchen und fahren Sie wie gew??nscht fort. -

-

-Unmittelbar nach erfolgter Zahlung (wenige Sekunden sp??ter) werden Sie -nach Namen und Versandadresse der zu Beschenkenden Person gefragt. -

-

-Der Geschenkservice ist aus administrativen Gr??nden leider nur bei Online-Spenden ab 90 Euro m??glich. -

-

+

+ Der/Die Beschenkte erh??lt eine attraktive Urkunde und pers??nliche + Zugangscodes, um "seine/ihre" Quadratmeter in Borneo leicht orten zu + k??nnen. Zudem kann er/sie den Quadratmetern einen Gru??text anh??ngen, + der f??r jeden Besucher einsehbar ist. +

+

+ Auch Sie erhalten die gleichen Zugangscodes und k??nnen z.B. einen + Geburtstagsgru?? "auf" den verschenkten Quadratmetern + hinterlassen. +

+

+ Setzen Sie das H??kchen und fahren Sie wie gew??nscht fort. +

+

+ Unmittelbar nach erfolgter Zahlung (wenige Sekunden sp??ter) + werden Sie nach Namen und Versandadresse der zu beschenkenden + Person gefragt. +

+

+ Der Geschenkservice ist aus administrativen Gr??nden leider nur + bei Online-Spenden ab 90 Euro m??glich. +

+

From bknr at bknr.net Fri Nov 28 11:02:47 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 28 Nov 2008 12:02:47 +0100 Subject: [bknr-cvs] hans changed deployed/bos/projects/bos/web/sponsor-handlers.lisp Message-ID: Revision: 4093 Author: hans URL: http://bknr.net/trac/changeset/4093 Fix date non-handling for manually created sponsors. U deployed/bos/projects/bos/web/sponsor-handlers.lisp Modified: deployed/bos/projects/bos/web/sponsor-handlers.lisp =================================================================== --- deployed/bos/projects/bos/web/sponsor-handlers.lisp 2008-11-28 01:07:18 UTC (rev 4092) +++ deployed/bos/projects/bos/web/sponsor-handlers.lisp 2008-11-28 11:02:47 UTC (rev 4093) @@ -101,18 +101,15 @@ (:td (checkbox-field "want-print" "" :checked nil))) (:tr (:td (submit-button "create" "create" :formcheck "javascript:return check_complete_sale()")))))))))) -(defun date-to-universal (date-string) - (apply #'encode-universal-time 0 0 0 (mapcar #'parse-integer (split #?r"\." date-string)))) - (defmethod handle-object-form ((handler edit-sponsor-handler) (action (eql :create)) (sponsor (eql nil))) - (with-query-params (numsqm country email name address date language want-print) + (with-query-params (numsqm country email name address language want-print) (let* ((sponsor (make-sponsor :email email :country country :language language)) (numsqm (parse-integer numsqm)) (contract (make-contract sponsor numsqm :paidp (format nil "~A: manually created by ~A" (format-date-time (get-universal-time)) (user-login (bknr.web:bknr-session-user))) - :date (date-to-universal date) + :date (get-universal-time) :download-only (or (< (* +price-per-m2+ numsqm) *mail-amount*) (not want-print))))) (contract-issue-cert contract name :address address :language language) From bknr at bknr.net Fri Nov 28 11:07:06 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 28 Nov 2008 12:07:06 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/web/sponsor-handlers.lisp Message-ID: Revision: 4094 Author: hans URL: http://bknr.net/trac/changeset/4094 merge from bos deployed branch U trunk/projects/bos/web/sponsor-handlers.lisp Modified: trunk/projects/bos/web/sponsor-handlers.lisp =================================================================== --- trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-28 11:02:47 UTC (rev 4093) +++ trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-28 11:07:06 UTC (rev 4094) @@ -101,18 +101,15 @@ (:td (checkbox-field "want-print" "" :checked nil))) (:tr (:td (submit-button "create" "create" :formcheck "javascript:return check_complete_sale()")))))))))) -(defun date-to-universal (date-string) - (apply #'encode-universal-time 0 0 0 (mapcar #'parse-integer (split #?r"\." date-string)))) - (defmethod handle-object-form ((handler edit-sponsor-handler) (action (eql :create)) (sponsor (eql nil))) - (with-query-params (numsqm country email name address date language want-print) + (with-query-params (numsqm country email name address language want-print) (let* ((sponsor (make-sponsor :email email :country country :language language)) (numsqm (parse-integer numsqm)) (contract (make-contract sponsor numsqm :paidp (format nil "~A: manually created by ~A" (format-date-time (get-universal-time)) (user-login (bknr.web:bknr-session-user))) - :date (date-to-universal date) + :date (get-universal-time) :download-only (or (< (* +price-per-m2+ numsqm) *mail-amount*) (not want-print))))) (contract-issue-cert contract name :address address :language language) From bknr at bknr.net Fri Nov 28 01:07:18 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 28 Nov 2008 02:07:18 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4092 Author: hans URL: http://bknr.net/trac/changeset/4092 Checkpoint new POI microsite work. A trunk/projects/bos/payment-website/static/movie-icon.gif A trunk/projects/bos/payment-website/static/panorama-icon.gif U trunk/projects/bos/payment-website/static/poi-ms.css U trunk/projects/bos/payment-website/static/poi-ms.html U trunk/projects/bos/payment-website/static/poi-ms.js U trunk/projects/bos/web/poi-handlers.lisp Added: trunk/projects/bos/payment-website/static/movie-icon.gif =================================================================== (Binary files differ) Property changes on: trunk/projects/bos/payment-website/static/movie-icon.gif ___________________________________________________________________ Name: svn:executable + * Name: svn:mime-type + application/octet-stream Added: trunk/projects/bos/payment-website/static/panorama-icon.gif =================================================================== (Binary files differ) Property changes on: trunk/projects/bos/payment-website/static/panorama-icon.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: trunk/projects/bos/payment-website/static/poi-ms.css =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-27 14:15:14 UTC (rev 4091) +++ trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-28 01:07:18 UTC (rev 4092) @@ -2,13 +2,13 @@ h2 { font-size: 160% } h3 { font-size: 120% } -ul.media-list li { +ul#media-list li { position: relative; height: 44px; margin-left: 42px; } -ul.media-list li img { +ul#media-list li img { position: absolute; left: -42px; top: 2px; Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 14:15:14 UTC (rev 4091) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-28 01:07:18 UTC (rev 4092) @@ -45,7 +45,7 @@
-
    +
    • 20.03.2004
      @@ -80,13 +80,6 @@
-

- Footer - Lorem ipsum dolor sit amet, consectetuer adipiscing - elit. Maecenas sit amet metus. Nunc quam elit, posuere nec, - auctor in, rhoncus quis, dui. Aliquam erat volutpat. Ut - dignissim, massa sit amet dignissim cursus, quam lacus - feugiat. -

Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-27 14:15:14 UTC (rev 4091) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 01:07:18 UTC (rev 4092) @@ -2,7 +2,69 @@ $(document).ready(init); +var poi_id; + +Date.prototype.renderDate = function() { + return this.getDate() + '.' + this.getMonth() + '.' + (1900 + this.getYear()); +} + +var makeMediumMenuEntry = { + image: function (medium) { + return LI(null, + IMG({ src: '/image/' + medium.id, width: 40, height: 40 }), + (new Date(medium.timestamp)).renderDate(), + BR(), + A({ href: '#' }, medium.title || medium.name)); + }, + panorama: function (medium) { + return LI(null, + IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }), + (new Date(medium.timestamp)).renderDate(), + BR(), + A({ href: '#' }, medium.title || medium.name)); + }, + movie: function (medium) { + return LI(null, + IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }), + (new Date(medium.timestamp)).renderDate(), + BR(), + A({ href: '#' }, medium.title || medium.name)); + } + +}; + +function loadPoi(poi) { + document.title = poi.title; + $('#hd h1').html(poi.title); + $('#hd h2').html(poi.subtitle); + $('#content').empty().html(poi.description); + $('#media-list').empty(); + map(function (medium) { + if (makeMediumMenuEntry[medium.mediumType]) { + $('#media-list').append(makeMediumMenuEntry[medium.mediumType](medium)); + } + }, poi.media); +} + +function loadData(data) { + var pois = data.pois; + + for (var i in pois) { + if (pois[i].id == poi_id) { + loadPoi(pois[i]); + return; + } + } + + alert('invalid poi id (not found)'); +} + function init() { + poi_id = document.location.hash.replace(/#/, ""); - alert('hey ho!'); + if (poi_id.match(/^[0-9]+$/)) { + loadJSONDoc('/poi-json').addCallback(loadData); + } else { + alert('invalid poi id'); + } } \ No newline at end of file Modified: trunk/projects/bos/web/poi-handlers.lisp =================================================================== --- trunk/projects/bos/web/poi-handlers.lisp 2008-11-27 14:15:14 UTC (rev 4091) +++ trunk/projects/bos/web/poi-handlers.lisp 2008-11-28 01:07:18 UTC (rev 4092) @@ -388,15 +388,20 @@ (or (sponsor-country (contract-sponsor contract)) "de") (length (contract-m2s contract)))) +(defun poi-handle-if-modified-since (&optional (pois (class-instances 'poi))) + (let ((pois-last-change (reduce #'max pois + :key (lambda (poi) (store-object-last-change poi 1)) + :initial-value 0))) + (hunchentoot:handle-if-modified-since pois-last-change) + (setf (hunchentoot:header-out :last-modified) + (hunchentoot:rfc-1123-date pois-last-change)))) + (defmethod handle ((handler poi-javascript-handler)) + (poi-handle-if-modified-since) (let* ((last-paid-contracts (last-paid-contracts)) - (timestamp (max (reduce #'max (class-instances 'poi) - :key (lambda (poi) (store-object-last-change poi 1))) - (reduce #'max last-paid-contracts - :key (lambda (contract) (store-object-last-change contract 0)))))) + (timestamp (reduce #'max last-paid-contracts + :key (lambda (contract) (store-object-last-change contract 0))))) (hunchentoot:handle-if-modified-since timestamp) - (setf (hunchentoot:header-out :last-modified) - (hunchentoot:rfc-1123-date timestamp)) (with-http-response (:content-type "text/html; charset=UTF-8") (with-http-body () (html @@ -617,13 +622,10 @@ ()) (defmethod handle ((handler poi-kml-all-handler)) + (let* ((relevant-pois (remove-if-not #'(lambda (poi) (and (poi-area poi) (poi-published-earth poi))) - (class-instances 'poi))) - (pois-last-change (reduce #'max relevant-pois :key (lambda (poi) (store-object-last-change poi 1)) - :initial-value 0))) - (hunchentoot:handle-if-modified-since pois-last-change) - (setf (hunchentoot:header-out :last-modified) - (hunchentoot:rfc-1123-date pois-last-change)) + (class-instances 'poi)))) + (poi-handle-if-modified-since relevant-pois) (with-query-params ((lang "en")) (with-xml-response () ;; (sax:processing-instruction cxml::*sink* "xml-stylesheet" "href=\"/static/tri.xsl\" type=\"text/xsl\"") @@ -689,6 +691,7 @@ ()) (defmethod handle ((handler poi-json-handler)) + (poi-handle-if-modified-since) (with-json-response () (json:with-object-element ("pois") (bos.m2:pois-as-json (request-language))))) \ No newline at end of file From bknr at bknr.net Thu Nov 27 12:18:22 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 13:18:22 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/ Message-ID: Revision: 4089 Author: hans URL: http://bknr.net/trac/changeset/4089 Add jQuery to be used in new poi microsite. A trunk/projects/bos/payment-website/static/jquery-1.2.6.min.js U trunk/projects/bos/payment-website/static/poi-ms.html Added: trunk/projects/bos/payment-website/static/jquery-1.2.6.min.js =================================================================== --- trunk/projects/bos/payment-website/static/jquery-1.2.6.min.js (rev 0) +++ trunk/projects/bos/payment-website/static/jquery-1.2.6.min.js 2008-11-27 12:18:22 UTC (rev 4089) @@ -0,0 +1,32 @@ +/* + * jQuery 1.2.6 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ + * $Rev: 5685 $ + */ +(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else +while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); \ No newline at end of file Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 11:58:24 UTC (rev 4088) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 12:18:22 UTC (rev 4089) @@ -4,6 +4,8 @@ POI Microsite + + From bknr at bknr.net Thu Nov 27 14:13:59 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 15:13:59 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/encode.lisp Message-ID: Revision: 4090 Author: hans URL: http://bknr.net/trac/changeset/4090 Optimize encoder for a 100x speedup. Don't use PRINC unless you know that you need it. U trunk/libraries/yason/encode.lisp Modified: trunk/libraries/yason/encode.lisp =================================================================== --- trunk/libraries/yason/encode.lisp 2008-11-27 12:18:22 UTC (rev 4089) +++ trunk/libraries/yason/encode.lisp 2008-11-27 14:13:59 UTC (rev 4090) @@ -17,99 +17,100 @@ (:documentation "Encode OBJECT to STREAM in JSON format. May be specialized by applications to perform specific rendering. STREAM - defaults to *STANDARD-OUTPUT*.") + defaults to *STANDARD-OUTPUT*.")) - (:method ((object string) &optional (stream *standard-output*)) - (with-standard-output-to (stream) - (princ #\") - (loop - for char across object - do (case char - ((#\\ #\" #\/) - (princ #\\) (princ char)) - (#\Backspace - (princ #\\) (princ #\b)) - (#\Page - (princ #\\) (princ #\f)) - (#\Newline - (princ #\\) (princ #\n)) - (#\Return - (princ #\\) (princ #\r)) - (#\Tab - (princ #\\) (princ #\t)) - (t - (princ char)))) - (princ #\")) - object) +(defparameter *char-replacements* + (alexandria:plist-hash-table + '(#\\ "\\\\" + #\" "\\\"" + #\/ "\\/" + #\Backspace "\\b" + #\Page "\\f" + #\Newline "\\n" + #\Return "\\r" + #\Tab "\\t"))) + - (:method ((object rational) &optional (stream *standard-output*)) - (encode (float object) stream) - object) +(defmethod encode ((string string) &optional (stream *standard-output*)) + (with-standard-output-to (stream) + (write-char #\") + (dotimes (i (length string)) + (let* ((char (aref string i)) + (replacement (gethash char *char-replacements*))) + (if replacement + (write-string replacement) + (write-char char)))) + (write-char #\") + string)) - (:method ((object integer) &optional (stream *standard-output*)) - (princ object stream)) +(defmethod encode ((object rational) &optional (stream *standard-output*)) + (encode (float object) stream) + object) - (:method ((object hash-table) &optional (stream *standard-output*)) - (with-standard-output-to (stream) - (princ #\{) - (let (printed) - (maphash (lambda (key value) - (if printed - (princ #\,) - (setf printed t)) - (encode key stream) - (princ #\:) - (encode value stream)) - object)) - (princ #\})) - object) +(defmethod encode ((object integer) &optional (stream *standard-output*)) + (princ object stream)) - (:method ((object vector) &optional (stream *standard-output*)) - (with-standard-output-to (stream) - (princ #\[) - (let (printed) - (loop - for value across object - do - (when printed - (princ #\,)) - (setf printed t) - (encode value stream))) - (princ #\])) - object) +(defmethod encode ((object hash-table) &optional (stream *standard-output*)) + (with-standard-output-to (stream) + (write-char #\{) + (let (printed) + (maphash (lambda (key value) + (if printed + (write-char #\,) + (setf printed t)) + (encode key stream) + (write-char #\:) + (encode value stream)) + object)) + (write-char #\})) + object) - (:method ((object list) &optional (stream *standard-output*)) - (with-standard-output-to (stream) - (princ #\[) - (let (printed) - (dolist (value object) - (if printed - (princ #\,) - (setf printed t)) - (encode value stream))) - (princ #\])) - object) +(defmethod encode ((object vector) &optional (stream *standard-output*)) + (with-standard-output-to (stream) + (write-char #\[) + (let (printed) + (loop + for value across object + do + (when printed + (write-char #\,)) + (setf printed t) + (encode value stream))) + (write-char #\])) + object) - (:method ((object (eql 'true)) &optional (stream *standard-output*)) - (princ "true" stream) - object) +(defmethod encode ((object list) &optional (stream *standard-output*)) + (with-standard-output-to (stream) + (write-char #\[) + (let (printed) + (dolist (value object) + (if printed + (write-char #\,) + (setf printed t)) + (encode value stream))) + (write-char #\])) + object) - (:method ((object (eql 'false)) &optional (stream *standard-output*)) - (princ "false" stream) - object) +(defmethod encode ((object (eql 'true)) &optional (stream *standard-output*)) + (write-string "true" stream) + object) - (:method ((object (eql 'null)) &optional (stream *standard-output*)) - (princ "null" stream) - object) +(defmethod encode ((object (eql 'false)) &optional (stream *standard-output*)) + (write-string "false" stream) + object) - (:method ((object (eql t)) &optional (stream *standard-output*)) - (princ "true" stream) - object) +(defmethod encode ((object (eql 'null)) &optional (stream *standard-output*)) + (write-string "null" stream) + object) - (:method ((object (eql nil)) &optional (stream *standard-output*)) - (princ "null" stream) - object)) +(defmethod encode ((object (eql t)) &optional (stream *standard-output*)) + (write-string "true" stream) + object) +(defmethod encode ((object (eql nil)) &optional (stream *standard-output*)) + (write-string "null" stream) + object) + (defclass json-output-stream () ((output-stream :reader output-stream :initarg :output-stream) @@ -119,7 +120,7 @@ (defun next-aggregate-element () (if (car (stack *json-output*)) - (princ (car (stack *json-output*)) (output-stream *json-output*)) + (write-char (car (stack *json-output*)) (output-stream *json-output*)) (setf (car (stack *json-output*)) #\,))) (defmacro with-output ((stream) &body body) @@ -147,12 +148,12 @@ (error 'no-json-output-context)) (when (stack *json-output*) (next-aggregate-element)) - (princ ,begin-char (output-stream *json-output*)) + (write-char ,begin-char (output-stream *json-output*)) (push nil (stack *json-output*)) (prog1 (progn , at body) (pop (stack *json-output*)) - (princ ,end-char (output-stream *json-output*))))) + (write-char ,end-char (output-stream *json-output*))))) (defmacro with-array (() &body body) "Open a JSON array, then run BODY. Inside the body, @@ -188,7 +189,7 @@ type for which an ENCODE method is defined." (next-aggregate-element) (encode key (output-stream *json-output*)) - (princ #\: (output-stream *json-output*)) + (write-char #\: (output-stream *json-output*)) (encode value (output-stream *json-output*)) value) From bknr at bknr.net Thu Nov 27 14:15:14 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 15:15:14 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4091 Author: hans URL: http://bknr.net/trac/changeset/4091 Add poi-json handler U trunk/projects/bos/m2/packages.lisp U trunk/projects/bos/payment-website/static/poi-ms.html A trunk/projects/bos/payment-website/static/poi-ms.js U trunk/projects/bos/web/poi-handlers.lisp U trunk/projects/bos/web/webserver.lisp Modified: trunk/projects/bos/m2/packages.lisp =================================================================== --- trunk/projects/bos/m2/packages.lisp 2008-11-27 14:13:59 UTC (rev 4090) +++ trunk/projects/bos/m2/packages.lisp 2008-11-27 14:15:14 UTC (rev 4091) @@ -239,6 +239,8 @@ #:poi-panoramas #:poi-movies #:make-poi-javascript + #:poi-as-json + #:pois-as-json ;; news #:news-item Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 14:13:59 UTC (rev 4090) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 14:15:14 UTC (rev 4091) @@ -6,6 +6,7 @@ + Added: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js (rev 0) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-27 14:15:14 UTC (rev 4091) @@ -0,0 +1,8 @@ +$ = jQuery; + +$(document).ready(init); + +function init() { + + alert('hey ho!'); +} \ No newline at end of file Modified: trunk/projects/bos/web/poi-handlers.lisp =================================================================== --- trunk/projects/bos/web/poi-handlers.lisp 2008-11-27 14:13:59 UTC (rev 4090) +++ trunk/projects/bos/web/poi-handlers.lisp 2008-11-27 14:15:14 UTC (rev 4091) @@ -684,3 +684,11 @@ imageproc-arguments)) (error "image index ~a out of bounds for poi ~a" image-index poi))))) +;;; poi-json-handler +(defclass poi-json-handler (page-handler) + ()) + +(defmethod handle ((handler poi-json-handler)) + (with-json-response () + (json:with-object-element ("pois") + (bos.m2:pois-as-json (request-language))))) \ No newline at end of file Modified: trunk/projects/bos/web/webserver.lisp =================================================================== --- trunk/projects/bos/web/webserver.lisp 2008-11-27 14:13:59 UTC (rev 4090) +++ trunk/projects/bos/web/webserver.lisp 2008-11-27 14:15:14 UTC (rev 4091) @@ -184,6 +184,7 @@ ("/map-browser" map-browser-handler) ("/poi-javascript" poi-javascript-handler) ("/m2-javascript" m2-javascript-handler) + ("/poi-json" poi-json-handler) ("/sponsor-login" sponsor-login-handler) ("/create-allocation-area" create-allocation-area-handler) ("/allocation-area" allocation-area-handler) From bknr at bknr.net Thu Nov 27 11:00:04 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 12:00:04 +0100 Subject: [bknr-cvs] hans changed deployed/bos/projects/bos/ Message-ID: Revision: 4087 Author: hans URL: http://bknr.net/trac/changeset/4087 Merge fixes from trunk U deployed/bos/projects/bos/m2/m2.lisp U deployed/bos/projects/bos/m2/mail-generator.lisp U deployed/bos/projects/bos/payment-website/templates/de/headline2.xml U deployed/bos/projects/bos/payment-website/templates/de/verschenken.xml U deployed/bos/projects/bos/payment-website/templates/en/headline2.xml U deployed/bos/projects/bos/web/news-tags.lisp U deployed/bos/projects/bos/web/poi-handlers.lisp U deployed/bos/projects/bos/web/sponsor-handlers.lisp U deployed/bos/projects/bos/web/tags.lisp Change set too large, please see URL above From bknr at bknr.net Thu Nov 27 11:58:24 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 12:58:24 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/poi-ms. Message-ID: Revision: 4088 Author: hans URL: http://bknr.net/trac/changeset/4088 beginnings of new poi microsite A trunk/projects/bos/payment-website/static/poi-ms.css A trunk/projects/bos/payment-website/static/poi-ms.html Added: trunk/projects/bos/payment-website/static/poi-ms.css =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.css (rev 0) +++ trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-27 11:58:24 UTC (rev 4088) @@ -0,0 +1,15 @@ +h1 { font-size: 200% } +h2 { font-size: 160% } +h3 { font-size: 120% } + +ul.media-list li { + position: relative; + height: 44px; + margin-left: 42px; +} + +ul.media-list li img { + position: absolute; + left: -42px; + top: 2px; +} \ No newline at end of file Added: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html (rev 0) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-27 11:58:24 UTC (rev 4088) @@ -0,0 +1,90 @@ + + + + POI Microsite + + + + + +
+
+ +

Malaienb??rgehege

+

Rund 60 Hektar f??r Renaturierung und B??ren

+
+
+
+
+ Am 11.01 wurde in Samboja Lestari ein + kleiner weiblicher Malaienb??r (indonesisch: Bruang Madu = + Honigb??r) von einem Dorfbewohner abgegeben, die Mutter ist + wahrscheinlich get??tet worden. Bei der Ankunft war sie + ungef??hr eine Woche alt und wog nur einen Kilo. Seitdem + wird sie von sechs Pflegern abwechselnd rund um die Uhr + gepflegt. Ihr wurde der Name Arkana gegeben, und + inzwischen wiegt sie schon 1,6 Kilo. Ihre neue + "Mutter", also die Hauptbezugsperson ist Slamet + Mulyono. T??glich wird sie f??r eine Stunde in die Sonne + gebracht, mehrmals t??glich wird ihre Temperatur gemessen, + die genau wie bei Menschen auch zwischen 36 und 37 Grad + liegt. F??llt die Temperatur, wird sie mit Eukalypus??l + (indonesisch: Minyak Kayu Putih) eingerieben. Auch nachdem + sie Milch bekommt, wird sie damit eingerieben, um den + Kreislauf anzuregen. Wenn sie ??lter ist, wird sie Fr??chte + zu essen bekommen, das Hauptnahrungsmittel der + Malaienb??ren. Die Fr??chte, meistens Salak, Mango, Papaya, + Ananas und Zuckerrohr kommen aus dem ??kologischen Garten + Samboja Lestaris Mehrmals t??glich bekommt sie ein frisches + Nest aus verschieden Bl??ttern, normalerweise frisst die + Mutter die Exkremente, und h??lt so das Nest sauber. +
+
+
+ +
+
+
+

+ Footer - Lorem ipsum dolor sit amet, consectetuer adipiscing + elit. Maecenas sit amet metus. Nunc quam elit, posuere nec, + auctor in, rhoncus quis, dui. Aliquam erat volutpat. Ut + dignissim, massa sit amet dignissim cursus, quam lacus + feugiat. +

+
+
+ + From bknr at bknr.net Thu Nov 27 09:37:31 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 10:37:31 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4085 Author: hans URL: http://bknr.net/trac/changeset/4085 Add JSON encoding for POIs U trunk/projects/bos/m2/bos.m2.asd U trunk/projects/bos/m2/poi.lisp U trunk/projects/bos/web/poi-handlers.lisp Modified: trunk/projects/bos/m2/bos.m2.asd =================================================================== --- trunk/projects/bos/m2/bos.m2.asd 2008-11-27 09:36:39 UTC (rev 4084) +++ trunk/projects/bos/m2/bos.m2.asd 2008-11-27 09:37:31 UTC (rev 4085) @@ -5,7 +5,8 @@ (asdf:defsystem :bos.m2 :depends-on (:bknr.datastore :bknr.modules :cl-smtp :cl-mime :kmrcl :iterate :arnesi - :cl-pdf :cl-pdf-parser :screamer :cl-fad) + :cl-pdf :cl-pdf-parser :screamer :cl-fad + :yason) :components ((:file "packages") (:file "geo-utm" :depends-on ("packages")) (:file "geometry" :depends-on ("packages")) Modified: trunk/projects/bos/m2/poi.lisp =================================================================== --- trunk/projects/bos/m2/poi.lisp 2008-11-27 09:36:39 UTC (rev 4084) +++ trunk/projects/bos/m2/poi.lisp 2008-11-27 09:37:31 UTC (rev 4085) @@ -84,7 +84,9 @@ ;;; poi-movie (defpersistent-class poi-movie (poi-medium) ((url :accessor poi-movie-url :initarg :url :initform nil) - (created :initform (get-universal-time) :reader poi-medium-creation-time))) + (created :initform (error "need :created initarg when creating poi-medium") + :initarg :created + :reader poi-medium-creation-time))) ;;; poi (defpersistent-class poi (textual-attributes-mixin) @@ -294,3 +296,55 @@ (warn "~s has a url of ~s" movie (poi-movie-url movie)))))) (mapc #'poi-sanity-check (class-instances 'poi)) (values))) + +(defvar *language* "en" + "Current language for JSON encoding") + +(defmethod json:encode ((object symbol) &optional stream) + (json:encode (string-downcase (symbol-name object)) stream)) + +(defgeneric json-encode (object) + (:method-combination progn)) + +(defmethod json-encode progn ((object store-object)) + (json:encode-object-element "id" (store-object-id object))) + +(defmethod json-encode progn ((blob blob)) + (json:encode-object-elements + "type" (blob-type blob) + "timestamp" (format-date-time (blob-timestamp blob) :mail-style t))) + +(defmethod json-encode progn ((image store-image)) + (json:encode-object-elements + "name" (store-image-name image) + "width" (store-image-width image) + "height" (store-image-height image))) + +(defmethod json-encode progn ((object bos.m2::textual-attributes-mixin)) + (dolist (field '(title subtitle description)) + (let ((string (slot-string object field *language*))) + (unless (equal "" string) + (json:encode-object-element field string))))) + +(defmethod json-encode progn ((medium poi-medium)) + (json:encode-object-element + "mediumType" + (cl-ppcre:regex-replace "^poi-" (string-downcase (class-name (class-of medium))) ""))) + +(defmethod json-encode progn ((movie poi-movie)) + (json:encode-object-elements + "url" (poi-movie-url movie) + "timestamp" (format-date-time (poi-medium-creation-time movie) :mail-style t))) + +(defun pois-as-json (language) + (let ((*language* language)) + (json:with-array () + (dolist (poi (class-instances 'poi)) + (when (poi-complete poi language) + (json:with-object () + (json-encode poi) + (json:with-object-element ("media") + (json:with-array () + (dolist (medium (poi-media poi)) + (json:with-object () + (json-encode medium))))))))))) Modified: trunk/projects/bos/web/poi-handlers.lisp =================================================================== --- trunk/projects/bos/web/poi-handlers.lisp 2008-11-27 09:36:39 UTC (rev 4084) +++ trunk/projects/bos/web/poi-handlers.lisp 2008-11-27 09:37:31 UTC (rev 4085) @@ -346,7 +346,7 @@ (flet ((make-new-medium (new-medium-type poi) (case new-medium-type (poi-movie - (make-instance 'poi-movie :poi poi :url (query-param "url"))) + (make-instance 'poi-movie :poi poi :url (query-param "url") :created (get-universal-time))) (otherwise (let ((upload (request-uploaded-file "image-file"))) (unless upload @@ -683,3 +683,4 @@ (store-object-id (nth image-index (poi-sat-images poi))) imageproc-arguments)) (error "image index ~a out of bounds for poi ~a" image-index poi))))) + From bknr at bknr.net Thu Nov 27 09:36:40 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 10:36:40 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/ Message-ID: Revision: 4084 Author: hans URL: http://bknr.net/trac/changeset/4084 Add encode-array-elementS and encode-object-elementS convenience functions. U trunk/libraries/yason/encode.lisp U trunk/libraries/yason/package.lisp Modified: trunk/libraries/yason/encode.lisp =================================================================== --- trunk/libraries/yason/encode.lisp 2008-11-26 21:28:07 UTC (rev 4083) +++ trunk/libraries/yason/encode.lisp 2008-11-27 09:36:39 UTC (rev 4084) @@ -176,6 +176,11 @@ (next-aggregate-element) (encode object (output-stream *json-output*))) +(defun encode-array-elements (&rest objects) + "Encode OBJECTS, a list of JSON encodeable object, as array elements." + (dolist (object objects) + (encode-array-element object))) + (defun encode-object-element (key value) "Encode KEY and VALUE as object element to the last JSON object opened with WITH-OBJECT in the dynamic context. KEY and VALUE are @@ -187,6 +192,11 @@ (encode value (output-stream *json-output*)) value) +(defun encode-object-elements (&rest elements) + "Encode plist ELEMENTS as object elements." + (loop for (key value) on elements by #'cddr + do (encode-object-element key value))) + (defmacro with-object-element ((key) &body body) "Open a new encoding context to encode a JSON object element. KEY is the key of the element. The value will be whatever BODY Modified: trunk/libraries/yason/package.lisp =================================================================== --- trunk/libraries/yason/package.lisp 2008-11-26 21:28:07 UTC (rev 4083) +++ trunk/libraries/yason/package.lisp 2008-11-27 09:36:39 UTC (rev 4084) @@ -30,7 +30,9 @@ #:no-json-output-context #:with-array #:encode-array-element + #:encode-array-elements #:with-object #:encode-object-element + #:encode-object-elements #:with-object-element #:with-response)) From bknr at bknr.net Wed Nov 26 21:26:41 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 26 Nov 2008 22:26:41 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4082 Author: hans URL: http://bknr.net/trac/changeset/4082 Fix sponsor XML problems. U trunk/projects/bos/m2/mail-generator.lisp U trunk/projects/bos/web/sponsor-handlers.lisp Modified: trunk/projects/bos/m2/mail-generator.lisp =================================================================== --- trunk/projects/bos/m2/mail-generator.lisp 2008-11-25 12:51:47 UTC (rev 4081) +++ trunk/projects/bos/m2/mail-generator.lisp 2008-11-26 21:26:40 UTC (rev 4082) @@ -202,8 +202,10 @@ (defun lookup-element-name (element-name) "Given an ELEMENT-NAME (which may be either a form field name or a name of a post parameter from worldpay), return the common XML element name" - (or (cdr (find element-name *common-element-names* :key #'car :test #'equal)) - element-name)) + (cl-ppcre:regex-replace-all "(?i)[^-a-z0-9]" + (or (cdr (find element-name *common-element-names* :key #'car :test #'equal)) + element-name) + "")) (defun make-contract-xml-part (id params) (make-instance 'text-mime Modified: trunk/projects/bos/web/sponsor-handlers.lisp =================================================================== --- trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-25 12:51:47 UTC (rev 4081) +++ trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-26 21:26:40 UTC (rev 4082) @@ -83,8 +83,6 @@ "Download complete sponsor DB in XML format"))) (:tr ((:th :colspan "2" :align "left") (:h2 "Create sponsor"))) - (:tr (:td "Date (DD.MM.YYYY)") - (:td (text-field "date" :size 10 :value (format-date-time (get-universal-time) :show-time nil)))) (:tr (:td "Number of square meters") (:td (text-field "numsqm" :size 5))) (:tr (:td "Country code (2 chars)") From bknr at bknr.net Wed Nov 26 21:28:07 2008 From: bknr at bknr.net (BKNR Commits) Date: Wed, 26 Nov 2008 22:28:07 +0100 Subject: [bknr-cvs] hans changed deployed/bos/projects/bos/ Message-ID: Revision: 4083 Author: hans URL: http://bknr.net/trac/changeset/4083 merge 4081 from trunk U deployed/bos/projects/bos/m2/mail-generator.lisp U deployed/bos/projects/bos/web/sponsor-handlers.lisp Modified: deployed/bos/projects/bos/m2/mail-generator.lisp =================================================================== --- deployed/bos/projects/bos/m2/mail-generator.lisp 2008-11-26 21:26:40 UTC (rev 4082) +++ deployed/bos/projects/bos/m2/mail-generator.lisp 2008-11-26 21:28:07 UTC (rev 4083) @@ -202,8 +202,10 @@ (defun lookup-element-name (element-name) "Given an ELEMENT-NAME (which may be either a form field name or a name of a post parameter from worldpay), return the common XML element name" - (or (cdr (find element-name *common-element-names* :key #'car :test #'equal)) - element-name)) + (cl-ppcre:regex-replace-all "(?i)[^-a-z0-9]" + (or (cdr (find element-name *common-element-names* :key #'car :test #'equal)) + element-name) + "")) (defun make-contract-xml-part (id params) (make-instance 'text-mime Modified: deployed/bos/projects/bos/web/sponsor-handlers.lisp =================================================================== --- deployed/bos/projects/bos/web/sponsor-handlers.lisp 2008-11-26 21:26:40 UTC (rev 4082) +++ deployed/bos/projects/bos/web/sponsor-handlers.lisp 2008-11-26 21:28:07 UTC (rev 4083) @@ -83,8 +83,6 @@ "Download complete sponsor DB in XML format"))) (:tr ((:th :colspan "2" :align "left") (:h2 "Create sponsor"))) - (:tr (:td "Date (DD.MM.YYYY)") - (:td (text-field "date" :size 10 :value (format-date-time (get-universal-time) :show-time nil)))) (:tr (:td "Number of square meters") (:td (text-field "numsqm" :size 5))) (:tr (:td "Country code (2 chars)") From bknr at bknr.net Thu Nov 27 10:05:53 2008 From: bknr at bknr.net (BKNR Commits) Date: Thu, 27 Nov 2008 11:05:53 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/m2/poi.lisp Message-ID: Revision: 4086 Author: hans URL: http://bknr.net/trac/changeset/4086 add poi-as-json function. U trunk/projects/bos/m2/poi.lisp Modified: trunk/projects/bos/m2/poi.lisp =================================================================== --- trunk/projects/bos/m2/poi.lisp 2008-11-27 09:37:31 UTC (rev 4085) +++ trunk/projects/bos/m2/poi.lisp 2008-11-27 10:05:53 UTC (rev 4086) @@ -336,15 +336,18 @@ "url" (poi-movie-url movie) "timestamp" (format-date-time (poi-medium-creation-time movie) :mail-style t))) +(defun poi-as-json (poi language) + (let ((*language* language)) + (json:with-object () + (json-encode poi) + (json:with-object-element ("media") + (json:with-array () + (dolist (medium (poi-media poi)) + (json:with-object () + (json-encode medium)))))))) + (defun pois-as-json (language) - (let ((*language* language)) - (json:with-array () - (dolist (poi (class-instances 'poi)) - (when (poi-complete poi language) - (json:with-object () - (json-encode poi) - (json:with-object-element ("media") - (json:with-array () - (dolist (medium (poi-media poi)) - (json:with-object () - (json-encode medium))))))))))) + (json:with-array () + (dolist (poi (class-instances 'poi)) + (when (poi-complete poi language) + (poi-as-json poi language))))) From bknr at bknr.net Fri Nov 28 19:30:00 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 28 Nov 2008 20:30:00 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4095 Author: hans URL: http://bknr.net/trac/changeset/4095 POI microsite kind of usable. U trunk/projects/bos/m2/poi.lisp U trunk/projects/bos/payment-website/static/poi-ms.css U trunk/projects/bos/payment-website/static/poi-ms.html U trunk/projects/bos/payment-website/static/poi-ms.js Modified: trunk/projects/bos/m2/poi.lisp =================================================================== --- trunk/projects/bos/m2/poi.lisp 2008-11-28 11:07:06 UTC (rev 4094) +++ trunk/projects/bos/m2/poi.lisp 2008-11-28 19:30:00 UTC (rev 4095) @@ -309,6 +309,13 @@ (defmethod json-encode progn ((object store-object)) (json:encode-object-element "id" (store-object-id object))) +(defmethod json-encode progn ((poi poi)) + (json:encode-object-elements + "name" (poi-name poi) + "icon" (poi-icon poi) + "x" (poi-center-x poi) + "y" (poi-center-y poi))) + (defmethod json-encode progn ((blob blob)) (json:encode-object-elements "type" (blob-type blob) Modified: trunk/projects/bos/payment-website/static/poi-ms.css =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-28 11:07:06 UTC (rev 4094) +++ trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-28 19:30:00 UTC (rev 4095) @@ -12,4 +12,16 @@ position: absolute; left: -42px; top: 2px; -} \ No newline at end of file +} + +ul#media-list .active { + background-color: #0f0; +} + +.map { + position: relative; +} + +.map .icon { + position: absolute; +} Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-28 11:07:06 UTC (rev 4094) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-28 19:30:00 UTC (rev 4095) @@ -13,8 +13,6 @@
-

Malaienb??rgehege

-

Rund 60 Hektar f??r Renaturierung und B??ren

@@ -45,6 +43,7 @@
+

Malaienb??rgehege

  • Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 11:07:06 UTC (rev 4094) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 19:30:00 UTC (rev 4095) @@ -3,60 +3,130 @@ $(document).ready(init); var poi_id; +var poi; Date.prototype.renderDate = function() { return this.getDate() + '.' + this.getMonth() + '.' + (1900 + this.getYear()); } -var makeMediumMenuEntry = { - image: function (medium) { - return LI(null, - IMG({ src: '/image/' + medium.id, width: 40, height: 40 }), - (new Date(medium.timestamp)).renderDate(), - BR(), - A({ href: '#' }, medium.title || medium.name)); +var B = createDOMFunc('b', null); +var OBJECT = createDOMFunc('object'); +var PARAM = createDOMFunc('param'); +var EMBED = createDOMFunc('embed'); +var APPLET = createDOMFunc('applet'); + +var mediaHandlers = { + image: { + icon: function (medium) { return IMG({ src: '/image/' + medium.id, width: 40, height: 40 }) }, + action: function (medium) { + $('#content') + .empty() + .append(H2(null, medium.title), + IMG({ src: '/image/' + medium.id }), BR(), + H3(null, medium.subtitle), + P(null, medium.description)); + } }, - panorama: function (medium) { - return LI(null, - IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }), - (new Date(medium.timestamp)).renderDate(), - BR(), - A({ href: '#' }, medium.title || medium.name)); + panorama: { + icon: function (medium) { return IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }) }, + action: function (medium) { + $('#content') + .empty() + .append(H2(null, medium.title), + APPLET({ archive: '/static/ptviewer.jar', + code: 'ptviewer.class', + width: 400, + height: 300}, + PARAM({ name: 'file', value: '/image/' + medium.id}), + PARAM({ name: 'cursor', value: 'MOVE' })), + H3(null, medium.subtitle), + P(null, medium.description)); + } }, - movie: function (medium) { - return LI(null, - IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }), - (new Date(medium.timestamp)).renderDate(), - BR(), - A({ href: '#' }, medium.title || medium.name)); + movie: { + icon: function (medium) { return IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }) }, + action: function (medium) { + $('#content') + .empty() + .append(H2(null, medium.title), + OBJECT({ width: 360, height: 360 }, + PARAM({ name: "movie", value: medium.url }), + EMBED({ src: medium.url, type: 'application/x-shockwave-flash', + width: 400, height: 300 })), BR(), + H3(null, medium.subtitle), + P(null, medium.description)); + } } - }; -function loadPoi(poi) { +function selectMedium(fn, e) { + $('#media-list *').removeClass('active'); + $(e.target).addClass('active'); + fn(); +} + +function loadMainInfo() { + var map = []; + for (var y = -1; y < 3; y++) { + var tiles = []; + for (var x = -1; x < 3; x++) { + tiles.push(IMG({ 'class': 'map-tile', + src: '/overview/' + + (Math.floor(poi.x / 90) + x) * 90 + + '/' + + (Math.floor(poi.y / 90) + y) * 90, + width: 90, height: 90 })); + } + map.push(DIV(null, tiles)); + } + map.push(IMG({ 'class': 'icon', + src: '/images/' + poi.icon + '.gif', + style: 'left: ' + (poi.x - ((Math.floor(poi.x / 90) - 1) * 90) - 8) + 'px; ' + + 'top: ' + (poi.y - ((Math.floor(poi.y / 90) - 1) * 90) - 8) + 'px'})); + + $('#content').empty().append(H2(null, poi.subtitle), + DIV({ 'class': 'map' }, map), + P(null, poi.description)); +} + +var B = createDOMFunc('b', null); + +function loadPoi() { document.title = poi.title; - $('#hd h1').html(poi.title); - $('#hd h2').html(poi.subtitle); - $('#content').empty().html(poi.description); + $('.yui-b h1').html(poi.title); + loadMainInfo(); $('#media-list').empty(); map(function (medium) { - if (makeMediumMenuEntry[medium.mediumType]) { - $('#media-list').append(makeMediumMenuEntry[medium.mediumType](medium)); + if (mediaHandlers[medium.mediumType]) { + $('#media-list') + .append($(A({ href: '#' }, + LI(null, + mediaHandlers[medium.mediumType].icon(medium), + (new Date(medium.timestamp)).renderDate(), + BR(), + B(null, medium.title || medium.name)))) + .bind('click', null, partial(selectMedium, partial(mediaHandlers[medium.mediumType].action, medium)))); } }, poi.media); } function loadData(data) { - var pois = data.pois; + try { + var pois = data.pois; - for (var i in pois) { - if (pois[i].id == poi_id) { - loadPoi(pois[i]); - return; + for (var i in pois) { + if (pois[i].id == poi_id) { + poi = pois[i]; + loadPoi(); + return; + } } + + alert('invalid poi id (not found)'); } - - alert('invalid poi id (not found)'); + catch (e) { + alert(e); + } } function init() { From bknr at bknr.net Fri Nov 28 19:36:24 2008 From: bknr at bknr.net (BKNR Commits) Date: Fri, 28 Nov 2008 20:36:24 +0100 Subject: [bknr-cvs] hans changed trunk/libraries/yason/yason.asd Message-ID: Revision: 4096 Author: hans URL: http://bknr.net/trac/changeset/4096 Add missing dependency on alexandria U trunk/libraries/yason/yason.asd Modified: trunk/libraries/yason/yason.asd =================================================================== --- trunk/libraries/yason/yason.asd 2008-11-28 19:30:00 UTC (rev 4095) +++ trunk/libraries/yason/yason.asd 2008-11-28 19:36:24 UTC (rev 4096) @@ -23,7 +23,7 @@ :description "JSON parser/encoder" :long-description "" - :depends-on () + :depends-on (:alexandria) :components ((:file "package") (:file "encode" :depends-on ("package")) From bknr at bknr.net Fri Nov 28 23:56:22 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 29 Nov 2008 00:56:22 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/poi-ms. Message-ID: Revision: 4097 Author: hans URL: http://bknr.net/trac/changeset/4097 Refactor a bit. Add overview page. Add POI selector. U trunk/projects/bos/payment-website/static/poi-ms.css U trunk/projects/bos/payment-website/static/poi-ms.html U trunk/projects/bos/payment-website/static/poi-ms.js Modified: trunk/projects/bos/payment-website/static/poi-ms.css =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-28 19:36:24 UTC (rev 4096) +++ trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-28 23:56:21 UTC (rev 4097) @@ -2,6 +2,10 @@ h2 { font-size: 160% } h3 { font-size: 120% } +ul#media-list { + margin: 2em 0em 2em 0em; +} + ul#media-list li { position: relative; height: 44px; @@ -18,6 +22,11 @@ background-color: #0f0; } +#poi-selector { + width: 300px; + font-size: 180%; +} + .map { position: relative; } Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-28 19:36:24 UTC (rev 4096) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-28 23:56:21 UTC (rev 4097) @@ -17,64 +17,16 @@
    - Am 11.01 wurde in Samboja Lestari ein - kleiner weiblicher Malaienb??r (indonesisch: Bruang Madu = - Honigb??r) von einem Dorfbewohner abgegeben, die Mutter ist - wahrscheinlich get??tet worden. Bei der Ankunft war sie - ungef??hr eine Woche alt und wog nur einen Kilo. Seitdem - wird sie von sechs Pflegern abwechselnd rund um die Uhr - gepflegt. Ihr wurde der Name Arkana gegeben, und - inzwischen wiegt sie schon 1,6 Kilo. Ihre neue - "Mutter", also die Hauptbezugsperson ist Slamet - Mulyono. T??glich wird sie f??r eine Stunde in die Sonne - gebracht, mehrmals t??glich wird ihre Temperatur gemessen, - die genau wie bei Menschen auch zwischen 36 und 37 Grad - liegt. F??llt die Temperatur, wird sie mit Eukalypus??l - (indonesisch: Minyak Kayu Putih) eingerieben. Auch nachdem - sie Milch bekommt, wird sie damit eingerieben, um den - Kreislauf anzuregen. Wenn sie ??lter ist, wird sie Fr??chte - zu essen bekommen, das Hauptnahrungsmittel der - Malaienb??ren. Die Fr??chte, meistens Salak, Mango, Papaya, - Ananas und Zuckerrohr kommen aus dem ??kologischen Garten - Samboja Lestaris Mehrmals t??glich bekommt sie ein frisches - Nest aus verschieden Bl??ttern, normalerweise frisst die - Mutter die Exkremente, und h??lt so das Nest sauber.
    -

    Malaienb??rgehege

    +
    + +
    +
    Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 19:36:24 UTC (rev 4096) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 23:56:21 UTC (rev 4097) @@ -2,8 +2,7 @@ $(document).ready(init); -var poi_id; -var poi; +var pois = {}; Date.prototype.renderDate = function() { return this.getDate() + '.' + this.getMonth() + '.' + (1900 + this.getYear()); @@ -17,55 +16,52 @@ var mediaHandlers = { image: { - icon: function (medium) { return IMG({ src: '/image/' + medium.id, width: 40, height: 40 }) }, - action: function (medium) { - $('#content') - .empty() - .append(H2(null, medium.title), - IMG({ src: '/image/' + medium.id }), BR(), - H3(null, medium.subtitle), - P(null, medium.description)); + icon: function (medium) { + return IMG({ src: '/image/' + medium.id + '/thumbnail,,40,40', width: 40, height: 40 }) + }, + makeViewer: function (medium) { + return IMG({ src: '/image/' + medium.id }); } }, panorama: { - icon: function (medium) { return IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }) }, - action: function (medium) { - $('#content') - .empty() - .append(H2(null, medium.title), - APPLET({ archive: '/static/ptviewer.jar', - code: 'ptviewer.class', - width: 400, - height: 300}, - PARAM({ name: 'file', value: '/image/' + medium.id}), - PARAM({ name: 'cursor', value: 'MOVE' })), - H3(null, medium.subtitle), - P(null, medium.description)); + icon: function (medium) { + return IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }) + }, + makeViewer: function (medium) { + return APPLET({ archive: '/static/ptviewer.jar', + code: 'ptviewer.class', + width: 400, + height: 300}, + PARAM({ name: 'file', value: '/image/' + medium.id}), + PARAM({ name: 'cursor', value: 'MOVE' })); } }, movie: { - icon: function (medium) { return IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }) }, - action: function (medium) { - $('#content') - .empty() - .append(H2(null, medium.title), - OBJECT({ width: 360, height: 360 }, - PARAM({ name: "movie", value: medium.url }), - EMBED({ src: medium.url, type: 'application/x-shockwave-flash', - width: 400, height: 300 })), BR(), - H3(null, medium.subtitle), - P(null, medium.description)); + icon: function (medium) { + return IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }) + }, + makeViewer: function (medium) { + return OBJECT({ width: 360, height: 360 }, + PARAM({ name: "movie", value: medium.url }), + EMBED({ src: medium.url, type: 'application/x-shockwave-flash', + width: 400, height: 300 })); } } }; -function selectMedium(fn, e) { +function showMedium(e) { + var medium = e.data; $('#media-list *').removeClass('active'); $(e.target).addClass('active'); - fn(); + $('#content') + .empty() + .append(H2(null, medium.title), + mediaHandlers[medium.mediumType].makeViewer(medium), + H3(null, medium.subtitle), + P(null, medium.description)); } -function loadMainInfo() { +function loadMainInfo(poi) { var map = []; for (var y = -1; y < 3; y++) { var tiles = []; @@ -81,6 +77,7 @@ } map.push(IMG({ 'class': 'icon', src: '/images/' + poi.icon + '.gif', + width: 16, height: 16, style: 'left: ' + (poi.x - ((Math.floor(poi.x / 90) - 1) * 90) - 8) + 'px; ' + 'top: ' + (poi.y - ((Math.floor(poi.y / 90) - 1) * 90) - 8) + 'px'})); @@ -89,40 +86,69 @@ P(null, poi.description)); } -var B = createDOMFunc('b', null); +function showPOI(e) { + var poi = pois[(e.target && e.target.value) || e.data]; -function loadPoi() { - document.title = poi.title; - $('.yui-b h1').html(poi.title); - loadMainInfo(); $('#media-list').empty(); - map(function (medium) { - if (mediaHandlers[medium.mediumType]) { - $('#media-list') - .append($(A({ href: '#' }, - LI(null, - mediaHandlers[medium.mediumType].icon(medium), - (new Date(medium.timestamp)).renderDate(), - BR(), - B(null, medium.title || medium.name)))) - .bind('click', null, partial(selectMedium, partial(mediaHandlers[medium.mediumType].action, medium)))); - } - }, poi.media); + if (!poi) { + showOverview(); + } else { + document.title = poi.title; + $('.yui-b h1').html(poi.title); + loadMainInfo(poi); + map(function (medium) { + if (mediaHandlers[medium.mediumType]) { + $('#media-list') + .append($(A({ href: '#' }, + LI(null, + mediaHandlers[medium.mediumType].icon(medium), + (new Date(medium.timestamp)).renderDate(), + BR(), + B(null, medium.title || medium.name)))) + .bind('click', medium, showMedium)); + } + }, poi.media); + } } +function showOverview() { + + var elements = []; + elements.push(IMG({ src: '/infosystem/bilder/karte_uebersicht.jpg', width: 360, height: 360 })); + for (var i in pois) { + var poi = pois[i]; + var link = A({ href: '#' }, + IMG({ 'class': 'icon', + src: '/images/' + poi.icon + '.gif', + width: 16, height: 16, + title: poi.title, + style: 'left: ' + (Math.round(poi.x / 30) - 8) + 'px; ' + + 'top: ' + (Math.round(poi.y / 30) - 8) + 'px' })); + $(link).bind('click', poi.id, showPOI); + elements.push(link); + } + + $('#content') + .empty() + .append(H2(null, 'XXuebersichtXX'), + DIV({ 'class': 'map' }, elements)); +} + function loadData(data) { try { - var pois = data.pois; + for (var i in data.pois) { + var poi = data.pois[i]; + pois[poi.id] = poi; + $('#poi-selector').append(OPTION({ value: poi.id }, poi.title)); + } + $('#poi-selector').bind('change', null, showPOI); - for (var i in pois) { - if (pois[i].id == poi_id) { - poi = pois[i]; - loadPoi(); - return; - } + var poi_id = document.location.hash.replace(/#/, ""); + if (poi_id) { + showPOI({ data: poi_id }); + } else { + showOverview(); } - - alert('invalid poi id (not found)'); } catch (e) { alert(e); @@ -130,11 +156,5 @@ } function init() { - poi_id = document.location.hash.replace(/#/, ""); - - if (poi_id.match(/^[0-9]+$/)) { - loadJSONDoc('/poi-json').addCallback(loadData); - } else { - alert('invalid poi id'); - } + loadJSONDoc('/poi-json').addCallback(loadData); } \ No newline at end of file From bknr at bknr.net Sat Nov 29 00:02:13 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 29 Nov 2008 01:02:13 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/poi-ms.js Message-ID: Revision: 4098 Author: hans URL: http://bknr.net/trac/changeset/4098 Add width/height attributes to images. U trunk/projects/bos/payment-website/static/poi-ms.js Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-28 23:56:21 UTC (rev 4097) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 00:02:12 UTC (rev 4098) @@ -20,7 +20,9 @@ return IMG({ src: '/image/' + medium.id + '/thumbnail,,40,40', width: 40, height: 40 }) }, makeViewer: function (medium) { - return IMG({ src: '/image/' + medium.id }); + return IMG({ src: '/image/' + medium.id, + width: medium.width, + height: medium.height }); } }, panorama: { From bknr at bknr.net Sat Nov 29 14:35:53 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 29 Nov 2008 15:35:53 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/ Message-ID: Revision: 4099 Author: hans URL: http://bknr.net/trac/changeset/4099 Work around IE problems with flash and applet handling. A trunk/projects/bos/payment-website/static/c.swf A trunk/projects/bos/payment-website/static/jquery-nightly.js U trunk/projects/bos/payment-website/static/poi-ms.css U trunk/projects/bos/payment-website/static/poi-ms.html U trunk/projects/bos/payment-website/static/poi-ms.js Change set too large, please see URL above From bknr at bknr.net Sat Nov 29 16:11:08 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 29 Nov 2008 17:11:08 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/payment-website/static/poi-ms.js Message-ID: Revision: 4100 Author: hans URL: http://bknr.net/trac/changeset/4100 refactor some U trunk/projects/bos/payment-website/static/poi-ms.js Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 14:35:52 UTC (rev 4099) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 16:11:08 UTC (rev 4100) @@ -19,33 +19,31 @@ icon: function (medium) { return IMG({ src: '/image/' + medium.id + '/thumbnail,,40,40', width: 40, height: 40 }) }, - makeViewer: function (medium, container) { - replaceChildNodes(container, - IMG({ src: '/image/' + medium.id, - width: medium.width, - height: medium.height })); + makeViewer: function (medium) { + return IMG({ src: '/image/' + medium.id, + width: medium.width, + height: medium.height }); } }, panorama: { icon: function (medium) { return IMG({ src: '/static/panorama-icon.gif', width: 40, height: 40 }) }, - makeViewer: function (medium, container) { - replaceChildNodes(container, - APPLET({ id: 'applet', - archive: '/static/ptviewer.jar', - code: 'ptviewer.class', - width: 400, - height: 300}, - PARAM({ name: 'file', value: '/image/' + medium.id}), - PARAM({ name: 'cursor', value: 'MOVE' }))); + makeViewer: function (medium) { + return APPLET({ id: 'applet', + archive: '/static/ptviewer.jar', + code: 'ptviewer.class', + width: 400, + height: 300}, + PARAM({ name: 'file', value: '/image/' + medium.id}), + PARAM({ name: 'cursor', value: 'MOVE' })); } }, movie: { icon: function (medium) { return IMG({ src: '/static/movie-icon.gif', width: 40, height: 40 }) }, - makeViewer: function (medium, container) { + makeViewer: function (medium) { /* can't use DOM objects like below because IE does not grok it * return OBJECT({ id: 'applet', * width: 360, height: 360, @@ -54,11 +52,13 @@ * PARAM({ name: "movie", * value: "c.swf?path=" + medium.url })); */ - container.innerHTML = + var div = DIV(); + div.innerHTML = "" + "" + ""; + return div; } } @@ -76,13 +76,10 @@ $('#media-list *').removeClass('active'); $(e.target).addClass('active'); - var container = DIV(); - mediaHandlers[medium.mediumType].makeViewer(medium, container); - $('#content') .empty() .append(H2(null, medium.title), - container, + mediaHandlers[medium.mediumType].makeViewer(medium), H3(null, medium.subtitle), P(null, medium.description)); } From bknr at bknr.net Sat Nov 29 21:55:51 2008 From: bknr at bknr.net (BKNR Commits) Date: Sat, 29 Nov 2008 22:55:51 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4101 Author: hans URL: http://bknr.net/trac/changeset/4101 Show last sponsors on overview page. U trunk/projects/bos/m2/m2.lisp U trunk/projects/bos/m2/packages.lisp U trunk/projects/bos/payment-website/static/poi-ms.html U trunk/projects/bos/payment-website/static/poi-ms.js U trunk/projects/bos/web/contract-handlers.lisp U trunk/projects/bos/web/poi-handlers.lisp U trunk/projects/bos/web/sponsor-handlers.lisp U trunk/projects/bos/web/webserver.lisp Modified: trunk/projects/bos/m2/m2.lisp =================================================================== --- trunk/projects/bos/m2/m2.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/m2/m2.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -173,6 +173,9 @@ (or (call-next-method) "en")) +(defun sponsor-paid-contracts (sponsor) + (remove-if-not #'contract-paidp (sponsor-contracts sponsor))) + (defvar *sponsor-counter-lock* (bknr.datastore::mp-make-lock "Sponsor Counter Lock")) (defvar *sponsor-counter* 0) @@ -494,10 +497,6 @@ (with-points (center) (geo-utm:utm-x-y-to-lon-lat (+ +nw-utm-x+ center-x) (- +nw-utm-y+ center-y) +utm-zone+ t)))) -(defun tx-make-contract (sponsor m2-count &key date paidp expires) - (warn "Old tx-make-contract transaction used, contract dates may be wrong") - (tx-do-make-contract sponsor m2-count :date date :paidp paidp :expires expires)) - (define-condition allocation-areas-exhausted (simple-error) ((numsqm :initarg :numsqm :reader numsqm)) (:report (lambda (condition stream) @@ -684,22 +683,53 @@ (defun make-m2-javascript (sponsor) "Erzeugt das Quadratmeter-Javascript f??r die angegebenen Contracts" (with-output-to-string (*standard-output*) - (let ((paid-contracts (remove nil (sponsor-contracts sponsor) :key #'contract-paidp))) - (format t "profil = {};~%") - (format t "profil.id = ~D;~%" (store-object-id sponsor)) - (format t "profil.name = ~S;~%" (string-safe (or (user-full-name sponsor) "[anonym]"))) - (format t "profil.country = ~S;~%" (or (sponsor-country sponsor) "[unbekannt]")) - (format t "profil.anzahl = ~D;~%" (loop for contract in paid-contracts - sum (length (contract-m2s contract)))) - (format t "profil.nachricht = \"~A\";~%" (string-safe (sponsor-info-text sponsor))) - (format t "profil.contracts = [ ];~%") - (loop for contract in paid-contracts - do (destructuring-bind (left top width height) (contract-bounding-box contract) - (format t "profil.contracts.push({ id: ~A, left: ~A, top: ~A, width: ~A, height: ~A, date: ~S });~%" - (store-object-id contract) - left top width height - (format-date-time (contract-date contract) :show-time nil))))))) + (format t "profil = {};~%") + (format t "profil.id = ~D;~%" (store-object-id sponsor)) + (format t "profil.name = ~S;~%" (string-safe (or (user-full-name sponsor) "[anonym]"))) + (format t "profil.country = ~S;~%" (or (sponsor-country sponsor) "[unbekannt]")) + (format t "profil.anzahl = ~D;~%" (loop for contract in (sponsor-paid-contracts sponsor) + sum (length (contract-m2s contract)))) + (format t "profil.nachricht = \"~A\";~%" (string-safe (sponsor-info-text sponsor))) + (format t "profil.contracts = [ ];~%") + (dolist (contract (sponsor-paid-contracts sponsor)) + (destructuring-bind (left top width height) (contract-bounding-box contract) + (format t "profil.contracts.push({ id: ~A, left: ~A, top: ~A, width: ~A, height: ~A, date: ~S });~%" + (store-object-id contract) + left top width height + (format-date-time (contract-date contract) :show-time nil)))))) +(defmethod json-encode progn ((contract contract)) + (destructuring-bind (left top width height) (contract-bounding-box contract) + (json:encode-object-elements + "timestamp" (format-date-time (contract-date contract) :mail-style t) + "count" (length (contract-m2s contract)) + "top" top + "left" left + "width" width + "height" height))) + +(defmethod json-encode progn ((sponsor sponsor)) + (json:encode-object-elements + "name" (user-full-name sponsor) + "country" (or (sponsor-country sponsor) "sponsor-country-unknown") + "sqmCount" (reduce #'+ (mapcar (alexandria:compose #'length #'contract-m2s) (sponsor-contracts sponsor)) + :initial-value 0) + "infoText" (sponsor-info-text sponsor)) + (unless (user-full-name sponsor) + (json:encode-object-element "anonymous" t)) + (json:with-object-element ("contracts") + (json:with-array () + (dolist (contract (sponsor-paid-contracts sponsor)) + (json:with-object () + (json-encode contract)))))) + +(defun last-sponsors-as-json () + "Render the last sponsors as JSON" + (json:with-array () + (dolist (sponsor (mapcar #'contract-sponsor (last-paid-contracts))) + (json:with-object () + (json-encode sponsor))))) + (defun delete-directory (pathname) (cl-fad:delete-directory-and-files pathname :if-does-not-exist :ignore)) Modified: trunk/projects/bos/m2/packages.lisp =================================================================== --- trunk/projects/bos/m2/packages.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/m2/packages.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -171,6 +171,7 @@ #:contract-stats-for-country #:last-paid-contracts #:do-sponsor-countries + #:last-sponsors-as-json #:make-m2-javascript #:recolorize-contracts Modified: trunk/projects/bos/payment-website/static/poi-ms.html =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/payment-website/static/poi-ms.html 2008-11-29 21:55:51 UTC (rev 4101) @@ -21,13 +21,15 @@
- + + +
-
    -
+
+
Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 21:55:51 UTC (rev 4101) @@ -3,11 +3,16 @@ $(document).ready(init); var pois = {}; +var sponsors = []; Date.prototype.renderDate = function() { return this.getDate() + '.' + this.getMonth() + '.' + (this.getYear() > 2000 ? this.getYear() : (1900 + this.getYear())); } +function NLS(key) { + return key; // for now +} + var B = createDOMFunc('b', null); var OBJECT = createDOMFunc('object'); var PARAM = createDOMFunc('param'); @@ -84,20 +89,28 @@ P(null, medium.description)); } -function loadMainInfo(poi) { - var map = []; +function makeMap(centerX, centerY) { + var rows = []; + for (var y = -1; y < 3; y++) { var tiles = []; for (var x = -1; x < 3; x++) { tiles.push(IMG({ 'class': 'map-tile', src: '/overview/' - + (Math.floor(poi.x / 90) + x) * 90 + + (Math.floor(centerX / 90) + x) * 90 + '/' - + (Math.floor(poi.y / 90) + y) * 90, + + (Math.floor(centerY / 90) + y) * 90, width: 90, height: 90 })); } - map.push(DIV(null, tiles)); + rows.push(DIV(null, tiles)); } + + return DIV({ 'class': 'map' }, rows); +} + +function loadMainInfo(poi) { + var map = []; + map.push(makeMap(poi.x, poi.y)); map.push(IMG({ 'class': 'icon', src: '/images/' + poi.icon + '.gif', width: 16, height: 16, @@ -112,10 +125,13 @@ function showPOI(e) { var poi = pois[(e.target && e.target.value) || e.data]; - $('#media-list').empty(); + + $('#left-bar').empty().append(UL({ id: 'media-list' })); if (!poi) { showOverview(); } else { + $('#poi-selector').val(poi.id); + document.title = poi.title; $('.yui-b h1').html(poi.title); loadMainInfo(poi); @@ -134,8 +150,18 @@ } } +function showSponsor(e) { + var sponsor = e.data; + $('#content') + .empty() + .append(H2(null, sponsor.name), + makeMap(sponsor.contracts[0].left, sponsor.contracts[0].top)); +} + function showOverview() { + $('#poi-selector').val('overview'); + var elements = []; elements.push(IMG({ src: '/infosystem/bilder/karte_uebersicht.jpg', width: 360, height: 360 })); for (var i in pois) { @@ -153,18 +179,33 @@ $('#content') .empty() - .append(H2(null, 'XXuebersichtXX'), + .append(H2(null, NLS('??bersicht')), DIV({ 'class': 'map' }, elements)); + + $('#left-bar') + .empty() + .append(H3(NLS("Letzte Sponsoren")), + UL({ id: 'sponsor-list' })); + + map(function (sponsor) { + $('#sponsor-list') + .append($(A({ href: '#' }, + LI(null, + IMG({ src: '/images/flags/' + sponsor.country.toLowerCase() + '.gif'}), + (new Date(sponsor.contracts[0].timestamp)).renderDate(), + BR(), + B(null, sponsor.anonymous ? NLS('anonym') : sponsor.name), + " ", sponsor.contracts[0].count, " m??"))) + .bind('click', sponsor, showSponsor)); + }, sponsors.slice(0, 10)); } -function loadData(data) { +function loadSponsors(data) { try { - for (var i in data.pois) { - var poi = data.pois[i]; - pois[poi.id] = poi; - $('#poi-selector').append(OPTION({ value: poi.id }, poi.title)); + for (var i in data.sponsors) { + var sponsor = data.sponsors[i]; + sponsors.push(sponsor); } - $('#poi-selector').bind('change', null, showPOI); var poi_id = document.location.hash.replace(/#/, ""); if (poi_id) { @@ -178,6 +219,24 @@ } } +function loadPOIs(data) { + try { + for (var i in data.pois) { + var poi = data.pois[i]; + pois[poi.id] = poi; + $('#poi-selector').append(OPTION({ value: poi.id }, poi.title)); + } + $('#poi-selector').bind('change', null, showPOI); + + loadJSONDoc('/last-sponsors-json').addCallback(loadSponsors); + } + catch (e) { + alert(e); + } +} + function init() { - loadJSONDoc('/poi-json').addCallback(loadData); + $('#small-map a').bind('click', showPOI); + + loadJSONDoc('/poi-json').addCallback(loadPOIs); } \ No newline at end of file Modified: trunk/projects/bos/web/contract-handlers.lisp =================================================================== --- trunk/projects/bos/web/contract-handlers.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/web/contract-handlers.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -44,3 +44,4 @@ (not (contract-tree-needs-update-p))) "READY" "PROCESSING"))))) + Modified: trunk/projects/bos/web/poi-handlers.lisp =================================================================== --- trunk/projects/bos/web/poi-handlers.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/web/poi-handlers.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -396,20 +396,22 @@ (setf (hunchentoot:header-out :last-modified) (hunchentoot:rfc-1123-date pois-last-change)))) +(defun last-contracts-handle-if-modified-since () + (hunchentoot:handle-if-modified-since + (reduce #'max (last-paid-contracts) + :key (lambda (contract) (store-object-last-change contract 0))))) + (defmethod handle ((handler poi-javascript-handler)) (poi-handle-if-modified-since) - (let* ((last-paid-contracts (last-paid-contracts)) - (timestamp (reduce #'max last-paid-contracts - :key (lambda (contract) (store-object-last-change contract 0))))) - (hunchentoot:handle-if-modified-since timestamp) - (with-http-response (:content-type "text/html; charset=UTF-8") - (with-http-body () - (html - ((:script :language "JavaScript") - (:princ (make-poi-javascript (request-language))) - (:princ "parent.poi_fertig(pois, anzahlSponsoren, anzahlVerkauft);") - (:princ (format nil "parent.last_sponsors([~{~A~^,~%~}]);" - (mapcar #'contract-js last-paid-contracts))))))))) + (last-contracts-handle-if-modified-since) + (with-http-response (:content-type "text/html; charset=UTF-8") + (with-http-body () + (html + ((:script :language "JavaScript") + (:princ (make-poi-javascript (request-language))) + (:princ "parent.poi_fertig(pois, anzahlSponsoren, anzahlVerkauft);") + (:princ (format nil "parent.last_sponsors([~{~A~^,~%~}]);" + (mapcar #'contract-js (last-paid-contracts))))))))) ;;; poi-xml-handler (defun write-poi-xml (poi language) Modified: trunk/projects/bos/web/sponsor-handlers.lisp =================================================================== --- trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -345,4 +345,15 @@ (mail-print-pdf contract) (html "The print certificate has been sent to the relevant BOS office address by email." :br)) (let ((sponsor (contract-sponsor contract))) - (cmslink #?"edit-sponsor/$((store-object-id sponsor))" "return to sponsor"))))) \ No newline at end of file + (cmslink #?"edit-sponsor/$((store-object-id sponsor))" "return to sponsor"))))) + + +;;; last-sponsors-json-handler +(defclass last-sponsors-json-handler (page-handler) + ()) + +(defmethod handle ((handler last-sponsors-json-handler)) + (last-contracts-handle-if-modified-since) + (with-json-response () + (json:with-object-element ("sponsors") + (bos.m2:last-sponsors-as-json)))) \ No newline at end of file Modified: trunk/projects/bos/web/webserver.lisp =================================================================== --- trunk/projects/bos/web/webserver.lisp 2008-11-29 16:11:08 UTC (rev 4100) +++ trunk/projects/bos/web/webserver.lisp 2008-11-29 21:55:51 UTC (rev 4101) @@ -185,6 +185,7 @@ ("/poi-javascript" poi-javascript-handler) ("/m2-javascript" m2-javascript-handler) ("/poi-json" poi-json-handler) + ("/last-sponsors-json" last-sponsors-json-handler) ("/sponsor-login" sponsor-login-handler) ("/create-allocation-area" create-allocation-area-handler) ("/allocation-area" allocation-area-handler) From bknr at bknr.net Sun Nov 30 10:29:41 2008 From: bknr at bknr.net (BKNR Commits) Date: Sun, 30 Nov 2008 11:29:41 +0100 Subject: [bknr-cvs] hans changed trunk/projects/bos/ Message-ID: Revision: 4102 Author: hans URL: http://bknr.net/trac/changeset/4102 Improve map handling. Add infrastructure for sponsor queries. U trunk/projects/bos/m2/m2.lisp U trunk/projects/bos/m2/packages.lisp U trunk/projects/bos/payment-website/static/poi-ms.css U trunk/projects/bos/payment-website/static/poi-ms.js U trunk/projects/bos/web/packages.lisp U trunk/projects/bos/web/sponsor-handlers.lisp U trunk/projects/bos/web/webserver.lisp Modified: trunk/projects/bos/m2/m2.lisp =================================================================== --- trunk/projects/bos/m2/m2.lisp 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/m2/m2.lisp 2008-11-30 10:29:40 UTC (rev 4102) @@ -712,8 +712,6 @@ (json:encode-object-elements "name" (user-full-name sponsor) "country" (or (sponsor-country sponsor) "sponsor-country-unknown") - "sqmCount" (reduce #'+ (mapcar (alexandria:compose #'length #'contract-m2s) (sponsor-contracts sponsor)) - :initial-value 0) "infoText" (sponsor-info-text sponsor)) (unless (user-full-name sponsor) (json:encode-object-element "anonymous" t)) @@ -723,10 +721,10 @@ (json:with-object () (json-encode contract)))))) -(defun last-sponsors-as-json () - "Render the last sponsors as JSON" +(defun sponsors-as-json (sponsors) + "Render the SPONSORS as JSON" (json:with-array () - (dolist (sponsor (mapcar #'contract-sponsor (last-paid-contracts))) + (dolist (sponsor sponsors) (json:with-object () (json-encode sponsor))))) Modified: trunk/projects/bos/m2/packages.lisp =================================================================== --- trunk/projects/bos/m2/packages.lisp 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/m2/packages.lisp 2008-11-30 10:29:40 UTC (rev 4102) @@ -171,7 +171,7 @@ #:contract-stats-for-country #:last-paid-contracts #:do-sponsor-countries - #:last-sponsors-as-json + #:sponsors-as-json #:make-m2-javascript #:recolorize-contracts Modified: trunk/projects/bos/payment-website/static/poi-ms.css =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/payment-website/static/poi-ms.css 2008-11-30 10:29:40 UTC (rev 4102) @@ -23,10 +23,14 @@ font-size: 180%; } +.map img { + vertical-align: bottom; /* align images without space for character descenders */ +} + .map { position: relative; } -.map .icon { +.map .icon, .map .contract { position: absolute; } Modified: trunk/projects/bos/payment-website/static/poi-ms.js =================================================================== --- trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/payment-website/static/poi-ms.js 2008-11-30 10:29:40 UTC (rev 4102) @@ -72,7 +72,7 @@ function showMedium(e) { var medium = e.data; - /* work around jQuery bug when trying to remove applet from dom with IE */ + /* Work around jQuery bug when trying to remove applet from DOM with IE. */ var applet = $("#applet")[0]; if (applet) { applet.parentNode.removeChild(applet); @@ -105,28 +105,36 @@ rows.push(DIV(null, tiles)); } - return DIV({ 'class': 'map' }, rows); + return DIV(null, rows); } +function positionMapIcon(img, x, y) { + img.style.left = (x - (Math.floor(x / 90) - 1) * 90) + 'px'; + img.style.top = (y - (Math.floor(y / 90) - 1) * 90) + 'px'; + return img; +} + function loadMainInfo(poi) { - var map = []; - map.push(makeMap(poi.x, poi.y)); - map.push(IMG({ 'class': 'icon', - src: '/images/' + poi.icon + '.gif', - width: 16, height: 16, - style: 'left: ' + (poi.x - ((Math.floor(poi.x / 90) - 1) * 90) - 8) + 'px; ' - + 'top: ' + (poi.y - ((Math.floor(poi.y / 90) - 1) * 90) - 8) + 'px'})); - $('#content').empty().append(H2(null, poi.subtitle), - DIV({ 'class': 'map' }, map), - P(null, poi.description)); + $('#content') + .empty() + .append(H2(null, poi.subtitle), + DIV({ 'class': 'map' }, + makeMap(poi.x, poi.y), + positionMapIcon(IMG({ 'class': 'icon', + src: '/images/' + poi.icon + '.gif', + width: 16, height: 16}), + poi.x - 8, poi.y - 8)), + P(null, poi.description)); } function showPOI(e) { var poi = pois[(e.target && e.target.value) || e.data]; - $('#left-bar').empty().append(UL({ id: 'media-list' })); + $('#left-bar') + .empty() + .append(UL({ id: 'media-list' })); if (!poi) { showOverview(); } else { @@ -152,10 +160,17 @@ function showSponsor(e) { var sponsor = e.data; + var contract = sponsor.contracts[0]; $('#content') .empty() .append(H2(null, sponsor.name), - makeMap(sponsor.contracts[0].left, sponsor.contracts[0].top)); + DIV({ 'class': 'map' }, + makeMap(contract.left, contract.top), + positionMapIcon(IMG({ 'class': 'contract', + src: '/contract-image/' + contract.id, + width: contract.width, height: contract.height}), + contract.left, contract.top)) + ); } function showOverview() { @@ -228,7 +243,7 @@ } $('#poi-selector').bind('change', null, showPOI); - loadJSONDoc('/last-sponsors-json').addCallback(loadSponsors); + loadJSONDoc('/sponsors-json').addCallback(loadSponsors); } catch (e) { alert(e); Modified: trunk/projects/bos/web/packages.lisp =================================================================== --- trunk/projects/bos/web/packages.lisp 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/web/packages.lisp 2008-11-30 10:29:40 UTC (rev 4102) @@ -7,6 +7,7 @@ :cl-user :cl-interpol :cl-ppcre + :alexandria :xhtml-generator :cxml :puri @@ -22,4 +23,5 @@ :bos.m2 :bos.m2.config) (:shadowing-import-from :cl-interpol #:quote-meta-chars) + (:shadowing-import-from :alexandria #:array-index) (:export)) Modified: trunk/projects/bos/web/sponsor-handlers.lisp =================================================================== --- trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/web/sponsor-handlers.lisp 2008-11-30 10:29:40 UTC (rev 4102) @@ -348,12 +348,32 @@ (cmslink #?"edit-sponsor/$((store-object-id sponsor))" "return to sponsor"))))) -;;; last-sponsors-json-handler -(defclass last-sponsors-json-handler (page-handler) +;;; sponsors-json-handler +(defclass sponsors-json-handler (page-handler) ()) -(defmethod handle ((handler last-sponsors-json-handler)) +(defun sponsors-matching (query) + (when (< 2 (length query)) + (remove-if-not (curry #'search (string-downcase query)) + (class-instances 'sponsor) + :key (compose #'string-downcase #'user-full-name)))) + +(defun largest-sponsors () + (mapcar #'contract-sponsor + (subseq (sort (copy-list (class-instances 'contract)) + #'> + :key (compose #'length #'contract-m2s)) + 0 20))) + +(defmethod handle ((handler sponsors-json-handler)) (last-contracts-handle-if-modified-since) (with-json-response () (json:with-object-element ("sponsors") - (bos.m2:last-sponsors-as-json)))) \ No newline at end of file + (bos.m2:sponsors-as-json + (cond + ((query-param "q") + (sponsors-matching (query-param "q"))) + ((query-param "largest") + (largest-sponsors)) + (t + (mapcar #'contract-sponsor (last-paid-contracts)))))))) \ No newline at end of file Modified: trunk/projects/bos/web/webserver.lisp =================================================================== --- trunk/projects/bos/web/webserver.lisp 2008-11-29 21:55:51 UTC (rev 4101) +++ trunk/projects/bos/web/webserver.lisp 2008-11-30 10:29:40 UTC (rev 4102) @@ -185,7 +185,7 @@ ("/poi-javascript" poi-javascript-handler) ("/m2-javascript" m2-javascript-handler) ("/poi-json" poi-json-handler) - ("/last-sponsors-json" last-sponsors-json-handler) + ("/sponsors-json" sponsors-json-handler) ("/sponsor-login" sponsor-login-handler) ("/create-allocation-area" create-allocation-area-handler) ("/allocation-area" allocation-area-handler)