|
|
|
@ -1,6 +1,8 @@ |
|
|
|
|
gEditHtmlTextboxDlgState = null ; |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
function initTrumbowyg( $ctrl, buttons, $parentDlg ) |
|
|
|
|
{ |
|
|
|
|
// initialize
|
|
|
|
|
var nats = get_sorted_nats().filter( |
|
|
|
@ -14,7 +16,7 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
// from the WYSIWYG control to the raw HTML textarea, it doesn't really help, since manipulating
|
|
|
|
|
// the content in the <textarea> directly doesn't work, we need to use Trumbowyg's "html" API, and that
|
|
|
|
|
// works from the WYSIWYG control.
|
|
|
|
|
$elem.trumbowyg( { |
|
|
|
|
$ctrl.trumbowyg( { |
|
|
|
|
btnsDef: { |
|
|
|
|
format: { |
|
|
|
|
dropdown: gAppConfig.trumbowyg[ "format-options" ], |
|
|
|
@ -55,19 +57,19 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
}, |
|
|
|
|
tagsToRemove: gAppConfig.trumbowyg[ "tag-blacklist" ], |
|
|
|
|
} ) ; |
|
|
|
|
var $parent = $elem.parent() ; |
|
|
|
|
var $parent = $ctrl.parent() ; |
|
|
|
|
var $btnPane = $parent.find( ".trumbowyg-button-pane" ) ; |
|
|
|
|
var $textarea = $parent.find( ".trumbowyg-textarea" ) ; |
|
|
|
|
|
|
|
|
|
// update the flags dropdown for the current players
|
|
|
|
|
if ( $btnPane.find( ".trumbowyg-flags-button" ).length > 0 ) |
|
|
|
|
updateTrumbowygFlagsDropdown( $elem ) ; |
|
|
|
|
updateTrumbowygFlagsDropdown( $ctrl ) ; |
|
|
|
|
|
|
|
|
|
// prepare for our jQuery event handlers
|
|
|
|
|
var eventHandlers = $elem.data( "eventHandlers" ) ; |
|
|
|
|
var eventHandlers = $ctrl.data( "eventHandlers" ) ; |
|
|
|
|
if ( ! eventHandlers ) { |
|
|
|
|
eventHandlers = new jQueryHandlers() ; |
|
|
|
|
$elem.data( "eventHandlers", eventHandlers ) ; |
|
|
|
|
$ctrl.data( "eventHandlers", eventHandlers ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// allow a hotkey to toggle the WYSIWYG editor
|
|
|
|
@ -78,12 +80,12 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
function onKeyDown( evt ) { |
|
|
|
|
// check for Ctrl-M
|
|
|
|
|
if ( evt.keyCode == 77 && evt.ctrlKey ) { |
|
|
|
|
$elem.trumbowyg( "toggle" ) ; |
|
|
|
|
$ctrl.trumbowyg( "toggle" ) ; |
|
|
|
|
setTimeout( function() { |
|
|
|
|
if ( $elem.parent().hasClass( "trumbowyg-editor-visible" ) ) |
|
|
|
|
$elem.focus() ; |
|
|
|
|
if ( $ctrl.parent().hasClass( "trumbowyg-editor-visible" ) ) |
|
|
|
|
$ctrl.focus() ; |
|
|
|
|
else |
|
|
|
|
$elem.parent().find( ".trumbowyg-textarea" ).focus() ; |
|
|
|
|
$ctrl.parent().find( ".trumbowyg-textarea" ).focus() ; |
|
|
|
|
}, 20 ) ; |
|
|
|
|
evt.preventDefault() ; |
|
|
|
|
return ; |
|
|
|
@ -92,7 +94,7 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
if ( $parentDlg ) |
|
|
|
|
auto_dismiss_dialog( $parentDlg, evt, "OK" ) ; |
|
|
|
|
} |
|
|
|
|
eventHandlers.addHandler( $elem, "keydown", onKeyDown ) ; |
|
|
|
|
eventHandlers.addHandler( $ctrl, "keydown", onKeyDown ) ; |
|
|
|
|
eventHandlers.addHandler( $textarea, "keydown", onKeyDown ) ; |
|
|
|
|
// FUDGE! There should be spaces around the +, but this causes the tooltip to wrap on Windows :-/
|
|
|
|
|
$btnPane.find( ".trumbowyg-viewHTML-button" ).attr( "title", "View HTML (Ctrl+M)" ) ; |
|
|
|
@ -102,14 +104,23 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
var resizeObserver = new ResizeObserver( function( entries ) { |
|
|
|
|
// FUDGE! Couldn't get Trumbowyg to sit nicely inside a flexbox, so we set the height dynamically :-/
|
|
|
|
|
var height = "calc(100% - " + $btnPane.height() + "px)" ; |
|
|
|
|
$elem.css( { height: height } ) ; |
|
|
|
|
$ctrl.css( { height: height } ) ; |
|
|
|
|
$textarea.css( { height: height } ) ; |
|
|
|
|
// limit the height of dropdown's
|
|
|
|
|
if ( $parentDlg ) { |
|
|
|
|
$parent.find( ".trumbowyg-dropdown" ).css( { |
|
|
|
|
"max-height": $elem.height() + 5 |
|
|
|
|
"max-height": $ctrl.height() + 5 |
|
|
|
|
} ) ; |
|
|
|
|
} |
|
|
|
|
// FUDGE! We also need to stop the HTML textboxes that are in a flexbox from expanding out
|
|
|
|
|
// if they contain long words with no spaces. The layout still isn't quite right, but this
|
|
|
|
|
// isn't something that will happen often, so we just live with it :-/
|
|
|
|
|
// NOTE: Things work when the SCENARIO panel gets wider, but not when it narrows (because
|
|
|
|
|
// the HTML textbox has expanded out, and doesn't want to narrow when the parent element
|
|
|
|
|
// narrows, and so the panel doesn't narrow). We work-around this by checking the width
|
|
|
|
|
// of the SCENARIO NOTES panel, which will always be the same width as the SCENARIO panel.
|
|
|
|
|
var $panel = $( "fieldset[name='scenario_notes']" ) ; |
|
|
|
|
$( ".row" ).css( "max-width", $panel.width() ) ; |
|
|
|
|
} ) ; |
|
|
|
|
resizeObserver.observe( $parent[0] ) ; |
|
|
|
|
$parent.data( "resizeObserver", resizeObserver ) ; |
|
|
|
@ -118,23 +129,32 @@ function initTrumbowyg( $elem, buttons, $parentDlg ) |
|
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
|
|
|
|
function destroyTrumbowyg( $elem ) |
|
|
|
|
function destroyTrumbowyg( $ctrl ) |
|
|
|
|
{ |
|
|
|
|
// destroy the Trumbowyg control and clean up
|
|
|
|
|
var eventHandlers = $elem.data( "eventHandlers" ) ; |
|
|
|
|
var eventHandlers = $ctrl.data( "eventHandlers" ) ; |
|
|
|
|
if ( eventHandlers ) { |
|
|
|
|
eventHandlers.cleanUp() ; |
|
|
|
|
$elem.removeData( "eventHandlers" ) ; |
|
|
|
|
$ctrl.removeData( "eventHandlers" ) ; |
|
|
|
|
} |
|
|
|
|
$elem.trumbowyg( "destroy" ) ; |
|
|
|
|
$ctrl.trumbowyg( "destroy" ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function unloadTrumbowyg( $elem, removeFirstPara ) |
|
|
|
|
function resetTrumbowyg( $ctrl ) |
|
|
|
|
{ |
|
|
|
|
// reset the Trumbowyg control
|
|
|
|
|
if ( $ctrl.parent().hasClass( "trumbowyg-fullscreen" ) ) |
|
|
|
|
$ctrl.trumbowyg( "execCmd", { cmd: "fullscreen" } ) ; |
|
|
|
|
if ( $ctrl.parent().hasClass( "trumbowyg-editor-hidden" ) ) |
|
|
|
|
$ctrl.trumbowyg( "toggle" ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function unloadTrumbowyg( $ctrl, removeFirstPara ) |
|
|
|
|
{ |
|
|
|
|
// unload the Trumbowyg control
|
|
|
|
|
var val = $elem.trumbowyg( "html" ).trim() ; |
|
|
|
|
var val = $ctrl.trumbowyg( "html" ).trim() ; |
|
|
|
|
|
|
|
|
|
// FUDGE! Trumbowyg really wants to wrap everything in <p> blocks, but this causes problems
|
|
|
|
|
// since many of the templates are expecting a bit of plain old text, not blocks of content
|
|
|
|
@ -163,20 +183,20 @@ function unloadTrumbowyg( $elem, removeFirstPara ) |
|
|
|
|
function initVictoryConditionsTrumbowyg() |
|
|
|
|
{ |
|
|
|
|
// initialize the Victory Conditions Trumbowyg control
|
|
|
|
|
var $elem = $( "div.param[name='VICTORY_CONDITIONS']" ) ; |
|
|
|
|
initTrumbowyg( $elem, gAppConfig.trumbowyg["victory-conditions"], null ) ; |
|
|
|
|
var $ctrl = $( "div.param[name='VICTORY_CONDITIONS']" ) ; |
|
|
|
|
initTrumbowyg( $ctrl, gAppConfig.trumbowyg["victory-conditions"], null ) ; |
|
|
|
|
if ( gPendingVictoryConditions ) |
|
|
|
|
$elem.trumbowyg( "html", gPendingVictoryConditions ) ; |
|
|
|
|
$ctrl.trumbowyg( "html", gPendingVictoryConditions ) ; |
|
|
|
|
|
|
|
|
|
// FUDGE! For some reason, we need to do this :shrug:
|
|
|
|
|
$elem.trumbowyg().on( "tbwopenfullscreen", function() { |
|
|
|
|
$ctrl.trumbowyg().on( "tbwopenfullscreen", function() { |
|
|
|
|
$( "#menu" ).hide() ; |
|
|
|
|
} ).on( "tbwclosefullscreen", function() { |
|
|
|
|
$( "#menu" ).show() ; |
|
|
|
|
} ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function updateTrumbowygFlagsDropdown( $elem ) |
|
|
|
|
function updateTrumbowygFlagsDropdown( $ctrl ) |
|
|
|
|
{ |
|
|
|
|
// FUDGE! For convenience, we show the flags for the current players at the start of the dropdown list,
|
|
|
|
|
// and while we can do this in makeDropdown() in our plugin, this only happens once for the Victory Conditions
|
|
|
|
@ -184,7 +204,7 @@ function updateTrumbowygFlagsDropdown( $elem ) |
|
|
|
|
// so we do it by modifying the DOM.
|
|
|
|
|
|
|
|
|
|
// initialize
|
|
|
|
|
var trumbowyg = $elem.data( "trumbowyg" ) ; |
|
|
|
|
var trumbowyg = $ctrl.data( "trumbowyg" ) ; |
|
|
|
|
if ( ! trumbowyg ) |
|
|
|
|
return ; |
|
|
|
|
var plugin = trumbowyg.o.plugins.flags ; |
|
|
|
@ -192,7 +212,7 @@ function updateTrumbowygFlagsDropdown( $elem ) |
|
|
|
|
var nat2 = get_player_nat( 2 ) ; |
|
|
|
|
|
|
|
|
|
// locate the flags dropdown
|
|
|
|
|
$dropdown = $elem.parent().find( ".trumbowyg-dropdown-flags" ) ; |
|
|
|
|
$dropdown = $ctrl.parent().find( ".trumbowyg-dropdown-flags" ) ; |
|
|
|
|
if ( $dropdown.length === 0 ) |
|
|
|
|
return ; |
|
|
|
|
|
|
|
|
@ -221,6 +241,122 @@ function updateTrumbowygFlagsDropdown( $elem ) |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
var gNextHtmlTextboxId = 1 ; |
|
|
|
|
|
|
|
|
|
function initHtmlTextbox( $ctrl, objName, small ) |
|
|
|
|
{ |
|
|
|
|
// NOTE: It's tricky designing a UX that allows single-line textbox's to be editable as HTML.
|
|
|
|
|
// Most of the time, fields such as scenario name and ID will be plain-text, so we present
|
|
|
|
|
// a contenteditable div (so that the user can make simple edits directly), with a discreet option
|
|
|
|
|
// to open a full WYSIWYG editor (in a dialog), if they want more complex content.
|
|
|
|
|
|
|
|
|
|
function onActivate( evt ) { |
|
|
|
|
// show the "edit HTML" dialog
|
|
|
|
|
onEditHtmlTextbox( $ctrl, objName ) ; |
|
|
|
|
evt.preventDefault() ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// make the HTML textbox editable
|
|
|
|
|
var htbId = gNextHtmlTextboxId ++ ; |
|
|
|
|
$ctrl.attr( { |
|
|
|
|
contenteditable: true, |
|
|
|
|
"data-htb-id": htbId, |
|
|
|
|
} ) ; |
|
|
|
|
$ctrl.click( function( evt ) { |
|
|
|
|
// check for Alt-Click (to open the edit dialog)
|
|
|
|
|
// NOTE: We can't use Ctrl-Click, since that it used to delete caps/comments in the "edit v/o" dialog.
|
|
|
|
|
// NOTE: Alt-Click doesn't trigger an even on Linux, but Ctrl-Alt-Click and Shift-Alt-Click work... :-/
|
|
|
|
|
if ( evt.altKey ) |
|
|
|
|
onActivate( evt ) ; |
|
|
|
|
} ).keydown( function( evt ) { |
|
|
|
|
if ( evt.keyCode == 77 && evt.ctrlKey ) { |
|
|
|
|
onActivate( evt ) ; // nb: Ctrl-M opens the "edit HTML" dialog
|
|
|
|
|
evt.preventDefault() ; |
|
|
|
|
} else if ( evt.keyCode == $.ui.keyCode.ENTER ) |
|
|
|
|
evt.preventDefault() ; // nb: disable ENTER
|
|
|
|
|
} ) ; |
|
|
|
|
|
|
|
|
|
// add an icon to open the "edit html textbox" dialog
|
|
|
|
|
var paramName = $ctrl.attr( "name" ) ; |
|
|
|
|
var $img = $( "<svg class='edit-html-textbox' data-htb-id='" + htbId + "'>" + |
|
|
|
|
"<use xlink:href='#trumbowyg-view-html'></use>" + |
|
|
|
|
"<title> Edit HTML (Ctrl-M) </title>" + |
|
|
|
|
"</svg>" |
|
|
|
|
).css( { |
|
|
|
|
width: "10px", height: "15px", |
|
|
|
|
position: "relative", top: small?"-2px":"-4px", right: "13px", |
|
|
|
|
"margin-right": "-10px", |
|
|
|
|
opacity: 0.5, |
|
|
|
|
cursor: "pointer", |
|
|
|
|
} ) ; |
|
|
|
|
$ctrl.after( $img ) ; |
|
|
|
|
$img.click( function( evt ) { |
|
|
|
|
onActivate( evt ) ; |
|
|
|
|
} ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function onEditHtmlTextbox( $ctrl, objName ) { |
|
|
|
|
|
|
|
|
|
// initialize
|
|
|
|
|
var paramName = $ctrl.attr( "name" ) ; // nb: might be undefined e.g. vehicle/ordnance capabilities/comments
|
|
|
|
|
var dlgTitle = "Edit " + (objName || "HTML") ; |
|
|
|
|
var $content, origVal ; |
|
|
|
|
|
|
|
|
|
function unloadData() { |
|
|
|
|
// unload the HTML content
|
|
|
|
|
return unloadTrumbowyg( $content, true ).trim() ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// show the dialog
|
|
|
|
|
var $dlg = $( "#edit-html_textbox-dialog" ).dialog( { |
|
|
|
|
dialogClass: "edit-html_textbox", |
|
|
|
|
title: dlgTitle, |
|
|
|
|
modal: true, |
|
|
|
|
closeOnEscape: false, |
|
|
|
|
position: gEditHtmlTextboxDlgState ? gEditHtmlTextboxDlgState.position : { my: "center", at: "center", of: window }, |
|
|
|
|
width: gEditHtmlTextboxDlgState ? gEditHtmlTextboxDlgState.width : $(window).width() * 0.5, |
|
|
|
|
height: gEditHtmlTextboxDlgState ? gEditHtmlTextboxDlgState.height : Math.max( $(window).height() * 0.5, 325 ), |
|
|
|
|
minWidth: 680, minHeight: 280, |
|
|
|
|
create: function() { |
|
|
|
|
init_dialog( $(this), "OK", true ) ; |
|
|
|
|
}, |
|
|
|
|
open: function() { |
|
|
|
|
$content = $(this).find( "div.content" ) ; |
|
|
|
|
on_dialog_open( $(this), $content ) ; |
|
|
|
|
// initialize the Trumbowyg HTML editor
|
|
|
|
|
if ( ! gEditHtmlTextboxDlgState ) // nb: check if this is the first time the dialog has been opened
|
|
|
|
|
initTrumbowyg( $content, gAppConfig.trumbowyg["html-textbox-dialog"], $(this) ) ; |
|
|
|
|
else { |
|
|
|
|
// always start non-maximized, and in HTML mode
|
|
|
|
|
resetTrumbowyg( $content ) ; |
|
|
|
|
} |
|
|
|
|
// load the dialog
|
|
|
|
|
$content.trumbowyg( "html", $ctrl.html().trim() ) ; |
|
|
|
|
origVal = unloadData() ; |
|
|
|
|
}, |
|
|
|
|
beforeClose: function() { |
|
|
|
|
gEditHtmlTextboxDlgState = getDialogState( $(this) ) ; |
|
|
|
|
}, |
|
|
|
|
buttons: { |
|
|
|
|
OK: function() { |
|
|
|
|
$ctrl.html( unloadData() ) ; |
|
|
|
|
$(this).dialog( "close" ) ; |
|
|
|
|
}, |
|
|
|
|
Cancel: function() { |
|
|
|
|
if ( unloadData() != origVal ) { |
|
|
|
|
ask( dlgTitle, "Discard your changes?", { |
|
|
|
|
ok: function() { $dlg.dialog( "close" ) ; }, |
|
|
|
|
} ) ; |
|
|
|
|
return ; |
|
|
|
|
} |
|
|
|
|
$(this).dialog( "close" ) ; |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} ) ; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function sanitizeParams( params ) |
|
|
|
|
{ |
|
|
|
|
// recursively sanitize the scenario params
|
|
|
|
@ -230,11 +366,16 @@ function sanitizeParams( params ) |
|
|
|
|
if ( typeof params[key] === "object" ) |
|
|
|
|
sanitizeParams( params[key] ) ; |
|
|
|
|
else if ( typeof params[key] === "string" ) { |
|
|
|
|
var sanitized = DOMPurify.sanitize( |
|
|
|
|
params[key], |
|
|
|
|
{ USE_PROFILES: { html: true } } |
|
|
|
|
) ; |
|
|
|
|
params[key] = sanitized ; |
|
|
|
|
params[key] = sanitizeHTML( params[key] ) ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function sanitizeHTML( val ) |
|
|
|
|
{ |
|
|
|
|
// sanitize the HTML value
|
|
|
|
|
return DOMPurify.sanitize( |
|
|
|
|
val, |
|
|
|
|
{ USE_PROFILES: { html: true } } |
|
|
|
|
) ; |
|
|
|
|
} |
|
|
|
|