実践 Ajax が Amazon で在庫ありになっていたので注文したところ早速到着したので、 MochiKit の勉強も兼ねて第2章の地図を MochiKit で作ってみた。
ドラッグの処理を MochiKit.Signal.connect でやろうとすると、 地図のドラッグがうまくできなかったので、仕方なく普通のイベント処理で作ってみた。
どうやればうまく動くんだろう。
(画像をドラッグしようとしてしまい、地図が動かない。 img に preventDefault とかをくっつけてみたけどダメだった。)
addLoadEvent(init);
function init() {
    setElementDimensions($('innerDiv'), { w : 2000, h : 1400 });
    var div = $('outerDiv');
    updateNodeAttributes(div, {'onmousedown' : startMove, 'onmousemove' : processMove, 'onmouseup' : stopMove});
//    connect(div, 'onmousedown', window, startMove);
//    connect(div, 'onmousemove', window, processMove);
//    connect(div, 'onmouseup', window, stopMove);
    checkTiles();
}
var dragging = false;
var top;
var left;
var dragStartTop;
var dragStartLeft;
function startMove(e) {
    e = e || event;
    dragStartLeft = e.clientX;
    dragStartTop = e.clientY;
//    dragStartLeft = e.mouse().client.x;
//    dragStartTop = e.mouse().client.y;
    $('innerDiv').style.cursor = '-moz-grab';
    var pos = getElementPosition($('innerDiv'), $('outerDiv'));
    left = pos.x;
    top = pos.y;
    dragging = true;
    return false;
}
function processMove(e) {
    e = e || event;
    if (dragging) {
        setElementPosition($('innerDiv'),
                           { x : left + e.clientX - dragStartLeft, y : top + e.clientY - dragStartTop });
//                           { x : left + e.mouse().client.x - dragStartLeft, y : top + e.mouse().client.y - dragStartTop });
    }
    checkTiles();
}
function stopMove(e) {
    $('innerDiv').style.cursor = '';
    dragging = false;
}
function checkTiles() {
    var visibleTiles = getVisibleTiles();
    var div = $('innerDiv');
    var visibleTilesMap = {};
    forEach(visibleTiles, function(t) {
            var tileName = 'x' + t[0] + 'y' + t[1] + 'z0';
            visibleTilesMap[tileName] = true;
            if (!$(tileName)) {
                var img = IMG({ id : tileName, src : 'resources/tiles/' + tileName + '.jpg' });
                img.style.position = 'absolute';
                setElementPosition(img, { x : t[0] * tileSize, y : t[1] * tileSize });
                appendChildNodes(div, img);
            }
        });
    var sweptImgs = filter(function(e){ return !visibleTilesMap[e.getAttribute('id')] }, div.getElementsByTagName('img'));
    forEach(sweptImgs, removeElement);
}
var tileSize = 100;
var viewportSize = [800, 600];
function getVisibleTiles() {
    var mapPos = getElementPosition($('innerDiv'), $('outerDiv'));
    var startX = Math.abs(Math.floor(mapPos.x / tileSize)) - 1;
    var startY = Math.abs(Math.floor(mapPos.y / tileSize)) - 1;
    var tiles = map(function(n){ return Math.ceil(n / tileSize) + 1 }, viewportSize);
    var visibleTileArray = [];
    forEach(range(startX, tiles[0] + startX), function(x) {
            forEach(range(startY, tiles[1] + startY), function(y) {
                    visibleTileArray.push([x, y]);
                })
        });
    return visibleTileArray;
}
<html>
<head>
  <title>Ajaxian Maps</title>
  <style type="text/css">
h1 {
  font: 20pt sans-serif;
}
#outerDiv {
  height: 600px;
  width: 800px;
  border: 1px solid #000;
  position: relative;
  overflow: hidden;
}
#innerDiv {
  position: relative;
  left: 0px;
  top: 0px;
  z-index: 0;
}
#zoomWidget {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
}
  </style>
  <script type="text/javascript" src="MochiKit/MochiKit.js"></script>
  <script type="text/javascript" src="map-mochi.js"></script>
</head>
<body>
<p><h1>Ajaxian Maps</h1></p>
<div id="outerDiv">
  <div id="innerDiv">
    The rain in Spain falls mainly in the plains.
  </div>
</div>
</body>
</html>