// rozstawienie stołów po wirtualnej sali var Tables = function(viewport, quantity) { var that = this; that.quantity = quantity; that.viewport = viewport; that.dim = 0; that.tables = []; that.singleDiv = jQuery('
').css({position: 'absolute'}).addClass('table'); // reprezentacja pojedynczego stołu that.direction = undefined; that.setDirection = function(direction) { // ustawia orientację sali/ustawienie stołów var alignment = direction.split('-'); // dzielimy kierunek na składową horyzontalną i wertykalną var rows = 2; // stoły stoją w dwóch rzędach var cols = parseInt(Math.ceil(that.quantity / 2)); // i w tylu kolumnach, ilu trzeba if (alignment[0] == 'top' || alignment[0] == 'bottom') { // chyba, że stoją w drugą stronę rows = cols; // to wtedy w tylu rzędach, ilu kolumnach i w w tylu kolumnach, ile 2. cols = 2; } var width = that.viewport.width / cols; // wyznaczamy rozmiar obszaru zajmowanego przez pojedynczy stół var height = that.viewport.height / rows; that.dim = Math.min(width, height) * 0.5; // stół zajmuje dłuższym bokiem (mimo, że jest kwadratowy, jeden bok jest względnie dłuższy wobec w/w obszaru) połowę przeznaczonego obszaru that.singleDiv.css({height: that.dim, width: that.dim}); var positions = []; for (var i = 0; i < that.quantity; i++) { // pozycjonujemy kolejne stoliki (w przestrzeni (0,0) - (1,1)) var position = { top: 0, left: 0 }; switch (alignment[1]) { case 'straight': // w dwóch rzędach/kolumnach position.top = parseInt(Math.floor(2 * i / that.quantity)); position.left = i % parseInt((that.quantity+1) / 2); break; case 'uturn': // w dwóch rzędach/kolumnach, ale zakręcają position.top = parseInt(Math.floor(2 * i / that.quantity)); position.left = Math.min(i, (that.quantity-1-i)); break; case 'snake': // wężykiem position.top = i % 4; position.top = Math.min(position.top, 3 - position.top); position.left = parseInt(Math.floor(i / 2)); break; } positions[i] = position; } if (that.quantity % 2) { // nieparzysty stolik wysuwamy na środek, dla wężyka - ostatni, dla pozostałych - środkowy var skewed = (alignment[1] == 'snake') ? (that.quantity - 1) : ((that.quantity - 1) / 2); positions[skewed].top = 0.5; } that.tables = []; for (var i = 0; i < that.quantity; i++) { that.tables[i] = that.singleDiv.clone(); that.tables[i].text(i+1); switch (alignment[0]) { case 'left': positions[i].left += (width-that.dim)/(width*2); positions[i].top += (height-that.dim)/(height*2); that.tables[i].css({left: positions[i].left * width, top: positions[i].top * height}); break; case 'right': positions[i].left -= (width-that.dim)/(width*2); positions[i].top += (height-that.dim)/(height*2); that.tables[i].css({left: (Math.ceil(that.quantity / 2) - positions[i].left - 1) * width, top: positions[i].top * height}); break; case 'top': positions[i].left += (height-that.dim)/(height*2); positions[i].top += (width-that.dim)/(width*2); that.tables[i].css({top: positions[i].left * height, left: positions[i].top * width}); break; case 'bottom': positions[i].left -= (height-that.dim)/(height*2); positions[i].top += (width-that.dim)/(width*2); that.tables[i].css({top: (Math.ceil(that.quantity / 2) - positions[i].left - 1) * height, left: positions[i].top * width}); break; } } } that.getPosition = function(position, size) { // podaje współrzędne pikselowe dla danej pozycji zawodnika (/[0-9]+[WNES]/) lub stołu var pos = position.toString().match(/^(\d+)([NEWS]?)$/); if (!pos) { throw 'Illegal position'; } var tableNo = parseInt(pos[1]); if (tableNo > that.tables.length || tableNo < 1) { throw 'Table number out of range'; } var offset = { left: parseInt(that.tables[tableNo-1].css('left')), top: parseInt(that.tables[tableNo-1].css('top')) }; switch (pos[2]) { case 'N': // N u góry offset.left += that.dim / 2; break; case 'E': // E po prawej offset.left += that.dim; offset.top += that.dim / 2; break; case 'S': // S u dołu offset.left += that.dim / 2; offset.top += that.dim; break; case 'W': // W po lewej offset.top += that.dim / 2; break; default: // stół od lewej góry offset.top += that.dim / 2; offset.left += that.dim / 2; } if (size) { // jeśli podajemy rozmiar pikselowy stolika, dostajemy korektę na środek stołu offset.top -= size/2; offset.left -= size/2; } return offset; } that.display = function() { for (var i = 0; i < that.tables.length; i++) { that.viewport.container.append(that.tables[i]); } } }; // para. var Pair = function(startingPosition, number, movement) { var that = this; that.players = [ startingPosition, startingPosition.replace("N", "S").replace("E", "W") ]; // para składa się z 2 graczy, N gra z S, E gra z W that.number = number; that.movement = movement; that.containers = []; that.size = that.movement.tables.dim * 0.3; // para ma rozmiar 30% obszaru stolika. sprawdzić, czy nie gra Klichowicz. that.colour = '#'+('00000'+(Math.random()*(1<<24)|0).toString(16)).slice(-6); // para ma jeden kolor, ale losowy for (var i = 0; i < 2; i++) { that.containers[i] = jQuery('
').addClass('player').css({backgroundColor: that.colour, position: 'absolute', lineHeight: that.size+'px', width: that.size, height: that.size, borderRadius: that.size}).text(that.number); } that.moveTo = function(position) { for (var i = 0; i < 2; i++) { if (position) { // przesuwamy parę na określoną pozycję that.players[i] = position; var pos = that.movement.tables.getPosition(position, that.size); that.containers[i].animate(pos, 1000); position = position.replace('N', 'S').replace('E', 'W'); } else { // albo kończymy turniej i ściągamy ją do rogu that.containers[i].animate({left: 0, top: 0}, 1000); } } } that.display = function() { for (var i = 0; i < 2; i++) { movement.viewport.container.append(that.containers[i]); that.containers[i].animate(that.movement.tables.getPosition(that.players[i], that.size), 1000); // wypuszczamy zawodników na salę } } } // pudełko rozdaniowe var Set = function(startingPosition, movement, number) { var that = this; that.movement = movement; that.number = number; that.position = startingPosition; that.colour = '#'+('00000'+(Math.random()*(1<<24)|0).toString(16)).slice(-6); // losowy kolor that.size = that.movement.tables.dim * 0.2; that.container = jQuery('
').addClass('cards').css({position: 'absolute', width: that.size, height: that.size*1.5, backgroundColor: that.colour, top: that.movement.viewport.height-that.size}).text(that.number); that.getPosition = function(position) { // translacja pozycji na pozycję w pikselach var pos; if (parseInt(position) == pos && position > 0) { // jeśli rozdanie leży na stole, to leży na stole pos = that.movement.tables.getPosition(position, that.size); } else { // jesli nie lezy na stole, leży na zbiornicy var after = parseInt(position); pos = that.movement.tables.getPosition(after, that.size); var afterPos = that.movement.tables.getPosition(after%that.movement.tables.quantity+1, that.size); if (after == that.movement.tables.quantity) { // zbiornica na końcu sali pos.top = Math.min(Math.max(0, pos.top + (pos.top - afterPos.top) * (position - after)), that.movement.viewport.height - that.size); pos.left = Math.min(Math.max(0, pos.left + (pos.left - afterPos.left) * (position - after)), that.movement.viewport.width - that.size); } else { // zbiornica między stołami pos.top = Math.min(Math.max(0, pos.top + (afterPos.top - pos.top) * (position - after)), that.movement.viewport.height - that.size); pos.left = Math.min(Math.max(0, pos.left + (afterPos.left - pos.left) * (position - after)), that.movement.viewport.width - that.size); } } pos.top -= that.size * 0.25; return pos; } that.moveTo = function(position) { that.position = position; if (position) { that.container.animate(that.getPosition(position), 1000); } else { that.container.animate({left: 0, top: that.movement.viewport.height - that.size}, 1000); // na koniec turnieju ściągamy rozdania } } that.display = function() { that.movement.viewport.container.append(that.container); that.moveTo(that.position); // rozdania puszczane na salę } } // reprezentacja schematu var Movement = function(viewport, movement, tables, summary) { var that = this; that.viewport = viewport; that.data = movement; that.tables = tables; that.round = 1; that.summary = summary; that.pairs = []; for (var i = 0; i < tables.quantity * 2; i++) { // rozstawiamy pary z kolejnych pozycji z movements.json that.pairs[that.data.positions[i]] = new Pair(parseInt(Math.ceil((i+1)/2)) + ['N','E'][i%2], that.data.positions[i], that); that.pairs[that.data.positions[i]].display(); } that.sets = []; var lastTable = 0; var emptySets = 0; for (var i = 0; i < that.data.sets.length; i++) { // kolejne komplety z movements.json if (that.data.sets[i]) { // komplet zaczyna na stoliku lastTable = that.data.sets[i]; emptySets = 0; } else { // komplet zaczyna na zbiornicy emptySets++; for (var j = i - emptySets + 1; j <= i; j++) { that.data.sets[j] = lastTable + 0.25 + 0.5*(j - i + emptySets)/(emptySets+1); // więc jest na wirtualnym stoliku ułamkowym } } } for (var i = 0; i < that.data.sets.length; i++) { that.sets[i] = new Set(that.data.sets[i], that, i+1); that.sets[i].display(); } // krok głównej pętli rotacji that.step = function() { if (summary) { summary.update(that); // aktualizacja tabelki krzyżowej } if (that.round < that.data.rounds) { // przechodzimy do kolejnej rundy... for (var p in that.pairs) { var position = that.pairs[p].players[0]; var ind = that.data.movement.indexOf(position); if (ind > -1) { // pary chodzące (obecne w rotacji z movements.json) chodzą that.pairs[p].moveTo(that.data.movement[(ind+1)%that.data.movement.length]); } else { // pozostałe siedzą, ale musimy je najpierw usadzić that.pairs[p].moveTo(position); } } that.data.sets.unshift(that.data.sets.pop()); // rotujemy komplety for (var s in that.sets) { that.sets[s].moveTo(that.data.sets[s]); } that.round++; return true; } else { // ...albo zwijamy się z sali for (var p in that.pairs) { that.pairs[p].moveTo(); } for (var s in that.sets) { that.sets[s].moveTo(); } return false; } } } // tabelka krzyżówki var Summary = function(pairs) { var that = this; that.pairs = pairs; that.table = jQuery('').addClass('summary'); // generujemy tabelką z pustymi komórkami, ew. nagłówkami for (var i = 0; i <= that.pairs; i++) { var row = jQuery(''); for (var j = 0; j <= that.pairs; j++) { var cell; if (i == 0 && j) { cell = jQuery('
'); cell.text(j); } else if (j == 0 && i) { cell = jQuery(''); cell.text(that.pairs - i + 1); } else { cell = jQuery(''); } row.append(cell); } that.table.append(row); } that.update = function(movement) { for (var pair in movement.pairs) { // kolorujemy pary that.table.find('th:contains('+movement.pairs[pair].number+')').css({backgroundColor: movement.pairs[pair].colour}); } var played = []; for (var t = 1; t <= movement.tables.quantity; t++) { played[t] = { set: movement.data.sets.indexOf(t), pairs: [] }; } // oznaczanie rozdań (kompletów) rozegranych przez pary for (var p in movement.pairs) { played[parseInt(movement.pairs[p].players[0])].pairs.push(parseInt(p)); } for (var pl in played) { played[pl].pairs.sort(function(a,b){return b-a;}); var cell = that.table.find('tr').eq(movement.tables.quantity*2 + 1 - played[pl].pairs[0]).find('td').eq(played[pl].pairs[1]-1); cell.css({backgroundColor: movement.sets[played[pl].set].colour}); cell.text(pl); } } that.clear = function() { that.table.remove(); } that.render = function(where) { if (!where) { where = jQuery('body'); } jQuery(where).append(that.table); } } var Viewport = function(width, height) { var that = this; that.width = width; that.height = height; that.container = jQuery('
').css({height: that.height, width: that.width, position: 'relative'}).addClass('viewport'); that.clear = function() { that.container.html(''); } that.render = function(where) { if (!where) { where = jQuery('body'); } jQuery(where).prepend(that.container); } }; var Control = function(movement) { var that = this; that.movement = movement; that.setMovement = function(movement) { that.movement = movement; } that.step = function() { if (!that.movement.step()) { that.stop(); that.container.hide(); }; if (that.autoplay) { that.autoTimeout = setTimeout(that.step, 1500); } } that.autoplay = false; that.autoTimeout = undefined; that.play = function() { that.autoplay = true; that.step(); that.playButton.hide(); that.stopButton.show(); that.stepButton.hide(); } that.stop = function() { that.autoplay = false; clearTimeout(that.autoTimeout); that.stopButton.hide(); that.playButton.show(); that.stepButton.show(); } that.container = jQuery('
').addClass('controls'); that.stepButton = jQuery('
').addClass('step').text('>|'); that.playButton = jQuery('
').addClass('play').text('>'); that.stopButton = jQuery('
').addClass('stop').text('||'); that.container.append(that.stepButton).append(that.playButton).append(that.stopButton); jQuery('body').append(that.container); that.stepButton.bind('click', that.step); that.playButton.bind('click', that.play); that.stopButton.bind('click', that.stop); } jQuery(document).ready(function() { var vp = new Viewport(jQuery(window).width()*0.70, jQuery(window).height()*0.9); vp.render(); var movements; var moveList = jQuery('.selector .list'); jQuery.getJSON('movements.json', function(data) { movements = data; for (var m in movements) { var move = m.split('-'); var listCell = jQuery('
').addClass('list-movement').attr('data-movement', m).text(move[0]); var listRow = moveList.find('div[data-rounds="'+move[1]+'"]'); if (listRow.length) { listRow.append(listCell); } else { moveList.append(jQuery('
').addClass('list-row').attr('data-rounds', move[1]).append(jQuery('
').addClass('list-movement').text(move[1])).append(listCell)); } } var sum; var control = new Control(); control.container.hide(); jQuery('.selector .list .list-movement[data-movement]').click(function() { vp.clear(); if (sum) { sum.clear(); } control.stop(); var move = movements[jQuery(this).attr('data-movement')]; var t = new Tables(vp, move.tables); var direction = jQuery('.layout .direction').val() + '-' + jQuery('.layout .way').val(); t.setDirection(direction); t.display(); sum = new Summary(move.tables * 2); sum.render(); move = new Movement(vp, move, t, sum); control.setMovement(move); control.container.show(); }); }); });