// NOTE: These fields aren't mandatory in the sense that snippet generation will fail // if they're not set, but they're really, really, really expected to be there. var _MANDATORY_PARAMS = { scenario: { "SCENARIO_NAME": "scenario name", "SCENARIO_DATE": "scenario date" }, } ; var _MONTH_NAMES = [ // nb: we assume English :-/ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] ; var _DAY_OF_MONTH_POSTFIXES = { // nb: we assume English :-/ 0: "th", 1: "st", 2: "nd", 3: "rd", 4: "th", 5: "th", 6: "th", 7: "th", 8: "th", 9: "th", 10: "th", 11: "th", 12: "th", 13: "th" } ; var gDefaultScenario = null ; var gLastSavedScenario = null ; var gLastSavedScenarioFilename = null; // -------------------------------------------------------------------- function generate_snippet( $btn, extra_params ) { // extract the scenario date components var params = {} ; var scenario_date = $("input[name='SCENARIO_DATE']").datepicker( "getDate" ) ; if ( scenario_date ) { params.SCENARIO_DAY_OF_MONTH = scenario_date.getDate() ; var postfix ; if ( params.SCENARIO_DAY_OF_MONTH in _DAY_OF_MONTH_POSTFIXES ) postfix = _DAY_OF_MONTH_POSTFIXES[ params.SCENARIO_DAY_OF_MONTH ] ; else postfix = _DAY_OF_MONTH_POSTFIXES[ params.SCENARIO_DAY_OF_MONTH % 10 ] ; params.SCENARIO_DAY_OF_MONTH_POSTFIX = params.SCENARIO_DAY_OF_MONTH + postfix ; params.SCENARIO_MONTH = 1 + scenario_date.getMonth() ; params.SCENARIO_MONTH_NAME = _MONTH_NAMES[scenario_date.getMonth()] ; params.SCENARIO_YEAR = scenario_date.getFullYear() ; } // unload the template parameters var template_id = $btn.data( "id" ) ; unload_snippet_params( params, true ) ; // set player-specific parameters var curr_tab = $("#tabs .ui-tabs-active a").attr( "href" ) ; var colors ; if ( curr_tab === "#tabs-ob1" ) { params.PLAYER_NAME = get_nationality_display_name( params.PLAYER_1 ) ; colors = get_player_colors( 1 ) ; params.OB_COLOR = colors[0] ; params.OB_COLOR_2 = colors[2] ; } else if ( curr_tab === "#tabs-ob2" ) { params.PLAYER_NAME = get_nationality_display_name( params.PLAYER_2 ) ; colors = get_player_colors( 2 ) ; params.OB_COLOR = colors[0] ; params.OB_COLOR_2 = colors[2] ; } // set player-specific parameters if ( template_id == "ob_vehicles_1" ) { template_id = "ob_vehicles" ; params.OB_VEHICLES = params.OB_VEHICLES_1 ; params.OB_VEHICLES_WIDTH = params.OB_VEHICLES_WIDTH_1 ; } else if ( template_id == "ob_vehicles_2" ) { template_id = "ob_vehicles" ; params.OB_VEHICLES = params.OB_VEHICLES_2 ; params.OB_VEHICLES_WIDTH = params.OB_VEHICLES_WIDTH_2 ; } if ( template_id == "ob_ordnance_1" ) { template_id = "ob_ordnance" ; params.OB_ORDNANCE = params.OB_ORDNANCE_1 ; params.OB_ORDNANCE_WIDTH = params.OB_ORDNANCE_WIDTH_1 ; } else if ( template_id == "ob_ordnance_2" ) { template_id = "ob_ordnance" ; params.OB_ORDNANCE = params.OB_ORDNANCE_2 ; params.OB_ORDNANCE_WIDTH = params.OB_ORDNANCE_WIDTH_2 ; } // include the player display names params.PLAYER_1_NAME = get_nationality_display_name( params.PLAYER_1 ) ; params.PLAYER_2_NAME = get_nationality_display_name( params.PLAYER_2 ) ; // generate PF parameters if ( params.SCENARIO_YEAR < 1944 || (params.SCENARIO_YEAR == 1944 && params.SCENARIO_MONTH < 6) ) params.PF_RANGE = 1 ; else if ( params.SCENARIO_YEAR == 1944 ) params.PF_RANGE = 2 ; else params.PF_RANGE = 3 ; if ( params.SCENARIO_YEAR < 1943 || (params.SCENARIO_YEAR == 1943 && params.SCENARIO_MONTH <= 9) ) { params.PF_CHECK_DRM = "+1" ; params.PF_CHECK_DR = 2 ; } else if ( params.SCENARIO_YEAR >= 1945 ) { params.PF_CHECK_DRM = "-1" ; params.PF_CHECK_DR = 4 ; } else { params.PF_CHECK_DRM = "" ; params.PF_CHECK_DR = 3 ; } // generate BAZ parameters if ( params.SCENARIO_YEAR >= 1945 ) { params.BAZ_TYPE = 45 ; params.BAZ_BREAKDOWN = 11 ; params.BAZ_TOKILL = 16 ; params.BAZ_WP = 6 ; params.BAZ_RANGE = 5 ; } else if ( params.SCENARIO_YEAR >= 1944 ) { params.BAZ_TYPE = 44 ; params.BAZ_BREAKDOWN = 11 ; params.BAZ_TOKILL = 16 ; params.BAZ_RANGE = 4 ; } else if ( params.SCENARIO_YEAR == 1943 || (params.SCENARIO_YEAR == 1942 && params.SCENARIO_MONTH >= 11) ) { params.BAZ_TYPE = 43 ; params.BAZ_BREAKDOWN = 10 ; params.BAZ_TOKILL = 13 ; params.BAZ_RANGE = 4 ; } // check for mandatory parameters if ( template_id in _MANDATORY_PARAMS ) { var missing_params = [] ; for ( var param_id in _MANDATORY_PARAMS[template_id] ) { if ( ! (param_id in params && params[param_id].length > 0) ) missing_params.push( _MANDATORY_PARAMS[template_id][param_id] ) ; } if ( missing_params.length > 0 ) showWarningMsg( makeBulletListMsg( "Missing parameters:", missing_params, li_class="pre" ) ) ; } // check for date-specific parameters if ( template_id === "pf" && ! is_pf_available() ) showWarningMsg( "PF are only available after September 1943." ) ; if ( template_id === "psk" && ! is_psk_available() ) showWarningMsg( "PSK are only available after September 1943." ) ; if ( template_id === "baz" && ! is_baz_available() ) showWarningMsg( "BAZ are only available from November 1942." ) ; if ( template_id === "atmm" && ! is_atmm_available() ) showWarningMsg( "ATMM are only available from 1944." ) ; // add in any extra parameters if ( extra_params ) $.extend( true, params, extra_params ) ; // check that the players have different nationalities if ( params.PLAYER_1 === params.PLAYER_2 ) showWarningMsg( "Both players have the same nationality!" ) ; // get the template to generate the snippet from var templ = get_template( template_id ) ; if ( templ === null ) return ; var func ; try { func = jinja.compile( templ ).render ; } catch( ex ) { showErrorMsg( "Can't compile template:
" + escapeHTML(ex) + "
" ) ; return ; } // process the template var val ; try { // NOTE: While it's generally not a good idea to disable auto-escaping, the whole purpose // of this application is to generate HTML snippets, and so virtually every single // template parameter would have to be piped through the "safe" filter :-/ We never render // any of the generated HTML, so any risk exists only when the user pastes the HTML snippet // into a VASL scenario, which uses an ancient HTML engine (with probably no Javascript)... val = func( params, { autoEscape: false, filters: { join: function(val,sep) { return val.join(sep) ; } } , } ) ; val = val.trim() ; } catch( ex ) { showErrorMsg( "Can't process template: " + template_id + "
" + escapeHTML(ex) + "
" ) ; return ; } try { copyToClipboard( val ) ; } catch( ex ) { showErrorMsg( "Can't copy to the clipboard:
" + escapeHTML(ex) + "
" ) ; return ; } showInfoMsg( "The HTML snippet has been copied to the clipboard." ) ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function unload_snippet_params( params, check_date_capabilities ) { // collect all the template parameters add_param = function($elem) { params[ $elem.attr("name") ] = $elem.val() ; } ; $("input[type='text'].param").each( function() { add_param($(this)) ; } ) ; $("textarea.param").each( function() { add_param($(this)) ; } ) ; $("select.param").each( function() { add_param($(this)) ; } ) ; // collect the SSR's params.SSR = [] ; var data = $("#ssr-sortable").sortable2( "get-entry-data" ) ; for ( var i=0 ; i < data.length ; ++i ) params.SSR.push( data[i].caption ) ; // collect the vehicles/ordnance function get_vo( vo_type, player_no, key ) { var $sortable2 = $( "#ob_" + vo_type + "-sortable_" + player_no ) ; var objs = [] ; $sortable2.children( "li" ).each( function() { var vo_entry = $(this).data( "sortable2-data" ).vo_entry ; var vo_image_id = $(this).data( "sortable2-data" ).vo_image_id ; var obj = { id: vo_entry.id, image_id: (vo_image_id !== null) ? vo_image_id[0]+"/"+vo_image_id[1] : null, name: vo_entry.name, note_number: vo_entry.note_number, notes: vo_entry.notes } ; if ( gUserSettings["include-vasl-images-in-snippets"] ) { var url = get_vo_image_url( vo_entry, vo_image_id ) ; if ( url ) obj.image = APP_URL_BASE + url ; } if ( vo_entry.no_radio ) obj.no_radio = vo_entry.no_radio ; if ( vo_entry.no_if ) { obj.no_if = "no IF" ; if ( typeof(vo_entry.no_if) === "string" ) { // nb: only for the French B1-bis :-/ var no_if = vo_entry.no_if ; if ( no_if.substring(no_if.length-1) == "\u2020" ) obj.no_if += ""+no_if.substring(0,no_if.length-1)+"\u2020" ; else obj.no_if += ""+no_if+"" ; } } // NOTE: It would be nice to have a Jinja2 filter that inserted the raw capabilities or selected // the correct one for the scenario date e.g. // {% for c in veh.capabilities %} {{c|selcap}} {%endif%}} // but the problem is that if a capability is not available, we want nothing to appear, // but by the time the filter gets called, it's too late :-( Instead, we provide a "raw_capabilities" // parameter that people can use in their templates - ugly, but probably not something that will // get a lot of use :-/ var nat = params[ "PLAYER_"+player_no ] ; var capabilities = make_capabilities( vo_entry, nat, params.SCENARIO_THEATER, params.SCENARIO_YEAR, params.SCENARIO_MONTH, check_date_capabilities, false ) ; if ( capabilities ) obj.capabilities = capabilities ; capabilities = make_capabilities( vo_entry, nat, params.SCENARIO_THEATER, params.SCENARIO_YEAR, params.SCENARIO_MONTH, check_date_capabilities, true ) ; if ( capabilities ) obj.raw_capabilities = capabilities ; var crew_survival = make_crew_survival( vo_entry ) ; if ( crew_survival ) obj.crew_survival = crew_survival ; objs.push( obj ) ; } ) ; if ( objs.length > 0 ) params[key] = objs ; } get_vo( "vehicles", 1, "OB_VEHICLES_1" ) ; get_vo( "vehicles", 2, "OB_VEHICLES_2" ) ; get_vo( "ordnance", 1, "OB_ORDNANCE_1" ) ; get_vo( "ordnance", 2, "OB_ORDNANCE_2" ) ; return params ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function make_capabilities( vo_entry, nat, scenario_theater, scenario_year, scenario_month, check_date_capabilities, raw ) { var capabilities = [] ; // extract the static capabilities var i ; if ( "capabilities" in vo_entry ) { for ( i=0 ; i < vo_entry.capabilities.length ; ++i ) capabilities.push( vo_entry.capabilities[i] ) ; } // extract the variable capabilities if ( "capabilities2" in vo_entry ) { var indeterminate_caps=[], unexpected_caps=[], invalid_caps=[] ; for ( var key in vo_entry.capabilities2 ) { // check if the capability is dependent on the scenario date if ( !( vo_entry.capabilities2[key] instanceof Array ) ) { capabilities.push( key + vo_entry.capabilities2[key] ) ; continue ; } // check for LF if ( key == "LF" ) { var caps = $.extend( true, [], vo_entry.capabilities2[key] ) ; if ( caps[caps.length-1] == "\u2020" ) { caps.pop() ; capabilities.push( "LF\u2020" ) ; } else capabilities.push( "LF" ) ; capabilities[ capabilities.length-1 ] += " [" + caps.join(", ") + "]" ; continue ; } if ( $.inArray( key, ["HE","AP","A","D","C","H","B","s","sM","sD","sN","WP","IR","Towed"] ) === -1 ) { unexpected_caps.push( key ) ; continue ; } // check if we should return the raw capability, or select the one for the scenario date if ( ! scenario_year ) { indeterminate_caps.push( key ) ; raw = true ; } if ( raw ) { capabilities.push( make_raw_capability( key, vo_entry.capabilities2[key] ) ) ; } else { var cap = _select_capability_by_date( vo_entry.capabilities2[key], nat, scenario_theater, scenario_year, scenario_month ) ; if ( cap === null ) continue ; if ( cap == "" ) { invalid_caps.push( vo_entry.name + ": " + key + ": " + vo_entry.capabilities2[key] ) ; continue ; } capabilities.push( key + cap ) ; } } // check if there were any capabilities not set if ( check_date_capabilities && indeterminate_caps.length > 0 ) { showWarningMsg( makeBulletListMsg( "Can't determine capabilities without a scenario year:", indeterminate_caps ) ) ; } // check if there were any unexpected capabilities if ( unexpected_caps.length > 0 ) { showErrorMsg( makeBulletListMsg( "Internal error: unexpected date-based capabilities:", unexpected_caps ) ) ; } // check if there were any invalid capabilities if ( invalid_caps.length > 0 ) { showErrorMsg( makeBulletListMsg( "Internal error: invalid date-based capabilities:", invalid_caps ) ) ; } } // extract any other capabilities if ( "capabilities_other" in vo_entry ) { for ( i=0 ; i < vo_entry.capabilities_other.length ; ++i ) capabilities.push( vo_entry.capabilities_other[i] ) ; } // include damage points (for Landing Craft) if ( "damage_points" in vo_entry ) capabilities.push( "DP " + vo_entry.damage_points ) ; return capabilities.length > 0 ? capabilities : null ; } function make_raw_capability( name, capability ) { // NOTE: The capability can sometimes not have a # e.g. Tetrarch CS has a s# of "ref1". if ( capability[0] === null ) { var cap = capability[1] ; if ( cap.match( /^\d\+?$/ ) ) cap = "" + cap + "" ; return name + cap ; } // generate the raw capability string var buf = [ name ] ; for ( var i=0 ; i < capability.length ; ++i ) { if ( typeof(capability[i]) === "string" ) buf.push( capability[i] ) ; else { if ( capability[i][0] ) buf.push( escapeHTML( capability[i][0] ) ) ; if ( capability[i][1] ) buf.push( "", escapeHTML( capability[i][1] ), "" ) ; } } return buf.join( "" ) ; } function _select_capability_by_date( capabilities, nat, scenario_theater, scenario_year, scenario_month ) { // NOTE: The capability can sometimes not have a number e.g. Tetrarch CS s# = "ref1", Stuart III(a) = "HE(4+)" var timestamp, val ; if ( capabilities[0] === null ) { timestamp = capabilities[1] ; if ( timestamp.match( /^\d\+?$/ ) ) { val = _check_capability_timestamp( capabilities, timestamp, nat, scenario_theater, scenario_year, scenario_month ) ; if ( val === "" ) return null ; return ""; } return timestamp ; } // initialize capabilities = capabilities.slice() ; var ref = has_ref( capabilities ) ; // check all the capability timestamps var retval = "???" ; for ( var i=0 ; i < capabilities.length ; ++i ) { timestamp = capabilities[i][1].toString() ; val = _check_capability_timestamp( capabilities[i], timestamp, nat, scenario_theater, scenario_year, scenario_month ) ; if ( val === "" ) return val ; if ( val === "" ) continue ; retval = val ; } if ( retval === "???" ) return null ; if ( retval === null ) retval = "" ; // nb: this can happen for IR return ref ? retval+ref : retval ; } function _check_capability_timestamp( capabilities, timestamp, nat, scenario_theater, scenario_year, scenario_month ) { var MONTH_NAMES = { F:2, J:6, A:8, S:9, N:11 } ; // check for a theater flag if ( timestamp.substring( timestamp.length-1 ) === "E" ) { if ( scenario_theater != "ETO" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-1 ) ; } if ( timestamp.substring( timestamp.length-1 ) === "P" ) { if ( scenario_theater != "PTO" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-1 ) ; } if ( timestamp.substring( timestamp.length-1 ) === "B" ) { if ( scenario_theater != "BURMA" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-1 ) ; } if ( timestamp.substring( timestamp.length-1 ) === "R" ) { if ( nat != "romanian" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-1 ) ; } if ( timestamp.substring( timestamp.length-2 ) === "CS" ) { if ( nat != "croatian" && nat != "slovakian" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-2 ) ; } if ( timestamp.substring( timestamp.length-1 ) === "S" ) { if ( nat != "slovakian" ) return "" ; timestamp = timestamp.substring( 0, timestamp.length-1 ) ; } // remove any trailing "+" (FIXME! What does it even mean? Doesn't make sense :-/) if ( timestamp.substring( timestamp.length-1 ) == "+" ) timestamp = timestamp.substring( 0, timestamp.length-1 ) ; // check if there is anything left if ( ! timestamp ) { // nope - the capability is always available return capabilities[0] ; } // parse the month/year the capability becomes available var month = MONTH_NAMES[ timestamp.substring(0,1) ] ; if ( month ) timestamp = timestamp.substring( 1 ) ; if ( /^\d$/.test( timestamp ) ) { // this is a single year timestamp = parseInt( timestamp ) ; // check if the capabilitity is available if ( scenario_year > 1940 + timestamp ) return capabilities[0] ; else if ( scenario_year == 1940 + timestamp ) { if( !month || scenario_month >= month ) return capabilities[0] ; } } else if ( /^\d-\d$/.test( timestamp ) ) { // this is a range of years var timestamp1 = parseInt( timestamp[0] ) ; var timestamp2 = parseInt( timestamp[timestamp.length-1] ) ; // check if the capabilitity is available if ( 1940+timestamp1 <= scenario_year && scenario_year <= 1940+timestamp2 ) return capabilities[0] ; } else return "" ; return "" ; } function has_ref( val ) { var last = val[ val.length-1 ] ; if ( typeof(last) === "string" && last.match( /^\u2020(\d<\/sup>)?$/ ) ) { val.pop() ; return last ; } return null ; } function make_crew_survival( vo_entry ) { function make_cs_string( prefix, val ) { if ( val.length === 2 && val[0] === null && val[1] === "\u2020" ) return "\u2020" ; else return prefix + " " + val ; } // check if the vehicle has a crew survival field var crew_survival = null ; if ( "CS#" in vo_entry ) crew_survival = make_cs_string( "CS", vo_entry["CS#"] ) ; else if ( "cs#" in vo_entry ) crew_survival = make_cs_string( "cs", vo_entry["cs#"] ) ; if ( crew_survival === null ) return null ; // check if the vehicle is subject to brew up var pos = crew_survival.indexOf( ":brewup" ) ; if ( pos !== -1 ) crew_survival = crew_survival.substring(0,pos) + " (brew up)" + crew_survival.substring(pos+7) ; return crew_survival ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function get_template( template_id ) { // get the specified template if ( template_id in gTemplatePack.templates ) return gTemplatePack.templates[template_id] ; showErrorMsg( "Unknown template: " + escapeHTML(template_id) + "" ) ; return null ; } // -------------------------------------------------------------------- function edit_template( template_id ) { // get the specified template if ( template_id.substring(0,12) == "ob_ordnance_" ) template_id = "ob_ordnance" ; else if ( template_id.substring(0,12) == "ob_vehicles_" ) template_id = "ob_vehicles" ; var template = get_template( template_id ) ; if ( template === null ) return ; function on_template_change() { // install the new template gTemplatePack.templates[template_id] = $("#edit-template textarea").val() ; } // let the user edit the template $("#edit-template textarea").val( template ) ; $("#edit-template").dialog( { dialogClass: "edit-template", title: "Editing template: " + escapeHTML(template_id), modal: false, minWidth: 400, minHeight: 200, create: function() { init_dialog( $(this), "Close", true ) ; }, open: function() { on_dialog_open( $(this) ) ; $(this).height( $(this).height() ) ; // fudge: force the textarea to resize $("#edit-template textarea").change( on_template_change ) ; }, close: function() { $("#edit-template textarea").off( "change", on_template_change ) ; }, buttons: { Close: function() { $(this).dialog( "close" ) ; }, }, } ) ; } // -------------------------------------------------------------------- function on_load_scenario() { // check if the scenario is dirty if ( ! is_scenario_dirty() ) do_on_load_scenario() ; else { // yup - confirm the operation ask( "Load scenario", "

This scenario has been changed.

Do you want load another scenario, and lose your changes?", { ok: do_on_load_scenario, cancel: function() {}, } ) ; } function do_on_load_scenario() { // FOR TESTING PORPOISES! We can't control a file upload from Selenium (since // the browser will use native controls), so we get the data from a