diff --git a/vasl_templates/webapp/data/default-template-pack/ob_vo.image.include b/vasl_templates/webapp/data/default-template-pack/ob_vo.image.include new file mode 100644 index 0000000..826ca32 --- /dev/null +++ b/vasl_templates/webapp/data/default-template-pack/ob_vo.image.include @@ -0,0 +1 @@ +{%if vo.image%} {%endif%} diff --git a/vasl_templates/webapp/data/default-template-pack/ob_vo.j2 b/vasl_templates/webapp/data/default-template-pack/ob_vo.j2 index f367680..3b7a8d1 100644 --- a/vasl_templates/webapp/data/default-template-pack/ob_vo.j2 +++ b/vasl_templates/webapp/data/default-template-pack/ob_vo.j2 @@ -6,40 +6,68 @@ td { margin: 0 ; padding: 0 ; } sup { font-size: 75% ; } -.note { margin-top: 2px ; font-size: 90% ; font-style: italic ; color: #808080 ; } +.note { font-size: 90% ; font-style: italic ; color: #808080 ; } +.capability {} .comment { font-size: 90% ; font-style: italic ; color: #404040 ; } +{# NOTE: We set a narrow width to stop lots of notes making us very wide. #} - - +{%else%} + +{%endif%} + +{% if vo.name_len <= MAX_VO_NAME_LEN %} +{# NOTE: If the vehicle/ordnance name is short, put the capabilities to the right of it. #} + + +
- {%if PLAYER_FLAG%} {%endif%}{{PLAYER_NAME}} {{VO_TYPES}} + + {%if PLAYER_FLAG%} {%endif%}{{PLAYER_NAME|nbsp}} {{VO_TYPES}} {%for vo in OB_VO%} -
- {{vo.name}} {%if vo.elite%}Ⓔ{%endif%} - {%if vo.image%}
{%endif%} -
- {%if vo.extn_id%} ❖ {%endif%} - {%if vo.notes%} - {{vo.note_number}}, {{vo.notes | join(", ")}} - {%else%} - {{vo.note_number}} - {%endif%} -
-
- {%for cap in vo.capabilities%}
{{cap}}
{%endfor%} - {%for cmnt in vo.comments%}
{{cmnt}}
{%endfor%} + +{% if vo.index == 0 %} +
+ {{INCLUDE:ob_vo.name}}
+ {{INCLUDE:ob_vo.image}} +{%else%} +{# NOTE: If the vehicle/ordnance name is long, put it on its own line, and the capabilities underneath. #} +
+ {{INCLUDE:ob_vo.name}} +
+ {{INCLUDE:ob_vo.image}} +{%endif%} + +{% if vo.capabilities_len >= 5 or !vo.image %} +{# NOTE: If there are a lot of capabilities, tuck the note number & notes under the image. #} +{# But if there is no image, we always do this, and squeeze them in to the left of the capabilities. #} +
+ {{INCLUDE:ob_vo.notes}} +
+{%endif%} + +
+ {%for cap in vo.capabilities%}
{{cap|nobr}}
{%endfor%} + {%for cmnt in vo.comments%}
{{cmnt}}
{%endfor%} + +{% if vo.capabilities_len < 5 and vo.image %} +{# NOTE: If there are only a few capabilities, let the note number & notes spread full-width. #} +{# But if there is no image, we never do this (see above). #} +
+ {{INCLUDE:ob_vo.notes}} +{%endif%} + {%endfor%}
diff --git a/vasl_templates/webapp/data/default-template-pack/ob_vo.name.include b/vasl_templates/webapp/data/default-template-pack/ob_vo.name.include new file mode 100644 index 0000000..4cf922d --- /dev/null +++ b/vasl_templates/webapp/data/default-template-pack/ob_vo.name.include @@ -0,0 +1 @@ +{{vo.name}} {%if vo.elite%}Ⓔ{%endif%} diff --git a/vasl_templates/webapp/data/default-template-pack/ob_vo.notes.include b/vasl_templates/webapp/data/default-template-pack/ob_vo.notes.include new file mode 100644 index 0000000..ea543b8 --- /dev/null +++ b/vasl_templates/webapp/data/default-template-pack/ob_vo.notes.include @@ -0,0 +1,6 @@ +{%if vo.extn_id%} ❖ {%endif%} +{%if vo.notes%} + {{vo.note_number}}, {{vo.notes | join(", ",true)}} +{%else%} + {{vo.note_number}} + {%endif%} diff --git a/vasl_templates/webapp/snippets.py b/vasl_templates/webapp/snippets.py index c07f72f..6b89914 100644 --- a/vasl_templates/webapp/snippets.py +++ b/vasl_templates/webapp/snippets.py @@ -33,7 +33,7 @@ def get_template_pack(): load_default_template_pack() return jsonify( globvars.template_pack ) -def load_default_template_pack(): +def load_default_template_pack(): #pylint: disable=too-many-locals """Load the default template pack.""" # initialize @@ -52,7 +52,7 @@ def load_default_template_pack(): # can add to them, or modify existing ones, but not remove them. dname = os.path.join( base_dir, "extras" ) if os.path.isdir( dname ): - _, extra_templates, _ = _do_get_template_pack( dname ) + _, extra_templates, _, _ = _do_get_template_pack( dname ) for key,val in extra_templates.items(): data["templates"]["extras/"+key] = val @@ -67,10 +67,11 @@ def load_default_template_pack(): # check if we're loading the template pack from a directory if os.path.isdir( dname ): # yup - return the files in it - nat, templates, css =_do_get_template_pack( dname ) + nat, templates, css, includes =_do_get_template_pack( dname ) data["nationalities"].update( nat ) data["templates"] = templates data["css"] = css + data["includes"] = includes else: # extract the template pack files from the specified ZIP file if not os.path.isfile( dname ): @@ -98,7 +99,7 @@ def _do_get_template_pack( dname ): dname = os.path.abspath( dname ) if not os.path.isdir( dname ): abort( 404 ) - nationalities, templates, css = {}, {}, {} + nationalities, templates, css, includes = {}, {}, {}, {} for root,_,fnames in os.walk(dname): for fname in fnames: # add the next file to the results @@ -130,7 +131,9 @@ def _do_get_template_pack( dname ): templates[fname_stem] = fp.read() elif extn == ".css": css[fname_stem] = fp.read() - return nationalities, templates, css + elif extn == ".include": + includes[fname_stem] = fp.read() + return nationalities, templates, css, includes # --------------------------------------------------------------------- diff --git a/vasl_templates/webapp/static/snippets.js b/vasl_templates/webapp/static/snippets.js index 4192c6c..7d26e4f 100644 --- a/vasl_templates/webapp/static/snippets.js +++ b/vasl_templates/webapp/static/snippets.js @@ -157,6 +157,12 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) params.VO_TYPE = "Ordnance" ; params.VO_TYPES = "Ordnance" ; } + if ( params.PLAYER_NAME && params.VO_TYPE ) { + // NOTE: How long the vehicle/ordnance name can be before we force it to be full-width + // depends on how wide the snippet is, which depends on the nationality + vehicle/ordnance type. + var max_cap_width = 5 ; // FIXME! We should really calculate this :-/ + params.MAX_VO_NAME_LEN = ( params.PLAYER_NAME.length + 1 + params.VO_TYPE.length ) - max_cap_width ; + } // set player-specific parameters if ( template_id === "ob_vehicles_1" ) { @@ -367,6 +373,8 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) var templ = get_template( template_id, true ) ; if ( templ === null ) return { content: "[error: can't find template]" } ; + for ( var incl in gTemplatePack.includes ) + templ = strReplaceAll( templ, "{{INCLUDE:"+incl+"}}", gTemplatePack.includes[incl] ) ; var func ; try { func = jinja.compile( templ ).render ; @@ -387,7 +395,15 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) snippet = func( params, { autoEscape: false, filters: { - join: function(snippet,sep) { return snippet.join(sep) ; } + join: function( snippet, sep, nbsp ) { + // nb: we get better results using   than :shrug: + var vals = [] ; + for ( var i=0 ; i < snippet.length ; ++i ) + vals.push( nbsp ? strReplaceAll( snippet[i], " ", " " ) : snippet[i] ) ; + return vals.join( sep ) ; + }, + nobr: function( snippet ) { return "" + snippet + "" ; }, + nbsp: function( snippet ) { return strReplaceAll( snippet, " ", " " ) ; }, } , } ) ; snippet = snippet.trim() ; @@ -706,16 +722,18 @@ function unload_snippet_params( unpack_scenario_date, template_id ) function get_vo( vo_type, player_no, key, show_warnings ) { var $sortable2 = $( "#ob_" + vo_type + "-sortable_" + player_no ) ; var objs = [] ; - $sortable2.children( "li" ).each( function() { + $sortable2.children( "li" ).each( function( index ) { var data = $(this).data( "sortable2-data" ) ; var vo_entry = data.vo_entry ; var vo_image_id = data.vo_image_id ; var elite = data.elite ; var obj = { + index: index, id: vo_entry.id, seq_id: data.id, image_id: (vo_image_id !== null) ? vo_image_id[0]+"/"+vo_image_id[1] : null, name: vo_entry.name, + name_len: vo_entry.name.length, note_number: vo_entry.note_number, notes: vo_entry.notes } ; @@ -737,6 +755,7 @@ function unload_snippet_params( unpack_scenario_date, template_id ) var capabilities = $(this).data( "sortable2-data" ).custom_capabilities ; if ( capabilities ) { obj.capabilities = capabilities ; + obj.capabilities_len = capabilities.length ; obj.custom_capabilities = capabilities.slice() ; } else { // NOTE: We don't show warnings here; if there's something wrong, @@ -747,8 +766,10 @@ function unload_snippet_params( unpack_scenario_date, template_id ) params.SCENARIO_THEATER, params.SCENARIO_YEAR, params.SCENARIO_MONTH, false ) ; - if ( capabilities ) + if ( capabilities ) { obj.capabilities = capabilities ; + obj.capabilities_len = capabilities.length ; + } } capabilities = make_capabilities( true, @@ -1693,6 +1714,7 @@ function do_load_template_pack( fname, data ) nationalities: $.extend( true, {}, gDefaultTemplatePack.nationalities ), templates: {}, css: {}, + includes: {}, } ; // NOTE: We always start with the default extras templates; user-defined template packs @@ -1717,7 +1739,7 @@ function do_load_template_pack( fname, data ) return ; } var extn = getFilenameExtn( fname ) ; - if ( [".j2",".css"].indexOf( extn ) === -1 ) { + if ( [".j2",".css",".include"].indexOf( extn ) === -1 ) { invalid_filename_extns.push( fname ) ; return ; } @@ -1725,6 +1747,8 @@ function do_load_template_pack( fname, data ) var template_id = fname.substring( 0, fname.length-extn.length ).toLowerCase() ; if ( extn === ".css" ) template_pack.css[template_id] = data ; + else if ( extn === ".include" ) + template_pack.includes[template_id] = data ; else if ( template_id === "ob_vo" ) template_pack.templates.ob_vehicles = template_pack.templates.ob_ordnance = data ; else if ( template_id === "ob_vo_note" ) diff --git a/vasl_templates/webapp/tests/fixtures/vo-snippet-layout-scenario.json b/vasl_templates/webapp/tests/fixtures/vo-snippet-layout-scenario.json new file mode 100644 index 0000000..aa93eb8 --- /dev/null +++ b/vasl_templates/webapp/tests/fixtures/vo-snippet-layout-scenario.json @@ -0,0 +1,9 @@ +{ + +"_comment_": "This isn't used by any of the tests, but is useful for checking the layout of vehicle/ordnance snippets.", +"_comment_": "NOTE: Don't forget to test with and without counter images in the snippets!", +"_comment_": "NOTE: Also try testing with really short/long nationality names.", + +"SCENARIO_NAME":"","SCENARIO_ID":"","SCENARIO_LOCATION":"","SCENARIO_DATE":"1945-01-01","SCENARIO_WIDTH":"","VICTORY_CONDITIONS_WIDTH":"300px","SSR_WIDTH":"300px","OB_VEHICLES_WIDTH_1":"","OB_VEHICLES_MA_NOTES_WIDTH_1":"300px","OB_ORDNANCE_WIDTH_1":"","OB_ORDNANCE_MA_NOTES_WIDTH_1":"300px","OB_VEHICLES_WIDTH_2":"","OB_VEHICLES_MA_NOTES_WIDTH_2":"300px","OB_ORDNANCE_WIDTH_2":"","OB_ORDNANCE_MA_NOTES_WIDTH_2":"300px","VICTORY_CONDITIONS":"","SCENARIO_THEATER":"ETO","PLAYER_1":"german","PLAYER_1_ELR":"5","PLAYER_1_SAN":"2","PLAYER_2":"american","PLAYER_2_ELR":"5","PLAYER_2_SAN":"2","SSR":[],"OB_VEHICLES_1":[{"id":"ge/v:030","seq_id":2,"name":"PzKpfw VG"},{"id":"ge/v:803","seq_id":1,"name":"PzKpfw II Bridgelayer"},{"id":"ge/v:030","seq_id":3,"name":"PzKpfw VG","custom_capabilities":["sN7","CS 6","ABC 1","DEF 2","GHI 3","JKL 4"]},{"id":"ge/v:803","seq_id":4,"name":"PzKpfw II Bridgelayer","custom_capabilities":["sD6","CS 2","ABC 1","DEF 2","GHI 3","JKL 4"]}],"OB_VEHICLES_2":[{"id":"am/v:850","seq_id":1,"name":"M5A1C"},{"id":"am/v:857","seq_id":2,"name":"M4C(105)","custom_capabilities":["H10","WP10","s8","sM9","CS 6","custom cap"],"elite":true}],"OB_ORDNANCE_1":[{"id":"ge/o:029","seq_id":1,"name":"8.8cm FlaK 18 o. 36"}],"OB_ORDNANCE_2":[{"id":"am/o:021","seq_id":1,"name":"M51 Multiple .50-cal MG Carriage"}],"SCENARIO_NOTES":[],"OB_SETUPS_1":[{"caption":"Pz VG: short name, short caps, short notes","id":1,"width":""},{"caption":"Pz Bridgelayer: long name, short caps, short notes","id":2,"width":""},{"caption":"Pz VG: short name, long caps, short notes","width":"","id":3},{"caption":"Pz Bridgelayer: long name, long caps, short notes","width":"","id":4}],"OB_SETUPS_2":[{"caption":"M5A1C: short name, short caps, long notes","id":1,"width":""},{"caption":"M4C(105): short name, long caps, short notes","id":2,"width":""}],"OB_NOTES_1":[{"caption":"8.8cm FlaK: long name, long caps, short notes","id":1,"width":""}],"OB_NOTES_2":[{"caption":"M51: long name, long caps, no notes","id":1,"width":""}],"_app_version":"v0.9","_last_update_time":"2019-03-30T02:23:49.602Z","_creation_time":"2019-03-29T10:14:41.804Z" + +} diff --git a/vasl_templates/webapp/tests/test_vasl_extensions.py b/vasl_templates/webapp/tests/test_vasl_extensions.py index 8927ec8..a2a4b17 100644 --- a/vasl_templates/webapp/tests/test_vasl_extensions.py +++ b/vasl_templates/webapp/tests/test_vasl_extensions.py @@ -265,14 +265,12 @@ def test_bfp_extensions( webapp, webdriver ): btn.click() wait_for_clipboard( 2, re.compile( 'Type 97A CHI-HA' - '.+
' + '.+
1, C\u20202' - '.+
' r'.+M3A1 Scout Car\(a\)' - '.+
' + '.+
2, Jp A\u20201, Ch F\u2020' - '.+
', + '.+17, A, C, AllM 34\u20202, Jp A\u20201, Ch F\u2020', re.DOTALL ) ) @@ -334,19 +332,16 @@ def test_bfp_extensions2( webapp, webdriver ): btn.click() wait_for_clipboard( 2, re.compile( r'\bM5A1\b' - '.+
' + '.+
2, F\u20201, G, N, Y' - '.+
' r'.+\bM5A1F\b' - '.+
' + '.+
2, US F\u20201, US G, US N, US Y, C' - '.+
' + '.+5\u2020, US C\u20202, US F\u20201, US G, US N, US Y, C' r'.+\bM5A1C\b' - '.+
' + '.+
2, US F\u20201, US G, US N, US Y, A, B' - '.+
', + '.+5\u2020, US C\u20202, US F\u20201, US G, US N, US Y, A, B', re.DOTALL ) )