Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
@ -0,0 +1,93 @@ |
|||||||
|
<html> <!-- vasl-templates:id {{SNIPPET_ID}} --> |
||||||
|
|
||||||
|
<head> |
||||||
|
<meta charset="utf-8"> |
||||||
|
<style> {{CSS:common}} </style> |
||||||
|
|
||||||
|
<style> |
||||||
|
|
||||||
|
td { |
||||||
|
width: 50px ; min-width: 50px ; |
||||||
|
height: {%if TURN_TRACK_PREVIEW_MODE%} 50px {%else%} 43px {%endif%} ; |
||||||
|
padding: 2px ; |
||||||
|
border: 1px solid black ; |
||||||
|
} |
||||||
|
{% set RESET_TD = "min-width: unset ; height: unset ; padding: 0 ; border: none" %} |
||||||
|
td.turn-no { |
||||||
|
{{RESET_TD}} ; width: unset ; |
||||||
|
text-align: center ; vertical-align: center ; font-size: 18px ; font-weight: bold ; |
||||||
|
} |
||||||
|
td.no-reinforce { {{RESET_TD}} ; width: 13px ; } |
||||||
|
{# NOTE: We do the reinforcement flags as CSS backgrounds, since VASSAL is incredibly slow downloading normal images. #} |
||||||
|
td.reinforce1 { {{RESET_TD}} ; width: 13px ; background: url("{{TURN_TRACK_FLAG_1}}") top left no-repeat ; vertical-align: top ; } |
||||||
|
td.reinforce2 { {{RESET_TD}} ; width: 13px ; background: url("{{TURN_TRACK_FLAG_2}}") bottom right no-repeat ; vertical-align: bottom ; } |
||||||
|
td.half-turn { |
||||||
|
background: url("{{IMAGES_BASE_URL}}/turn-track-half-turn.png") bottom right no-repeat ; |
||||||
|
background-size: contain ; {# nb: doesn't work in VASSAL, the image file needs to be the correct size :-/ #} |
||||||
|
} |
||||||
|
|
||||||
|
{% if TURN_TRACK_PREVIEW_MODE %} |
||||||
|
body { margin: 0 ; } |
||||||
|
.reinforce1, .reinforce2 { opacity: 0 ; } |
||||||
|
.click { width: 13px ; height: 13px ; cursor: pointer ; } |
||||||
|
{%endif%} |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
</head> |
||||||
|
|
||||||
|
{% if TURN_TRACK_PREVIEW_MODE %} |
||||||
|
<script> |
||||||
|
// notify the parent window of a click on a reinforcement flag |
||||||
|
function onFlagClick( turnNo, playerNo ) { |
||||||
|
window.parent.postMessage( { |
||||||
|
type: "FlagClick", |
||||||
|
turnNo: turnNo, uiPlayerNo: playerNo |
||||||
|
}, "*" ) ; |
||||||
|
} |
||||||
|
</script> |
||||||
|
{%endif%} |
||||||
|
|
||||||
|
<table class="turn-track"> |
||||||
|
|
||||||
|
{% for row in TURN_TRACK_SQUARES %} |
||||||
|
<tr> |
||||||
|
|
||||||
|
{% for turnSquare in row %} |
||||||
|
<td {%if turnSquare[0] == TURN_TRACK_HALF_TURN%} class="half-turn" {%endif%} > |
||||||
|
|
||||||
|
<table style="width:100%;height:100%;"> <tr> |
||||||
|
|
||||||
|
<td id="flag-{{turnSquare[0]}}_1" width="100%" |
||||||
|
class = {% if turnSquare[1] %} "reinforce1" {%else%} "no-reinforce" {%endif%} |
||||||
|
> |
||||||
|
{% if TURN_TRACK_PREVIEW_MODE %} |
||||||
|
<div class="click" |
||||||
|
onclick = "onFlagClick( {{turnSquare[0]}}, 1 )" |
||||||
|
> </div> |
||||||
|
{%endif%} |
||||||
|
</td> |
||||||
|
|
||||||
|
<td class="turn-no"> {{turnSquare[0]}} </td> |
||||||
|
|
||||||
|
<td id="flag-{{turnSquare[0]}}_2" width="100%" |
||||||
|
class = {% if turnSquare[2] and turnSquare[0] != TURN_TRACK_HALF_TURN %} "reinforce2" {%else%} "no-reinforce" {%endif%} |
||||||
|
> |
||||||
|
{% if TURN_TRACK_PREVIEW_MODE and turnSquare[0] != TURN_TRACK_HALF_TURN %} |
||||||
|
<div class="click" |
||||||
|
onclick = "onFlagClick( {{turnSquare[0]}}, 2 )" |
||||||
|
> </div> |
||||||
|
{%endif%} |
||||||
|
</td> |
||||||
|
|
||||||
|
</tr> </table> |
||||||
|
|
||||||
|
</td> |
||||||
|
{%endfor%} |
||||||
|
|
||||||
|
</tr> |
||||||
|
{%endfor%} |
||||||
|
|
||||||
|
</table> |
||||||
|
|
||||||
|
</html> |
@ -0,0 +1,27 @@ |
|||||||
|
#turn-track { display: flex ; overflow: hidden ; } |
||||||
|
.ui-dialog.turn-track .ui-dialog-titlebar { background: #80d0ff ; } |
||||||
|
.ui-dialog.turn-track .ui-dialog-buttonpane { border: none ; margin-top: 0 !important ; padding-top: 0 !important ; } |
||||||
|
|
||||||
|
.ui-dialog.turn-track .controls { display: flex ; } |
||||||
|
.ui-dialog.turn-track .controls .row { display: flex ; align-items: center ; } |
||||||
|
.ui-dialog.turn-track .controls .select2 { margin: -1px 0 0 0.5em ; } |
||||||
|
.ui-dialog.turn-track .controls input { margin-top: 0.25em ; } |
||||||
|
.ui-dialog.turn-track .controls button.reset { |
||||||
|
height: 26px ; width: 4.5em ; |
||||||
|
margin-top: 1em ; padding: 2px 10px 2px 5px ; |
||||||
|
display: none ; |
||||||
|
} |
||||||
|
|
||||||
|
.ui-dialog.turn-track iframe { margin: -4px ; border: 0 ; overflow: scroll ; } |
||||||
|
|
||||||
|
/* special layout settings for a horizontal turn track */ |
||||||
|
#turn-track.horz { flex-direction: column ; } |
||||||
|
#turn-track.horz .controls { flex-direction: row ; } |
||||||
|
#turn-track.horz .controls>div { margin: 0 1em 0 0 ; } |
||||||
|
#turn-track.horz .preview { height: auto ; width: 100% ; margin: 10px 0 0 0 ; padding-bottom: 15px ; } |
||||||
|
|
||||||
|
/* special layout settings for a vertical turn track */ |
||||||
|
#turn-track.vert { flex-direction: row ; } |
||||||
|
#turn-track.vert .controls { flex-direction: column ; } |
||||||
|
#turn-track.vert .controls>div { margin: 0 0 1em 0 ; } |
||||||
|
#turn-track.vert .preview { height: 100% ; width: auto ; margin: 0 0 0 10px ; padding-right: 15px ; } |
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,309 @@ |
|||||||
|
DEFAULT_TURN_TRACK_TURNS_MIN = 6 ; |
||||||
|
DEFAULT_TURN_TRACK_TURNS_MAX = 10 ; |
||||||
|
|
||||||
|
// NOTE: Reinforcement flags get clipped on turn 100, but this is unlikely to be an issue :-/
|
||||||
|
_MAX_TURN_TRACK_TURNS = 100 ; |
||||||
|
|
||||||
|
gTurnTrackReinforcements = null ; |
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
function editTurnTrackSettings() |
||||||
|
{ |
||||||
|
// initialize
|
||||||
|
var $dlg, $iframe, iframeSeqNo=0 ; |
||||||
|
// FUDGE! This should work as a local variable, but causes a weird problem where it doesn't get reset properly :-/
|
||||||
|
gTurnTrackReinforcements = null ; |
||||||
|
|
||||||
|
function loadControls() { |
||||||
|
// load the dialog controls
|
||||||
|
$dlg.find( "select[name='nturns']" ).val( |
||||||
|
$panel.find( "select[name='TURN_TRACK_NTURNS'].param" ).val() |
||||||
|
).trigger("change" ) ; |
||||||
|
var width = $panel.find( "input[name='TURN_TRACK_WIDTH']" ).val() ; |
||||||
|
$dlg.find( "select[name='width']" ).val( |
||||||
|
isNaN( parseInt( width ) ) ? "" : width |
||||||
|
).trigger( "change" ) ; |
||||||
|
$dlg.find( "input[name='vertical']" ).prop( "checked", |
||||||
|
$panel.find( "input[name='TURN_TRACK_VERTICAL']" ).prop( "checked" ) |
||||||
|
) ; |
||||||
|
$dlg.find( "input[name='swap-players']" ).prop( "checked", |
||||||
|
$panel.find( "input[name='TURN_TRACK_SWAP_PLAYERS']" ).prop( "checked" ) |
||||||
|
) ; |
||||||
|
// load the reinforcements
|
||||||
|
var params = updatePreview( false ) ; |
||||||
|
var args = parseTurnTrackParams( params ) ; |
||||||
|
gTurnTrackReinforcements = { 1: args.reinforce1, 2: args.reinforce2 } ; |
||||||
|
// update the UI
|
||||||
|
updateUI() ; |
||||||
|
} |
||||||
|
|
||||||
|
function onResetControls() { |
||||||
|
// reset all the controls
|
||||||
|
ask( "Reset turn track", "Do you want to reset the turn track?", { |
||||||
|
ok: function() { |
||||||
|
setTurnTrackNTurns( DEFAULT_TURN_TRACK_TURNS_MIN ) ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_WIDTH']" ).val( "" ) ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_VERTICAL']" ).prop( "checked", false ) ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_SWAP_PLAYERS']" ).prop( "checked", false ) ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_1']" ).val( "" ) ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_2']" ).val( "" ) ; |
||||||
|
gTurnTrackReinforcements = null ; |
||||||
|
loadControls() ; |
||||||
|
} |
||||||
|
} ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function initTurnCountSelect2( $sel ) { |
||||||
|
// initialize the TURN COUNT droplist
|
||||||
|
init_select2( |
||||||
|
$sel, "4em", false, formatTurnTrackOption |
||||||
|
).on( "select2:open", function() { |
||||||
|
restrict_droplist_height( $(this) ) ; |
||||||
|
} ).on( "change", function() { |
||||||
|
setTurnTrackNTurns( $(this).val() ) ; |
||||||
|
if ( $dlg ) |
||||||
|
updateUI() ; |
||||||
|
} ) ; |
||||||
|
for ( var nTurns=1 ; nTurns <= _MAX_TURN_TRACK_TURNS ; nTurns += 0.5 ) |
||||||
|
$sel.append( $( "<option value='" + nTurns + "'>" + nTurns + "</option>" ) ) ; |
||||||
|
} |
||||||
|
function initWidthSelect2( $sel ) { |
||||||
|
// initialize the WIDTH droplist
|
||||||
|
init_select2( |
||||||
|
$sel, "3.5em", false, null |
||||||
|
).on( "select2:open", function() { |
||||||
|
restrict_droplist_height( $(this) ) ; |
||||||
|
} ).on( "change", function() { |
||||||
|
$panel.find( "input[name='TURN_TRACK_WIDTH']" ).val( $(this).val() ) ; |
||||||
|
if ( $dlg ) |
||||||
|
updateUI() ; |
||||||
|
} ) ; |
||||||
|
$sel.append( $( "<option value=''>-</option>" ) ) ; |
||||||
|
for ( var i=1 ; i <= 30 ; ++i ) |
||||||
|
$sel.append( $( "<option value='" + i + "'>" + i + "</option>" ) ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function syncCheckbox( $elem, $target ) { |
||||||
|
// sync the target checkbox in the SCENARIO panel with the one in this dialog
|
||||||
|
$target.prop( "checked", $elem.prop("checked") ) ; |
||||||
|
updateUI() ; |
||||||
|
} |
||||||
|
|
||||||
|
function updatePreview( showAllFlags ) { |
||||||
|
|
||||||
|
// generate the turn track snippet
|
||||||
|
var params = unload_snippet_params( true, "turn_track" ) ; |
||||||
|
if ( showAllFlags ) |
||||||
|
params.TURN_TRACK.REINFORCEMENTS_1 = params.TURN_TRACK.REINFORCEMENTS_2 = makeCommaList( _MAX_TURN_TRACK_TURNS ) ; |
||||||
|
params.TURN_TRACK_PREVIEW_MODE = true ; |
||||||
|
var $btn = $( "button.generate[data-id='turn_track']" ) ; |
||||||
|
var snippet = make_snippet( $btn, params, {}, false ).content ; |
||||||
|
|
||||||
|
// update the preview
|
||||||
|
// NOTE: To minimize flickering, we load the snippet into a hidden <iframe>,
|
||||||
|
// then replace the preview <iframe> with it.
|
||||||
|
// FUDGE! We should be able to wait until the new iframe has finished loading before removing the old one,
|
||||||
|
// but for some inexplicable reason, the remove doesn't work on Windows, and the iframe's just build up :-/
|
||||||
|
// Instead, we remove the old iframe here, but by fiddling with opacity, we can avoid flicker. Sigh...
|
||||||
|
var style = $iframe.attr( "style" ) ; |
||||||
|
style.opacity = 0 ; |
||||||
|
var $newFrame = $( "<iframe></iframe>", { style: style, "data-seqno": ++iframeSeqNo } ) ; |
||||||
|
$iframe.after( $newFrame ) ; |
||||||
|
$iframe.remove() ; |
||||||
|
$iframe = $newFrame ; |
||||||
|
$newFrame.attr( "srcdoc", snippet ).on( "load", function() { |
||||||
|
// update the state of each reinforcement flag
|
||||||
|
for ( var turnNo=1 ; turnNo <= _MAX_TURN_TRACK_TURNS ; ++turnNo ) { |
||||||
|
for ( var playerNo=1 ; playerNo <= 2 ; ++playerNo ) |
||||||
|
updateFlag( turnNo, playerNo ) ; |
||||||
|
} |
||||||
|
// install the new <iframe>
|
||||||
|
$newFrame.attr( "id", "turn-track-preview" ).css( "opacity", 1 ) ; |
||||||
|
// FUDGE! This works around a weird problem when we load a scenario with a vertical turn track
|
||||||
|
// and show it in a turn track dialog that was previously showing a horizontal turn track :-/
|
||||||
|
updateLayout() ; |
||||||
|
} ) ; |
||||||
|
|
||||||
|
return params ; |
||||||
|
} |
||||||
|
|
||||||
|
function onFlagClick( turnNo, playerNo ) { |
||||||
|
// NOTE: This method gets called by a click handler in the snippet HTML.
|
||||||
|
// toggle the player turn reinforcements
|
||||||
|
if ( gTurnTrackReinforcements[playerNo][turnNo] ) |
||||||
|
delete gTurnTrackReinforcements[playerNo][turnNo] ; |
||||||
|
else |
||||||
|
gTurnTrackReinforcements[playerNo][turnNo] = true ; |
||||||
|
$panel.find( "input[name='TURN_TRACK_REINFORCEMENTS_" + playerNo + "']" ).val( |
||||||
|
Object.keys( gTurnTrackReinforcements[playerNo] ).join( "," ) |
||||||
|
) ; |
||||||
|
updateFlag( turnNo, playerNo ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function updateFlag( turnNo, playerNo ) { |
||||||
|
// update the specified reinforcement flag
|
||||||
|
$iframe.contents().find( "#flag-" + turnNo + "_" + flipPlayerNo2(playerNo) ).css( { |
||||||
|
opacity: gTurnTrackReinforcements && gTurnTrackReinforcements[playerNo][turnNo] ? 1 : 0.4, |
||||||
|
} ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function updateUI() { |
||||||
|
// update the UI
|
||||||
|
updateLayout() ; |
||||||
|
updatePreview( true ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function updateLayout() { |
||||||
|
// update the layout based on the direction of the turn track
|
||||||
|
if ( $dlg.find( "input[name='vertical']" ).prop( "checked" ) ) { |
||||||
|
// vertical layout
|
||||||
|
$iframe.css( { position: "absolute", |
||||||
|
top: "10px", height: "calc(100% - 20px)", left: "150px", width: "calc(100% - 160px)" |
||||||
|
} ) ; |
||||||
|
$dlg.addClass( "vert" ).removeClass( "horz" ) ; |
||||||
|
$dlg.find( ".controls" ).addClass( "vert" ).removeClass( "horz" ) ; |
||||||
|
$dlg.find( ".reset1" ).hide() ; |
||||||
|
$dlg.find( ".reset2" ).show() ; |
||||||
|
} else { |
||||||
|
// horizontal layout
|
||||||
|
$iframe.css( { position: "absolute", |
||||||
|
top: "95px", height: "calc(100% - 105px)", left: "10px", width: "calc(100% - 20px)" |
||||||
|
} ) ; |
||||||
|
$dlg.addClass( "horz" ).removeClass( "vert" ) ; |
||||||
|
$dlg.find( ".controls" ).addClass( "horz" ).removeClass( "vert" ) ; |
||||||
|
$dlg.find( ".reset1" ).show() ; |
||||||
|
$dlg.find( ".reset2" ).hide() ; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// NOTE: Since Player 1 in the UI is Player 2 in the Turn Track template (by default),
|
||||||
|
// and vice versa, we often need to flip the player numbers.
|
||||||
|
function flipPlayerNo( playerNo ) { return parseInt(playerNo) === 1 ? 2 : 1 ; } |
||||||
|
function flipPlayerNo2( playerNo ) { |
||||||
|
if ( ! $panel.find("input[name='TURN_TRACK_SWAP_PLAYERS']").prop( "checked" ) ) |
||||||
|
playerNo = flipPlayerNo( playerNo ) ; |
||||||
|
return playerNo ; |
||||||
|
} |
||||||
|
|
||||||
|
function makeCommaList( nVals ) { |
||||||
|
// generate a comma-separated list of values
|
||||||
|
var vals = [] ; |
||||||
|
for ( var i=1 ; i <= nVals ; ++i ) |
||||||
|
vals.push( i ) ; |
||||||
|
return vals.join( "," ) ; |
||||||
|
} |
||||||
|
|
||||||
|
// show the TURN TRACK dialog
|
||||||
|
var $panel = $( "#panel-scenario" ) ; |
||||||
|
$( "#turn-track" ).dialog( { |
||||||
|
"title": "Turn track", |
||||||
|
dialogClass: "turn-track", |
||||||
|
modal: true, |
||||||
|
minWidth: 500, minHeight: 250, |
||||||
|
resizable: true, |
||||||
|
create: function() { |
||||||
|
// initialize the dialog
|
||||||
|
init_dialog( $(this), "OK", true ) ; |
||||||
|
initTurnCountSelect2( $(this).find( "select[name='nturns']" ) ) ; |
||||||
|
initWidthSelect2( $(this).find( "select[name='width']" ) ) ; |
||||||
|
$(this).find( "button.reset" ).button().on( |
||||||
|
"click", onResetControls |
||||||
|
) ; |
||||||
|
// keep the settings in the SCENARIO panel in sync with the dialog
|
||||||
|
$(this).find( "input[name='vertical']" ).on( "change", function() { |
||||||
|
syncCheckbox( $(this), $panel.find("input[name='TURN_TRACK_VERTICAL']") ) ; |
||||||
|
} ) ; |
||||||
|
$(this).find( "input[name='swap-players']" ).on( "change", function() { |
||||||
|
syncCheckbox( $(this), $panel.find("input[name='TURN_TRACK_SWAP_PLAYERS']") ) ; |
||||||
|
} ) ; |
||||||
|
// update the UI when the direction of the turn track is changed
|
||||||
|
$(this).find( "input[name='vertical']" ).on( "change", function() { |
||||||
|
updateUI() ; |
||||||
|
} ) ; |
||||||
|
// handle clicks on reinforcement flags in the turn track preview
|
||||||
|
window.addEventListener( "message", function( evt ) { |
||||||
|
if ( evt.data.type === "FlagClick" ) |
||||||
|
onFlagClick( evt.data.turnNo, flipPlayerNo2(evt.data.uiPlayerNo) ) ; |
||||||
|
} ) ; |
||||||
|
|
||||||
|
}, |
||||||
|
open: function() { |
||||||
|
// initialize the dialog
|
||||||
|
var $btnPane = $( ".ui-dialog.turn-track .ui-dialog-buttonpane" ) ; |
||||||
|
var $btn = $btnPane.find( "button.snippet" ) ; |
||||||
|
$btn.prepend( |
||||||
|
$( "<img src='" + gImagesBaseUrl+"/snippet.png" + "' style='height:0.9em;margin:0 0.35em -1px 0;'>" ) |
||||||
|
) ; |
||||||
|
$btn.css( { position: "absolute", left: 5 } ) ; |
||||||
|
// load the dialog
|
||||||
|
$dlg = $(this) ; |
||||||
|
$iframe = $dlg.find( "iframe#turn-track-preview" ) ; |
||||||
|
loadControls() ; |
||||||
|
}, |
||||||
|
buttons: { |
||||||
|
Snippet: { text:" Snippet", class: "snippet", click: function( evt ) { |
||||||
|
var $btn = $( "button.generate[data-id='turn_track']" ) ; |
||||||
|
generate_snippet( $btn, evt.shiftKey, null ) ; |
||||||
|
} }, |
||||||
|
Close: function() { $(this).dialog( "close" ) ; }, |
||||||
|
}, |
||||||
|
} ) ; |
||||||
|
} |
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
function setTurnTrackNTurns( nTurns ) |
||||||
|
{ |
||||||
|
// select the specified number of turns
|
||||||
|
updateTurnTrackNTurns( nTurns ) ; |
||||||
|
$( "select[name='TURN_TRACK_NTURNS']" ).val( |
||||||
|
nTurns |
||||||
|
).trigger( "change" ) ; |
||||||
|
} |
||||||
|
|
||||||
|
function updateTurnTrackNTurns( nTurns ) |
||||||
|
{ |
||||||
|
function makeExtraOption( val, caption ) { |
||||||
|
return $( "<option class='extra' value='" + val + "'>" + caption + "</option>" ) ; |
||||||
|
} |
||||||
|
|
||||||
|
// initialize
|
||||||
|
var $sel = $( "select[name='TURN_TRACK_NTURNS']" ) ; |
||||||
|
var $extra = $sel.find( "option.extra" ) ; |
||||||
|
|
||||||
|
// check if the specified number of turns is already in the droplist
|
||||||
|
var $opt = $sel.find( "option[value='" + nTurns + "']" ) ; |
||||||
|
if ( $opt.length > 0 ) { |
||||||
|
// yup - check if it's the special extra entry
|
||||||
|
if ( ! $opt.hasClass( "extra" ) ) { |
||||||
|
// nope - we don't need it any more
|
||||||
|
$extra.remove() ; |
||||||
|
} |
||||||
|
// check if the turn track has been disabled
|
||||||
|
if ( nTurns === "" ) { |
||||||
|
// yup - add a special extra entry to open the turn track dialog
|
||||||
|
$sel.append( makeExtraOption( "(show-dialog)", "(more)" ) ) ; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// nope - add it as a special extra entry
|
||||||
|
if ( $extra.length > 0 ) { |
||||||
|
// FUDGE! If the special entry is already there, we delete and re-create it to get the select2 to work :-/
|
||||||
|
$extra.remove() ; |
||||||
|
} |
||||||
|
var $opt2 = makeExtraOption( nTurns, nTurns ) ; |
||||||
|
if ( nTurns < DEFAULT_TURN_TRACK_TURNS_MIN ) |
||||||
|
$sel.find( "option[value='']" ).after( $opt2 ) ; |
||||||
|
else |
||||||
|
$sel.append( $opt2 ) ; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function formatTurnTrackOption( opt ) { |
||||||
|
// format the turn track <option> element
|
||||||
|
if ( opt.id === "(show-dialog)" ) |
||||||
|
return $( "<span style='font-size:80%;font-style:italic;color:#666;'>" + opt.text + "</span>" ) ; |
||||||
|
if ( opt.text.substr( opt.text.length-2 ) === ".5" ) |
||||||
|
return $( "<span>" + opt.text.substr( 0, opt.text.length-2 ) + "½" + "</span>" ) ; |
||||||
|
return opt.text ; |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
<div id="turn-track" style="display:none;"> |
||||||
|
|
||||||
|
<div class="controls"> |
||||||
|
|
||||||
|
<div style="display:flex;flex-direction:column;"> |
||||||
|
<div class="row"> Turns: <select name="nturns"></select> </div> |
||||||
|
<button class="reset reset1"> Reset </button> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style="display:flex;flex-direction:column;"> |
||||||
|
<div class="row"> Width: <select name="width"></select> </div> |
||||||
|
<div> <input type="checkbox" name="vertical"> Vertical </input> </div> |
||||||
|
<div> <input type="checkbox" name="swap-players"> Swap players </input> </div> |
||||||
|
<button class="reset reset2"> Reset </button> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<iframe id="turn-track-preview"></iframe> |
||||||
|
|
||||||
|
</div> |
@ -0,0 +1,4 @@ |
|||||||
|
TURN TRACK: |
||||||
|
{% for row in TURN_TRACK_SQUARES %} |
||||||
|
{% for turnSquare in row %}[{{turnSquare}}] {%endfor%} |
||||||
|
{%endfor%} |
@ -0,0 +1 @@ |
|||||||
|
Customized TURN_TRACK. |
@ -0,0 +1 @@ |
|||||||
|
New default TURN_TRACK. |
@ -0,0 +1,343 @@ |
|||||||
|
""" Test the turn track functionality. """ |
||||||
|
|
||||||
|
from selenium.webdriver.support.ui import Select |
||||||
|
from selenium.webdriver.common.keys import Keys |
||||||
|
|
||||||
|
from vasl_templates.webapp.tests.utils import \ |
||||||
|
init_webapp, get_turn_track_nturns, set_turn_track_nturns, select_droplist_val, get_droplist_vals, \ |
||||||
|
SwitchFrame, unload_table, wait_for, wait_for_elem, wait_for_clipboard, find_child |
||||||
|
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, save_scenario |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_turn_track_basic( webapp, webdriver ): |
||||||
|
"""Test basic turn track functionality.""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||||
|
init_webapp( webapp, webdriver ) |
||||||
|
|
||||||
|
# check the initial state of the UI |
||||||
|
assert get_turn_track_nturns() == "" |
||||||
|
assert not find_child( "button#turn-track-settings" ).is_displayed() |
||||||
|
assert not find_child( ".snippet-control[data-id='turn_track']" ).is_displayed() |
||||||
|
|
||||||
|
# configure a number of turns |
||||||
|
set_turn_track_nturns( 6 ) |
||||||
|
assert find_child( "button#turn-track-settings" ).is_displayed() |
||||||
|
assert find_child( ".snippet-control[data-id='turn_track']" ).is_displayed() |
||||||
|
|
||||||
|
# generate a snippet |
||||||
|
assert _generate_turn_track_snippet( None ) == [ |
||||||
|
[ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_turn_track_controls( webapp, webdriver ): |
||||||
|
"""Test the basic controls for configuring the turn track.""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||||
|
init_webapp( webapp, webdriver ) |
||||||
|
|
||||||
|
# show the turn track dialog |
||||||
|
dlg = _show_turn_track_dialog( 6 ) |
||||||
|
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||||
|
_click_reinf_flag( 1, 1 ) |
||||||
|
_click_reinf_flag( 2, 1 ) |
||||||
|
_click_reinf_flag( 2, 2 ) |
||||||
|
_click_reinf_flag( 3, 2 ) |
||||||
|
|
||||||
|
# change the width |
||||||
|
_change_turn_track_width( dlg, 3 ) |
||||||
|
def check_for_width(): |
||||||
|
return _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,"player1",None), (2,"player1","player2"), (3,None,"player2") ], |
||||||
|
[ (4,None,None), (5,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
wait_for( 2, check_for_width ) |
||||||
|
|
||||||
|
# swap the players |
||||||
|
_swap_turn_track_players( dlg ) |
||||||
|
def check_for_swap_players(): |
||||||
|
return _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,None,"player2"), (2,"player1","player2"), (3,"player1",None) ], |
||||||
|
[ (4,None,None), (5,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
wait_for( 2, check_for_swap_players ) |
||||||
|
|
||||||
|
# make the turn track vertical |
||||||
|
_change_turn_track_direction( dlg ) |
||||||
|
def check_for_vertical(): |
||||||
|
return _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,None,"player2"), (3,"player1",None), (5,None,None) ], |
||||||
|
[ (2,"player1","player2"), (4,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
wait_for( 2, check_for_vertical ) |
||||||
|
|
||||||
|
# reset the controls |
||||||
|
find_child( "button.reset2" ).click() |
||||||
|
ask = wait_for_elem( 2, ".ui-dialog.ask" ) |
||||||
|
find_child( "button.ok", ask ).click() |
||||||
|
def check_for_reset(): |
||||||
|
return _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,None,None), (2,None,None), (3,None,None), (4,None,None), (5,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
wait_for( 2, check_for_reset ) |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_turn_track_reinforcements( webapp, webdriver ): |
||||||
|
"""Test configuring reinforcements on the turn track.""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||||
|
init_webapp( webapp, webdriver ) |
||||||
|
|
||||||
|
# show the turn track dialog |
||||||
|
dlg = _show_turn_track_dialog( 6.5 ) |
||||||
|
|
||||||
|
# turn on some reinforcements, then check the snippet |
||||||
|
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||||
|
_click_reinf_flag( 2, 1 ) |
||||||
|
_click_reinf_flag( 3, 1 ) |
||||||
|
_click_reinf_flag( 3, 2 ) |
||||||
|
_click_reinf_flag( 7, 1 ) |
||||||
|
assert _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,None,None), (2,"player1",None), (3,"player1","player2"), |
||||||
|
(4,None,None), (5,None,None) , (6,None,None), (7,"player1",None) |
||||||
|
] |
||||||
|
] |
||||||
|
|
||||||
|
# turn off some reinforcements, turn some on, then check the snippet |
||||||
|
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||||
|
_click_reinf_flag( 2, 2 ) |
||||||
|
_click_reinf_flag( 3, 1 ) |
||||||
|
_click_reinf_flag( 3, 2 ) |
||||||
|
_click_reinf_flag( 5, 2 ) |
||||||
|
_click_reinf_flag( 7, 1 ) |
||||||
|
assert _generate_turn_track_snippet( dlg ) == [ |
||||||
|
[ (1,None,None), (2,"player1","player2"), (3,None,None), |
||||||
|
(4,None,None), (5,None,"player2") , (6,None,None), (7,None,None) |
||||||
|
] |
||||||
|
] |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_turn_track_persistence( webapp, webdriver ): |
||||||
|
"""Test saving and loading turn track settings.""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||||
|
init_webapp( webapp, webdriver, scenario_persistence=1 ) |
||||||
|
|
||||||
|
# show the turn track dialog |
||||||
|
load_scenario( { |
||||||
|
"PLAYER_1": "japanese", "PLAYER_2": "american", |
||||||
|
"TURN_TRACK": { "NTURNS": 6.5 }, |
||||||
|
} ) |
||||||
|
dlg = _show_turn_track_dialog( None ) |
||||||
|
|
||||||
|
# configure the turn track |
||||||
|
with SwitchFrame( webdriver, "#turn-track-preview" ): |
||||||
|
_click_reinf_flag( 1, 1 ) |
||||||
|
_click_reinf_flag( 2, 2 ) |
||||||
|
_click_reinf_flag( 3, 1 ) |
||||||
|
_click_reinf_flag( 3, 2 ) |
||||||
|
_change_turn_track_width( dlg, 4 ) |
||||||
|
_swap_turn_track_players( dlg ) |
||||||
|
_change_turn_track_direction( dlg ) |
||||||
|
|
||||||
|
# check the snippet |
||||||
|
expected = [ |
||||||
|
[ (1,None,"player2"), (3,"player1","player2"), (5,None,None), (7,None,None) ], |
||||||
|
[ (2,"player1",None) , (4,None,None), (6,None,None) ] |
||||||
|
] |
||||||
|
wait_for( 2, |
||||||
|
lambda: _generate_turn_track_snippet( dlg ) == expected |
||||||
|
) |
||||||
|
|
||||||
|
# save the scenario |
||||||
|
dlg.send_keys( Keys.ESCAPE ) |
||||||
|
saved_scenario = save_scenario() |
||||||
|
assert saved_scenario["TURN_TRACK"] == { |
||||||
|
"NTURNS": "6.5", |
||||||
|
"WIDTH": "4", "VERTICAL": True, "SWAP_PLAYERS": True, |
||||||
|
"REINFORCEMENTS_1": "2,3", "REINFORCEMENTS_2": "1,3", |
||||||
|
} |
||||||
|
assert _generate_turn_track_snippet( None ) == expected |
||||||
|
|
||||||
|
# reset the scenario |
||||||
|
webdriver.refresh() |
||||||
|
assert not find_child( "button.generate[data-id='turn_track']" ).is_displayed() |
||||||
|
|
||||||
|
# load the scenario and generate the snippet |
||||||
|
load_scenario( saved_scenario ) |
||||||
|
assert _generate_turn_track_snippet( None ) == expected |
||||||
|
|
||||||
|
# open the turn track dialog and check that the controls were loaded correctly |
||||||
|
dlg = _show_turn_track_dialog( None ) |
||||||
|
assert get_turn_track_nturns() == "6.5" |
||||||
|
sel = Select( find_child( "select[name='width']", dlg ) ) |
||||||
|
assert sel.first_selected_option.get_attribute( "value" ) == "4" |
||||||
|
assert find_child( "input[name='vertical']" ).is_selected() |
||||||
|
assert find_child( "input[name='swap-players']" ).is_selected() |
||||||
|
assert _generate_turn_track_snippet( dlg ) == expected |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_turn_track_droplist( webapp, webdriver ): |
||||||
|
"""Test updating entries in the turn track droplist (#turns).""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
webapp.control_tests.set_data_dir( "{REAL}" ) |
||||||
|
init_webapp( webapp, webdriver, scenario_persistence=1 ) |
||||||
|
|
||||||
|
# check the initial state of the droplist |
||||||
|
assert _unload_turn_track_droplist() == [ |
||||||
|
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)" |
||||||
|
] |
||||||
|
|
||||||
|
# configure a number of turns that is not in the default list (small) |
||||||
|
sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) ) |
||||||
|
select_droplist_val( sel, "(show-dialog)" ) |
||||||
|
sel2 = Select( find_child( "#turn-track select[name='nturns']" ) ) |
||||||
|
select_droplist_val( sel2, "2" ) |
||||||
|
assert _unload_turn_track_droplist() == [ |
||||||
|
"", "2", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10" |
||||||
|
] |
||||||
|
|
||||||
|
# configure a number of turns that is not in the default list (large) |
||||||
|
select_droplist_val( sel2, "14.5" ) |
||||||
|
expected = [ "", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "14.5" ] |
||||||
|
assert _unload_turn_track_droplist() == expected |
||||||
|
|
||||||
|
# save and reload the scenario |
||||||
|
find_child( ".ui-dialog.turn-track" ).send_keys( Keys.ESCAPE ) |
||||||
|
saved_scenario = save_scenario() |
||||||
|
webdriver.refresh() |
||||||
|
load_scenario( saved_scenario ) |
||||||
|
assert _unload_turn_track_droplist() == expected |
||||||
|
|
||||||
|
# configure a number of turns that is in the default list |
||||||
|
sel = Select( find_child( "select[name='TURN_TRACK_NTURNS']" ) ) |
||||||
|
select_droplist_val( sel, "8" ) |
||||||
|
assert _unload_turn_track_droplist() == [ |
||||||
|
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10" |
||||||
|
] |
||||||
|
|
||||||
|
# disable the turn track |
||||||
|
select_droplist_val( sel, "" ) |
||||||
|
assert _unload_turn_track_droplist() == [ |
||||||
|
"", "6", "6.5", "7", "7.5", "8", "8.5", "9", "9.5", "10", "(show-dialog)" |
||||||
|
] |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def _show_turn_track_dialog( nturns ): |
||||||
|
"""Show the TURN TRACK dialog.""" |
||||||
|
if nturns: |
||||||
|
set_turn_track_nturns( nturns ) |
||||||
|
btn = wait_for_elem( 2, "button#turn-track-settings" ) |
||||||
|
btn.click() |
||||||
|
dlg = wait_for_elem( 2, ".ui-dialog.turn-track" ) |
||||||
|
return dlg |
||||||
|
|
||||||
|
def _generate_turn_track_snippet( dlg ): |
||||||
|
"""Generate a turn track snippet.""" |
||||||
|
|
||||||
|
# generate the snippet |
||||||
|
btn = find_child( "button.snippet", dlg ) if dlg else find_child( "button.generate[data-id='turn_track']" ) |
||||||
|
assert btn.is_displayed() |
||||||
|
btn.click() |
||||||
|
clipboard = wait_for_clipboard( 2, "<!-- vasl-templates:id", contains=True ) |
||||||
|
|
||||||
|
def get_reinforce_class( cell ): |
||||||
|
cell_class = str( cell.xpath( ".//@class" )[0] ) |
||||||
|
if cell_class == "reinforce1": |
||||||
|
return "player1" |
||||||
|
elif cell_class == "reinforce2": |
||||||
|
return "player2" |
||||||
|
else: |
||||||
|
assert cell_class == "no-reinforce" |
||||||
|
return None |
||||||
|
|
||||||
|
def unload_square( square ): |
||||||
|
cells = square.xpath( ".//td" ) |
||||||
|
assert len(cells) == 3 |
||||||
|
return ( |
||||||
|
int( cells[1].text ), |
||||||
|
get_reinforce_class( cells[0] ), |
||||||
|
get_reinforce_class( cells[2] ), |
||||||
|
) |
||||||
|
|
||||||
|
# unload the snippet contents |
||||||
|
squares = unload_table( "//table[@class='turn-track']", html=clipboard, unload=False ) |
||||||
|
for row_no, row in enumerate(squares): |
||||||
|
for col_no, square in enumerate(row): |
||||||
|
squares[row_no][col_no] = unload_square( square ) |
||||||
|
|
||||||
|
return squares |
||||||
|
|
||||||
|
def _unload_turn_track_droplist(): |
||||||
|
"""Get the available options in the turn track droplist.""" |
||||||
|
keys = [] |
||||||
|
prev_key = None |
||||||
|
options = get_droplist_vals( Select( |
||||||
|
find_child( "select[name='TURN_TRACK_NTURNS']" ) |
||||||
|
) ) |
||||||
|
for key, caption in options: |
||||||
|
if key == "": |
||||||
|
assert caption == "-" |
||||||
|
elif key == "(show-dialog)": |
||||||
|
assert caption == "(more)" |
||||||
|
else: |
||||||
|
assert key == caption |
||||||
|
key2 = float( key ) |
||||||
|
if prev_key: |
||||||
|
assert key2 > prev_key |
||||||
|
assert int( 10 * key2 ) % 10 in (0,5) |
||||||
|
prev_key = key2 |
||||||
|
keys.append( key ) |
||||||
|
return keys |
||||||
|
|
||||||
|
def _click_reinf_flag( turn_no, player_no ): |
||||||
|
"""Click on a reinforcement flag.""" |
||||||
|
find_child( |
||||||
|
"#flag-{}_{} .click".format( turn_no, player_no ) |
||||||
|
).click() |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def _change_turn_track_width( dlg, width ): |
||||||
|
"""Change the turn track width.""" |
||||||
|
sel = Select( find_child( "select[name='width']", dlg ) ) |
||||||
|
_wait_for_preview( dlg, |
||||||
|
lambda: select_droplist_val( sel, width ) |
||||||
|
) |
||||||
|
|
||||||
|
def _change_turn_track_direction( dlg ): |
||||||
|
"""Toggle the direction of the turn track.""" |
||||||
|
_wait_for_preview( dlg, |
||||||
|
lambda: find_child( "input[name='vertical']", dlg ).click() |
||||||
|
) |
||||||
|
|
||||||
|
def _swap_turn_track_players( dlg ): |
||||||
|
"""Swap the turn track players.""" |
||||||
|
_wait_for_preview( dlg, |
||||||
|
lambda: find_child( "input[name='swap-players']", dlg ).click() |
||||||
|
) |
||||||
|
|
||||||
|
def _wait_for_preview( dlg, func ): |
||||||
|
"""Make a change to the preview, and wait for the UI to update.""" |
||||||
|
# NOTE: The preview <iframe> is replaced with a new one, so we need to be ready for the old one to disappear. |
||||||
|
iframe = find_child( "iframe", dlg ) |
||||||
|
seqno = iframe.get_attribute( "data-seqno" ) |
||||||
|
func() |
||||||
|
def check_seqno(): |
||||||
|
iframe = find_child( "iframe", dlg ) |
||||||
|
if not iframe: |
||||||
|
return False |
||||||
|
return iframe.get_attribute( "data-seqno" ) != seqno |
||||||
|
wait_for( 20, check_seqno ) |