Made the vehicle/ordnance snippets adapt their layout to the content.

master
Pacman Ghost 5 years ago
parent ff5752fc74
commit 418dc4732b
  1. 1
      vasl_templates/webapp/data/default-template-pack/ob_vo.image.include
  2. 76
      vasl_templates/webapp/data/default-template-pack/ob_vo.j2
  3. 1
      vasl_templates/webapp/data/default-template-pack/ob_vo.name.include
  4. 6
      vasl_templates/webapp/data/default-template-pack/ob_vo.notes.include
  5. 13
      vasl_templates/webapp/snippets.py
  6. 32
      vasl_templates/webapp/static/snippets.js
  7. 9
      vasl_templates/webapp/tests/fixtures/vo-snippet-layout-scenario.json
  8. 21
      vasl_templates/webapp/tests/test_vasl_extensions.py

@ -0,0 +1 @@
{%if vo.image%} <img src="{{vo.image}}"> {%endif%}

@ -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 ; }
</style>
</head>
{# NOTE: We set a narrow width to stop lots of notes making us very wide. #}
<table style="
{%if OB_VO_WIDTH%} width: {{OB_VO_WIDTH}} ; {%endif%}
{%if OB_VO_WIDTH%} width: {{OB_VO_WIDTH}} ; {%else%} width: 1px ; {%endif%}
">
<tr>
<td colspan="2" style="
background: {{OB_COLOR}} ;
border-bottom: 1px solid {{OB_COLOR_2}} ;
padding: 2px 5px ;
font-weight: bold ;
">
{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME}} {{VO_TYPES}}
<td colspan="2" style="
background: {{OB_COLOR}} ;
border-bottom: 1px solid {{OB_COLOR_2}} ;
padding: 2px 5px 0 5px ;
font-weight: bold ;
">
<nobr>{%if PLAYER_FLAG%}<img src="{{PLAYER_FLAG}}">&nbsp;{%endif%}{{PLAYER_NAME|nbsp}}&nbsp;{{VO_TYPES}}</nobr>
{%for vo in OB_VO%}
<tr style="border-bottom:1px dotted #e0e0e0;">
<td valign="top" style="padding:2px 5px 5px;">
<b>{{vo.name}}</b> {%if vo.elite%}&#x24ba;{%endif%}
{%if vo.image%} <br> <img src="{{vo.image}}"> {%endif%}
<div class="note">
{%if vo.extn_id%} &#x2756; {%endif%}
{%if vo.notes%}
{{vo.note_number}}, {{vo.notes | join(", ")}}
{%else%}
{{vo.note_number}}
{%endif%}
</div>
<td valign="top" style="padding:2px 5px;">
{%for cap in vo.capabilities%} <div class="capability"> {{cap}} </div> {%endfor%}
{%for cmnt in vo.comments%} <div class="comment"> {{cmnt}} </div> {%endfor%}
{% if vo.index == 0 %}
<tr>
{%else%}
<tr style="border-top:1px dotted #e0e0e0;">
{%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. #}
<td valign="top" style="padding:5px 5px 2px 5px;">
{{INCLUDE:ob_vo.name}} <br>
{{INCLUDE:ob_vo.image}}
{%else%}
{# NOTE: If the vehicle/ordnance name is long, put it on its own line, and the capabilities underneath. #}
<td colspan="2" valign="top" style="padding:5px 5px 0 5px;">
{{INCLUDE:ob_vo.name}}
<tr>
<td valign="top" style="padding:0 5px 2px 5px;">
{{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. #}
<div class="note" style="margin-top:5px;">
{{INCLUDE:ob_vo.notes}}
</div>
{%endif%}
<td valign="top" style="padding:5px 5px 2px 5px;">
{%for cap in vo.capabilities%} <div class="capability"> {{cap|nobr}} </div> {%endfor%}
{%for cmnt in vo.comments%} <div class="comment"> {{cmnt}} </div> {%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). #}
<tr>
<td class="note" valign="top" colspan="2" style="padding:2px 5px;">
{{INCLUDE:ob_vo.notes}}
{%endif%}
{%endfor%}
</table>

@ -0,0 +1 @@
<nobr><b>{{vo.name}}</b>&nbsp;{%if vo.elite%}&#x24ba;{%endif%}</nobr>

@ -0,0 +1,6 @@
{%if vo.extn_id%} &#x2756; {%endif%}
{%if vo.notes%}
{{vo.note_number}}, {{vo.notes | join(", ",true)}}
{%else%}
{{vo.note_number}}
{%endif%}

@ -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
# ---------------------------------------------------------------------

@ -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 &nbsp; than <nobr> :shrug:
var vals = [] ;
for ( var i=0 ; i < snippet.length ; ++i )
vals.push( nbsp ? strReplaceAll( snippet[i], " ", "&nbsp;" ) : snippet[i] ) ;
return vals.join( sep ) ;
},
nobr: function( snippet ) { return "<nobr>" + snippet + "</nobr>" ; },
nbsp: function( snippet ) { return strReplaceAll( snippet, " ", "&nbsp;" ) ; },
} ,
} ) ;
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" )

@ -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"
}

@ -265,14 +265,12 @@ def test_bfp_extensions( webapp, webdriver ):
btn.click()
wait_for_clipboard( 2, re.compile(
'Type 97A CHI-HA'
'.+<div class="note">'
'.+<div class="note"'
'.+8\u2020, B\u2020<sup>1</sup>, C\u2020<sup>2</sup>'
'.+</div>'
r'.+M3A1 Scout Car\(a\)'
'.+<div class="note">'
'.+<div class="note"'
'.+&#x2756;'
'.+17, A, C, AllM 34\u2020<sup>2</sup>, Jp A\u2020<sup>1</sup>, Ch F\u2020'
'.+</div>',
'.+17, A, C, AllM&nbsp;34\u2020<sup>2</sup>, Jp&nbsp;A\u2020<sup>1</sup>, Ch&nbsp;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'
'.+<div class="note">'
'.+<div class="note"'
'.+5\u2020, C\u2020<sup>2</sup>, F\u2020<sup>1</sup>, G, N, Y'
'.+</div>'
r'.+\bM5A1F\b'
'.+<div class="note">'
'.+<div class="note"'
'.+&#x2756;'
'.+5\u2020, US C\u2020<sup>2</sup>, US F\u2020<sup>1</sup>, US G, US N, US Y, C'
'.+</div>'
'.+5\u2020, US&nbsp;C\u2020<sup>2</sup>, US&nbsp;F\u2020<sup>1</sup>, US&nbsp;G, US&nbsp;N, US&nbsp;Y, C'
r'.+\bM5A1C\b'
'.+<div class="note">'
'.+<div class="note"'
'.+&#x2756;'
'.+5\u2020, US C\u2020<sup>2</sup>, US F\u2020<sup>1</sup>, US G, US N, US Y, A, B'
'.+</div>',
'.+5\u2020, US&nbsp;C\u2020<sup>2</sup>, US&nbsp;F\u2020<sup>1</sup>, US&nbsp;G, US&nbsp;N, US&nbsp;Y, A, B',
re.DOTALL
) )

Loading…
Cancel
Save