From 95167f48883f13931e5097d230fc9116c9a0ed78 Mon Sep 17 00:00:00 2001 From: Taka Date: Sat, 23 Jul 2022 00:01:19 +1000 Subject: [PATCH] Allow snippets to be generated from the "add/edit simple note" dialog. --- vasl_templates/webapp/static/simple_notes.js | 103 ++++++++++++++---- vasl_templates/webapp/static/snippets.js | 38 ++++--- vasl_templates/webapp/static/utils.js | 3 + vasl_templates/webapp/tests/test_snippets.py | 106 ++++++++++++++++++- 4 files changed, 212 insertions(+), 38 deletions(-) diff --git a/vasl_templates/webapp/static/simple_notes.js b/vasl_templates/webapp/static/simple_notes.js index 2bb75f4..c64d911 100644 --- a/vasl_templates/webapp/static/simple_notes.js +++ b/vasl_templates/webapp/static/simple_notes.js @@ -4,30 +4,73 @@ // -------------------------------------------------------------------- -function add_scenario_note() { _do_edit_simple_note( $("#scenario_notes-sortable"), null, gDefaultScenario._SCENARIO_NOTE_WIDTH ) ; } +function add_scenario_note() { _do_edit_simple_note( "scenario_note", null, $("#scenario_notes-sortable"), null, gDefaultScenario._SCENARIO_NOTE_WIDTH ) ; } function do_add_scenario_note( $sortable2, data ) { _do_add_simple_note($sortable2,data) ; } -function edit_scenario_note( $sortable2, $entry ) { _do_edit_simple_note( $sortable2, $entry, null ) ; } +function edit_scenario_note( $sortable2, $entry ) { _do_edit_simple_note( "scenario_note", null, $sortable2, $entry, null ) ; } -function add_ssr() { _do_edit_simple_note( $("#ssr-sortable"), null, null ) ; } +function add_ssr() { _do_edit_simple_note( "ssr", null, $("#ssr-sortable"), null, null ) ; } function do_add_ssr( $sortable2, data ) { _do_add_simple_note($sortable2,data) ; } -function edit_ssr( $sortable2, $entry ) { _do_edit_simple_note( $sortable2, $entry, null ) ; } +function edit_ssr( $sortable2, $entry ) { _do_edit_simple_note( "ssr", null, $sortable2, $entry, null ) ; } -function add_ob_setup( player_no ) { _do_edit_simple_note( $("#ob_setups-sortable_"+player_no), null, gDefaultScenario._OB_SETUP_WIDTH ) ; } +function add_ob_setup( player_no ) { _do_edit_simple_note( "ob_setup", player_no, $("#ob_setups-sortable_"+player_no), null, gDefaultScenario._OB_SETUP_WIDTH ) ; } function do_add_ob_setup( $sortable2, data ) { _do_add_simple_note($sortable2,data) ; } -function edit_ob_setup( $sortable2, $entry ) { _do_edit_simple_note( $sortable2, $entry, null ) ; } +function edit_ob_setup( $sortable2, $entry ) { _do_edit_simple_note( "ob_setup", get_player_no_for_element($sortable2), $sortable2, $entry, null ) ; } -function add_ob_note( player_no ) { _do_edit_simple_note( $("#ob_notes-sortable_"+player_no), null, gDefaultScenario._OB_NOTE_WIDTH ) ; } +function add_ob_note( player_no ) { _do_edit_simple_note( "ob_note", player_no, $("#ob_notes-sortable_"+player_no), null, gDefaultScenario._OB_NOTE_WIDTH ) ; } function do_add_ob_note( $sortable2, data ) { _do_add_simple_note($sortable2,data) ; } -function edit_ob_note( $sortable2, $entry ) { _do_edit_simple_note( $sortable2, $entry, null ) ; } +function edit_ob_note( $sortable2, $entry ) { _do_edit_simple_note( "ob_note", get_player_no_for_element($sortable2), $sortable2, $entry, null ) ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -function _do_edit_simple_note( $sortable2, $entry, default_width ) +function _do_edit_simple_note( template_id, player_no, $sortable2, $entry, default_width ) { // figure out what we're editing var note_type = _get_note_type_for_sortable( $sortable2 ) ; var note_type0 = note_type.substring( 0, note_type.length-1 ) ; // plural -> singular :-/ + // determine the next available ID + var usedIds = {} ; + $sortable2.children( "li" ).each( function() { + usedIds[ $(this).data("sortable2-data").id ] = true ; + } ) ; + var nextAvailableId = auto_assign_id( usedIds, "id" ) ; + + function makeSimpleSnippet( evt ) { + // initialize + var $btnPane = $( ".ui-dialog.edit-simple_note .ui-dialog-buttonpane" ) ; + var $btn = $btnPane.find( "button.snippet" ) ; + var caption = $caption.val().trim() ; + var width = $width.val().trim() ; + // prepare the template parameters + // NOTE: We don't bother handling the case of an empty caption. + var extraParams = {} ; + if ( template_id === "ssr" ) { + // NOTE: All the SSR's are combined into a single snippet, so it doesn't actually make sense + // to have a snippet button for individual SSR's, but it's convenient. We unload all the SSR's + // from the UI, then update the content for the one being edited (if it already exists), or + // add it to the end of the list (if it's a new one). + var ssrs = unload_ssrs() ; + if ( $entry ) { + // find and update the SSR being edited + $( "#ssr-sortable > li" ).each( function( index ) { + if ( $(this)[0] === $entry[0] ) + ssrs[ index ] = caption ; + } ) ; + } else { + // add the new SSR to the end of the list + ssrs.push( caption ) ; + } + extraParams.SSR = ssrs ; + } else { + // override the template parameters unloaded from the UI with the current values from the dialog + var paramKey = template_id.toUpperCase() ; + extraParams[ paramKey ] = caption ; + extraParams[ paramKey+"_WIDTH" ] = width ; + } + // generate the snippet + generate_snippet( $btn, evt.shiftKey, extraParams ) ; + } + // let the user edit the note var $caption, $width ; $("#edit-simple_note").dialog( { @@ -45,16 +88,35 @@ function _do_edit_simple_note( $sortable2, $entry, default_width ) on_dialog_open( $(this), $caption ) ; add_flag_to_dialog_titlebar( $(this), get_player_no_for_element($sortable2) ) ; var $btn_pane = $(".ui-dialog.edit-simple_note .ui-dialog-buttonpane") ; - $width = $btn_pane.children( "input[name='width']" ) ; + var $btn = $btn_pane.find( "button.snippet" ) ; + $btn.prepend( + $( "" ) + ) ; + $width = $btn_pane.find( "input[name='width']" ) ; if ( $width.length === 0 ) { // create the width controls - $btn_pane.prepend( $(" ") ) ; - $width = $btn_pane.children( "input[name='width']" ) ; + $btn_pane.prepend( $( "
" + + " " + + "
" ) ) ; + $width = $btn_pane.find( "input[name='width']" ) ; } + // tweak the SNIPPETS button so that snippets will work + $btn.data( { id: template_id, "player-no": player_no } ) ; + var snippet_id = template_id ; + if ( player_no ) + snippet_id += "_" + player_no ; + var entryData = $entry ? $entry.data("sortable2-data") : null ; + if ( template_id !== "ssr" ) + snippet_id += "." + (entryData ? entryData.id : nextAvailableId) ; + $btn.data( "snippet-id", snippet_id ) ; + $btn.button( is_template_available( template_id ) ? "enable" : "disable" ) ; // show/hide the width controls (nb: SSR's have a separate width setting that affects all of them) var show = (note_type !== "ssr") ; - $btn_pane.children( "label[for='width']" ).css( "display", show?"inline":"none" ) ; + $btn_pane.find( "label[for='width']" ).css( "display", show?"inline":"none" ) ; $width.css( "display", show?"inline":"none" ) ; + $btn.css( { position: "absolute", left: + show ? $width.offset().left + $width.width() - $btn_pane.find("label[for='width']").offset().left + 25 : 15 + } ) ; // enable auto-dismiss for the dialog var $dlg = $(this) ; $width.keydown( function(evt) { auto_dismiss_dialog( $dlg, evt, "OK" ) ; } ) ; @@ -65,12 +127,12 @@ function _do_edit_simple_note( $sortable2, $entry, default_width ) border: "1px solid "+colors[2] } ) ; // load the dialog - var data = $entry ? $entry.data("sortable2-data") : null ; - $caption.val( data ? data.caption : "" ).focus() ; - $width.val( data ? data.width : default_width ) ; + $caption.val( entryData ? entryData.caption : "" ).focus() ; + $width.val( entryData ? entryData.width : default_width ) ; $(this).height( $(this).height() ) ; // fudge: force the textarea to resize }, buttons: { + Snippet: { text:" Snippet", class: "snippet", click: makeSimpleSnippet }, OK: function() { var caption = $caption.val().trim() ; var width = $width.val().trim() ; @@ -88,13 +150,8 @@ function _do_edit_simple_note( $sortable2, $entry, default_width ) // create a new note if ( caption !== "" ) { data = { caption: caption, width: width } ; - if ( note_type === "scenario_notes" || note_type === "ob_setups" || note_type === "ob_notes" ) { - var usedIds = {} ; - $sortable2.children( "li" ).each( function() { - usedIds[ $(this).data("sortable2-data").id ] = true ; - } ) ; - data.id = auto_assign_id( usedIds, "id" ) ; - } + if ( note_type === "scenario_notes" || note_type === "ob_setups" || note_type === "ob_notes" ) + data.id = nextAvailableId ; $entry = _do_add_simple_note( $sortable2, data ) ; } } diff --git a/vasl_templates/webapp/static/snippets.js b/vasl_templates/webapp/static/snippets.js index 836ee5b..93903d3 100644 --- a/vasl_templates/webapp/static/snippets.js +++ b/vasl_templates/webapp/static/snippets.js @@ -162,15 +162,20 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) } // set the snippet ID - var data ; - if ( ["ob_setup","ob_note","ob_vehicle_note","ob_ordnance_note"].indexOf( template_id ) !== -1 ) { - data = $btn.parent().parent().data( "sortable2-data" ) ; - params.SNIPPET_ID = template_id + "_" + player_no + "." + data.id ; - } else if ( template_id === "scenario_note" ) { - data = $btn.parent().parent().data( "sortable2-data" ) ; - params.SNIPPET_ID = template_id + "." + data.id ; - } else - params.SNIPPET_ID = template_id ; + var snippetId = $btn.data( "snippet-id" ) ; + if ( snippetId ) + params.SNIPPET_ID = snippetId ; + else { + var data ; + if ( ["ob_setup","ob_note","ob_vehicle_note","ob_ordnance_note"].indexOf( template_id ) !== -1 ) { + data = $btn.parent().parent().data( "sortable2-data" ) ; + params.SNIPPET_ID = template_id + "_" + player_no + "." + data.id ; + } else if ( template_id === "scenario_note" ) { + data = $btn.parent().parent().data( "sortable2-data" ) ; + params.SNIPPET_ID = template_id + "." + data.id ; + } else + params.SNIPPET_ID = template_id ; + } if ( player_nat ) params.SNIPPET_ID = player_nat + "/" + params.SNIPPET_ID ; @@ -985,10 +990,7 @@ function unload_snippet_params( unpack_scenario_date, template_id ) } ) ; // 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 ) ; + params.SSR = unload_ssrs() ; // collect the vehicles/ordnance function get_vo( vo_type, player_no, key, show_warnings ) { @@ -1084,6 +1086,16 @@ function unload_snippet_params( unpack_scenario_date, template_id ) return params ; } +function unload_ssrs() +{ + // unload the SSR's + ssrs = [] ; + var data = $( "#ssr-sortable" ).sortable2( "get-entry-data" ) ; + for ( var i=0 ; i < data.length ; ++i ) + ssrs.push( data[i].caption ) ; + return ssrs ; +} + function get_vo_comments( vo_entry, month, year ) { if ( ! vo_entry.comments ) diff --git a/vasl_templates/webapp/static/utils.js b/vasl_templates/webapp/static/utils.js index c7f4d86..a0c35bc 100644 --- a/vasl_templates/webapp/static/utils.js +++ b/vasl_templates/webapp/static/utils.js @@ -77,6 +77,9 @@ function make_player_flag_url( nat, for_snippet, force_local_image ) { function get_player_no_for_element( $elem ) { // get the player that owns the specified element + var playerNo = $elem.data( "player-no" ) ; + if ( playerNo ) + return playerNo ; if ( $.contains( $("#tabs-ob1")[0], $elem[0] ) ) return 1 ; if ( $.contains( $("#tabs-ob2")[0], $elem[0] ) ) diff --git a/vasl_templates/webapp/tests/test_snippets.py b/vasl_templates/webapp/tests/test_snippets.py index c0da5c5..05bdc1b 100644 --- a/vasl_templates/webapp/tests/test_snippets.py +++ b/vasl_templates/webapp/tests/test_snippets.py @@ -1,5 +1,6 @@ """ Test HTML snippet generation. """ +import re import base64 import pytest @@ -9,8 +10,8 @@ from selenium.webdriver.common.keys import Keys from vasl_templates.webapp.tests import pytest_options from vasl_templates.webapp.tests.utils import \ init_webapp, select_tab, find_snippet_buttons, set_template_params, wait_for, wait_for_clipboard, \ - get_stored_msg, set_stored_msg_marker, find_child, find_children, adjust_html, \ - for_each_template, add_simple_note, edit_simple_note, \ + get_stored_msg, set_stored_msg_marker, find_child, find_children, find_sortable_helper, adjust_html, \ + for_each_template, add_simple_note, edit_simple_note, click_dialog_button, \ get_sortable_entry_count, generate_sortable_entry_snippet, drag_sortable_entry_to_trash, \ new_scenario, set_scenario_date from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario @@ -229,6 +230,107 @@ def test_players_snippets( webapp, webdriver ): # --------------------------------------------------------------------- +def test_simple_snippets_from_dialog( webapp, webdriver ): + """Test generating snippets from the "add/edit simple note" dialog.""" + + # initialize + webapp.control_tests.set_data_dir( "{REAL}" ) + init_webapp( webapp, webdriver, scenario_persistence=1 ) + load_scenario( { + "SCENARIO_NOTES": [ + { "id": 1, "caption": "scenario note 1", "width": "111px" }, + { "id": 2, "caption": "scenario note 2" } + ], + "SSR": [ "SSR #1", "SSR #2" ], + "SSR_WIDTH": "222px", + "OB_SETUPS_1": [ + { "id": 1, "caption": "german ob setup #1", "width": "991px" }, + { "id": 2, "caption": "german ob setup #2" } + ], + "OB_NOTES_2": [ + { "id": 1, "caption": "russian setup note #1", "width": "992px" }, + { "id": 2, "caption": "russian setup note #2" } + ] + } ) + + def test_existing_simple_note( sortable, entry_no, expected ): + # edit the simple note + elems = find_children( "li", sortable ) + ActionChains(webdriver).double_click( elems[entry_no] ).perform() + # change the snippet content + elem = find_child( ".ui-dialog.edit-simple_note textarea" ) + elem.clear() + elem.send_keys( "modified content" ) + # change the snippet width + elem = find_child( ".ui-dialog.edit-simple_note input[name='width']" ) + if elem.is_displayed(): + elem.clear() + elem.send_keys( "123px" ) + # generate the snippet + click_dialog_button( "Snippet" ) + if isinstance( expected, str ): + # NOTE: We also check that the snippet ID is correct. + expected = re.compile( ".*".join( [ + "".format( expected ), + "width: 123px", + "modified content", + ] ), re.DOTALL ) + wait_for_clipboard( 2, expected ) + click_dialog_button( "Cancel" ) + + def test_new_simple_note( sortable, expected ) : + # add a new simple note + find_sortable_helper( sortable, "add" ).click() + elem = find_child( ".ui-dialog.edit-simple_note textarea" ) + elem.send_keys( "new content" ) + elem = find_child( ".ui-dialog.edit-simple_note input[name='width']" ) + if elem.is_displayed(): + elem.clear() + elem.send_keys( "789px" ) + # generate the snippet + click_dialog_button( "Snippet" ) + if isinstance( expected, str ): + # NOTE: We also check that the snippet ID is correct. + expected = re.compile( ".*".join( [ + "".format( expected ), + "width: 789px", + "new content", + ] ), re.DOTALL ) + wait_for_clipboard( 2, expected ) + click_dialog_button( "Cancel" ) + + # test scenario notes + sortable = find_child( "#scenario_notes-sortable" ) + test_existing_simple_note( sortable, 1, "scenario_note.2" ) + test_new_simple_note( sortable, "scenario_note.3" ) + + # test SSR's + sortable = find_child( "#ssr-sortable" ) + test_existing_simple_note( sortable, 1, re.compile( ".*".join( [ + "", + "width: 222px", + r'', + ] ), re.DOTALL ) ) + test_new_simple_note( sortable, re.compile( ".*".join( [ + "", + "width: 222px", + r'', + ] ), re.DOTALL ) ) + + # test OB setups + select_tab( "ob1" ) + sortable = find_child( "#ob_setups-sortable_1" ) + test_existing_simple_note( sortable, 1, "german/ob_setup_1.2" ) + test_new_simple_note( sortable, "german/ob_setup_1.3" ) + + # test OB setups + select_tab( "ob2" ) + sortable = find_child( "#ob_notes-sortable_2" ) + test_existing_simple_note( sortable, 1, "russian/ob_note_2.2" ) + test_new_simple_note( sortable, "russian/ob_note_2.3" ) + +# --------------------------------------------------------------------- + def test_edit_templates( webapp, webdriver ): """Test editing templates."""