" ) ;
update_page_load_status( "ordnance-notes" ) ;
} ) ;
// get the VASL piece info
$.getJSON( gGetVaslPieceInfoUrl, function(data) {
gVaslPieceInfo = data ;
update_page_load_status( "vasl-piece-info" ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the VASL piece info:
" + escapeHTML(errorMsg) + "
" ) ;
update_page_load_status( "vasl-piece-info" ) ;
} ) ;
// get the online counter images data
$.getJSON( gGetOnlineCounterImagesUrl, function(data) {
gOnlineCounterImages = data ;
update_page_load_status( "online-counter-images" ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the online counter images:
" + escapeHTML(errorMsg) + "
" ) ;
update_page_load_status( "online-counter-images" ) ;
} ) ;
// get the template pack
$.getJSON( gGetTemplatePackUrl, function(data) {
if ( "error" in data )
showErrorMsg( "Can't get the template pack:
" + escapeHTML(data.error) + "
" ) ;
else {
if ( "_path_" in data ) {
showInfoMsg( "Auto-loaded template pack:
" + escapeHTML(data._path_) + "
" ) ;
delete data._path_ ;
}
}
gDefaultTemplatePack = $.extend( true, {}, data ) ;
install_template_pack( data ) ;
// NOTE: If we are loading a user-defined template pack, then what we think
// is the set of valid template ID's will depend on what's in it :-/
gValidTemplateIds = Object.keys( data.templates ) ;
update_page_load_status( "template-pack" ) ;
// figure out which player flags are available (because some nationalities don't have one)
var nats = Object.keys( gTemplatePack.nationalities ) ;
var nFlagsChecked = 0 ;
function onFlagChecked() {
if ( ++nFlagsChecked === nats.length ) {
// we've checked all the flags - now we can build the player droplists
init_player_droplists() ;
update_ob_tab_header( 1 ) ;
update_ob_tab_header( 2 ) ;
update_page_load_status( "flag-urls" ) ;
}
}
nats.forEach( function( nat ) {
var url = make_player_flag_url( nat, false ) + "?no-spacer=1" ;
$.ajax( url, {
success: function() { gHasPlayerFlag[nat] = true ; onFlagChecked() ; },
error: function() { onFlagChecked() ; },
} ) ;
} ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the template pack:
" + escapeHTML(errorMsg) + "
" ) ;
update_page_load_status( "template-pack" ) ;
} ) ;
// get the ASL Rulebook2 vehicle/ordnance note targets
$.getJSON( gGetAslRulebook2VoNoteTargetsUrl, function(data) {
gAslRulebook2VoNoteTargets = data ;
update_page_load_status( "asl-rulebook2-vo-note-targets" ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the ASL Rulebook2 vehicle/ordnance note targets:
" + escapeHTML(errorMsg) + "
" ) ;
update_page_load_status( "asl-rulebook2-vo-note-targets" ) ;
} ) ;
// fixup the layout
var prevHeight = [] ;
$(window).resize( function() {
// FUDGE! CSS grids don't seem to update their layout vertically when
// inside a jQuery tab control - we do it manually :-/
$(".ui-tabs-panel").each( function() {
$(this).css( "padding", "5px" ) ; // FUDGE! doesn't work when set in the CSS :-/
var id = $(this).attr( "id" ) ;
var h = $(this).parent().innerHeight() - navHeight - 20 ;
if ( h !== prevHeight[id] )
{
$(this).css( "height", h+"px" ) ;
prevHeight[id] = h ;
}
} ) ;
// FUDGE! Some panels are rendering with the wrong width in IE :-/
if ( isIE() ) {
var set_width = function($elem) { $elem.width( $elem.parent().width() ) ; } ;
set_width( $("#panel-vc textarea") ) ;
set_width( $("#panel-ssr .content") ) ;
}
} ) ;
$(window).trigger( "resize" ) ;
// replace all the "generate" buttons with "generate/edit" button/droplist's
$("button.generate").each( function() { init_snippet_button( $(this) ) ; } ) ;
// add a tooltip to the snippet width textbox's
$( "input.param.snippet-width" ).each( function() {
// NOTE: I tried putting a little icon in the textbox background, and placeholder text, but it didn't
// look good (and the whole point of removing the "Width:" labels was to remove visual clutter),
// so we just leave the textbox's blank. Hopefully, the default values in some of them, plus the tooltip,
// will be enough for the user to figure out what's going on.
$(this).attr( "title", "Snippet width" ) ;
} ) ;
// add special options to the COMPASS snippet button menu
var $compassMenu = $( "select[data-id='compass']" ) ;
var compassDirns = [ "", "right", "left", "down", "up" ] ;
compassDirns.forEach( function( dirn ) {
var caption = (dirn === "") ? "-" : dirn[0].toUpperCase() + dirn.substring(1) ;
$compassMenu.prepend(
""
) ;
} ) ;
$( ".snippet-control[data-id='compass'] select" ).on( "selectmenuselect", function( evt, ui ) {
var dirn = ui.item.value ;
if ( ! compassDirns.includes( dirn ) )
return ;
$( "input.param[name='COMPASS']" ).val( dirn ) ;
updateCompassImage() ;
} ) ;
// handle requests to edit the templates
$("button.edit-template").click( function() {
edit_template( $(this).data( "id" ) ) ;
} ).html( "
Edit
" )
.attr( "title", EDIT_TEMPLATE_HINT )
.button( {} ) ;
// watch for changes to the scenario details
$("input[name='SCENARIO_NAME']").on( "input propertychange paste", update_scenario_status ) ;
$("input[name='SCENARIO_ID']").on( "input propertychange paste", update_scenario_status ) ;
// NOTE: The following is to add/remove the "scenario modified" indicator. It's pretty inefficent
// to do this using a timer, but we would otherwise have to attach a "on change" event handler
// to every single input field, simple note, etc., which would be far more complicated and error-prone.
if ( ! getUrlParam( "disable-dirty-scenario-check" ) )
setInterval( update_scenario_status, 1*1000 ) ;
// adjust the layout on resize
$(window).resize( function() {
// update the max height of sortable2 entries
var tab_id = $("#tabs .ui-tabs-tab.ui-state-active").attr( "aria-controls" ) ;
$( "#"+tab_id ).find( ".sortable" ).each( function() {
$(this).sortable2( "adjust-entry-heights" ) ;
} ) ;
} ) ;
// initialize keyboard handlers
init_hotkeys() ;
$(document).on( "keydown", function( evt ) {
if ( evt.keyCode == $.ui.keyCode.ESCAPE )
handle_escape( evt ) ;
} ) ;
// check for a dirty scenario before leaving the page
if ( ! getUrlParam( "disable_close_window_check" ) ) {
window.addEventListener( "beforeunload", function(evt) {
// NOTE: We don't check for this if we're running inside the desktop app, since it will intercept the click
// and open the page in a new external browser window (see AppWebPage.acceptNavigationRequest()).
if ( !gWebChannelHandler && is_scenario_dirty() ) {
evt.returnValue = "This scenario has been changed. Do you want to leave the page, and lose your changes?" ;
return evt.returnValue ;
}
} ) ;
}
// prevent files from being dragged in
// NOTE: It would be nice to stop the cursor from changing, but there doesn't seem to be any way of doing that :-/
// In particualar, the dragstart events doesn't fire if something is being dragged into the browser from outside.
$(document).on( { dragenter: stopEvent, dragleave: stopEvent, dragover: stopEvent, drop: stopEvent } ) ;
// FUDGE! We need this to get the input box in the LFA colorpicker to work :-/
// https://github.com/bgrins/spectrum/issues/161#issuecomment-108089372
$.widget( "custom.dialog", $.ui.dialog, {
_allowInteraction: function( evt ) {
return !! $(evt.target).is( ".sp-input" ) || this._super( evt );
}
} ) ;
// figure out how many pixels an em is
var $em = $( "M" ) ;
$("body").append( $em ) ;
gEmSize = $em.width() ;
$em.remove() ;
// add some dummy links for the test suite to edit templates
if ( getUrlParam( "edit_template_links" ) ) {
$("button.generate").each( function() {
var template_id = $(this).attr( "data-id" ) ;
if ( template_id.substring(0,9) === "ob_setup_" )
template_id = "ob_setup" ;
else if ( template_id.substring(0,21) === "ob_vehicles_ma_notes_" )
template_id = "ob_vehicles_ma_notes" ;
else if ( template_id.substring(0,21) === "ob_ordnance_ma_notes_" )
template_id = "ob_ordnance_ma_notes" ;
else if ( template_id.substring(0,12) === "ob_vehicles_" )
template_id = "ob_vehicles" ;
else if ( template_id.substring(0,12) === "ob_ordnance_" )
template_id = "ob_ordnance" ;
else if ( template_id.substring(0,9) === "nat_caps_" )
template_id = "nat_caps" ;
$( ""
).appendTo( "body" ) ;
} ) ;
}
// flag that we've finished initialization
update_page_load_status( "main" ) ;
$("input[name='SCENARIO_NAME']").focus().focus() ;
} ) ;
function init_player_droplists()
{
// initialize the player droplists
function on_player_droplist_open( $sel ) {
// remember the current selection
$sel.data( "prev-val", $sel.val() ) ;
// limit the droplist's height to the available space
restrict_droplist_height( $sel ) ;
}
function format_player_droplist_item( opt ) {
if ( ! opt.id )
return opt.text ;
var url = gHasPlayerFlag[opt.id] ? make_player_flag_url( opt.id, false ) : "" ;
return $( "
" +
"
" +
"" +
"
" +
" " + opt.text +
"
" ) ;
}
init_select2( $( "select[name='PLAYER_1']" ),
"auto", false, format_player_droplist_item
).on( "select2:open", function() {
on_player_droplist_open( $(this) ) ;
} ).on( "change", function() {
on_player_change_with_confirm( 1 ) ;
} ) ;
init_select2( $( "select[name='PLAYER_2']" ),
"auto", false, format_player_droplist_item
).on( "select2:open", function() {
on_player_droplist_open( $(this) ) ;
} ).on( "change", function() {
on_player_change_with_confirm( 2 ) ;
} ) ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function initSplitters()
{
// add splitters to the SCENARIO tab
Split( [ "#tabs-scenario .left", "#tabs-scenario .right" ], {
direction: "horizontal",
sizes: [ 1, 99 ],
gutterSize: 8,
} ) ;
addSplitterGripper( $( "#tabs-scenario .left + .gutter" ), true, 8, {
margin: "5px -2px", "background-color": "inherit"
} ) ;
Split( [ "#tabs-scenario .left .tl", "#tabs-scenario .left .bl" ], {
direction: "vertical",
sizes: [ 99, 1 ],
gutterSize: 8,
} ) ;
addSplitterGripper( $( "#tabs-scenario .left .gutter" ), false, 8, {
margin: "-2px 5px -6px 5px", "background-color": "inherit",
} ) ;
Split( [ "#tabs-scenario .right .tr", "#tabs-scenario .right .br" ], {
direction: "vertical",
sizes: [ 50, 50 ],
gutterSize: 8,
} ) ;
addSplitterGripper( $( "#tabs-scenario .right .gutter" ), false, 8, {
margin: "-2px 5px -6px 5px", "background-color": "inherit",
} ) ;
// add splitters to the OB tabs
// NOTE: The split sizes will be set later by on_player_change().
function initOBSplitter( col ) {
var $col = $( "#tabs-ob" + playerNo + " ." + col ) ;
gPlayerOBSplitters[ playerNo ].push(
Split( [ $col.find( ".t"+col[0] )[0], $col.find( ".b"+col[0] )[0] ], {
direction: "vertical",
gutterSize: 8,
} )
) ;
addSplitterGripper( $col.find(".gutter"), false, 8, {
margin: "-2px 5px -6px 5px", "background-color": "inherit",
} ) ;
}
for ( var playerNo=1 ; playerNo <= 2 ; ++playerNo ) {
gPlayerOBSplitters[ playerNo ] = [] ;
gPlayerOBSplitters[ playerNo ].push(
Split( [ "#tabs-ob"+playerNo+" .left", "#tabs-ob"+playerNo+" .right" ], {
direction: "horizontal",
gutterSize: 8,
} )
) ;
addSplitterGripper( $( "#tabs-ob"+playerNo+" .left + .gutter" ), true, 8, {
margin: "5px -2px", "background-color": "inherit",
} ) ;
[ "left", "right" ].forEach( initOBSplitter ) ;
}
}
function updatePlayerOBSplitters( playerNo )
{
// FUDGE! We want to set the minimum width of the left column in the OB tab based on how many
// extra template buttons the nationality has in the NOTES panel, but their widths will be 0
// if another tab is active.
// Instead, we count how many buttons there are, and estimate what the min-width should be,
// based on the number of buttons and their captions.
var nButtons=0, nChars=0 ;
$( "#panel-ob_notes_" + playerNo + " .footer .snippet-control" ).each( function() {
if ( $(this).css( "display" ) === "none" )
return ;
nButtons += 1 ;
nChars += $(this).find( "button.generate" ).text().length ;
} ) ;
var minWidth = Math.max( 270 + 60*nButtons + gEmSize*nChars*0.5, 320 ) ;
$( "#tabs-ob" + playerNo + " .left" ).css( "min-width", minWidth ) ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function init_snippet_button( $btn )
{
// figure out what template we're dealing with
var template_id = $btn.attr( "data-id" ) ;
var template_id2 ;
if ( template_id.substring(0,9) === "ob_setup_" )
template_id2 = "ob_setup" ;
else if ( template_id.substring(0,21) === "ob_vehicles_ma_notes_" )
template_id2 = "ob_vehicles_ma_notes" ;
else if ( template_id.substring(0,21) === "ob_ordnance_ma_notes_" )
template_id2 = "ob_ordnance_ma_notes" ;
else if ( template_id.substring(0,12) === "ob_vehicles_" )
template_id2 = "ob_vehicles" ;
else if ( template_id.substring(0,12) === "ob_ordnance_" )
template_id2 = "ob_ordnance" ;
else if ( template_id.substring(0,9) === "nat_caps_" )
template_id2 = "nat_caps" ;
else
template_id2 = template_id ;
// create the new button
var buf = [ "
",
$btn.prop( "outerHTML" ),
"",
"
"
] ;
var $newBtn = $( buf.join("") ) ;
var fname="snippet.png", style="" ;
if ( template_id.substring( 0, 9 ) === "nat_caps_" ) {
fname = "nat-caps.png" ;
style = "height:15px;" ;
} else if ( template_id == "compass" ) {
fname = "compass/none.png" ;
}
$newBtn.find( "button" )
.prepend( $( "" ) )
.click( function( evt ) {
generate_snippet( $(this), evt.shiftKey, null ) ;
return false ;
} )
.attr( "title", GENERATE_SNIPPET_HINT )
.css( { "padding-right": $btn.text() !== "" ? "10px" : "0" } ) ;
// add in the droplist
$newBtn.controlgroup() ;
$newBtn.children( "select" ).each( function() {
$(this).selectmenu( {
classes: {
"ui-selectmenu-button": "ui-button-icon-only",
"ui-selectmenu-menu": "snippet-control-menu-item",
},
} ) ;
} ) ;
$newBtn.children( ".ui-button-icon-only" ).css( "width", "1em" ) ;
$newBtn.children( ".ui-selectmenu-button" ).click( function() { $btn.blur() ; } ) ;
// handle requests to edit the template
$newBtn.children( "select" ).on( "selectmenuselect", function( evt, ui ) {
if ( ui.item.value === "edit" )
edit_template( $(this).attr("data-id") ) ;
else if ( ui.item.value === "as-image" )
generate_snippet( $newBtn, true, null ) ;
} ) ;
// replace the existing button with the new replacement button
$btn.replaceWith( $newBtn ) ;
}
function updateCompassImage() {
// update the image in the COMPASS snippet button
var dirn = $( "input.param[name='COMPASS']" ).val() || "none" ;
var $btn = $( "button.generate[data-id='compass']" ) ;
var imagePadding = {
up: "2px 0 0 0", down: "0 0 2px 0", left: "0", right: "0",
none: "0"
} ;
$btn.find( "img" ).attr( "src",
make_app_url( "/static/images/compass/" + dirn + ".png" )
).css( {
opacity: (dirn === "none") ? 1 : 0.85,
padding: imagePadding[dirn]
} ) ;
$btn.button( dirn === "none" ? "disable" : "enable" ) ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gPageLoadStatus = [
"main", "app-config",
"vehicle-listings", "ordnance-listings", "reset-scenario",
"vehicle-notes", "ordnance-notes", "asl-rulebook2-vo-note-targets",
"vasl-piece-info", "online-counter-images", "template-pack", "flag-urls", "default-scenario"
] ;
function update_page_load_status( id )
{
// track the page load progress
var pos = gPageLoadStatus.indexOf( id ) ;
if ( pos === -1 ) {
if ( id !== "default-scenario" )
console.log( "Multiple page-load status:", id ) ;
return ;
}
gPageLoadStatus.splice( pos, 1 ) ;
if ( id === "template-pack" )
$("fieldset[name='scenario']").fadeIn( 2*1000 ) ;
// check if we can reset the scenario
if ( gPageLoadStatus.indexOf( "reset-scenario" ) !== -1 ) {
// NOTE: If the default scenario contains any vehicles or ordnance, it will look up the V/O listings,
// so we need to wait until those have arrived. Note that while the default scenario will normally
// be empty, having stuff in it is very useful during development.
// NOTE: We also have to wait for the vehicle/ordnance notes to arrive, so that we can show/hide
// the UI controls in the OB tabs.
if ( gPageLoadStatus.indexOf( "vehicle-listings" ) === -1 && gPageLoadStatus.indexOf( "ordnance-listings" ) === -1 &&
gPageLoadStatus.indexOf( "vehicle-notes" ) === -1 && gPageLoadStatus.indexOf( "ordnance-notes" ) === -1 ) {
// NOTE: We also need to wait for the app config to arrive (for the scenario theaters).
if ( gPageLoadStatus.indexOf( "app-config" ) === -1 ) {
do_on_new_scenario( false ) ;
update_page_load_status( "reset-scenario" ) ;
}
}
}
function show_startup_msgs( msgs, msg_type ) {
if ( msg_type in msgs ) {
for ( var i=0 ; i < msgs[msg_type].length ; ++i )
doShowNotificationMsg( msg_type, msgs[msg_type][i] ) ;
}
}
// check if the page has finished loading
if ( gPageLoadStatus.length === 0 ) {
// yup - update the UI
apply_user_settings() ;
$( "a[href='#tabs-extras']" ).html(
"Extras"
) ;
$("#tabs").tabs({ disabled: [] }) ;
$("#loader").fadeOut( 500 ) ;
// initialize the HTML WYSIWYG editors (nb: we do it here, since we need the app config
// and template pack (for the player flags))
initVictoryConditionsTrumbowyg() ;
// FUDGE! There are problems with the layout jumping around during startup in the desktop app,
// so we hide the footers on the scenario tab (which is the one visible during startup),
// and only show them them when we're ready.
$( "#tabs-scenario .footer" ).fadeIn( 2*1000 ) ;
adjust_footer_vspacers() ;
// FUDGE! This works around a timing problem during startup, where we unload the current parameters
// before the Victory Conditions Trumbowyg control has initialized (and so doesn't get included).
gLastSavedScenario = unload_params_for_save( false ) ;
// NOTE: The watermark image appears briefly in IE when reloading the page, but not even
// creating the watermark dynamically and removing it when the page unloads fixes it :-(
$("#watermark").fadeIn( 5*1000 ) ;
// notify the test suite
$("body").append( $("") ) ;
// notify the PyQT desktop application
if ( gWebChannelHandler )
gWebChannelHandler.on_app_loaded() ;
// show any startup messages
$.get( gGetStartupMsgsUrl, function( resp ) {
$("body").append( $("") ) ;
show_startup_msgs( resp, "error" ) ;
show_startup_msgs( resp, "warning" ) ;
show_startup_msgs( resp, "info" ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the startup messages:
" + escapeHTML(errorMsg) + "
" ) ;
} ) ;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function init_hotkeys()
{
// initialize hotkeys
jQuery.hotkeys.options.filterInputAcceptingElements = false ;
jQuery.hotkeys.options.filterContentEditable = false ;
jQuery.hotkeys.options.filterTextInputs = false ;
function set_focus_to( tab, $ctrl ) {
if ( $(".ui-widget-overlay").length > 0 )
return ; // nb: a dialog is up
if ( $( ".select2-dropdown" ).length > 0 ) {
// FUDGE! A select2 dropdown is showing, but which one? We just close them all :-/
$( ".app-select2" ).each( function() {
$(this).select2( "close" ) ;
} ) ;
}
var curr_tab = $("#tabs .ui-tabs-active a").attr( "href" ) ;
if ( curr_tab !== tab )
$("#tabs .ui-tabs-nav a[href='"+tab+"']").trigger( "click" ) ;
if ( $ctrl )
$ctrl.focus() ;
}
$(document).bind( "keydown", "alt+c", function() {
set_focus_to( "#tabs-scenario", $("input[name='SCENARIO_NAME']") ) ;
} ) ;
$(document).bind( "keydown", "alt+p", function() {
set_focus_to( "#tabs-scenario", $("select[name='PLAYER_1']") ) ;
} ) ;
$(document).bind( "keydown", "alt+y", function() {
var $elem = $(".trumbowyg-editor[name='VICTORY_CONDITIONS']") ;
if ( $elem.parent().hasClass( "trumbowyg-editor-visible" ) )
set_focus_to( "#tabs-scenario", $elem ) ;
else
set_focus_to( "#tabs-scenario", $elem.parent().find( ".trumbowyg-textarea" ) ) ;
} ) ;
$(document).bind( "keydown", "alt+0", function() {
set_focus_to( "#tabs-scenario", $("input[name='SCENARIO_NAME']") ) ; // nb: for consistency with Alt-1 and Alt-2
} ) ;
$(document).bind( "keydown", "alt+1", function() {
set_focus_to( "#tabs-ob1" ) ;
} ) ;
$(document).bind( "keydown", "alt+2", function() {
set_focus_to( "#tabs-ob2" ) ;
} ) ;
$(document).bind( "keydown", "alt+x", function() {
set_focus_to( "#tabs-extras" ) ;
} ) ;
}
// --------------------------------------------------------------------
function install_template_pack( data )
{
// install the template pack
gTemplatePack = data ;
init_extras() ;
// update the player droplists
var nats = get_sorted_nats() ;
var curSel = {
1: $("select[name='PLAYER_1']").val(),
2: $("select[name='PLAYER_2']").val()
} ;
var buf = [] ;
for ( var i=0 ; i < nats.length ; ++i )
buf.push( "" ) ;
buf = buf.join( "" ) ;
for ( var player_no=1 ; player_no <= 2 ; ++player_no ) {
var $sel = $( "select[name='PLAYER_" + player_no + "']" ) ;
$sel.html( buf ) ;
if ( curSel[player_no] )
$sel.val( curSel[player_no] ) ; // nb: we don't trigger a "change" event
}
// update the OB tab headers
// NOTE: We don't do this while the page is initially loading, since we need the player flags.
if ( gPageLoadStatus.indexOf( "flag-urls" ) === -1 ) {
update_ob_tab_header( 1 ) ;
update_ob_tab_header( 2 ) ;
}
// update the snippet buttons
function update_button( $btn ) {
var template_id = $btn.attr( "data-id" ) ;
if ( template_id.substr( 0, 7 ) === "extras/" )
return ;
if ( template_id.match( /^ob_(vehicles|ordnance).*_[12]$/ ) )
template_id = template_id.substring( 0, template_id.length-2 ) ;
var enable = is_template_available( template_id ) ;
if ( $btn.parent().hasClass( "snippet-control" ) ) {
$btn.parent().controlgroup( enable ? "enable" : "disable" ) ;
// check if there's a corresponding "snippet-width" textbox
var sel = $btn.attr( "data-id" ) ;
if ( sel.match( /_[12]$/ ) )
sel = sel.substr( 0, sel.length-2 ) + "_width" + sel.substr( sel.length-2 ) ;
else
sel += "_width" ;
var $width = $( "input.snippet-width[name='" + sel.toUpperCase() + "']" ) ;
if ( $width.length > 0 ) {
// yup - update it as well
$width.val( "" ).prop( "disabled", !enable ) ;
}
} else
$btn.button( enable ? "enable": "disable" ) ;
}
$( "button.generate" ).each( function() { update_button( $(this) ) ; } ) ;
$( "button.edit-template" ).each( function() { update_button( $(this) ) ; } ) ;
// update the turn track controls
enable = is_template_available( "turn_track" ) ;
$( "select[name='TURN_TRACK_NTURNS']" ).css("color","red").prop( "disabled", !enable ) ;
$( "button#turn-track-settings" ).button( enable ? "enable" : "disable" ) ;
}
// --------------------------------------------------------------------
function on_player_change_with_confirm( player_no )
{
// check if we need to do anything
var $select = $( "select[name='PLAYER_" + player_no + "']" ) ;
if ( $select.val() == $select.data("prev-val") )
return ;
// check if we should confirm this operation
if ( is_player_ob_empty( player_no ) ) {
// nope - just do it
on_player_change( player_no ) ;
} else {
// yup - make it so
ask( "Change player nationality",
"
Do you want to change this player's nationality?
You will lose changes made to their OB.", {
ok: function() { on_player_change( player_no ) ; },
cancel: function() { $select.val( $select.data("prev-val") ).trigger( "change" ) ; },
} ) ;
}
}
function is_player_ob_empty( player_no )
{
// check if the specified player's OB is empty
var is_empty = true ;
$( "#tabs-ob" + player_no + " .sortable" ).each( function() {
if ( $(this).children( "li" ).length > 0 )
is_empty = false ;
} ) ;
return is_empty ;
}
function on_player_change( player_no )
{
// update the UI
var player_nat = update_ob_tab_header( player_no ) ;
update_nationality_specific_buttons( player_no ) ;
$( "input[name='PLAYER_" + player_no + "_DESCRIPTION']" ).val( "" ) ;
updateTrumbowygFlagsDropdown( $( ".param[name='VICTORY_CONDITIONS']" ) ) ;
// show/hide the vehicle/ordnance multi-applicable notes controls
function update_ma_notes_controls( vo_type ) {
if ( ! gTemplatePack.nationalities )
return ;
var show = ( gVehicleOrdnanceNotes[vo_type] && gVehicleOrdnanceNotes[vo_type][player_nat] ) ||
["allied-minor","axis-minor"].indexOf( gTemplatePack.nationalities[ player_nat ].type ) !== -1 ||
player_nat === "free-french" ;
var $fieldset = $( "#tabs-ob" + player_no + " fieldset[name='ob_" + vo_type + "_" + player_no ) ;
$fieldset.find( ".snippets-notes" ).css( "display", show?"block":"none" ) ;
$fieldset.find( "label[for='ob']" ).css( "display", show?"inline-block":"none" ) ;
return show ;
}
var hasVehicles = update_ma_notes_controls( "vehicles" ) ;
var hasOrdnance = update_ma_notes_controls( "ordnance" ) ;
// TO DO: We should also show a button that lets the ob_vehicle/ordnance_note template to be edited.
// reset the OB params
$( "#ob_setups-sortable_" + player_no ).sortable2( "delete-all" ) ;
$("input[name='OB_SETUP_WIDTH_"+player_no+"']").val( "" ) ;
$( "#ob_notes-sortable_" + player_no ).sortable2( "delete-all" ) ;
$( "#ob_vehicles-sortable_" + player_no ).sortable2( "delete-all" ) ;
$("input[name='OB_VEHICLES_WIDTH_"+player_no+"']").val( "" ) ;
$( "#ob_ordnance-sortable_" + player_no ).sortable2( "delete-all" ) ;
$("input[name='OB_ORDNANCE_WIDTH_"+player_no+"']").val( "" ) ;
// disable the vehicles/ordnance panels if none are available
function update_add_vo_button( vo_type ) {
var $panel = $( "#panel-ob_" + vo_type + "_" + player_no ) ;
if ( gVehicleOrdnanceListings[ vo_type ] !== undefined && gVehicleOrdnanceListings[vo_type][ player_nat ] !== undefined ) {
var hasMaNotes = (vo_type === "vehicles" && hasVehicles) || (vo_type === "ordnance" && hasOrdnance) ;
$panel.parent().css( "min-height", hasMaNotes ? "10.7em" : "9em" ) ;
$panel.find( ".footer" ).show() ;
} else {
$panel.parent().css( "min-height", "2.5em" ) ;
$panel.find( ".footer" ).hide() ;
$panel.find( ".sortable-hint" ).html( "There are no " + vo_type + " available." ) ;
}
}
update_add_vo_button( "vehicles" ) ;
update_add_vo_button( "ordnance" ) ;
// reset the player's splitters
updatePlayerOBSplitters( player_no ) ;
gPlayerOBSplitters[ player_no ].forEach( function( splitter ) {
splitter.setSizes( [ 50, 50 ] ) ;
} ) ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function update_ob_tab_header( player_no )
{
// update the OB tab header for the specified player
var player_nat = $( "select[name='PLAYER_" + player_no + "']" ).val() ;
var display_name = get_nationality_display_name( player_nat ) ;
var image_url = make_player_flag_url( player_nat, false ) ;
var $elem = $( "#tabs .ui-tabs-nav a[href='#tabs-ob" + player_no + "']" ) ;
var buf = [] ;
if ( gHasPlayerFlag[ player_nat ] )
buf.push( "" ) ;
buf.push( "" + escapeHTML(display_name) + " OB" ) ;
$elem.html( buf.join("") ) ;
return player_nat ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function update_nationality_specific_buttons( player_no )
{
// initialize
var player_nat = $( "select[name='PLAYER_" + player_no + "']" ).val() ;
var theater = $( "select.param[name='SCENARIO_THEATER']" ).val() ;
if ( theater )
theater = theater.toLowerCase() ;
// hide/show each nationality-specific button
var $elem ;
for ( var button_id in NATIONALITY_SPECIFIC_BUTTONS ) {
var show = false ;
for ( var i=0 ; i < NATIONALITY_SPECIFIC_BUTTONS[button_id].length ; ++i ) {
var nat = NATIONALITY_SPECIFIC_BUTTONS[button_id][i] ;
if ( nat == player_nat )
show = true ;
else if ( nat == player_nat+"@"+theater )
show = true ;
else {
var pos = nat.indexOf( "!" ) ;
if ( pos !== -1 )
show = nat.substr(0,pos) == player_nat && nat.substr(pos+1) !== theater ;
}
}
$elem = $( "#panel-ob_notes_" + player_no + " div.snippet-control[data-id='" + button_id + "']" ) ;
$elem.css( "display", show ? "inline-block" : "none" ) ;
}
// update the CAPABILITIES button
var $btn = $( "button.generate[data-id='nat_caps_" + player_no + "']" ) ;
if ( get_national_capabilities( player_nat ) )
$btn.removeClass( "inactive" ) ;
else
$btn.addClass( "inactive" ) ;
}
// --------------------------------------------------------------------
function on_tab_activate( evt, ui )
{
function set_colors( tab_id, bgd, border ) {
var $elem = $("#tabs .ui-tabs-tab[aria-controls='" + tab_id + "']" ) ;
$elem.css( {
background: bgd,
border: "1px solid "+border,
"border-bottom": "none",
} ) ;
$( "#"+tab_id ).css( "border", "1px solid "+border ) ;
}
// style the tab being de-activated
var tab_id = ui.oldPanel.prop( "id" ) ;
set_colors( tab_id, "#f6f6f6", "#c5c5c5" ) ;
// set the tab being activated
function set_colors_for_player( tab_id, player_no ) {
var colors = get_player_colors( player_no ) ;
set_colors( tab_id, colors[0], colors[2] ) ;
}
tab_id = ui.newPanel.prop( "id" ) ;
var $tab = $( "#"+tab_id ) ;
if ( tab_id === "tabs-ob1" )
set_colors_for_player( tab_id, 1 ) ;
else if ( tab_id === "tabs-ob2" )
set_colors_for_player( tab_id, 2 ) ;
else
set_colors( tab_id, "#ddd", "#ccc" ) ;
// adjust the layout
$tab.find( ".sortable" ).each( function() {
$(this).sortable2( "adjust-entry-heights" ) ;
} ) ;
adjust_footer_vspacers() ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function adjust_footer_vspacers()
{
// FUDGE! Different browsers have different ideas about how big 100% of a fieldset is :-/
// To work-around this, we compare where the bottom of each footer is, relative to
// its parent fieldset, and adjust a vertical spacer element to compensate. Sigh...
$("fieldset").each( function() {
// check if the next fieldset has a footer
$footer = $(this).find( ".footer" ) ;
if ( $footer.length === 0 )
return ;
// check if we've already adjusted this fieldset
if ( $(this).find( ".vspacer" ).length !== 0 )
return ;
// locate the bottom of the fieldset
var $fieldset = $(this) ;
var fieldset_bottom = $fieldset.position().top + $fieldset.height() ;
if ( fieldset_bottom < 0 )
return ;
// locate the bottom of the footer
var footer_bottom = $footer.position().top + $footer.height() ;
var delta = footer_bottom - fieldset_bottom ;
delta -= 4 ;
if ( gWebChannelHandler && $(this).attr("name").indexOf( "ob_" ) === 0 )
delta += 28 ;
// add a vertical spacer after the footer (to push it up a bit)
$footer.after( "
" ) ;
} ) ;
}
// --------------------------------------------------------------------
function handle_escape( evt )
{
// NOTE: Handling Escape is messy, since we could have a modal dialog that opened another modal dialog
// that opened an image gallery, etc. We add a global keydown handler for Escape and try to figure out
// what we should do here. The only requirement is that dialogs set "closeOnEscape".
// NOTE: We ignore ESCAPE if an image gallery is on-screen (we let it handle it).
if ( $( ".lg-outer" ).length > 0 )
return ;
// always close the menu and any notifications
$("#menu input").popmenu( "hide" ) ;
$(".growl-close").each( function() {
$(this).trigger( "click" ) ;
} ) ;
// check if there is a Trumbowyg control full-screen
var $elem = $( ".trumbowyg-fullscreen" ) ;
if ( $elem.length > 0 ) {
// yup - restore it back to normal size
$elem.children( ".trumbowyg-editor" ).trumbowyg( "execCmd", { cmd: "fullscreen" } ) ;
return ;
}
// find the top-most dialog (if any) and close it
var $topmost = findTopmostDialog() ;
if ( $topmost ) {
var $dlg = $topmost.children( ".ui-dialog-content" ) ;
if ( [ "please-wait", "ask", "lfa" ].indexOf( $dlg.attr("id") ) === -1 ) {
// NOTE: We prefer clicking on a Cancel button, so that the dialog has a chance
// to confirm (and perhaps cancel) the operation.
var $btn = $topmost.find( ".ui-dialog-buttonpane button.cancel" ) ;
if ( $btn.length > 0 )
$btn.trigger( "click" ) ;
else
$topmost.children( ".ui-dialog-content" ).dialog( "close" ) ;
}
}
}
function findTopmostDialog()
{
// find the top-most dialog
var $topmost = null ;
$( ".ui-dialog" ).each( function() {
if ( $(this).css( "display" ) != "block" )
return ;
if ( $topmost === null || $(this).css("z-index") > $topmost.css("z-index") )
$topmost = $(this) ;
} ) ;
return $topmost ;
}
// --------------------------------------------------------------------
var gProgramInfoDlgState = null ;
function show_program_info()
{
// show the PROGRAM INFO dialog
var caption = "Program info" ;
if ( gAppConfig.APP_NAME ) {
caption = gAppConfig.APP_NAME ;
if ( gAppConfig.APP_VERSION )
caption += " (" + gAppConfig.APP_VERSION + ")" ;
}
$( "#program-info" ).dialog( {
title: caption,
dialogClass: "program-info",
modal: true,
position: gProgramInfoDlgState ? gProgramInfoDlgState.position : { my: "center", at: "center", of: window },
width: gProgramInfoDlgState ? gProgramInfoDlgState.width : $(window).width() * 0.8,
height: gProgramInfoDlgState ? gProgramInfoDlgState.height : $(window).height() * 0.8,
minWidth: 750,
minHeight: 400,
open: function() {
var $dlg = $(this) ;
$dlg.find( ".content" ).hide() ;
$dlg.find( ".loader" ).show() ;
var url = gGetProgramInfoUrl + "?tz_offset=" + -new Date().getTimezoneOffset() ;
$.get( url, function( resp ) {
$dlg.find( ".loader" ).hide() ;
$dlg.find( ".content" ).html( resp ).fadeIn( 250 ) ;
} ).fail( function( xhr, status, errorMsg ) {
showErrorMsg( "Can't get the program info:
" + escapeHTML(errorMsg) + "
" ) ;
$dlg.dialog( "close" ) ;
} ) ;
},
beforeClose: function() {
gProgramInfoDlgState = getDialogState( $(this) ) ;
},
buttons: {
OK: function() {
$(this).dialog( "close" ) ;
}
},
} ) ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
function show_help()
{
// check if we need to load the HELP tab
var $iframe = $("#tabs-help iframe") ;
if ( ! $iframe.attr( "src" ) ) {
// yup - make it so
// NOTE: We show the help in an iframe so that we can use the same files elsewhere e.g. on the web site or Github.
var url = gHelpUrl + "?version=" + gAppVersion + "&embedded=1&tab=userguide" ;
if ( getUrlParam( "pyqt" ) )
url += "&pyqt=1" ;
$iframe.attr( "src", url ) ;
$("#tabs .ui-tabs-tab[aria-controls='tabs-help']").show() ;
}
// show the HELP tab
$("#tabs a[href='#tabs-help']").click() ;
}