/** Il programma fa uso della libreria JQuery per risolvere i problemi di cross-browsing e per rendere più compatto il codice. */ // Costanti var GSConst = { pgn: 'http://www.giocareascacchi.it/images/pgn/', icon: 'http://www.giocareascacchi.it/images/icons/', ajax: 'http://www.giocareascacchi.it/member/egame-ajax.php', GSChess: 'gsChess', // ex jsGame GSTable: 'gsTable', // ex jsGameTable GSMovePanel: 'gsMove', GSArrowPanel: 'gsArrow', GSOptionPanel: 'gsOption', GSOptionPanelNoBorder: 'gsOptionNoBorder', GSWindow: 'gsWindow', GSRotate: 'gsRotate', GSExpFen: 'gsExpFen', GSImpFen: 'gsImpFen', GSImpPgn: 'gsImpPgn', GSExpPgn: 'gsExpPgn', GSReset: 'gsReset', GSSend: 'gsSend', GSLoading: 'gsLoading', GSMsgBoard: 'gsMsgBoard' }; /** Gruppo di Funzioni che servono a compattare il codice */ var Doc = { // document.createElement() cE: function(html) { return document.createElement(html); } } /** Oggeto che memorizza tutte le impostazioni della partita. Non tutte le informazioni saranno utili per la tipologia di scacchiera scelta. Per esempio se sto utilizzando la scacchiera di analisi allora playMatch non mi interessa, neanche chesstempo. */ function GSInfo(elem) { this.elem = elem; // Rappresenta l'oggetto DOM this.idElem = null; this.rotWhite = true; // visualizzo secondo Bianco this.idGame = -1; this.setFen = false; this.getFen = false; this.setPgn = false; //this.getPgn = false; this.showMoves = false; this.flip = false; this.arrow = false; this.setup = false; this.fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'; this.notoverride = false; this.doReverse = false; this.playMatch = false; this.reset = false; this.send = false; this.chesstempo = false; this.pgnText = null; this.variant = null; // ------------------- // Match this.memo = null; // ------------------- // Le dimensioni dei seguenti due array sono identici. this.fens = new Array(); // Array dei Fen this.iFen = 0; // Current Fen this.moves = new Array(); // Array delle mosse this.msg = new Array(); // Array dei Messaggi // ------------------- // Movimento dei pezzi this.pieceDragged = false; this.move = null; // Funzione che mi serve a resettare tutte le variabili // per lo spostamento di un pezzo nella scacchiera this.resetPiece = function () { this.pieceDragged = false; this.move = null; } } /** Questa è la funzione che viene chiamata non appena la pagina è stata caricata, quindi si occupa di construire la scacchiera a partire dalle informazioni che sono fornite. */ function GSParser() { this.init = function() { $('.'+GSConst.GSChess).each(this.active); } this.active = function(i, elem) { // Aggiungo un id per ogni elemento trovato. Mi serve per // identificare correttamente la scacchiera con JQuery $(this).attr('id' , GSConst.GSChess+(i+1)); var info = new GSInfo(this); info.idElem = i+1; //var inputs = elem.getElementsByTagName('input'); var inputs = $('#'+this.id+' input'); var leng = inputs.length; for (var i=0; i]*\>/ig, ''); } // Dopo aver preso le informazioni che c'erano svuoto tutto Utility.removeAllChilds(this); // ---------------------------------------------------------- // In base alle informazioni reperite scelgo la modalità di // scacchiera da attivare if (info.chesstempo === true) { // Attivo il Chess Tactic Trainer } else if (info.playMatch === true) { // Attivo la modalità Egames var a = new GSEgame(info); a.init(); } else if (info.playMatch === false && info.notoverride === true) { // Attivo la modalità di visualizzazione di una partita var a = new GSShowGame(info); a.init(); } else { // Se arrivo qui vuol dire che devo attivare // la modalità di analisi var a = new GSAnalysis(info); a.init(); } } } /** Oggetto che raccoglie elementi della scacchiera finale */ var GSElements = { /** Mi serve per riportare la scacchiera allo stato 0, dove non c'è alcun FEN, nè mossa inserita e dove la scacchiera ha la vista del Bianco */ reset: function(info) { info.fens = new Array(); info.moves = new Array(); info.msg = new Array(); info.iFen = 0; // if (info.rotWhite !== true) GSElements.chessboard.rotateCoord(info); }, /** Questa funzione mi serve per amministrare l'inserimento di un nuovo FEN, perchè se la stringa dice che tocca al Nero allora devo inserire come mossa "..." per il Bianco */ setFirstFen: function (info) { // Inserisco il FEN info.fens.push(info.fen); // Analizzo se devo inserire anche "..." come mossa var arr = info.fen.split(' '); if (arr[1] == 'b') GSElements.addMove(info, info.fen, '...', null); }, addMove: function (info, fen, move, msgBoard) { info.moves.push(move); info.fens.push(fen); info.msg.push(msgBoard); info.iFen++; }, /** Funzione che mi crea la tabella della scacchiera (vuota), comprensiva delle coordinate. La scacchiera è rivolta con il Bianco verso il basso */ createTableBoard: function(info) { // Creo la base della scacchiera var table = Doc.cE('table'); $(table).attr({'id' : GSConst.GSTable+info.idElem, 'class' : GSConst.GSTable}); var tbody = Doc.cE('tbody'); // per IE // Sintassi obbligatoria per IE table.appendChild(Doc.cE('thead')); table.appendChild(Doc.cE('tfoot')); table.appendChild(tbody); //Core.addEventListener(table, 'mousemove', ChessBoardManager.imgFollowMouse); //Core.addEventListener(table, 'click', ChessBoardManager.dragDropPiece); // Aggiungo le coordinate (colonne) in alto tbody.appendChild(GSElements.getTrRowCoord(info)); for (var i=0; i<8; i++) { var tr = Doc.cE('tr'); tr.appendChild(GSElements.getTdCoord(i, info)); for (var j=0; j<8; j++) { var td = Doc.cE('td'); // Coordinate della cella (univoci) td._x = i; td._y = j; if ((i+j)%2) td._styleName = 'c2'; else td._styleName = 'c1'; td.className = td._styleName; var div = Doc.cE('div'); var img = Doc.cE('img'); img.src = GSConst.pgn+'empty.gif'; div.appendChild(img); td.appendChild(div); tr.appendChild(td); } tr.appendChild(GSElements.getTdCoord(i, info)); tbody.appendChild(tr); } tbody.appendChild(GSElements.getTrRowCoord(info)); return table; }, listenerTableBoard: function (info) { $('#'+GSConst.GSTable+info.idElem) .click(function (event) { var src = $(event.target).attr('src'); var x = $(event.target).parent().parent().attr('_x'); var y = $(event.target).parent().parent().attr('_y'); if (info.pieceDragged === true) { // Lascio il pezzo nella scacchiera if (typeof(x) != 'number') { // Ho rimesso il pezzo al suo posto $('#pieceDragged').attr('id', ''); info.resetPiece(); GSElements.chessboard.updateBoard(info); } else { // Ho appoggiato il pezzo su una casella, ora devo controllare // che la mossa sia corretta ed effettuare le modifiche e gli // aggiornamenti necessari info.move = info.move + GSlib.GSChessboard.coordToCol(y) + GSlib.GSChessboard.coordToRow(x); if ( $('#pieceDragged').children().attr('src').match(/P[bn].gif$/) != null && (x == 0 || x == 7) ) { // Qui devo creare il pannello della promozione var objFen = new GSlib.GSFen(); objFen.analyze(info.fens[info.iFen]); info.who = objFen.who; $(GSElements.promotionPanel(info)).appendTo($('body')); GSElements.listenerPromotionPanel(info); } else GSElements.playMove(info, null); } } else if (typeof(src) == 'string' && info.pieceDragged === false && src != GSConst.pgn+'empty.gif') { if (info.memo == null || info.playMatch == false) { // Filtro solo le caselle (e tralascio le coordinate) $(event.target).parent().attr('id', 'pieceDragged'); info.pieceDragged = true; info.move = GSlib.GSChessboard.coordToCol(y) + GSlib.GSChessboard.coordToRow(x); } } }) .mousemove(function (event) { $('#pieceDragged').css({'left' : event.pageX - 20, 'top' : event.pageY + 1}); }); }, playMove: function (info, prom) { var message = new GSlib.GSMoveMessage(info.fens[info.iFen], info.move, prom, info.variant); var mMan = new GSlib.GSMoveManager(message); var r = mMan.play(); /* DEBUB */ // if (r === false) alert(mMan.log.get()); if (r !== false) { // Se non sono all'ultima mossa cancello tutte le mosse // successive, in questo modo analizzo una seconda variante if (info.iFen + 1 < info.fens.length && info.notoverride === false) { info.fens.splice(info.iFen + 1, info.fens.length - info.iFen + 1); info.moves.splice(info.iFen, info.moves.length - info.iFen); info.msg.splice(info.iFen, info.msg.length - info.iFen); } if (info.iFen + 1 >= info.fens.length) { info.memo = r.move; GSElements.addMove(info, r.strFen, r.move, r.msg); } } $('#pieceDragged').attr('id', ''); info.resetPiece(); GSElements.chessboard.updateBoard(info); GSElements.refreshMove(info); }, getTrRowCoord: function(info) { var tr = Doc.cE('tr'); tr.appendChild(Doc.cE('td')); //if (info.rotWhite === true) { for (var i=0; i<8; i++) { var td = Doc.cE('td'); td.className = 'gsTdCoord'; var div = Doc.cE('div'); div.className = 'gsCoord'; var text = null; switch(i) { case 0: text = 'a'; break; case 1: text = 'b'; break; case 2: text = 'c'; break; case 3: text = 'd'; break; case 4: text = 'e'; break; case 5: text = 'f'; break; case 6: text = 'g'; break; case 7: text = 'h'; break; } div.appendChild(document.createTextNode(text)); td.appendChild(div); tr.appendChild(td); } //} /** else { // Condizione che non dovrebbe mai verificarsi perchè // prima imposto la scacchiera secondo bianco, poi la rovescio. for (var i=0; i<8; i++) { } } */ tr.appendChild(Doc.cE('td')); return tr; }, getTdCoord: function(i, info) { //if (info.rotWhite === true) { var td = Doc.cE('td'); td.className = 'gsTdCoord'; var div = Doc.cE('div'); div.className = 'gsCoord'; div.appendChild( document.createTextNode( ''+(8-i) ) ); td.appendChild(div); return td; //} //return false; }, chessboard: { /** Questa funzione aggiorna la scacchiera in base al FEN, scelto in base a info.fens[info.iFen] */ updateBoard: function(info) { var i,j,k; var imgs = $('#'+GSConst.GSTable+info.idElem+' img'); // Ricavo la scacchiera var gsFen = new GSlib.GSFen(); gsFen.analyze(info.fens[info.iFen]); var board = gsFen.board; if (info.rotWhite === false) { k=63; for (i=0; i<8; i++) { for (j=0; j<8; j++) { imgs[k].src = GSElements.chessboard.getPiece(board[i][j]); k--; } } } else { k=0; for (i=0; i<8; i++) { for (j=0; j<8; j++) { imgs[k].src = GSElements.chessboard.getPiece(board[i][j]); k++; } } } }, /** Nel caso giri la scacchiera ho bisogno di aggiornare anche le coordinate nel modo corretto */ rotateCoord: function(info) { var arr = { 'a':'h', 'b':'g', 'c':'f', 'd':'e', 'e':'d', 'f':'c', 'g':'b', 'h':'a', '1':'8', '2':'7', '3':'6', '4':'5', '5':'4', '6':'3', '7':'2', '8':'1' }; $('#'+GSConst.GSTable+info.idElem+' .gsCoord').each(function () { $(this).text(arr[$(this).text()]); }); // Ora devo aggiornare anche le coordinate nascoste delle celle // in modo da poter eseguire correttamente le mosse in sala di analisi // anche con la scacchiera rovesciata $('#'+GSConst.GSTable+info.idElem+' td').each(function () { var x = $(this).attr('_x'); var y = $(this).attr('_y'); if (typeof(x) == 'number') $(this).attr({'_x' : 7-x , '_y' : 7-y}); }); }, /** Dato il pezzo in formato carattere, ritorna il nome dell'immagine associata */ getPiece: function(c) { var src = GSConst.pgn + 'empty.gif'; switch(c) { case 'P': src = GSConst.pgn + 'Pb.gif'; break; case 'R': src = GSConst.pgn + 'Tb.gif'; break; case 'N': src = GSConst.pgn + 'Cb.gif'; break; case 'B': src = GSConst.pgn + 'Ab.gif'; break; case 'Q': src = GSConst.pgn + 'Db.gif'; break; case 'K': src = GSConst.pgn + 'Rb.gif'; break; case 'p': src = GSConst.pgn + 'Pn.gif'; break; case 'r': src = GSConst.pgn + 'Tn.gif'; break; case 'n': src = GSConst.pgn + 'Cn.gif'; break; case 'b': src = GSConst.pgn + 'An.gif'; break; case 'q': src = GSConst.pgn + 'Dn.gif'; break; case 'k': src = GSConst.pgn + 'Rn.gif'; break; } return src; } }, createMovePanel: function (info) { var d1 = Doc.cE('div'); $(d1).attr({'id' : GSConst.GSMovePanel+info.idElem, 'class' : GSConst.GSMovePanel}); var i,k,len,tr; var t = Doc.cE('div'); t.appendChild(document.createTextNode('Game')); t.className = 'moveTitle'; d1.appendChild(t); var table = GSElements.createMoveTable(info); d1.appendChild(table); var $d = $('
').append($(d1)).append($('
')); return $d[0]; }, createMoveTable: function (info) { var table = Doc.cE('table'); var t = Doc.cE('tbody'); // per IE // Sintassi obbligatoria per IE table.appendChild(Doc.cE('thead')); table.appendChild(Doc.cE('tfoot')); table.appendChild(t); k=1; len = info.moves.length; tr = null; for (i=0; i 0) $('#'+GSConst.GSMovePanel+info.idElem+' td:eq('+(info.iFen-1)+')').attr('_bgColor', '#ebebeb'); $('#'+GSConst.GSMovePanel+info.idElem+' td').trigger('mouseout'); GSElements.updateMessage(info); }, updateMessage: function (info) { if (info.msg[info.iFen-1] == null) $('#'+GSConst.GSMsgBoard+info.idElem).html(''); else $('#'+GSConst.GSMsgBoard+info.idElem).html(info.msg[info.iFen-1]); }, /** Questa funzione aggiorna le mosse nel pannello */ refreshMove: function (info) { var table = GSElements.createMoveTable(info); $('#'+GSConst.GSMovePanel+info.idElem+' table').remove(); $('#'+GSConst.GSMovePanel+info.idElem).append(table); GSElements.listenerMove(info); GSElements.updateMove(info); }, createArrowPanel: function(info) { var d = Doc.cE('div'); $(d).attr({'id' : GSConst.GSArrowPanel+info.idElem, 'class' : GSConst.GSArrowPanel}); $('').appendTo($(d)); $('').appendTo($(d)); $('').appendTo($(d)); $('').appendTo($(d)); $(d).click(function (event) { if (event.target.value == '<<') info.iFen = 0; if (event.target.value == '<') {if (info.iFen > 0) info.iFen--;} if (event.target.value == '>') {if (info.iFen < info.fens.length-1) info.iFen++;} if (event.target.value == '>>') info.iFen = info.fens.length-1; GSElements.chessboard.updateBoard(info); GSElements.updateMove(info); }); return d; }, /** opt rappresenta un array che contiene le opzioni da inserire Opt: 'expfen', 'rot' */ createOptionPanel: function (info, dCSS = GSConst.GSOptionPanel) { var d = Doc.cE('div'); var d1 = Doc.cE('div'); // Prima linea di opzioni var d2 = Doc.cE('div'); // Seconda linea di opzioni var d3 = Doc.cE('div'); // Terza linea di opzioni var dw = Doc.cE('div'); // Finestra per i fen, i setup, ecc... $(d).attr({'id' : GSConst.GSOptionPanel+info.idElem, 'class' : dCSS}); $(dw).attr({'id' : GSConst.GSWindow+info.idElem, 'class' : GSConst.GSWindow}); $(d1).appendTo($(d)); $(d2).appendTo($(d)); $(d3).appendTo($(d)); $(dw).appendTo($(d)); if (info.flip === true) $('Rotate').appendTo($(d1)); if (info.getFen === true) $('Export FEN').appendTo($(d1)); if (info.getPgn === true) $('Export PGN').appendTo($(d1)); if (info.setFen === true) $('Import FEN').appendTo($(d2)); if (info.setPgn === true) $('Import PGN').appendTo($(d2)); if (info.reset === true) $('Reset').appendTo($(d3)); if (info.send === true) $('Send').appendTo($(d3)); return d; }, listenerOptionPanel: function (info) { $('#'+GSConst.GSSend+info.idElem) .click (function() { if (info.memo != null) { // Faccio questo passaggio di informazione perchè così // se l'utente clicca una seconda volta su "Send" questo non ha effetto. var mossa = info.memo; info.memo = null; $('#' + GSConst.GSLoading+info.idElem) .html('
Loading, wait please...
'); var message = info.msg[info.moves.length - 1]; $.post( GSConst.ajax, { idGame: info.idGame, m: mossa, msg: message }, function (data, textStatus) { if (textStatus == 'success') { $('#' + GSConst.GSLoading+info.idElem).html(''); window.location.reload(true); } }, 'text' ); } }); $('#'+GSConst.GSReset+info.idElem) .click (function() { if (info.memo != null) { info.memo = null; info.moves.splice(info.moves.length-1, 1); info.fens.splice(info.fens.length-1, 1); info.iFen = info.fens.length-1; GSElements.chessboard.updateBoard(info); GSElements.refreshMove(info); } }); $('#'+GSConst.GSRotate+info.idElem) .click (function() { if (info.rotWhite === true) info.rotWhite = false; else info.rotWhite = true; GSElements.chessboard.rotateCoord(info); GSElements.chessboard.updateBoard(info); }); $('#'+GSConst.GSExpFen+info.idElem) .click (function() { // Rimuovo tutto quello che c'era in precedenza var w = $('#'+GSConst.GSWindow+info.idElem); Utility.removeAllChilds(w[0]); $('
').appendTo(w); }); $('#'+GSConst.GSImpFen+info.idElem) .click (function() { // Rimuovo tutto quello che c'era in precedenza var w = $('#'+GSConst.GSWindow+info.idElem); Utility.removeAllChilds(w[0]); var $button = $(''); var $field = $(''); var $input = $('
').append($field).append($button); $input.appendTo(w); $button.click(function () { var t = $field.attr('value'); GSElements.reset(info); info.fen = t; GSElements.setFirstFen(info); var objFen = new GSlib.GSFen(); objFen.analyze(t); GSElements.chessboard.updateBoard(info); GSElements.refreshMove(info); }); }); $('#'+GSConst.GSImpPgn+info.idElem) .click (function() { // Rimuovo tutto quello che c'era in precedenza var w = $('#'+GSConst.GSWindow+info.idElem); Utility.removeAllChilds(w[0]); var $button = $('
'); var $field = $('
'); var $input = $('
').append($field).append($button); $input.appendTo(w); $button.click(function () { info.pgnText = $('#'+GSConst.GSWindow+info.idElem+' textarea:first').val(); GSElements.reset(info); GSElements.pgnAnalyze(info); GSElements.chessboard.updateBoard(info); GSElements.refreshMove(info); }); }); $('#'+GSConst.GSExpPgn+info.idElem) .click (function() { // Rimuovo tutto quello che c'era in precedenza var w = $('#'+GSConst.GSWindow+info.idElem); Utility.removeAllChilds(w[0]); var pgn = ''; var len = info.moves.length; for (var i=0; i').appendTo(w); }); }, promotionPanel: function (info) { var table = Doc.cE('table'); var tbody = Doc.cE('tbody'); // per IE var cap = Doc.cE('caption'); cap.appendChild(document.createTextNode('Promotion')); // Sintassi obbligatoria per IE table.appendChild(cap); table.appendChild(Doc.cE('thead')); table.appendChild(Doc.cE('tfoot')); table.appendChild(tbody); var col = (info.who == 'w') ? 'b' : 'n'; $(tbody).append('Q - Queen'); $(tbody).append('R - Rook'); $(tbody).append('B - Bishop'); $(tbody).append('N - Knight'); var p1 = $('
').append(table); var obj = $('
').append(p1); return obj[0]; }, listenerPromotionPanel: function (info) { $('#pawnPromotion img').click(function () { $('#pawnPromotion').remove(); GSElements.playMove(info, $(this).attr('alt')); }); }, /** TODO: non è possibile che debba ripetere due volte il codice */ pgnAnalyze: function (info) { var k; // Analizzo il PGN fornito come input per elaborarlo var pgn = new Pgn(info.pgnText); if (pgn.props['FEN']) info.fen = pgn.props['FEN']; if (pgn.props['Variant']) info.variant = pgn.props['Variant']; GSElements.setFirstFen(info); var len = pgn.moves.length; for (var i=0; i').appendTo(this.info.elem); // Creo le opzioni var opt = GSElements.createOptionPanel(this.info); this.info.elem.appendChild(opt); GSElements.listenerOptionPanel(this.info); this.info.iFen = this.info.fens.length-1; if (info.doReverse == true) { if (info.rotWhite === true) info.rotWhite = false; else info.rotWhite = true; GSElements.chessboard.rotateCoord(info); } // Imposto la scacchiera nella posizione iniziale GSElements.chessboard.updateBoard(this.info); GSElements.updateMove(this.info); } }