eggification
[Portfolio.git] / Products / Portfolio / skins / mosaique.js
diff --git a/Products/Portfolio/skins/mosaique.js b/Products/Portfolio/skins/mosaique.js
new file mode 100755 (executable)
index 0000000..47b8d1e
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+* © 2007-2008 Benoît Pin – Centre de recherche en informatique – École des mines de Paris
+* http://plinn.org
+* Licence Creative Commons http://creativecommons.org/licenses/by-nc/2.0/
+* 
+* 
+*/
+
+var Mosaique;
+
+(function(){
+
+var hiddenTilesNumber = 1;
+var batchSize = 5;
+var reNb = /\-?\d+/    ;
+
+Mosaique = function(screenArea, imgUrlBase, margins) {
+       this.screenArea = screenArea;
+       if (!margins)
+               margins = {'top':0, 'right':0, 'bottom':0, 'left':0};
+       this.margins = margins;
+       this.prepareScreen();
+       this.setContainerPosition(new Point(0,0));
+       
+       this.imgUrlBase = imgUrlBase;
+       this.xmlPath = imgUrlBase + "/tiling_infos.xml";
+       
+       this.tiles = null;
+       this.xTileRange = [0,0];
+       this.yTileRange = [0,0];
+       
+       this._loadingQueue = new Array();
+       this._currentSequence = null;
+       this._loadingIterator = 0;
+       this.loadingState = 0;
+       
+       this.dragInProgress = false;
+       this.initialClickPoint = null;
+       this.initialPosition = null;
+       var thisMos = this;
+       this._ddHandlers = {'down' : function(evt){thisMos._mouseDownHandler(evt);},
+                                               'move' : function(evt){thisMos._mouseMoveHandler(evt);},
+                                               'up' :   function(evt){thisMos._mouseUpHandler(evt);}};
+       this.ddHandlers = null;
+       
+       this.rcsItoC = new Point(0, 0); // vector to translate coordinate systems
+       
+       this.remainD = new Point(0, 0);
+       this.getXmlInfo();
+}
+
+Mosaique.prototype.getXmlInfo = function() {
+       var req = new XMLHttpRequest();
+       var thisMosaique = this;
+       req.onreadystatechange = function() {
+               if(req.readyState == 4)
+                       thisMosaique._loadXmlInfo(req);
+       };
+       req.open("GET",thisMosaique.xmlPath,true);
+       req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
+       req.send(null);
+};
+
+Mosaique.prototype._loadXmlInfo = function(req) {
+       var doc = req.responseXML.documentElement;
+       this.thumbnailWidth = parseInt(doc.getElementsByTagName('thumbnailwidth')[0].firstChild.data);
+       this.thumbnailHeight = parseInt(doc.getElementsByTagName('thumbnailheight')[0].firstChild.data);
+       this.tileSize = parseInt(doc.getElementsByTagName('tilesize')[0].firstChild.data);
+
+       var zoomList = doc.getElementsByTagName("zoom");
+       this.zoomTable = new Array(zoomList.length);
+       var zoom;
+       for (var i=0 ; i<zoomList.length ; i++) {
+               zoom = zoomList[i];
+               var zoomInfo = new Object();
+               zoomInfo['level'] = parseInt(zoom.getAttribute('zoomlevel'));
+               zoomInfo['width'] = parseInt(zoom.getElementsByTagName('width')[0].firstChild.data);
+               zoomInfo['height'] = parseInt(zoom.getElementsByTagName('height')[0].firstChild.data);
+               zoomInfo['tilesX'] = parseInt(zoom.getElementsByTagName('tilesx')[0].firstChild.data);
+               zoomInfo['tilesY'] = parseInt(zoom.getElementsByTagName('tilesy')[0].firstChild.data);
+               this.zoomTable[i] = zoomInfo;
+       }
+       
+       this.setCurrentDimensionValues(this.getBestFitZoom());
+       this.navigateur = new Navigateur(this);
+       
+       this.prepareContainer();
+       this.setLoadingOrder();
+       var ulc = new Point(this.imageWidth/2 - this.screenWidth/2, 0);
+       if (this.screenHeight >= this.imageHeight)
+               ulc.y = this.imageHeight/2 - this.screenHeight/2;
+       this.loadScreen(ulc);
+       this.addEventListeners();
+};
+
+Mosaique.prototype.addEventListeners = function() {
+       var thisMos = this;
+       addListener(document, 'mousedown', function(evt){thisMos.mouseDownHandler(evt);}, 'mosaique.dd');
+       addListener(document, 'mouseup', function(evt){thisMos.mouseUpHandler(evt);}, 'mosaique.dd');
+};
+
+Mosaique.prototype.getBestFitZoom = function() {
+       var i;
+       for (i = 1 ; i < this.zoomTable.length ; i++)
+               if (this.screenWidth - this.zoomTable[i]['width'] < 0 ||
+                       this.screenHeight - this.zoomTable[i]['height'] < 0)
+                               break;
+       return i-1;
+};
+
+Mosaique.prototype.setCurrentDimensionValues = function(zoomIndex) {
+       var zoomInfo = this.zoomTable[zoomIndex];
+       this.zoomIndex = zoomIndex;
+       this.zoomLevel = zoomInfo['level'];
+       this.imageWidth = zoomInfo['width'];
+       this.imageHeight = zoomInfo['height'];
+       this.xtiles = zoomInfo['tilesX'];
+       this.ytiles = zoomInfo['tilesY'];
+       this.halfTileSize = this.tileSize / 2;
+       this.gridWidth = this.xtiles*this.tileSize;
+       this.gridHeight = this.ytiles*this.tileSize;
+       
+       var WnbTiles = Math.ceil(this.screenWidth/this.tileSize) + 2 * hiddenTilesNumber;
+       var HnbTiles = Math.ceil(this.screenHeight/this.tileSize) + 2 * hiddenTilesNumber;
+       this.WnbTiles = (WnbTiles > this.xtiles) ? this.xtiles : WnbTiles;
+       this.HnbTiles = (HnbTiles > this.ytiles) ? this.ytiles : HnbTiles;
+       
+};
+
+Mosaique.prototype.prepareScreen = function() {
+       this.screenWidth = getObjectWidth(this.screenArea);
+       this.screenHeight = getObjectHeight(this.screenArea);
+       var mask = this.rootElement = document.createElement('div');
+       with (mask.style) {
+               position = 'absolute';
+               width = this.screenWidth - this.margins['right'] + 'px';
+               height = this.screenHeight - this.margins['bottom'] + 'px';
+               background = base_properties["contentBackgroundColor"];
+               overflow = 'hidden';
+       }
+       this.screenArea.insertBefore(mask, this.screenArea.firstChild);
+       
+       var container = document.createElement('div');
+       with (container.style) {
+               position = 'absolute';
+               top = '0px'; 
+               left = '0px';
+               cursor = 'move';
+       }
+       
+       mask.appendChild(container);
+       this.container = container;
+};
+
+Mosaique.prototype.prepareContainer = function() {
+       var __getImg;
+       if (browser.isGecko) {
+               __getImg = function(evt, o) {
+                       return o;
+               }
+       }
+       else {
+               __getImg = function(evt) {
+                       return getTargetedObject(evt);
+               }
+       }
+       var thisMos = this;
+       var loadNext;
+       if (browser.isIE) {
+               loadNext = function(evt) {
+                       var tile = __getImg(evt, this);
+                       tile.style.visibility = 'visible';
+                       setTimeout(function(){thisMos._loadNextTile();}, 1);
+               }
+       }
+       else {
+               loadNext = function(evt) {
+                       var tile = __getImg(evt, this);
+                       tile.style.visibility = 'visible';
+                       thisMos._loadNextTile();
+               }
+       }
+       
+    this.setContainerPosition(new Point(0,0));
+       this.xTileRange = [0,0];
+       this.yTileRange = [0,0];
+
+       var size = parseInt(this.tileSize);
+       this.tiles = new Array(this.WnbTiles);
+       for (var x = 0 ; x < this.WnbTiles ; x++) {
+               this.tiles[x] = new Array(this.HnbTiles);
+               for (var y = 0 ; y < this.HnbTiles ; y++) {
+                       var img = document.createElement("img");
+                       with(img) {
+                               width = size;
+                               height = size;
+                       }
+                       addListener(img, 'load', loadNext, 'mosaique.tiles');
+                       img.style.position='absolute';
+                       img.style.left = x * size + 'px';
+                       img.style.top = y * size + 'px';
+                       this.container.appendChild(img);
+                       this.tiles[x][y] = img;
+               }
+       }
+       with(this.container.style) {
+               width = size * this.WnbTiles + 'px';
+               height = size * this.HnbTiles + 'px';
+       }
+};
+
+
+Mosaique.prototype.setContainerPosition = function(point) {
+       with(this.container.style) {
+               left = point.x + 'px';
+               top = point.y + 'px';
+       }
+};
+
+Mosaique.prototype.getContainerPosition = function() {
+       var x = parseInt(this.container.style.left);
+       var y = parseInt(this.container.style.top);
+       var p = new Point(x, y);
+       return p;
+};
+
+Mosaique.prototype.setImagePosition = function(point) {
+       this.setContainerPosition(this.rcsItoC.diff(point));
+};
+
+Mosaique.prototype.getImagePosition = function() {
+    var cp = this.getContainerPosition();
+    return this.rcsItoC.diff(cp);
+};
+
+Mosaique.prototype.getImageCenterPosition = function() {
+    var ip = this.getImagePosition();
+    return ip.add(new Point(this.screenWidth/2, this.screenHeight/2));
+}
+
+
+
+Mosaique.prototype.loadScreen = function(position) {
+       
+       var tSize = this.tileSize;
+       var ulTileCoord = new Point(Math.floor(position.x/tSize), Math.floor(position.y/tSize));
+       var modulo = new Point(position.x % tSize, position.y % tSize);
+       
+       var adjX, adjY;
+       if (modulo.x > this.halfTileSize) {
+               adjX = function(n){return n - hiddenTilesNumber + 1;};
+               this.remainD.x = - (modulo.x - tSize);
+       }
+       else {
+               adjX = function(n){return n - hiddenTilesNumber;};
+               this.remainD.x = - modulo.x;
+       }
+       if (modulo.y > this.halfTileSize) {
+               this.remainD.y = - (modulo.y - tSize);
+               adjY = function(n){return n - hiddenTilesNumber + 1;};
+       }
+       else {
+               adjY = function(n){return n - hiddenTilesNumber;};
+               this.remainD.y = - modulo.y;
+       }
+               
+       var xTileRange, yTileRange;
+       xTileRange = [ulTileCoord.x, ulTileCoord.x + this.WnbTiles].map(adjX);
+       yTileRange = [ulTileCoord.y, ulTileCoord.y + this.HnbTiles].map(adjY);
+
+       //console.assert(xTileRange[1] - xTileRange[0] == this.WnbTiles, xTileRange, this.WnbTiles);
+       //console.assert(yTileRange[1] - yTileRange[0] == this.HnbTiles, yTileRange, this.HnbTiles);
+       
+       if (xTileRange[0] < 0) {
+               this.remainD.x = Math.abs(xTileRange[0] + ((position.x < 0) ? 1 : 0)) * tSize - modulo.x;
+               xTileRange = [0, this.WnbTiles];
+       }
+       else if (xTileRange[1] > this.xtiles) {
+               this.remainD.x = - (xTileRange[1] - this.xtiles - ((modulo.x > this.halfTileSize) ? 1 : 0)) * tSize - modulo.x;
+               xTileRange = [this.xtiles - this.WnbTiles, this.xtiles];
+       }
+
+       if (yTileRange[0] < 0) { 
+               this.remainD.y = Math.abs(yTileRange[0] + ((position.y < 0) ? 1 : 0)) * tSize - modulo.y;
+               yTileRange = [0, this.HnbTiles];
+       }
+       else if (yTileRange[1] > this.ytiles) {
+               this.remainD.y = - (yTileRange[1] - this.ytiles - ((modulo.y > this.halfTileSize) ? 1 : 0)) * tSize - modulo.y;
+               yTileRange = [this.ytiles - this.HnbTiles, this.ytiles];
+       }
+               
+       //console.assert(xTileRange[0] >= 0 && xTileRange[1] <= this.xtiles);
+       //console.assert(yTileRange[0] >= 0 && yTileRange[1] <= this.ytiles);
+       
+       var dTx = this.xTileRange[0] - xTileRange[0];
+       var dTy = this.yTileRange[0] - yTileRange[0];
+       this.rcsItoC = this.rcsItoC.diff(new Point(dTx * tSize, dTy * tSize));
+
+       this.setImagePosition(position);
+       
+       this.xTileRange = xTileRange;
+       this.yTileRange = yTileRange;
+       
+       var xOffset = this.xTileRange[0];
+       var yOffset = this.yTileRange[0];
+
+       var baseUrl = this.imgUrlBase + '/getTile?zoom=' + this.zoomLevel / 100.0;
+       
+       var tilesSrc = new Array(this.WnbTiles);
+       for (var x = 0 ; x < this.WnbTiles ; x++) {
+               tilesSrc[x] = new Array(this.HnbTiles);
+               for (var y = 0 ; y < this.HnbTiles ; y++) {
+                       tilesSrc[x][y] = baseUrl + '&x=' + (x + xOffset) + '&y=' + (y + yOffset);
+               }
+       }
+       this.queueLoadingSequence({'type':'full',
+                                                          'src' : tilesSrc,
+                                                          'order' : this.loadingOrder,
+                                                          'length' : this.loadingOrder.length});
+};
+
+Mosaique.prototype.loadColumns = function(n){
+       /*
+       n > 0 <=> x displacement > 0 => shift columns from right to left
+       Returns the number of columns that have been loaded.
+       */
+       
+       
+       var newRange;
+       
+       while (n != 0) {
+               newRange = [this.xTileRange[0] - n, this.xTileRange[1] - n]
+               if (newRange[0]<0 || newRange[1] > this.xtiles) {
+                       (n>0) ? n-- : n++;
+                       continue;
+               }
+               else {
+                       this.xTileRange = newRange;
+                       break;
+               }
+               return 0;
+       }
+       
+
+    var shift, from, to, increment;
+    if (n>0) {
+       shift = - (this.WnbTiles) * this.tileSize;
+       from = 0;
+       to = n;
+       increment = 1;
+    }
+    else {
+       shift = (this.WnbTiles) * this.tileSize;
+       from = this.WnbTiles - 1;
+       to = this.WnbTiles + n -1;
+       increment = -1;
+    }
+
+       var thisMos = this;
+       var beforeSequence = function(){thisMos._shiftColumns(n, shift, from, to, increment)};
+
+       var order = new Array();
+       var tilesSrc = new Array();
+       var baseUrl = this.imgUrlBase + '/getTile?zoom=' + this.zoomLevel / 100.0;
+       var xOffset = this.xTileRange[0];
+       var yOffset = this.yTileRange[0];
+
+       for (var x = from ; x != to ; x += increment) {
+               tilesSrc[x] = new Array();
+           for (var y = 0 ; y < this.HnbTiles ; y++) {
+                       order.push([x, y]);
+                       tilesSrc[x][y] = baseUrl + '&x=' + (x + xOffset) + '&y=' + (y + yOffset);
+               }
+       }
+       order = order.reverse();
+       this.queueLoadingSequence({'type':'column',
+                                                          'src' : tilesSrc,
+                                                          'order' : order,
+                                                          'length' : order.length,
+                                                          'beforeSequence' : beforeSequence});
+       return n;
+};
+
+Mosaique.prototype._shiftColumns = function(n, shift, from, to, increment){
+       /* rows rotations */
+       this.tiles = rotateArray(this.tiles, -n);
+       var tile, left;
+       /* positional shifting */
+       for (var x = from ; x != to ; x += increment) {
+               left = parseInt(this.tiles[x][0].style.left);
+               for (var y = 0 ; y < this.HnbTiles ; y++) {
+                   var tile = this.tiles[x][y];
+                       tile.style.left = left + shift + 'px';
+                       tile.style.visibility = 'hidden';
+               }
+       }
+       
+};
+
+
+
+Mosaique.prototype.loadRows = function(n) {
+       /*
+       n > 0 <=> y displacement > 0 => shift rows from bottom to top
+       Returns the number of rows that's have been loaded.
+       */
+
+       var newRange;
+       
+       while (n != 0) {
+               newRange = [this.yTileRange[0] - n, this.yTileRange[1] - n]
+               if (newRange[0]<0 || newRange[1] > this.ytiles) {
+                       (n>0) ? n-- : n++;
+                       continue;
+               }
+               else {
+                       this.yTileRange = newRange;
+                       break;
+               }
+               return 0;
+       }
+
+       var shift, from, to, increment;
+       if (n>0) {
+               shift = - (this.HnbTiles) * this.tileSize;
+               from = 0;
+               to = n;
+               increment = 1;
+       }
+       else {
+               shift = (this.HnbTiles) * this.tileSize;
+               from = this.HnbTiles - 1;
+               to = this.HnbTiles + n -1;
+               increment = -1;
+       }
+       
+       var thisMos = this;
+       var beforeSequence = function(){thisMos._shiftRows(n, shift, from, to, increment)};
+
+       var order = new Array();
+       var tilesSrc = new Array();
+       var baseUrl = this.imgUrlBase + '/getTile?zoom=' + this.zoomLevel / 100.0;
+       var xOffset = this.xTileRange[0];
+       var yOffset = this.yTileRange[0];
+       
+       for (var y = from ; y != to ; y += increment) {
+               for (var x = 0 ; x < this.WnbTiles ; x++) {
+                       order.push([x, y]);
+                       if (!tilesSrc[x])
+                               tilesSrc[x] = new Array();
+                       tilesSrc[x][y] = baseUrl + '&x=' + (x + xOffset) + '&y=' + (y + yOffset);
+               }
+       }
+       order = order.reverse();
+       this.queueLoadingSequence({'type':'row',
+                                                          'src' : tilesSrc,
+                                                          'order' : order,
+                                                          'length' : order.length,
+                                                          'beforeSequence' : beforeSequence});
+       return n;
+};
+
+Mosaique.prototype._shiftRows = function(n, shift, from, to, increment) {
+       /* columns rotations */
+       for (var x = 0 ; x < this.WnbTiles ; x++)
+               this.tiles[x] = rotateArray(this.tiles[x], -n);
+
+       var tile, top;
+       
+       /* positional shifting */
+       for (var y = from ; y != to ; y += increment) {
+               top = parseInt(this.tiles[0][y].style.top);
+               for (var x = 0 ; x < this.WnbTiles ; x++) {
+                   var tile = this.tiles[x][y];
+                       tile.style.top = top + shift + 'px';
+                       tile.style.visibility = 'hidden';
+               }
+       }
+};
+
+
+Mosaique.prototype.queueLoadingSequence = function(sequenceInfo) {
+    if(!sequenceInfo.length) return;
+       this._loadingQueue.push(sequenceInfo);
+       if (!this.loadingState && this._loadingQueue.length)
+               this._loadNextSequence();
+};
+
+Mosaique.prototype._loadNextSequence = function() {
+       var seq = this._loadingQueue.shift();
+       if (seq == null) {
+               this._loadingQueue = new Array();
+               this.loadingState = 0;
+               return;
+       }
+       switch(seq['type']) {
+               case 'full' :
+                       this.loadingState = 1;
+                       break;
+               case 'row' :
+               case 'column' :
+                       this.loadingState = 2;
+                       seq['beforeSequence']();
+                       break;
+       }
+       this._loadingIterator = 0;
+       //this._loadNextTile();
+       this._startSequence(seq);
+};
+
+Mosaique.prototype._startSequence = function(seq) {
+       this._currentSequence = seq;
+       
+       var size = Math.min(batchSize, this._currentSequence.length);
+       this._loadingIterator += size;
+
+       var coord, src, tile;
+       for (var i=0 ; i<size ; i++) {
+               coord = this._currentSequence.order[i];
+               src = this._currentSequence.src[coord[0]][coord[1]];
+               tile = this.tiles[coord[0]][coord[1]];
+               tile.src = src;
+       }
+};
+
+Mosaique.prototype._loadNextTile = function() {
+       if (this.loadingState == 0)
+               return;
+       else if (this._loadingIterator >= this._currentSequence['length']) {
+               this._loadNextSequence();
+               return;
+       }
+               
+       var coord = this._currentSequence.order[this._loadingIterator];
+       this._loadingIterator++;
+       
+       var src = this._currentSequence.src[coord[0]][coord[1]];
+       var tile = this.tiles[coord[0]][coord[1]];
+       tile.src = src;
+};
+
+
+/* drag and drop generic handlers */
+Mosaique.prototype.mouseDownHandler = function(evt) {
+       var target = getTargetedObject(evt);
+       if (target.tagName == 'INPUT' || target.tagName == 'TEXTAREA')
+               return;
+       disableDefault(evt);
+       evt = getEventObject(evt);
+       var navDisp = this.navigateur.display;
+       
+       if (target.parentNode.parentNode == navDisp) {
+               if (target == this.navigateur.frame.firstChild)
+                       this.ddHandlers = this.navigateur._ddHandlers;
+               else {
+                       this.ddHandlers = null;
+                       return;
+               }
+       }
+       else
+               this.ddHandlers = this._ddHandlers;
+       
+       addListener(document, 'mousemove', this.ddHandlers['move'], 'mosaique.dd');
+       
+       this.ddHandlers['down'](evt);
+};
+
+Mosaique.prototype.mouseUpHandler = function(evt) {
+       if (this.ddHandlers != null) {
+               removeListener(document, 'mousemove', this.ddHandlers['move']);
+               this.ddHandlers['up'](evt);
+               this.ddHandlers = null;
+       }
+}
+
+
+/* Mosaique drag and drop handlers */
+Mosaique.prototype._mouseDownHandler = function(evt) {
+       this.initialClickPoint = new Point(evt.clientX, evt.clientY);
+       this.initialPosition = this.getContainerPosition();
+       this.rShift = 0;
+       this.cShift = 0;
+       this.dragInProgress = true;
+}
+
+Mosaique.prototype._mouseMoveHandler = function(evt) {
+       disableDefault(evt);
+       if(!this.dragInProgress)
+               return;
+       
+       evt = getEventObject(evt);
+       var currentPoint = new Point(evt.clientX, evt.clientY);
+       var displacement = currentPoint.diff(this.initialClickPoint);
+       this.setContainerPosition(this.initialPosition.add(displacement));
+       
+       var r = (displacement.y + this.halfTileSize + this.remainD.y) / this.tileSize;
+       r = Math.floor(r);
+       
+       if (this.rShift - r != 0)
+               this.rShift += this.loadRows(r - this.rShift);
+
+       var c = (displacement.x + this.halfTileSize + this.remainD.x) / this.tileSize;
+       c = Math.floor(c);
+       
+       if (this.cShift - c != 0)
+               this.cShift += this.loadColumns(c - this.cShift);
+};
+
+Mosaique.prototype._mouseUpHandler = function(evt) {
+       this.dragInProgress = false;
+       evt = getEventObject(evt);
+       this._mouseMoveHandler(evt);
+       var finalPoint = new Point(evt.clientX, evt.clientY);
+       var displacement = finalPoint.diff(this.initialClickPoint);
+       this.remainD = this.remainD.add(new Point(displacement.x - this.cShift * this.tileSize, displacement.y - this.rShift * this.tileSize));
+       this.navigateur.alignFrame();
+};
+
+
+
+Mosaique.prototype.setLoadingOrder = function() {
+    var startX = 0;
+    var stopX = this.WnbTiles;
+    var startY = 0;
+    var stopY = this.HnbTiles;
+    var x = 0, y = 0;
+    var order = new Array();
+
+    var direction=0;
+    
+
+    while((startX != stopX) && (startY != stopY)) {
+        switch(direction) {
+            case 0 : // left -> right
+                startY++;
+                
+                for (x = startX ; x < stopX ; x++)
+                    order.push([x, y]);
+                x--;
+                break;
+
+            case 1 : // up -> bottom
+                stopX--;
+                for (y = startY ; y < stopY ; y++)
+                    order.push([x, y]);
+                y--;
+                break;
+
+            case 2 : // right -> left
+                stopY--;
+                
+                for (x = stopX-1 ; x >= startX ; x--)
+                    order.push([x, y])
+                x++;
+                break;
+                
+            case 3 : // bottom -> up
+                startX++;
+                
+                for (y = stopY-1 ; y >= startY ; y--)
+                    order.push([x,y]);
+                y++;
+                break;
+        }
+
+        direction++;
+        if (direction % 4 == 0)
+            direction = 0;
+    }
+    
+    
+    this.loadingOrder = order.reverse();
+}
+
+Mosaique.prototype.cleanContainer = function() {
+       removeGroupListeners('mosaique.tiles');
+       while (this.container.childNodes[0])
+               this.container.removeChild(this.container.childNodes[0]);
+}
+
+Mosaique.prototype.loadZoomLevel = function(zoomIndex) {
+       this.loadingState = 0;
+       var oldWnbTiles = this.WnbTiles;
+       var oldHnbTiles = this.HnbTiles;
+       var oldCenter = this.getImageCenterPosition();
+       var zoomInfo = this.zoomTable[zoomIndex];
+       var newLevel = zoomInfo['level'];
+
+       // center coordinates translated into target zoom level
+       var center = oldCenter.mul(newLevel / this.zoomLevel);
+       var ulc = center.diff(new Point(this.screenWidth/2, this.screenHeight/2)); // upper left corner
+       
+       this.setCurrentDimensionValues(zoomIndex);
+       
+       if (oldWnbTiles != this.WnbTiles || oldHnbTiles != this.HnbTiles) {
+               this.cleanContainer();
+               this.prepareContainer();
+               this.rcsItoC = new Point(0,0);
+               this.setLoadingOrder();
+       }
+       this.loadScreen(ulc);
+};
+
+Mosaique.prototype.unload = function() {
+       this.navigateur.unload();
+       removeGroupListeners('mosaique.dd');
+       removeGroupListeners('mosaique.tiles');
+       this.screenArea.removeChild(this.rootElement);
+};
+
+})();
+
+/* UTILS */
+function Point(x, y) {
+       this.x = Math.round(x);
+       this.y = Math.round(y);
+}
+Point.prototype.diff = function(point) { return new Point(this.x - point.x, this.y - point.y); };
+Point.prototype.add = function(point) { return new Point(this.x + point.x, this.y + point.y); };
+Point.prototype.mul = function(k) { return new Point(this.x * k, this.y *k)};
+Point.prototype.toString = function() { return "(" + String(this.x) + ", " + String(this.y) + ")"; };
+
+function rotateArray(t, n) {
+       return t.slice(n,t.length).concat(t.slice(0,n));
+}
+
+if (!Array.prototype.map) {
+       Array.prototype.map = function(f) {
+               var r = new Array(this.length);
+               for (var i = 0 ; i < this.length ; i++ ){
+                       r[i] = f(this[i]);
+               }
+               return r;
+       };
+}