Author: hhubner Date: Thu Jan 17 11:36:28 2008 New Revision: 2336
Added: branches/trunk-reorg/projects/scrabble/website/scrabble-resizable-attempt.js Modified: branches/trunk-reorg/bknr/web/src/bknr-web.asd branches/trunk-reorg/bknr/web/src/web/handlers.lisp branches/trunk-reorg/bknr/web/src/web/menu.lisp branches/trunk-reorg/bknr/web/src/web/templates.lisp branches/trunk-reorg/bknr/web/src/web/web-utils.lisp branches/trunk-reorg/projects/lisp-ecoop/src/lisp-ecoop.asd branches/trunk-reorg/projects/lisp-ecoop/src/macros.lisp branches/trunk-reorg/projects/lisp-ecoop/src/packages.lisp branches/trunk-reorg/projects/lisp-ecoop/src/schedule.lisp branches/trunk-reorg/projects/lisp-ecoop/src/webserver.lisp branches/trunk-reorg/projects/scrabble/src/web.lisp branches/trunk-reorg/projects/scrabble/website/scrabble.html branches/trunk-reorg/projects/scrabble/website/scrabble.js branches/trunk-reorg/thirdparty/hunchentoot-0.14.7/misc.lisp branches/trunk-reorg/thirdparty/parenscript/src/js-macrology.lisp branches/trunk-reorg/thirdparty/slime/slime.el branches/trunk-reorg/thirdparty/slime/swank.lisp Log: Save pending lisp-ecoop and scrabble changes.
Modified: branches/trunk-reorg/bknr/web/src/bknr-web.asd ============================================================================== --- branches/trunk-reorg/bknr/web/src/bknr-web.asd (original) +++ branches/trunk-reorg/bknr/web/src/bknr-web.asd Thu Jan 17 11:36:28 2008 @@ -32,7 +32,8 @@ :xhtmlgen :puri :bknr-datastore - :bknr-data-impex) + :bknr-data-impex + :parenscript)
:components ((:file "packages") @@ -59,7 +60,6 @@ :depends-on ("parse-xml" "rss"))) :depends-on ("packages"))
- #+notyet (:module "web" :components ((:file "site") ;; data (:file "host")
Modified: branches/trunk-reorg/bknr/web/src/web/handlers.lisp ============================================================================== --- branches/trunk-reorg/bknr/web/src/web/handlers.lisp (original) +++ branches/trunk-reorg/bknr/web/src/web/handlers.lisp Thu Jan 17 11:36:28 2008 @@ -229,7 +229,7 @@ (setf (session-variable :login-redirect-uri) (redirect-uri (request-uri req))) (redirect (website-make-path *website* "login") req)) - (if (member :notrap net.aserve::*debug-current* :test #'eq) + (if hunchentoot:*catch-errors-p* (handle handler req) (handler-bind ((error #'(lambda (e) (with-bknr-http-response (*req* :content-type "text/html; charset=UTF-8" @@ -533,7 +533,7 @@ (defun show-page-with-error-handlers (fn req &key response title) (unless response (setf response *response-ok*)) ; can't default because used from macros and *response-ok* is not a constant - (if (member :notrap net.aserve::*debug-current*) + (if hunchentoot:*catch-errors-p* (with-bknr-http-response (req :content-type "text/html; charset=UTF-8" :response response) (with-http-body (req *ent*) (website-show-page *website* fn title)))
Modified: branches/trunk-reorg/bknr/web/src/web/menu.lisp ============================================================================== --- branches/trunk-reorg/bknr/web/src/web/menu.lisp (original) +++ branches/trunk-reorg/bknr/web/src/web/menu.lisp Thu Jan 17 11:36:28 2008 @@ -50,7 +50,7 @@ (when title (html ((:div :class "title") (:princ-safe title)))) (dolist (item (menu-items menu)) - (let ((item-is-active (in-subtree (puri:uri-path (net.aserve:request-uri *req*)) (item-url item)))) + (let ((item-is-active (in-subtree (request-uri) (item-url item)))) (with-slots (url title active-image inactive-image) item (let ((link-url (format nil "~A~A" (website-base-href *website*) url))) (cond
Modified: branches/trunk-reorg/bknr/web/src/web/templates.lisp ============================================================================== --- branches/trunk-reorg/bknr/web/src/web/templates.lisp (original) +++ branches/trunk-reorg/bknr/web/src/web/templates.lisp Thu Jan 17 11:36:28 2008 @@ -12,11 +12,11 @@ "/usr/local/share/xml/catalog.ports"))
(eval-when (:load-toplevel :execute) - (let ((env-catalog (assoc :xmlcatalog ext:*environment-list*))) + (let ((env-catalog (sb-ext:posix-getenv "XMLCATALOG"))) (when env-catalog - (pushnew (cdr env-catalog) *template-dtd-catalog* :test #'equal)))) + (pushnew env-catalog *template-dtd-catalog* :test #'equal))))
-;; user-error is supposed to be raised when an error is provoced by +;; user-error is supposed to be raised when an error is provoked by ;; the user (i.e. by supplying invalid form data).
(define-condition user-error (simple-error) @@ -272,7 +272,7 @@ handler req)))))))
(defun invoke-with-error-handlers (fn handler req) - (if (member :notrap net.aserve::*debug-current*) + (if hunchentoot:*catch-errors-p* (handler-case (funcall fn) (template-not-found (c)
Modified: branches/trunk-reorg/bknr/web/src/web/web-utils.lisp ============================================================================== --- branches/trunk-reorg/bknr/web/src/web/web-utils.lisp (original) +++ branches/trunk-reorg/bknr/web/src/web/web-utils.lisp Thu Jan 17 11:36:28 2008 @@ -293,4 +293,4 @@ (princ " />"))))
(defun encode-urlencoded (string) - (regex-replace-all #?r"+" (net.aserve::encode-form-urlencoded string) "%20")) \ No newline at end of file + (url-encode string)) \ No newline at end of file
Modified: branches/trunk-reorg/projects/lisp-ecoop/src/lisp-ecoop.asd ============================================================================== --- branches/trunk-reorg/projects/lisp-ecoop/src/lisp-ecoop.asd (original) +++ branches/trunk-reorg/projects/lisp-ecoop/src/lisp-ecoop.asd Thu Jan 17 11:36:28 2008 @@ -16,15 +16,22 @@ :description "Website for the LISP ECOOP Workshops" :long-description ""
- :depends-on (:bknr-modules :cxml :klammerscript) + :depends-on (:bknr-datastore + :bknr-web + :cxml)
:components ((:file "packages") (:file "config" :depends-on ("packages")) (:file "macros" :depends-on ("config")) + #+(or) (:file "schedule" :depends-on ("macros")) + #+(or) (:file "participant" :depends-on ("macros" "schedule")) + #+(or) (:file "mail" :depends-on ("participant")) + #+(or) (:file "tags" :depends-on ("participant")) + #+(or) (:file "handlers" :depends-on ("participant")) - (:file "webserver" :depends-on ("handlers")) + (:file "webserver" :depends-on (#+(or) "handlers")) (:file "init" :depends-on ("webserver"))))
Modified: branches/trunk-reorg/projects/lisp-ecoop/src/macros.lisp ============================================================================== --- branches/trunk-reorg/projects/lisp-ecoop/src/macros.lisp (original) +++ branches/trunk-reorg/projects/lisp-ecoop/src/macros.lisp Thu Jan 17 11:36:28 2008 @@ -1,6 +1,6 @@ (in-package :lisp-ecoop)
-(defvar *dtd* (ext:unix-namestring (merge-pathnames #p"src/lisp-ecoop.dtd" lisp-ecoop.config::*root-directory*))) +(defvar *dtd* (namestring (merge-pathnames #p"src/lisp-ecoop.dtd" lisp-ecoop.config::*root-directory*)))
(defun compute-slot (class slot) (destructuring-bind (name access &rest rest &key attribute element &allow-other-keys) slot
Modified: branches/trunk-reorg/projects/lisp-ecoop/src/packages.lisp ============================================================================== --- branches/trunk-reorg/projects/lisp-ecoop/src/packages.lisp (original) +++ branches/trunk-reorg/projects/lisp-ecoop/src/packages.lisp Thu Jan 17 11:36:28 2008 @@ -23,7 +23,6 @@ (defpackage :lisp-ecoop (:use :cl :cl-user - :ext :cl-interpol :cl-ppcre :bknr.utils @@ -34,8 +33,7 @@ :bknr.images :bknr.impex :lisp-ecoop.config - :net.aserve - :net.post-office + :hunchentoot :xhtml-generator) (:shadowing-import-from :cl-interpol #:quote-meta-chars) (:export #:participant @@ -70,16 +68,14 @@ :cl-user :cl-ppcre :cl-interpol - :ext :bknr.web :bknr.utils :bknr.datastore :bknr.user :bknr.images - :net.aserve + :hunchentoot :xhtml-generator :lisp-ecoop.config :lisp-ecoop) (:shadowing-import-from :cl-interpol #:quote-meta-chars) - (:shadowing-import-from :acl-compat.mp #:process-kill #:process-wait) (:export #:hello)) \ No newline at end of file
Modified: branches/trunk-reorg/projects/lisp-ecoop/src/schedule.lisp ============================================================================== --- branches/trunk-reorg/projects/lisp-ecoop/src/schedule.lisp (original) +++ branches/trunk-reorg/projects/lisp-ecoop/src/schedule.lisp Thu Jan 17 11:36:28 2008 @@ -34,6 +34,8 @@ ())
(defun parse-time-spec (string) + (error "cannot parse time ~A yet" string) + #+(or) (or (ignore-errors (parse-integer string)) (ext:parse-time string :default-zone -2))) ; XXX deal with time zone correctly!
Modified: branches/trunk-reorg/projects/lisp-ecoop/src/webserver.lisp ============================================================================== --- branches/trunk-reorg/projects/lisp-ecoop/src/webserver.lisp (original) +++ branches/trunk-reorg/projects/lisp-ecoop/src/webserver.lisp Thu Jan 17 11:36:28 2008 @@ -5,9 +5,11 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+#+(or) (defun make-daily-statistics () (bknr.stats::make-yesterdays-stats :delete-events t :remove-referer-hosts '("lisp-ecoop.bknr.net")))
+#+(or) (defun publish-lisp-ecoop (&key (port *webserver-port*) (listeners 20) (base-href *base-path*))
(unless (bknr.cron:cron-job-with-name "daily webserver statistics") @@ -34,3 +36,11 @@ :javascript-urls (list (format nil "~Astatic/javascript.js" base-href)))
(start :port port :listeners listeners)) + +(defun start-webserver (&key (port 9000)) + (when (and (boundp '*server*) *server*) + (stop-server *server*)) + (setq *dispatch-table* + (list 'dispatch-easy-handlers + (create-folder-dispatcher-and-handler "/" *website-directory*))) + (setq *server* (start-server :port port))) \ No newline at end of file
Modified: branches/trunk-reorg/projects/scrabble/src/web.lisp ============================================================================== --- branches/trunk-reorg/projects/scrabble/src/web.lisp (original) +++ branches/trunk-reorg/projects/scrabble/src/web.lisp Thu Jan 17 11:36:28 2008 @@ -46,6 +46,7 @@
(defmethod encode-json ((tile blank-tile) stream) (encode-json-plist (append (list :letter nil + :letter-name nil :value 0) (awhen (used-for tile) (list :used-for it)))
Added: branches/trunk-reorg/projects/scrabble/website/scrabble-resizable-attempt.js ============================================================================== --- (empty file) +++ branches/trunk-reorg/projects/scrabble/website/scrabble-resizable-attempt.js Thu Jan 17 11:36:28 2008 @@ -0,0 +1,615 @@ +// -*- JavaScript -*- + +var boardScoring = [["triple-word",null,null,"double-letter",null,null,null,"triple-word", + null,null,null,"double-letter",null,null,"triple-word"], + [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"double-word",null], + [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-word",null,null], + ["double-letter",null,null,"double-word",null,null,null,"double-letter", + null,null,null,"double-word",null,null,"double-letter"], + [null,null,null,null,"double-word",null,null,null,null,null,"double-word", + null,null,null,null], + [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"triple-letter",null], + [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-letter",null,null], + ["triple-word",null,null,"double-letter",null,null,null,"double-word", + null,null,null,"double-letter",null,null,"triple-word"], + [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-letter",null,null], + [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"triple-letter",null], + [null,null,null,null,"double-word",null,null,null,null,null,"double-word", + null,null,null,null], + ["double-letter",null,null,"double-word",null,null,null,"double-letter", + null,null,null,"double-word",null,null,"double-letter"], + [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-word",null,null], + [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"double-word",null], + ["triple-word",null,null,"double-letter",null,null,null,"triple-word", + null,null,null,"double-letter",null,null,"triple-word"]]; + +// Scrabble rule enforcement + +function checkMoveLegality(placedTiles) +{ + // Given the board and list of placed tiles, either throw an error or + // return if the move is legal. + + var positions = map(function (placement) { return [ placement.x, placement.y ] }, placedTiles) + .sort(function (a, b) { return (a[0] - b[0]) || (a[1] - b[1])}); + + if (filter(partial(operator.ne, positions[0][0]), map(function (position) { return position[0] }, positions)).length + && filter(partial(operator.ne, positions[0][1]), map(function (position) { return position[1] }, positions)).length) { + throw "not-in-a-row"; + } + + var startOfPlacement = positions[0]; + var endOfPlacement = positions[positions.length - 1]; + + for (var x = startOfPlacement[0]; x <= endOfPlacement[0]; x++) { + for (var y = startOfPlacement[1]; y <= endOfPlacement[1]; y++) { + if (!letterAt(x, y) && (findValue(positions, [ x, y ]) == -1)) { + throw "placement-with-holes"; + } + } + } + + if (findValue(positions, [ 7, 7 ]) == -1) { + var found = false; + for (var x = startOfPlacement[0]; !found && (x <= endOfPlacement[0]); x++) { + for (var y = startOfPlacement[1]; !found && (y <= endOfPlacement[1]); y++) { + if (((x > 0) && letterAt(x - 1, y)) + || ((x < 14) && letterAt(x + 1, y)) + || ((y > 0) && letterAt(x, y - 1)) + || ((y < 14) && letterAt(x, y + 1))) { + found = true; + } + } + } + if (!found) { + throw "not-touching-other-tile"; + } + } +} + +// Size calculations + +var fieldBorderSize = 4; +var fieldSize = 40; +var tileBorderSize = 3; +var tileSize = 34; +var cellSize = 44; + +function calculateFieldSize() +{ + // Our maximum field size is 44x44 pixels. If the window is not + // high enough to accomodate the board, scale down. + + var requiredHeight = 16 * 44 + 40; // 16 fields (including player tray) + borders + var viewportHeight = YAHOO.util.Dom.getViewportHeight(); + fieldSize = Math.floor((Math.min(requiredHeight, viewportHeight) - 40) / 16); + fieldBorderSize = Math.floor(fieldSize / 10); + fieldSize -= fieldBorderSize; + tileSize = Math.floor(fieldSize * 34 / 40); + tileBorderSize = Math.round((fieldSize - tileSize) / 2); + cellSize = fieldBorderSize + fieldSize; +// alert('fieldSize: ' + fieldSize + ' fieldBorderSize: ' + fieldBorderSize +// + ' tileSize: ' + tileSize + ' tileBorderSize: ' + tileBorderSize); +} + + +// + +function getFieldScore(x, y) { + return boardScoring[x][y] || 'standard'; +} + +var theirTrays; +var tray = []; + +var gameID = 108; +var board; + +var border = 10; + +function makeBoard() { + calculateFieldSize(); + var container = $('playfield'); + board = []; + for (x = 0; x < 15; x++) { + board[x] = []; + for (y = 0; y < 15; y++) { + var element = IMG(); + element.style.position = 'absolute'; + element.style.width = fieldSize + 'px'; + element.style.height = fieldSize + 'px'; + element.xPos = x; + element.yPos = y; + var imageName = (x == 7 && y == 7) ? "start-field" : getFieldScore(x, y); + element.src = 'images/' + imageName + '.png'; + setElementPosition(element, { x: border + x * cellSize, y: border + y * cellSize }); + YAHOO.util.Event.on(element, 'click', emptyTileClicked); + board[x][y] = element; + } + appendChildNodes(container, board[x]); + } + + var shuffleButton = DIV(null, "shuffle"); + shuffleButton.style.color = 'white'; + shuffleButton.style.position = 'absolute'; + shuffleButton.onclick = shuffleMyTray; + setElementPosition(shuffleButton, { x: border + 480, y: border + 665 }); + appendChildNodes(container, shuffleButton); + + var gameLog = DIV({ id: 'gameLog' }, ""); + gameLog.style.position = 'absolute'; + gameLog.style.width = '280px'; + gameLog.style.height = '250px'; + gameLog.style.textAlign = 'left'; + gameLog.style.overflowY = 'scroll'; + setElementPosition(gameLog, { x: border + 680, y: border + 400 }); + appendChildNodes($('playfield'), gameLog); + + var nextTurn = DIV({ id: 'nextTurn' }, ""); + nextTurn.style.position = 'absolute'; + nextTurn.style.width = '280px'; + nextTurn.style.textAlign = 'left'; + setElementPosition(nextTurn, { x: border + 680, y: border + 665 }); + appendChildNodes($('playfield'), nextTurn); + + var nextTurn = DIV({ id: 'status' }, ""); + nextTurn.style.position = 'absolute'; + nextTurn.style.width = '280px'; + nextTurn.style.textAlign = 'left'; + setElementPosition(nextTurn, { x: border + 680, y: border + 680 }); + appendChildNodes($('playfield'), nextTurn); +} + +function setLetter(x, y, letter, isBlank) { + var image = IMG({ src: 'images/' + letter + (isBlank ? "-blank" : "") + '.png'}); + image.style.position = 'absolute'; + image.style.top = '3px'; + image.style.left = '3px'; + image.style.width = tileSize + 'px'; + image.style.height = tileSize + 'px'; + setElementPosition(image, { x: border + x * cellSize + tileBorderSize, + y: border + y * cellSize + tileBorderSize }); + appendChildNodes($('playfield'), image); + board[x][y].letterNode = image; + board[x][y].letter = letter; +} + +function removeLastLetterFromMove() { +} + +function letterAt(x, y) { + return board[x][y].letter && !board[x][y].justPlaced; +} + +function Cursor() +{ + var image = new IMG({ src: 'images/cursor.png' }); + image.style.position = 'absolute'; + image.style.top = '-' + tileBorderSize + 'px'; + image.style.left = '-' + tileBorderSize + 'px'; + + appendChildNodes($('playfield'), image); + this.image = image; + this.x = -1; + this.y = -1; + this.direction = 0; + + this.set = function(x, y) { + this.x = x; + this.y = y; + this.image.top = + this.image.style.visibility = 'visible'; + board[x][y].cursor = this.image; + }; + + this.clear = function() { + if (this.x != -1) { + this.image.style.visibility = 'hidden'; + board[this.x][this.y].cursor = undefined; + this.x = this.y = -1; + this.direction = 0; + } + }; + + this.advance = function(isHoriz) { + var horizontal = 1; + var vertical = 2; + var direction = this.direction; + if (direction == 0) { + // Direction not determined + if (isHoriz != undefined) { + direction = isHoriz ? horizontal : vertical; + } else if (((this.y < 14) && letterAt(this.x, this.y + 1)) + || ((this.y > 1) + && letterAt(this.x, this.y - 1) + && !letterAt(this.x, this.y - 2)) + || ((this.x > 1) + && letterAt(this.x - 1, this.y) + && letterAt(this.x - 2, this.y))) { + direction = vertical; + } else { + direction = horizontal; + } + } + var x = this.x; + var y = this.y; + this.clear(); + this.direction = direction; + if (this.direction == horizontal) { + x++; + } else { + y++; + } + if ((x != 15) && (y != 15)) { + this.set(x, y); + } + }; +} + +var cursor = new Cursor; + +function emptyTileClicked() { + cursor.clear(); + cursor.set(this.xPos, this.yPos); +} + +var move = []; + +function makeMask() +{ + var mask = IMG({ src: 'images/mask.png'}); + mask.style.position = 'absolute'; + mask.style.top = tileBorderSize + 'px'; + mask.style.left = tileBorderSize + 'px'; + mask.style.zIndex = '20'; + return mask; +} + +function addLetterToMove(x, y, tile) { + mask = makeMask(); + appendChildNodes(board[x][y], mask); + board[x][y].letterNode = tile; + board[x][y].letter = tile.letter; + board[x][y].justPlaced = true; + tile.mask = mask; + tile.anim = new YAHOO.util.Motion(tile, { points: { to: [ border + x * cellSize + tileBorderSize, + border + y * cellSize + tileBorderSize ]}}, + 0.15, + YAHOO.util.Easing.easeBoth); + tile.anim.animate(); + + move[move.length] = { x: x, y: y, tile: tile }; + try { + checkMoveLegality(move); + $('move').onclick = submitMove; + $('move').innerHTML = "submit move"; + displayStatus(''); + } + catch (e) { + if (typeof e != 'string') { + alert(e.message); + } else { + displayStatus(e); + } + $('move').onclick = undefined; + $('move').innerHTML = e.toString(); + } +} + +function confirmMove() { + for (var i = 0; i < move.length; i++) { + removeElement(move[i].tile.mask); + move[i].tile.mask = undefined; + } + cursor.clear(); + move = []; + $('move').onclick = null; + $('move').innerHTML = ''; + +} + +function moveAsString() { + // We internally keep the move as array of objects, but send it to the server rather unstructured: + var serverMessage = []; + for (var i = 0; i < move.length; i++) { + serverMessage.push(move[i].x); + serverMessage.push(move[i].y); + serverMessage.push(move[i].tile.letterName); + serverMessage.push(move[i].tile.letterName == undefined); + } + return serverMessage.toString(); +} + +function submitMove() +{ + var queryString = MochiKit.Base.queryString({ move: moveAsString(), game: gameID }); + var res = MochiKit.Async.doXHR("/place-tiles", + { method: 'POST', + sendContent: queryString, + headers: { "Content-Type": "application/x-www-form-urlencoded" } }); + res.addCallbacks(moveSuccess, moveFailure); +} + +function moveSuccess(result) +{ + try { + var response; + try { + response = eval('(' + result.responseText + ')'); + } + catch (e) { + alert("invalid JSON reply: " + result.responseText); + return; + } + if (response.error) { + alert(response.error); + } else { + confirmMove(); + $('playfield')['score-' + response.move.participantLogin].innerHTML = response.move.playerScore.toString(); + displayMyTray(response.tray); + } + } + catch (e) { + alert('error during moveSuccess: ' + e.message); + } +} + +function moveFailure(e) +{ + alert('failed: ' + e); +} + +function letterKeyPressed(e) { + if (e.which == 0 || e.altKey || e.ctrlKey || e.shiftKey) { + // not a letter key + return; + } + + var letter = String.fromCharCode(e.which).toUpperCase(); + + var x = cursor.x; + var y = cursor.y; + var tilePosition = -1; + for (var i = 0; (tilePosition == -1) && (i < tray.length); i++) { + if (tray[i].letter == letter) { + tilePosition = i; + } + } + if (tilePosition == -1) { + for (var i = 0; (tilePosition == -1) && (i < tray.length); i++) { + if (tray[i].letter == undefined) { + tilePosition = i; + } + } + } + if (tilePosition == -1) { + displayStatus('you-dont-have-that-letter', letter); + } else { + var isHoriz; + if (move.length > 0) { + isHoriz = (move[0].x != x); + } + cursor.advance(isHoriz); + if (!letterAt(x, y)) { + var tile = tray[tilePosition]; + tray.splice(tilePosition, 1); + addLetterToMove(x, y, tile); + } + } +} + +var leftKey = 37; +var upKey = 38; +var rightKey = 39; +var downKey = 40; +var backspaceKey = 8; + +function functionKeyPressed(type, args, obj) { + var x = cursor.x; + var y = cursor.y; + + switch (args[0]) { + case rightKey: + while (x < 14) + if (!letterAt(++x, y)) + break; + break; + case leftKey: + while (x > 0) + if (!letterAt(--x, y)) + break; + break; + case upKey: + while (y > 0) + if (!letterAt(x, --y)) + break; + break; + case downKey: + while (y < 14) + if (!letterAt(x, ++y)) + break; + break; + case backspaceKey: + if (move.length) { + removeLastLetterFromMove(); + } + } + if ((x >= 0) && (x <= 14) && (y >= 0) && (y <= 14)) { + cursor.clear(); + cursor.set(x, y); + } + YAHOO.util.Event.preventDefault(args[1]); +} + +function clearBoard() { + for (x = 0; x < 15; x++) { + for (y = 0; y < 15; y++) { + var letterNode = board[x][y].letterNode; + if (letterNode) { + letterNode.anim = new YAHOO.util.Motion(letterNode, + { points: { to: [ border + 7 * cellSize + tileBorderSize, + border + 7 * cellSize + tileBorderSize ]}}, + 0.15); + letterNode.anim.onComplete.subscribe(function () { removeElement(this); }); + letterNode.anim.animate(); + } + } + } +} + +function trayClick(letter) { + this.clicked = !this.clicked; + this.anim = new YAHOO.util.Motion(this, { points: { by: [ 0, (this.clicked ? 15 : -15 ) ]}}, 0.15); + this.anim.animate(); +} + +function displayMyTray(letters) { + map(removeElement, tray); + tray = []; + for (var i = 0; i < letters.length; i++) { + var element = IMG({src: 'images/' + letters[i].letterName + '.png'}); + element.letter = letters[i].letter; + element.letterName = letters[i].letterName; + element.style.position = 'absolute'; + element.style.width = tileSize + 'px'; + element.style.height = tileSize + 'px'; + element.style.zIndex = '10'; + element.onclick = trayClick; + setElementPosition(element, { x: border + i * fieldSize, + y: border + 15 * cellSize + 10 }); + tray[i] = element; + } + appendChildNodes($('playfield'), tray); +} + +function shuffleMyTray() { + var count = tray.length; + var newTray = []; + for (var i = 0; i < count; i++) { + do { + index = Math.floor(Math.random() * count); + } while (newTray[index]); + newTray[index] = tray[i]; + newTray[index].anim = new YAHOO.util.Motion(tray[i], { points: { to: [ border + 194 + i * 40, + border + 665 ] }}, + 0.5); + newTray[index].anim.animate(); + newTray[index].clicked = false; + } + tray = newTray; +} + +var otherPlayerIndex = 0; + +function makeTheirTray (participant) { + var tileCount = (typeof participant.remainingTiles == 'number') ? participant.remainingTiles : participant.remainingTiles.length; + + var tray = []; + for (var i = 0; i < tileCount; i++) { + var element = IMG({src: 'images/null.png'}); + element.style.position = 'absolute'; + element.style.width = tileSize + 'px'; + element.style.height = tileSize + 'px'; + element.style.zIndex = '10'; + setElementPosition(element, { x: border + 15 * cellSize + 10 + i * fieldSize, + y: border + 80 * otherPlayerIndex }); + tray[i] = element; + } + appendChildNodes($('playfield'), tray); + + var nameTag = DIV(null, participant.name); + nameTag.style.position = 'absolute'; + nameTag.style.width = '200px'; + nameTag.style.textAlign = 'left'; + setElementPosition(nameTag, { x: border + 680, y: border + 80 * otherPlayerIndex + 50 }); + appendChildNodes($('playfield'), nameTag); + + var scoreTag = DIV(null, participant.score); + scoreTag.style.position = 'absolute'; + scoreTag.style.width = '80px'; + scoreTag.style.textAlign = 'right'; + setElementPosition(scoreTag, { x: border + 870, y: border + 80 * otherPlayerIndex + 50 }); + appendChildNodes($('playfield'), scoreTag); + $('playfield')['score-' + participant.login] = scoreTag; + + otherPlayerIndex++; +} + +function renderMoveAsText(move) +{ + var retval = move.participantLogin; + if (move.type == 'move') { + retval += " score: " + move.score; + for (var i = 0; i < move.words.length; i++) { + retval += " " + move.words[i][0] + "(" + move.words[i][1] + ")"; + } + } else { + retval += move.type; + } + + return retval; +} + +function displayWhosTurnItIs(name) { + replaceChildNodes($('nextTurn'), + "Next: " + name); +} + +function drawGameState (gameState) { + try { + for (var i = 0; i < gameState.board.length; i++) { + var x = gameState.board[i][0]; + var y = gameState.board[i][1]; + var char = gameState.board[i][2]; + setLetter(x, y, char, gameState.board[i].length > 3); + } + var firstParticipant = gameState.participants[0]; + displayWhosTurnItIs(firstParticipant.name); + for (var i = 0; i < gameState.participants.length; i++) { + var participant = gameState.participants[i]; + makeTheirTray(participant); + if (typeof participant.remainingTiles != 'number') { + displayMyTray(participant.remainingTiles); + } + } + for (var i = 0; i < gameState.moves.length; i++) { + appendChildNodes($('gameLog'), DIV(null, renderMoveAsText(gameState.moves[i]))); + } + } + catch (e) { + alert('error ' + e + ' in drawGameState'); + } +} + +function displayStatus(status) +{ + replaceChildNodes('status', status); +} + +function init() { + makeBoard(); + + // does not work for ie (document.body needed)? + YAHOO.util.Event.on(window, 'keypress', letterKeyPressed); + + var functionKeyListener = new YAHOO.util.KeyListener(document, + { keys: [ leftKey, upKey, rightKey, downKey, backspaceKey ] }, + { fn: functionKeyPressed, scope: this, correctScope: true }); + functionKeyListener.enable(); + + var moveDisplay = DIV({ id: 'move' }, ""); + moveDisplay.style.color = 'white'; + moveDisplay.style.position = 'absolute'; + setElementPosition(moveDisplay, { x: border + 550, y: border + 665 }); + appendChildNodes(document.body, moveDisplay); + var d = loadJSONDoc("/game/" + gameID); + d.addCallbacks(drawGameState, function (error) { alert("Request error: " + error.message); }); +}
Modified: branches/trunk-reorg/projects/scrabble/website/scrabble.html ============================================================================== --- branches/trunk-reorg/projects/scrabble/website/scrabble.html (original) +++ branches/trunk-reorg/projects/scrabble/website/scrabble.html Thu Jan 17 11:36:28 2008 @@ -1,18 +1,18 @@ <html> - <head> - <link rel="stylesheet" type="text/css" href="/yui/reset-fonts-grids/reset-fonts-grids.css" /> - <link rel="stylesheet" type="text/css" href="scrabble.css" /> - <script type="text/javascript" src="/yui/yahoo/yahoo-min.js"> </script> - <script type="text/javascript" src="/yui/event/event-min.js"> </script> - <script type="text/javascript" src="/yui/dom/dom-min.js"> </script> - <script type="text/javascript" src="/yui/animation/animation-min.js"> </script> - <script type="text/javascript" src="/MochiKit/MochiKit.js"> </script> - <script type="text/javascript" src="scrabble.js"> </script> - </head> - <body onload="init()"> - <div id="playfield"> - </div> - <!-- <div style="position: absolute; right: 20px; top: 20px;"><a style="color: white;" href="/login?login=hans">hans</a></div> --> - <!-- <div style="position: absolute; right: 20px; top: 40px;"><a style="color: white;" href="/login?login=marna">marna</a></div> --> - </body> + <head> + <link rel="stylesheet" type="text/css" href="/yui/reset-fonts-grids/reset-fonts-grids.css" /> + <link rel="stylesheet" type="text/css" href="scrabble.css" /> + <script type="text/javascript" src="/yui/yahoo/yahoo-min.js"> </script> + <script type="text/javascript" src="/yui/event/event-min.js"> </script> + <script type="text/javascript" src="/yui/dom/dom-min.js"> </script> + <script type="text/javascript" src="/yui/animation/animation-min.js"> </script> + <script type="text/javascript" src="/MochiKit/MochiKit.js"> </script> + <script type="text/javascript" src="scrabble.js"> </script> + </head> + <body onload="init()"> + <div id="playfield"> + </div> + <div style="position: absolute; right: 20px; top: 20px;"><a style="color: white;" href="/login?login=hans">hans</a></div> + <div style="position: absolute; right: 20px; top: 40px;"><a style="color: white;" href="/login?login=marna">marna</a></div> + </body> </html>
Modified: branches/trunk-reorg/projects/scrabble/website/scrabble.js ============================================================================== --- branches/trunk-reorg/projects/scrabble/website/scrabble.js (original) +++ branches/trunk-reorg/projects/scrabble/website/scrabble.js Thu Jan 17 11:36:28 2008 @@ -1,386 +1,380 @@ // -*- JavaScript -*-
-var boardScoring = [["triple-word",null,null,"double-letter",null,null,null,"triple-word", - null,null,null,"double-letter",null,null,"triple-word"], - [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", - null,null,null,"double-word",null], - [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", - null,null,null,"double-word",null,null], - ["double-letter",null,null,"double-word",null,null,null,"double-letter", - null,null,null,"double-word",null,null,"double-letter"], - [null,null,null,null,"double-word",null,null,null,null,null,"double-word", - null,null,null,null], - [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", - null,null,null,"triple-letter",null], - [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", - null,null,null,"double-letter",null,null], - ["triple-word",null,null,"double-letter",null,null,null,"double-word", - null,null,null,"double-letter",null,null,"triple-word"], - [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", - null,null,null,"double-letter",null,null], - [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", - null,null,null,"triple-letter",null], - [null,null,null,null,"double-word",null,null,null,null,null,"double-word", - null,null,null,null], - ["double-letter",null,null,"double-word",null,null,null,"double-letter", - null,null,null,"double-word",null,null,"double-letter"], - [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", - null,null,null,"double-word",null,null], - [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", - null,null,null,"double-word",null], - ["triple-word",null,null,"double-letter",null,null,null,"triple-word", - null,null,null,"double-letter",null,null,"triple-word"]]; - -// for now -function requestError (error) { - alert("Request error: " + error.message) -} +var scrabbleRules = { + boardScoring: [["triple-word",null,null,"double-letter",null,null,null,"triple-word", + null,null,null,"double-letter",null,null,"triple-word"], + [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"double-word",null], + [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-word",null,null], + ["double-letter",null,null,"double-word",null,null,null,"double-letter", + null,null,null,"double-word",null,null,"double-letter"], + [null,null,null,null,"double-word",null,null,null,null,null,"double-word", + null,null,null,null], + [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"triple-letter",null], + [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-letter",null,null], + ["triple-word",null,null,"double-letter",null,null,null,"double-word", + null,null,null,"double-letter",null,null,"triple-word"], + [null,null,"double-letter",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-letter",null,null], + [null,"triple-letter",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"triple-letter",null], + [null,null,null,null,"double-word",null,null,null,null,null,"double-word", + null,null,null,null], + ["double-letter",null,null,"double-word",null,null,null,"double-letter", + null,null,null,"double-word",null,null,"double-letter"], + [null,null,"double-word",null,null,null,"double-letter",null,"double-letter", + null,null,null,"double-word",null,null], + [null,"double-word",null,null,null,"triple-letter",null,null,null,"triple-letter", + null,null,null,"double-word",null], + ["triple-word",null,null,"double-letter",null,null,null,"triple-word", + null,null,null,"double-letter",null,null,"triple-word"]],
-// Scrabble rule enforcement + // Scrabble rule enforcement
-function checkMoveLegality(placedTiles) -{ + checkMoveLegality : function (placedTiles) { // Given the board and list of placed tiles, either throw an error or // return if the move is legal.
var positions = map(function (placement) { return [ placement.x, placement.y ] }, placedTiles) - .sort(function (a, b) { return (a[0] - b[0]) || (a[1] - b[1])}); + .sort(function (a, b) { return (a[0] - b[0]) || (a[1] - b[1])});
if (filter(partial(operator.ne, positions[0][0]), map(function (position) { return position[0] }, positions)).length - && filter(partial(operator.ne, positions[0][1]), map(function (position) { return position[1] }, positions)).length) { - throw "not-in-a-row"; + && filter(partial(operator.ne, positions[0][1]), map(function (position) { return position[1] }, positions)).length) { + throw "not-in-a-row"; }
var startOfPlacement = positions[0]; var endOfPlacement = positions[positions.length - 1];
for (var x = startOfPlacement[0]; x <= endOfPlacement[0]; x++) { - for (var y = startOfPlacement[1]; y <= endOfPlacement[1]; y++) { - if (!letterAt(x, y) && (findValue(positions, [ x, y ]) == -1)) { - throw "placement-with-holes"; - } - } + for (var y = startOfPlacement[1]; y <= endOfPlacement[1]; y++) { + if (!letterAt(x, y) && (findValue(positions, [ x, y ]) == -1)) { + throw "placement-with-holes"; + } + } }
if (findValue(positions, [ 7, 7 ]) == -1) { - var found = false; - for (var x = startOfPlacement[0]; !found && (x <= endOfPlacement[0]); x++) { - for (var y = startOfPlacement[1]; !found && (y <= endOfPlacement[1]); y++) { - if (((x > 0) && letterAt(x - 1, y)) - || ((x < 14) && letterAt(x + 1, y)) - || ((y > 0) && letterAt(x, y - 1)) - || ((y < 14) && letterAt(x, y + 1))) { - found = true; - } - } - } - if (!found) { - throw "not-touching-other-tile"; - } + var found = false; + for (var x = startOfPlacement[0]; !found && (x <= endOfPlacement[0]); x++) { + for (var y = startOfPlacement[1]; !found && (y <= endOfPlacement[1]); y++) { + if (((x > 0) && letterAt(x - 1, y)) + || ((x < 14) && letterAt(x + 1, y)) + || ((y > 0) && letterAt(x, y - 1)) + || ((y < 14) && letterAt(x, y + 1))) { + found = true; + } + } + } + if (!found) { + throw "not-touching-other-tile"; + } } -} + },
-// + //
-function getFieldScore(x, y) { + fieldScore : function(x, y) { return boardScoring[x][y] || 'standard'; -} + } +};
var theirTrays; var tray = [];
+var gameID = 108; var board;
-var border = 10; - function makeBoard() { - var container = $('playfield'); - board = []; - for (x = 0; x < 15; x++) { - board[x] = []; - for (y = 0; y < 15; y++) { - var element = DIV(); - element.style.position = 'absolute'; - element.style.width = '40px'; - element.style.height = '40px'; - var imageName = (x == 7 && y == 7) ? "start-field" : getFieldScore(x, y); - element.style.backgroundImage = 'url(images/' + imageName + '.png)'; - element.x = x; - element.y = y; - setElementPosition(element, { x: border + x * 44, y: border + y * 44 }); - YAHOO.util.Event.on(element, 'click', emptyTileClicked) - board[x][y] = element; - } - appendChildNodes(container, board[x]); - } - - var shuffleButton = DIV(null, "shuffle"); - shuffleButton.style.color = 'white'; - shuffleButton.style.position = 'absolute'; - shuffleButton.onclick = shuffleMyTray; - setElementPosition(shuffleButton, { x: border + 480, y: border + 665 }); - appendChildNodes(container, shuffleButton); - - var gameLog = DIV({ id: 'gameLog' }, ""); - gameLog.style.position = 'absolute'; - gameLog.style.width = '280px'; - gameLog.style.height = '250px'; - gameLog.style.textAlign = 'left'; - gameLog.style.overflowY = 'scroll'; - setElementPosition(gameLog, { x: border + 680, y: border + 400 }); - appendChildNodes($('playfield'), gameLog); - - var nextTurn = DIV({ id: 'nextTurn' }, ""); - nextTurn.style.position = 'absolute'; - nextTurn.style.width = '280px'; - nextTurn.style.textAlign = 'left'; - setElementPosition(nextTurn, { x: border + 680, y: border + 665 }); - appendChildNodes($('playfield'), nextTurn); - - var nextTurn = DIV({ id: 'status' }, ""); - nextTurn.style.position = 'absolute'; - nextTurn.style.width = '280px'; - nextTurn.style.textAlign = 'left'; - setElementPosition(nextTurn, { x: border + 680, y: border + 680 }); - appendChildNodes($('playfield'), nextTurn); + var container = $('playfield'); + board = []; + for (x = 0; x < 15; x++) { + board[x] = []; + for (y = 0; y < 15; y++) { + var element = DIV(); + element.style.position = 'absolute'; + element.style.width = '40px'; + element.style.height = '40px'; + var imageName = (x == 7 && y == 7) ? "start-field" : scrabbleRules.fieldScore(x, y); + element.style.backgroundImage = 'url(images/' + imageName + '.png)'; + element.x = x; + element.y = y; + setElementPosition(element, { x: x * 44, y: y * 44 }); + YAHOO.util.Event.on(element, 'click', emptyTileClicked) + board[x][y] = element; + } + appendChildNodes(container, board[x]); + } + + var shuffleButton = DIV(null, "shuffle"); + shuffleButton.style.color = 'white'; + shuffleButton.style.position = 'absolute'; + shuffleButton.onclick = shuffleMyTray; + setElementPosition(shuffleButton, { x: 480, y: 665 }); + appendChildNodes(container, shuffleButton); + + var gameLog = DIV({ id: 'gameLog' }, ""); + gameLog.style.position = 'absolute'; + gameLog.style.width = '280px'; + gameLog.style.height = '250px'; + gameLog.style.textAlign = 'left'; + gameLog.style.overflowY = 'scroll'; + setElementPosition(gameLog, { x: 680, y: 400 }); + appendChildNodes($('playfield'), gameLog); + + var nextTurn = DIV({ id: 'nextTurn' }, ""); + nextTurn.style.position = 'absolute'; + nextTurn.style.width = '280px'; + nextTurn.style.textAlign = 'left'; + setElementPosition(nextTurn, { x: 680, y: 665 }); + appendChildNodes($('playfield'), nextTurn); + + var nextTurn = DIV({ id: 'status' }, ""); + nextTurn.style.position = 'absolute'; + nextTurn.style.width = '280px'; + nextTurn.style.textAlign = 'left'; + setElementPosition(nextTurn, { x: 680, y: 680 }); + appendChildNodes($('playfield'), nextTurn); }
function setLetter(x, y, letter, isBlank) { - var image = IMG({ src: 'images/' + letter + (isBlank ? "-blank" : "") + '.png'}); - image.style.position = 'absolute'; - image.style.top = '3px'; - image.style.left = '3px'; - setElementPosition(image, { x: border + x * 44 + 3, y: border + y * 44 + 3 }); - appendChildNodes($('playfield'), image); - board[x][y].letterNode = image; - board[x][y].letter = letter; -} - -function placeLetter(x, y, tile) { + var image = IMG({ src: 'images/' + letter + (isBlank ? "-blank" : "") + '.png'}); + image.style.position = 'absolute'; + image.style.top = '3px'; + image.style.left = '3px'; + setElementPosition(image, { x: x * 44 + 3, y: y * 44 + 3 }); + appendChildNodes($('playfield'), image); + board[x][y].letterNode = image; + board[x][y].letter = letter; }
function removeLastLetterFromMove() { }
function letterAt(x, y) { - return board[x][y].letter && !board[x][y].justPlaced; + return board[x][y].letter && !board[x][y].justPlaced; }
function Cursor() { - var image = new IMG({ src: 'images/cursor.png' }); - image.style.position = 'absolute'; - image.style.top = '-3px'; - image.style.left = '-3px'; - - this.image = image; - this.x = -1; - this.y = -1; - this.direction = 0; - - this.set = function(x, y) { - this.x = x; - this.y = y; - appendChildNodes(board[x][y], this.image); - board[x][y].cursor = this.image; - }; - - this.clear = function() { - if (this.x != -1) { - removeElement(board[this.x][this.y].cursor); - board[this.x][this.y].cursor = undefined; - this.x = this.y = -1; - this.direction = 0; - } - }; - - this.advance = function(isHoriz) { - var horizontal = 1; - var vertical = 2; - var direction = this.direction; - if (direction == 0) { - // Direction not determined - if (isHoriz != undefined) { - direction = isHoriz ? horizontal : vertical; - } else if (((this.y < 14) && letterAt(this.x, this.y + 1)) - || ((this.y > 1) - && letterAt(this.x, this.y - 1) - && !letterAt(this.x, this.y - 2)) - || ((this.x > 1) - && letterAt(this.x - 1, this.y) - && letterAt(this.x - 2, this.y))) { - direction = vertical; - } else { - direction = horizontal; - } - } - var x = this.x; - var y = this.y; - this.clear(); - this.direction = direction; - if (this.direction == horizontal) { - x++; - } else { - y++; - } - if ((x != 15) && (y != 15)) { - this.set(x, y); - } - }; + var image = new IMG({ src: 'images/cursor.png' }); + image.style.position = 'absolute'; + image.style.top = '-3px'; + image.style.left = '-3px'; + + this.image = image; + this.x = -1; + this.y = -1; + this.direction = 0; + + this.set = function(x, y) { + this.x = x; + this.y = y; + appendChildNodes(board[x][y], this.image); + board[x][y].cursor = this.image; + }; + + this.clear = function() { + if (this.x != -1) { + removeElement(board[this.x][this.y].cursor); + board[this.x][this.y].cursor = undefined; + this.x = this.y = -1; + this.direction = 0; + } + }; + + this.advance = function(isHoriz) { + var horizontal = 1; + var vertical = 2; + var direction = this.direction; + if (direction == 0) { + // Direction not determined + if (isHoriz != undefined) { + direction = isHoriz ? horizontal : vertical; + } else if (((this.y < 14) && letterAt(this.x, this.y + 1)) + || ((this.y > 1) + && letterAt(this.x, this.y - 1) + && !letterAt(this.x, this.y - 2)) + || ((this.x > 1) + && letterAt(this.x - 1, this.y) + && letterAt(this.x - 2, this.y))) { + direction = vertical; + } else { + direction = horizontal; + } + } + var x = this.x; + var y = this.y; + this.clear(); + this.direction = direction; + if (this.direction == horizontal) { + x++; + } else { + y++; + } + if ((x != 15) && (y != 15)) { + this.set(x, y); + } + }; }
var cursor = new Cursor;
function emptyTileClicked() { - cursor.clear(); - cursor.set(this.x, this.y); + cursor.clear(); + cursor.set(this.x, this.y); }
var move = [];
function makeMask() { - var mask = IMG({ src: 'images/mask.png'}); - mask.style.position = 'absolute'; - mask.style.top = '3px'; - mask.style.left = '3px'; - mask.style.zIndex = '20'; - return mask; -} - -function addLetterToMove(x, y, tile) { - mask = makeMask(); - appendChildNodes(board[x][y], mask); - board[x][y].letterNode = tile; - board[x][y].letter = tile.letter; - board[x][y].justPlaced = true; - tile.mask = mask; - tile.anim = new YAHOO.util.Motion(tile, { points: { to: [ border + x * 44 + 3, - border + y * 44 + 3 ]}}, - 0.15, - YAHOO.util.Easing.easeBoth); - tile.anim.animate(); - - move[move.length] = { x: x, y: y, tile: tile }; - try { - checkMoveLegality(move); - $('move').onclick = submitMove; - $('move').innerHTML = "submit move"; - displayStatus(''); - } - catch (e) { - if (typeof e != 'string') { - alert(e.message); - } else { - displayStatus(e); - } - $('move').onclick = undefined; - $('move').innerHTML = e.toString(); + var mask = IMG({ src: 'images/mask.png'}); + mask.style.position = 'absolute'; + mask.style.top = '3px'; + mask.style.left = '3px'; + mask.style.zIndex = '20'; + return mask; +} + +function addLetterToMove(x, y, tile, letter) { + mask = makeMask(); + appendChildNodes(board[x][y], mask); + board[x][y].letterNode = tile; + if (!tile.letter) { + tile.letter = letter; + } + board[x][y].justPlaced = true; + tile.mask = mask; + tile.anim = new YAHOO.util.Motion(tile, { points: { to: [ x * 44 + 3, + y * 44 + 3 ]}}, + 0.15, + YAHOO.util.Easing.easeBoth); + tile.anim.animate(); + + move[move.length] = { x: x, y: y, tile: tile }; + try { + checkMoveLegality(move); + $('move').onclick = submitMove; + $('move').innerHTML = "submit move"; + displayStatus(''); + } + catch (e) { + if (typeof e != 'string') { + alert(e.message); + } else { + displayStatus(e); } + $('move').onclick = undefined; + $('move').innerHTML = e.toString(); + } }
function confirmMove() { - for (var i = 0; i < move.length; i++) { - removeElement(move[i].tile.mask); - move[i].tile.mask = undefined; - } - cursor.clear(); - move = []; - $('move').onclick = null; - $('move').innerHTML = ''; - + for (var i = 0; i < move.length; i++) { + removeElement(move[i].tile.mask); + move[i].tile.mask = undefined; + } + cursor.clear(); + move = []; + $('move').onclick = null; + $('move').innerHTML = ''; + }
function moveAsString() { - // We internally keep the move as array of objects, but send it to the server rather unstructured: - var serverMessage = []; - for (var i = 0; i < move.length; i++) { - serverMessage.push(move[i].x); - serverMessage.push(move[i].y); - serverMessage.push(move[i].tile.letterName); - serverMessage.push(move[i].tile.letterName == undefined); - } - return serverMessage.toString(); + // We internally keep the move as array of objects, but send it to the server rather unstructured: + var serverMessage = []; + for (var i = 0; i < move.length; i++) { + serverMessage.push(move[i].x); + serverMessage.push(move[i].y); + serverMessage.push(move[i].tile.letterName || move[i].tile.letter); + serverMessage.push(move[i].tile.letterName == undefined); + } + return serverMessage.toString(); }
function submitMove() { - var queryString = MochiKit.Base.queryString({ move: moveAsString() }); - var res = MochiKit.Async.doXHR("/place-tiles", - { method: 'POST', + var queryString = MochiKit.Base.queryString({ move: moveAsString(), game: gameID }); + var res = MochiKit.Async.doXHR("/place-tiles", + { method: 'POST', sendContent: queryString, headers: { "Content-Type": "application/x-www-form-urlencoded" } }); - res.addCallbacks(moveSuccess, moveFailure); + res.addCallbacks(moveSuccess, moveFailure); }
function moveSuccess(result) { + try { + var response; try { - var response; - try { - response = eval('(' + result.responseText + ')'); - } - catch (e) { - alert("invalid JSON reply: " + result.responseText); - return; - } - if (response.error) { - alert(response.error); - } else { - confirmMove(); - alert(response.move.playerScore); - $('playfield')['score-' + response.move.participantLogin].innerHTML = response.move.playerScore.toString(); - displayMyTray(response.tray); - } + response = eval('(' + result.responseText + ')'); } catch (e) { - alert('error during moveSuccess: ' + e.message); + alert("invalid JSON reply: " + result.responseText); + return; } + if (response.error) { + alert(response.error); + } else { + confirmMove(); + alert(response.move.playerScore); + $('playfield')['score-' + response.move.participantLogin].innerHTML = response.move.playerScore.toString(); + displayMyTray(response.tray); + } + } + catch (e) { + alert('error during moveSuccess: ' + e.message); + } }
function moveFailure(e) { - alert('failed: ' + e); + alert('failed: ' + e); }
function letterKeyPressed(e) { - if (e.which == 0 || e.altKey || e.ctrlKey || e.shiftKey) { - // not a letter key - return; + if (e.which == 0 || e.altKey || e.ctrlKey || e.shiftKey) { + // not a letter key + return; + } + + var letter = String.fromCharCode(e.which).toUpperCase(); + + var x = cursor.x; + var y = cursor.y; + var tilePosition = -1; + for (var i = 0; (tilePosition == -1) && (i < tray.length); i++) { + if (tray[i].letter == letter) { + tilePosition = i; } - - var letter = String.fromCharCode(e.which).toUpperCase(); - - var x = cursor.x; - var y = cursor.y; - var tilePosition = -1; + } + if (tilePosition == -1) { for (var i = 0; (tilePosition == -1) && (i < tray.length); i++) { - if (tray[i].letter == letter) { - tilePosition = i; - } - } - if (tilePosition == -1) { - for (var i = 0; (tilePosition == -1) && (i < tray.length); i++) { - if (tray[i].letter == undefined) { - tilePosition = i; - } - } - } - if (tilePosition == -1) { - displayStatus('you-dont-have-that-letter', letter); - } else { - var isHoriz; - if (move.length > 0) { - isHoriz = (move[0].x != x); - } - cursor.advance(isHoriz); - if (!letterAt(x, y)) { - var tile = tray[tilePosition]; - tray.splice(tilePosition, 1); - addLetterToMove(x, y, tile); - } + if (tray[i].letter == undefined) { + tilePosition = i; + } + } + } + if (tilePosition == -1) { + displayStatus('you-dont-have-that-letter', letter); + } else { + var isHoriz; + if (move.length > 0) { + isHoriz = (move[0].x != x); + } + cursor.advance(isHoriz); + if (!letterAt(x, y)) { + var tile = tray[tilePosition]; + tray.splice(tilePosition, 1); + addLetterToMove(x, y, tile, letter); } + } }
var leftKey = 37; @@ -390,439 +384,200 @@ var backspaceKey = 8;
function functionKeyPressed(type, args, obj) { - var x = cursor.x; - var y = cursor.y; + var x = cursor.x; + var y = cursor.y;
- switch (args[0]) { - case rightKey: - while (x < 14) - if (!letterAt(++x, y)) - break; - break; - case leftKey: - while (x > 0) - if (!letterAt(--x, y)) - break; - break; - case upKey: - while (y > 0) - if (!letterAt(x, --y)) - break; - break; - case downKey: - while (y < 14) - if (!letterAt(x, ++y)) - break; - break; - case backspaceKey: - if (move.length) { - removeLastLetterFromMove(); - } - } - if ((x >= 0) && (x <= 14) && (y >= 0) && (y <= 14)) { - cursor.clear(); - cursor.set(x, y); + switch (args[0]) { + case rightKey: + while (x < 14) + if (!letterAt(++x, y)) + break; + break; + case leftKey: + while (x > 0) + if (!letterAt(--x, y)) + break; + break; + case upKey: + while (y > 0) + if (!letterAt(x, --y)) + break; + break; + case downKey: + while (y < 14) + if (!letterAt(x, ++y)) + break; + break; + case backspaceKey: + if (move.length) { + removeLastLetterFromMove(); } - YAHOO.util.Event.preventDefault(args[1]); + } + if ((x >= 0) && (x <= 14) && (y >= 0) && (y <= 14)) { + cursor.clear(); + cursor.set(x, y); + } + YAHOO.util.Event.preventDefault(args[1]); }
function clearBoard() { - for (x = 0; x < 15; x++) { - for (y = 0; y < 15; y++) { - var letterNode = board[x][y].letterNode; - if (letterNode) { - letterNode.anim = new YAHOO.util.Motion(letterNode, { points: { to: [ border + 7 * 44 + 3, - border + 7 * 44 + 3 ]}}, - 0.15); - letterNode.anim.onComplete.subscribe(function () { removeElement(this); }); - letterNode.anim.animate(); - } - } + for (x = 0; x < 15; x++) { + for (y = 0; y < 15; y++) { + var letterNode = board[x][y].letterNode; + if (letterNode) { + letterNode.anim = new YAHOO.util.Motion(letterNode, { points: { to: [ 7 * 44 + 3, + 7 * 44 + 3 ]}}, + 0.15); + letterNode.anim.onComplete.subscribe(function () { removeElement(this); }); + letterNode.anim.animate(); + } } + } }
function trayClick(letter) { - this.clicked = !this.clicked; - this.anim = new YAHOO.util.Motion(this, { points: { by: [ 0, (this.clicked ? 15 : -15 ) ]}}, 0.15); - this.anim.animate(); + this.clicked = !this.clicked; + this.anim = new YAHOO.util.Motion(this, { points: { by: [ 0, (this.clicked ? 15 : -15 ) ]}}, 0.15); + this.anim.animate(); }
function displayMyTray(letters) { - map(removeElement, tray); - tray = []; - for (var i = 0; i < letters.length; i++) { - var element = IMG({src: 'images/' + letters[i].letterName + '.png'}); - element.letter = letters[i].letter; - element.letterName = letters[i].letterName; - element.style.position = 'absolute'; - element.style.width = '34px'; - element.style.height = '34px'; - element.style.zIndex = '10'; - element.onclick = trayClick; - setElementPosition(element, { x: border + i * 40, y: border + 665 }); - tray[i] = element; - } - appendChildNodes($('playfield'), tray); + map(removeElement, tray); + tray = []; + for (var i = 0; i < letters.length; i++) { + var element = IMG({src: 'images/' + letters[i].letterName + '.png'}); + element.letter = letters[i].letter; + element.letterName = letters[i].letterName; + element.style.position = 'absolute'; + element.style.width = '34px'; + element.style.height = '34px'; + element.style.zIndex = '10'; + element.onclick = trayClick; + setElementPosition(element, { x: i * 40, y: 665 }); + tray[i] = element; + } + appendChildNodes($('playfield'), tray); }
function shuffleMyTray() { - var count = tray.length; - var newTray = []; - for (var i = 0; i < count; i++) { - do { - index = Math.floor(Math.random() * count); - } while (newTray[index]); - newTray[index] = tray[i]; - newTray[index].anim = new YAHOO.util.Motion(tray[i], { points: { to: [ border + 194 + i * 40, - border + 665 ] }}, - 0.5); - newTray[index].anim.animate(); - newTray[index].clicked = false; - } - tray = newTray; + var count = tray.length; + var newTray = []; + for (var i = 0; i < count; i++) { + do { + index = Math.floor(Math.random() * count); + } while (newTray[index]); + newTray[index] = tray[i]; + newTray[index].anim = new YAHOO.util.Motion(tray[i], { points: { to: [ 194 + i * 40, + 665 ] }}, + 0.5); + newTray[index].anim.animate(); + newTray[index].clicked = false; + } + tray = newTray; }
var otherPlayerIndex = 0;
function makeTheirTray (participant) { - var tileCount = (typeof participant.remainingTiles == 'number') ? participant.remainingTiles : participant.remainingTiles.length; + var tileCount = (typeof participant.remainingTiles == 'number') ? participant.remainingTiles : participant.remainingTiles.length;
- var tray = []; - for (var i = 0; i < tileCount; i++) { - var element = IMG({src: 'images/null.png'}); - element.style.position = 'absolute'; - element.style.width = '34px'; - element.style.height = '34px'; - element.style.zIndex = '10'; - setElementPosition(element, { x: border + 680 + i * 40, y: border + 80 * otherPlayerIndex }); - tray[i] = element; - } - appendChildNodes($('playfield'), tray); - - var nameTag = DIV(null, participant.name); - nameTag.style.position = 'absolute'; - nameTag.style.width = '200px'; - nameTag.style.textAlign = 'left'; - setElementPosition(nameTag, { x: border + 680, y: border + 80 * otherPlayerIndex + 50 }); - appendChildNodes($('playfield'), nameTag); - - var scoreTag = DIV(null, participant.score); - scoreTag.style.position = 'absolute'; - scoreTag.style.width = '80px'; - scoreTag.style.textAlign = 'right'; - setElementPosition(scoreTag, { x: border + 870, y: border + 80 * otherPlayerIndex + 50 }); - appendChildNodes($('playfield'), scoreTag); - $('playfield')['score-' + participant.login] = scoreTag; + var tray = []; + for (var i = 0; i < tileCount; i++) { + var element = IMG({src: 'images/null.png'}); + element.style.position = 'absolute'; + element.style.width = '34px'; + element.style.height = '34px'; + element.style.zIndex = '10'; + setElementPosition(element, { x: 680 + i * 40, y: 80 * otherPlayerIndex }); + tray[i] = element; + } + appendChildNodes($('playfield'), tray); + + var nameTag = DIV(null, participant.name); + nameTag.style.position = 'absolute'; + nameTag.style.width = '200px'; + nameTag.style.textAlign = 'left'; + setElementPosition(nameTag, { x: 680, y: 80 * otherPlayerIndex + 50 }); + appendChildNodes($('playfield'), nameTag); + + var scoreTag = DIV(null, participant.score); + scoreTag.style.position = 'absolute'; + scoreTag.style.width = '80px'; + scoreTag.style.textAlign = 'right'; + setElementPosition(scoreTag, { x: 870, y: 80 * otherPlayerIndex + 50 }); + appendChildNodes($('playfield'), scoreTag); + $('playfield')['score-' + participant.login] = scoreTag;
- otherPlayerIndex++; + otherPlayerIndex++; }
function renderMoveAsText(move) { - var retval = move.participantLogin; - if (move.type == 'move') { - retval += " score: " + move.score; - for (var i = 0; i < move.words.length; i++) { - retval += " " + move.words[i][0] + "(" + move.words[i][1] + ")"; - } - } else { - retval += move.type; - } + var retval = move.participantLogin; + if (move.type == 'move') { + retval += " score: " + move.score; + for (var i = 0; i < move.words.length; i++) { + retval += " " + move.words[i][0] + "(" + move.words[i][1] + ")"; + } + } else { + retval += move.type; + }
- return retval; + return retval; }
function displayWhosTurnItIs(name) { - replaceChildNodes($('nextTurn'), - "Next: " + name); + replaceChildNodes($('nextTurn'), + "Next: " + name); }
function drawGameState (gameState) { - try { - for (var i = 0; i < gameState.board.length; i++) { - var x = gameState.board[i][0]; - var y = gameState.board[i][1]; - var char = gameState.board[i][2]; - setLetter(x, y, char, gameState.board[i].length > 3); - } - var firstParticipant = gameState.participants[0]; - displayWhosTurnItIs(firstParticipant.name); - for (var i = 0; i < gameState.participants.length; i++) { - var participant = gameState.participants[i]; - makeTheirTray(participant); - if (typeof participant.remainingTiles != 'number') { - displayMyTray(participant.remainingTiles); - } - } - // small "Kilian fix" - probably there is a nicer way to do it... - if (gameState.moves != null) { - for (var i = 0; i < gameState.moves.length; i++) { - appendChildNodes($('gameLog'), DIV(null, renderMoveAsText(gameState.moves[i]))); - } - } - } - catch (e) { - alert('error ' + e + ' in drawGameState'); - } + try { + for (var i = 0; i < gameState.board.length; i++) { + var x = gameState.board[i][0]; + var y = gameState.board[i][1]; + var char = gameState.board[i][2]; + setLetter(x, y, char, gameState.board[i].length > 3); + } + var firstParticipant = gameState.participants[0]; + displayWhosTurnItIs(firstParticipant.name); + for (var i = 0; i < gameState.participants.length; i++) { + var participant = gameState.participants[i]; + makeTheirTray(participant); + if (typeof participant.remainingTiles != 'number') { + displayMyTray(participant.remainingTiles); + } + } + for (var i = 0; gameState.moves && (i < gameState.moves.length); i++) { + appendChildNodes($('gameLog'), DIV(null, renderMoveAsText(gameState.moves[i]))); + } + } + catch (e) { + alert('error ' + e + ' in drawGameState'); + } }
function displayStatus(status) { - replaceChildNodes('status', status); + replaceChildNodes('status', status); }
+function init() { + makeBoard();
-// Publication - -function Publication() { - this.callbacks = []; - this.object2callback = {}; -} - -function extendWithPublication (obj) { - var value; - Publication.call(obj); - for (p in Publication.prototype) { - value = Publication.prototype[p]; - if (value instanceof Function) - obj[p] = value; - } -} - -Publication.prototype = { - subscribe : function(fnOrObject, /*optional*/ method) { - if (!(((fnOrObject instanceof Function) && (method == undefined)) - || ((fnOrObject instanceof Object) && !(fnOrObject instanceof Function) - && (method instanceof Function)))) - throw new Error("bad args to subscribe. use either subscribe(fn) or subscribe(obj, method)"); - if (this._isAlreadySubscribed(fnOrObject)) - return null; - var callback = this._makeCallback(fnOrObject, method); - this._associateObjectWithCallback(fnOrObject, callback); - this._registerCallback(callback); - }, - unsubscribe : function(fnOrObject) { - var callback = this._associatedCallback(fnOrObject); - this._unregisterCallback(callback); - }, - unsubscribeAll : function() { - this.callbacks = []; - this.object2callback = {}; - }, - publish : function(/*optional*/ message) { - this.callbacks.forEach( - function (fn) { - fn(message); - }); - }, - _makeCallback : function (fnOrObject, /*optional*/ method) { - if (fnOrObject instanceof Function) { - return fnOrObject; - } else { - return function (message) { method.call(fnOrObject, message) }; - } - }, - _associateObjectWithCallback : function (fnOrObject, callback) { - if (!(fnOrObject instanceof Function)) { - this.object2callback[fnOrObject] = callback; - } - }, - _associatedCallback : function (fnOrObject) { - if (fnOrObject instanceof Function) - return fnOrObject; - else - return this.callbacks[fnOrObject]; - }, - _registerCallback : function(callback) { - this.callbacks.push(callback); - }, - _unregisterCallback : function(callback) { - this.callbacks = this.callbacks.filter( - function (c) { - (c === callback) - }); - }, - _isAlreadySubscribed : function (fnOrObject) { - return (this.object2callback[fnOrObject]) - || (this.callbacks.some(function (callback) {callback === fnOrObject})) - } -}; - -// loginManager -var loginManager = {isLoggedIn: - function () { - return this.loginName !== null; - }, - loggedInAs: - function () { - return this.loginName; - }, - login: - function (name, pwd) { - loadJSONDoc("/login?login=" + name + "&password=" + pwd).addCallbacks( - this._requestLoginName, - this._requestError); - }, - logout: - function () { - loadJSONDoc("/logout").addCallbacks( - this._requestLoginName, - this._requestError); - }, - publishLoginName: - function () { - this._requestLoginName(); - }, - _setLoginName: - function (loginName) { - var oldLoginName = loginManager.loginName; - loginManager.loginName = loginName; - // if (oldLoginName != loginName) - // loginManager.publish(loginName); - loginManager.publish(loginName); - }, - _requestLoginName: - function () { - loadJSONDoc("/logged-in-as").addCallbacks( - loginManager._setLoginName, - loginManager._requestError); - }, - _requestError: - function (error) { - alert("Request error: " + error.message) - } - }; -extendWithPublication(loginManager); - -var loginView = {loginManagerCallback: - function (loginName) { - if (loginName) - loginView.changeForLoggedIn(loginName); - else - loginView.changeForLoggedOut(); - }, - show: - function () { - var container = $('playfield'); - var form = FORM({id:"loginForm", action:"post", onsubmit:"return false"}, - DIV( - null, - LABEL({'for':"username"}, "Username: "), - INPUT({id:"username", name:"username", size:"20", type:"text"}), - LABEL({'for':"password"}, "Password: "), - INPUT({id:"password", name:"password", size:"20", type:"password"}), - P({id:"message"}), - BUTTON({id:"button", onlick:"return false"}, "Login") - )); - form.style.position = 'absolute'; - setElementPosition(form, { x: 750, y: 200 }); - setElementDimensions(form, {w:200, h:150}); - loginManager.subscribe(loginView, this.loginManagerCallback); - appendChildNodes(container, form) - }, - hide: - function () { - loginManager.subscribe(loginView, loginView.loginManagerCallback); - removeElement($('loginForm')); - }, - changeForLoggedIn: - function (name) { - loginView.hide(); - gameListView.show(); - }, - changeForLoggedOut: - function () { - $('message').innerHTML = "Enter your username and password to log in."; - $('button').innerHTML = "Login"; - $('button').onclick = function () { loginManager.login($('username').value, $('password').value)}; - }}; - -var gameListManager = {logout: - function () { - gameListView.hide(); - loginView.show(); - loginManager.logout(); - }, - playGame: - function (id) { - var d = loadJSONDoc("/play-game?gameid=" + id); - d.addCallbacks( - function () { - var d = loadJSONDoc("/game"); - d.addCallbacks(drawGameState, function (error) { alert("Request error: " + error.message); }); - gameListView.hide(); - returnToGameListView.show(); - }, - requestError); - } - }; - -var gameListView = {show: - function () { - var container = $('playfield'); - var div = DIV({id:"gameListView"}); - div.innerHTML = '<button onclick="gameListManager.logout();">Logout</button><br /><button onclick="gameListManager.playGame(108);">play game 108</button>'; - div.style.position = 'absolute'; - setElementPosition(div, { x: 750, y: 200 }); - setElementDimensions(div, {w:200, h:150}); - appendChildNodes(container, div); - }, - hide: - function () { - removeElement($('gameListView')); - }}; - - -// a stub really -var returnToGameListView = {show: - function () { - var container = $('playfield'); - var button = BUTTON({id:'returnToGameListButton'}, "return to game list"); - button.style.position = 'absolute'; - setElementPosition(button, { x: 750, y: 200 }); - setElementDimensions(button, {w:150, h:30}); - YAHOO.util.Event.on(button, 'click', this.returnToGameList, null, this); - // loginManager.subscribe(loginView, this.loginManagerCallback); - appendChildNodes(container, button); - }, - hide: - function () { - removeElement('returnToGameListButton'); - // FIXME for now we reload - document.location = "scrabble.html"; - }, - returnToGameList: - function () { - var d = loadJSONDoc("/leave-game"); - d.addCallbacks( - function () { - returnToGameListView.hide(); - gameListView.show(); - }, - requestError)} - }; - + // does not work for ie (document.body needed)? + YAHOO.util.Event.on(window, 'keypress', letterKeyPressed);
-function init() { - makeBoard(); - - // does not work for ie (document.body needed)? - YAHOO.util.Event.on(window, 'keypress', letterKeyPressed); - - var functionKeyListener = new YAHOO.util.KeyListener(document, - { keys: [ leftKey, upKey, rightKey, downKey, backspaceKey ] }, - { fn: functionKeyPressed, scope: this, correctScope: true }); - functionKeyListener.enable(); - var moveDisplay = DIV({ id: 'move' }, ""); - moveDisplay.style.color = 'white'; - moveDisplay.style.position = 'absolute'; - setElementPosition(moveDisplay, { x: border + 550, y: border + 665 }); - appendChildNodes(document.body, moveDisplay); - loginView.show(); - loginManager.subscribe(function (loginName) { - if (loginName) { - // loginView.hide(); - } - }); - loginManager.publishLoginName(); + var functionKeyListener = new YAHOO.util.KeyListener(document, + { keys: [ leftKey, upKey, rightKey, downKey, backspaceKey ] }, + { fn: functionKeyPressed, scope: this, correctScope: true }); + functionKeyListener.enable(); + + var moveDisplay = DIV({ id: 'move' }, ""); + moveDisplay.style.color = 'white'; + moveDisplay.style.position = 'absolute'; + setElementPosition(moveDisplay, { x: 550, y: 665 }); + appendChildNodes(document.body, moveDisplay); + loadJSONDoc("/game/" + gameID) + .addCallbacks(drawGameState, function (error) { alert("Request error: " + error.message); }); }
Modified: branches/trunk-reorg/thirdparty/hunchentoot-0.14.7/misc.lisp ============================================================================== --- branches/trunk-reorg/thirdparty/hunchentoot-0.14.7/misc.lisp (original) +++ branches/trunk-reorg/thirdparty/hunchentoot-0.14.7/misc.lisp Thu Jan 17 11:36:28 2008 @@ -180,7 +180,10 @@ (defun enough-url (url url-prefix) "Returns the relative portion of URL relative to URL-PREFIX, similar to what ENOUGH-NAMESTRING does for pathnames." - (subseq url (mismatch url url-prefix))) + (let ((start (mismatch url url-prefix))) + (if start + (subseq url start) + "")))
(defun create-folder-dispatcher-and-handler (uri-prefix base-path &optional content-type) "Creates and returns a dispatch function which will dispatch to a
Modified: branches/trunk-reorg/thirdparty/parenscript/src/js-macrology.lisp ============================================================================== --- branches/trunk-reorg/thirdparty/parenscript/src/js-macrology.lisp (original) +++ branches/trunk-reorg/thirdparty/parenscript/src/js-macrology.lisp Thu Jan 17 11:36:28 2008 @@ -4,7 +4,9 @@
;;; literals (defmacro defpsliteral (name string) - `(define-ps-special-form ,name (expecting) (list 'js-literal ,string))) + `(define-ps-special-form ,name (expecting) + (declare (ignore expecting)) + (list 'js-literal ,string)))
(defpsliteral this "this") (defpsliteral t "true") @@ -15,45 +17,54 @@ (defpsliteral undefined "undefined")
(defmacro defpskeyword (name string) - `(define-ps-special-form ,name (expecting) (list 'js-keyword ,string))) + `(define-ps-special-form ,name (expecting) + (declare (ignore expecting)) + (list 'js-keyword ,string)))
(defpskeyword break "break") (defpskeyword continue "continue")
(define-ps-special-form array (expecting &rest values) + (declare (ignore expecting)) (cons 'array-literal (mapcar (lambda (form) (compile-parenscript-form form :expecting :expression)) values)))
(define-ps-special-form aref (expecting array &rest coords) + (declare (ignore expecting)) (list 'js-aref (compile-parenscript-form array :expecting :expression) (mapcar (lambda (form) (compile-parenscript-form form :expecting :expression)) coords)))
(define-ps-special-form {} (expecting &rest arrows) + (declare (ignore expecting)) (cons 'object-literal (loop for (key value) on arrows by #'cddr collect (cons key (compile-parenscript-form value :expecting :expression)))))
;;; operators (define-ps-special-form incf (expecting x &optional (delta 1)) + (declare (ignore expecting)) (if (equal delta 1) (list 'unary-operator "++" (compile-parenscript-form x :expecting :expression) :prefix t) (list 'operator '+= (list (compile-parenscript-form x :expecting :expression) (compile-parenscript-form delta :expecting :expression)))))
(define-ps-special-form decf (expecting x &optional (delta 1)) + (declare (ignore expecting)) (if (equal delta 1) (list 'unary-operator "--" (compile-parenscript-form x :expecting :expression) :prefix t) (list 'operator '-= (list (compile-parenscript-form x :expecting :expression) (compile-parenscript-form delta :expecting :expression)))))
(define-ps-special-form - (expecting first &rest rest) + (declare (ignore expecting)) (if (null rest) (list 'unary-operator "-" (compile-parenscript-form first :expecting :expression) :prefix t) (list 'operator '- (mapcar (lambda (val) (compile-parenscript-form val :expecting :expression)) (cons first rest)))))
(define-ps-special-form not (expecting x) + (declare (ignore expecting)) (let ((form (compile-parenscript-form x :expecting :expression)) (not-op nil)) (if (and (eql (first form) 'operator) @@ -72,6 +83,7 @@ (list 'unary-operator "!" form :prefix t))))
(define-ps-special-form ~ (expecting x) + (declare (ignore expecting)) (list 'unary-operator "~" (compile-parenscript-form x :expecting :expressin) :prefix t))
(defun flatten-blocks (body) @@ -97,18 +109,21 @@
;;; function definition (define-ps-special-form %js-lambda (expecting args &rest body) + (declare (ignore expecting)) (list 'js-lambda (mapcar (lambda (arg) (compile-parenscript-form arg :expecting :symbol)) args) (compile-parenscript-form `(progn ,@body))))
(define-ps-special-form %js-defun (expecting name args &rest body) + (declare (ignore expecting)) (list 'js-defun (compile-parenscript-form name :expecting :symbol) (mapcar (lambda (val) (compile-parenscript-form val :expecting :symbol)) args) (compile-parenscript-form `(progn ,@body))))
;;; object creation (define-ps-special-form create (expecting &rest args) + (declare (ignore expecting)) (list 'js-object (loop for (name val) on args by #'cddr collecting (let ((name-expr (compile-parenscript-form name :expecting :expression))) (assert (or (stringp name-expr) @@ -121,6 +136,7 @@ (list name-expr (compile-parenscript-form val :expecting :expression))))))
(define-ps-special-form %js-slot-value (expecting obj slot) + (declare (ignore expecting)) (if (ps::ps-macroexpand slot) (list 'js-slot-value (compile-parenscript-form obj :expecting :expression) (compile-parenscript-form slot)) (compile-parenscript-form obj :expecting :expression))) @@ -157,6 +173,7 @@ (compile-parenscript-form else :expecting :expression)))))
(define-ps-special-form switch (expecting test-expr &rest clauses) + (declare (ignore expecting)) (let ((clauses (mapcar (lambda (clause) (let ((val (car clause)) (body (cdr clause))) @@ -207,9 +224,11 @@ (list 'js-assign lhs rhs)))
(define-ps-special-form setf1% (expecting lhs rhs) + (declare (ignore expecting)) (smart-setf (compile-parenscript-form lhs :expecting :expression) (compile-parenscript-form rhs :expecting :expression)))
(define-ps-special-form defvar (expecting name &rest value) + (declare (ignore expecting)) (append (list 'js-defvar (compile-parenscript-form name :expecting :symbol)) (when value (assert (= (length value) 1) () "Wrong number of arguments to defvar: ~s" `(defvar ,name ,@value)) @@ -228,6 +247,7 @@ collect (compile-parenscript-form (third decl) :expecting :expression)))
(define-ps-special-form do (expecting decls termination-test &rest body) + (declare (ignore expecting)) (let ((vars (make-for-vars decls)) (steps (make-for-steps decls)) (test (compile-parenscript-form `(not ,(first termination-test)) :expecting :expression)) @@ -235,20 +255,24 @@ (list 'js-for vars steps test body)))
(define-ps-special-form doeach (expecting decl &rest body) + (declare (ignore expecting)) (list 'js-for-each (compile-parenscript-form (first decl) :expecting :symbol) (compile-parenscript-form (second decl) :expecting :expression) (compile-parenscript-form `(progn ,@body))))
(define-ps-special-form while (expecting test &rest body) + (declare (ignore expecting)) (list 'js-while (compile-parenscript-form test :expecting :expression) (compile-parenscript-form `(progn ,@body))))
(define-ps-special-form with (expecting expression &rest body) + (declare (ignore expecting)) (list 'js-with (compile-parenscript-form expression :expecting :expression) (compile-parenscript-form `(progn ,@body))))
(define-ps-special-form try (expecting form &rest clauses) + (declare (ignore expecting)) (let ((catch (cdr (assoc :catch clauses))) (finally (cdr (assoc :finally clauses)))) (assert (not (cdar catch)) nil "Sorry, currently only simple catch forms are supported.") @@ -260,23 +284,28 @@ :finally (when finally (compile-parenscript-form `(progn ,@finally))))))
(define-ps-special-form regex (expecting regex) + (declare (ignore expecting)) (list 'js-regex (string regex)))
;;; TODO instanceof (define-ps-special-form instanceof (expecting value type) + (declare (ignore expecting)) (list 'js-instanceof (compile-parenscript-form value :expecting :expression) (compile-parenscript-form type :expecting :expression)))
;;; single operations (mapcar (lambda (op) (eval `(define-ps-special-form ,op (expecting value) + (declare (ignore expecting)) (list 'js-named-operator ',op (compile-parenscript-form value))))) '(throw delete void typeof new))
(define-ps-special-form return (expecting &optional value) + (declare (ignore expecting)) (list 'js-return (compile-parenscript-form value :expecting :expression)))
;;; conditional compilation (define-ps-special-form cc-if (expecting test &rest body) + (declare (ignore expecting)) (list 'cc-if test (mapcar #'compile-parenscript-form body)))
;;; standard macros
Modified: branches/trunk-reorg/thirdparty/slime/slime.el ============================================================================== --- branches/trunk-reorg/thirdparty/slime/slime.el (original) +++ branches/trunk-reorg/thirdparty/slime/slime.el Thu Jan 17 11:36:28 2008 @@ -6287,7 +6287,9 @@ ("*SLIME macroexpansion*" :mode lisp-mode :reusep t) package (slime-macroexpansion-minor-mode) (erase-buffer) - (insert expansion) + (save-excursion + (insert expansion)) + (indent-sexp) (font-lock-fontify-buffer))))))
(defun slime-eval-macroexpand-inplace (expander) @@ -6316,6 +6318,59 @@ (indent-sexp) (goto-char point))))))))
+(defun slime-enclosing-macro-context-establishers () + (flet ((establishes-context-p (form-spec) + (let ((operator-name (first form-spec))) + (when (stringp operator-name) + (let ((symbol-name (slime-cl-symbol-name operator-name))) + (or (equal symbol-name "macrolet") (equal symbol-name "symbol-macrolet"))))))) + (multiple-value-bind (form-specs indices points) + (slime-enclosing-form-specs) + (loop for form-spec in form-specs + for index in indices + for point in points + when (establishes-context-p form-spec) + collect form-spec into form-specs* and + collect index into indices* and + collect point into points* + finally (return (values form-specs* indices* points*)))))) + +(defun slime-collect-macro-context () + (multiple-value-bind (form-specs indices points) + (slime-enclosing-macro-context-establishers) + (save-excursion + (let ((context)) + (cl-mapc #'(lambda (form-spec index point) + (when (= index 2) + (destructuring-bind (operator-name) form-spec + (goto-char point) + (slime-forward-sexp) + (forward-char) + (push (cons operator-name (slime-parse-sexp-at-point 1 t)) context)))) + form-specs indices points) + context)))) + +(defun slime-rebuild-macro-context-around-string (string context) + (if (null context) + string + (destructuring-bind (let-operator . bindings) (first context) + (format "(%s %s %s)" let-operator bindings + (slime-rebuild-macro-context-around-string string (rest context)))))) + +(defun slime-macroexpand-locally-1 (&optional repeatedly) + (interactive "P") + (let ((sexp (first (slime-sexp-at-point-for-macroexpansion))) + (macro-context (slime-collect-macro-context))) + (if repeatedly + (slime-eval-macroexpand 'swank:swank-macroexpand-locally + (slime-rebuild-macro-context-around-string + (format "(swank::macroexpand-locally %s)" sexp) + macro-context)) + (slime-eval-macroexpand 'swank:swank-macroexpand-locally-1 + (slime-rebuild-macro-context-around-string + (format "(swank::macroexpand-locally-1 %s)" sexp) + macro-context))))) + (defun slime-macroexpand-1 (&optional repeatedly) "Display the macro expansion of the form at point. The form is expanded with CL:MACROEXPAND-1 or, if a prefix argument is given, with
Modified: branches/trunk-reorg/thirdparty/slime/swank.lisp ============================================================================== --- branches/trunk-reorg/thirdparty/slime/swank.lisp (original) +++ branches/trunk-reorg/thirdparty/slime/swank.lisp Thu Jan 17 11:36:28 2008 @@ -78,7 +78,8 @@ "Abbreviate dotted package names to their last component if T.")
(defvar *swank-io-package* - (let ((package (make-package :swank-io-package :use '()))) + (let ((package (or (find-package :swank-io-package) + (make-package :swank-io-package :use '())))) (import '(nil t quote) package) package))
@@ -2401,6 +2402,22 @@ (let ((*print-readably* nil)) (disassemble (fdefinition (from-string name)))))))
+(defslimefun swank-macroexpand-locally (string) + (apply-macro-expander #'eval string)) + +(defslimefun swank-macroexpand-locally-1 (string) + (apply-macro-expander #'eval string)) + +(defmacro macroexpand-locally (form &environment env) + (multiple-value-bind (expansion expanded-p) + (macroexpand form env) + `(values ',expansion ',expanded-p))) + +(defmacro macroexpand-locally-1 (form &environment env) + (multiple-value-bind (expansion expanded-p) + (macroexpand-1 form env) + `(values ',expansion ',expanded-p))) + ;;;; Simple completion