Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F1431585
Dialog.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
49 KB
Referenced Files
None
Subscribers
None
Dialog.js
View Options
const
LanguageSearchWidget
=
require
(
'./widgets/LanguageSearchWidget.js'
),
Metrics
=
require
(
'./Metrics.js'
),
Model
=
require
(
'ext.templateDataGenerator.data'
).
Model
,
ParamImportWidget
=
require
(
'./widgets/ParamImportWidget.js'
),
ParamSelectWidget
=
require
(
'./widgets/ParamSelectWidget.js'
),
ParamWidget
=
require
(
'./widgets/ParamWidget.js'
);
/**
* TemplateData Dialog
*
* @class
* @extends OO.ui.ProcessDialog
*
* @constructor
* @param {Object} config Dialog configuration object
*
* @external LanguageResultWidget
*/
function
Dialog
(
config
)
{
// Parent constructor
Dialog
.
super
.
call
(
this
,
config
);
this
.
model
=
null
;
this
.
modified
=
false
;
this
.
language
=
null
;
this
.
availableLanguages
=
[];
this
.
selectedParamKey
=
''
;
this
.
propInputs
=
{};
this
.
propFieldLayout
=
{};
this
.
isSetup
=
false
;
this
.
mapsCache
=
undefined
;
this
.
descriptionChanged
=
false
;
this
.
paramsReordered
=
false
;
this
.
paramPropertyChangeTracking
=
{};
// Initialize
this
.
$element
.
addClass
(
'tdg-templateDataDialog'
);
}
/* Inheritance */
OO
.
inheritClass
(
Dialog
,
OO
.
ui
.
ProcessDialog
);
/* Static properties */
Dialog
.
static
.
name
=
'TemplateDataDialog'
;
Dialog
.
static
.
title
=
mw
.
msg
(
'templatedata-modal-title'
);
Dialog
.
static
.
size
=
'large'
;
Dialog
.
static
.
actions
=
[
{
action
:
'apply'
,
label
:
mw
.
msg
(
'templatedata-modal-button-apply'
),
flags
:
[
'primary'
,
'progressive'
],
modes
:
'list'
},
{
action
:
'done'
,
label
:
mw
.
msg
(
'templatedata-modal-button-done'
),
flags
:
[
'primary'
,
'progressive'
],
modes
:
[
'edit'
,
'maps'
]
},
{
action
:
'add'
,
label
:
mw
.
msg
(
'templatedata-modal-button-addparam'
),
icon
:
'add'
,
flags
:
[
'progressive'
],
modes
:
'list'
},
{
action
:
'delete'
,
label
:
mw
.
msg
(
'templatedata-modal-button-delparam'
),
modes
:
'edit'
,
flags
:
'destructive'
},
{
action
:
'cancel'
,
label
:
mw
.
msg
(
'templatedata-modal-button-cancel'
),
modes
:
'maps'
,
flags
:
'destructive'
},
{
label
:
mw
.
msg
(
'templatedata-modal-button-cancel'
),
flags
:
[
'safe'
,
'close'
],
modes
:
[
'list'
,
'error'
]
},
{
action
:
'back'
,
label
:
mw
.
msg
(
'templatedata-modal-button-back'
),
flags
:
[
'safe'
,
'back'
],
modes
:
[
'language'
,
'add'
]
}
];
/**
* Initialize window contents.
*
* The first time the window is opened, #initialize is called so that changes to the window that
* will persist between openings can be made. See #getSetupProcess for a way to make changes each
* time the window opens.
*
* @throws {Error} If not attached to a manager
* @chainable
*/
Dialog
.
prototype
.
initialize
=
function
()
{
// Parent method
Dialog
.
super
.
prototype
.
initialize
.
call
(
this
);
this
.
$spinner
=
$
(
'<div>'
).
addClass
(
'tdg-spinner'
).
text
(
'working...'
);
this
.
$body
.
append
(
this
.
$spinner
);
this
.
noticeMessage
=
new
OO
.
ui
.
MessageWidget
()
.
toggle
(
false
);
this
.
panels
=
new
OO
.
ui
.
StackLayout
(
{
continuous
:
false
}
);
this
.
listParamsPanel
=
new
OO
.
ui
.
PanelLayout
(
{
padded
:
true
,
scrollable
:
true
}
);
this
.
editParamPanel
=
new
OO
.
ui
.
PanelLayout
(
{
padded
:
true
}
);
this
.
languagePanel
=
new
OO
.
ui
.
PanelLayout
();
this
.
addParamPanel
=
new
OO
.
ui
.
PanelLayout
(
{
padded
:
true
}
);
this
.
editMapsPanel
=
new
OO
.
ui
.
PanelLayout
();
// Language panel
this
.
newLanguageSearch
=
new
LanguageSearchWidget
();
// Add parameter panel
this
.
newParamInput
=
new
OO
.
ui
.
TextInputWidget
(
{
placeholder
:
mw
.
msg
(
'templatedata-modal-placeholder-paramkey'
)
}
);
this
.
addParamButton
=
new
OO
.
ui
.
ButtonWidget
(
{
label
:
mw
.
msg
(
'templatedata-modal-button-addparam'
),
flags
:
[
'progressive'
,
'primary'
],
disabled
:
true
}
);
const
addParamFieldlayout
=
new
OO
.
ui
.
ActionFieldLayout
(
this
.
newParamInput
,
this
.
addParamButton
,
{
align
:
'top'
,
label
:
mw
.
msg
(
'templatedata-modal-title-addparam'
)
}
);
// Maps panel
this
.
templateMapsInput
=
new
OO
.
ui
.
MultilineTextInputWidget
(
{
classes
:
[
'mw-templateData-template-maps-input mw-editfont-monospace'
],
autosize
:
true
,
rows
:
this
.
getBodyHeight
()
/
22.5
,
maxRows
:
this
.
getBodyHeight
()
/
22.5
,
placeholder
:
mw
.
msg
(
'templatedata-modal-placeholder-mapinfo'
),
scrollable
:
true
}
);
this
.
removeMapButton
=
new
OO
.
ui
.
ButtonWidget
(
{
classes
:
[
'mw-templateData-template-remove-map-button'
],
label
:
mw
.
msg
(
'templatedata-modal-button-removemap'
),
icon
:
'trash'
,
flags
:
[
'destructive'
]
}
);
this
.
addNewMapButton
=
new
OO
.
ui
.
ButtonWidget
(
{
classes
:
[
'mw-templateData-template-add-map-button'
],
label
:
mw
.
msg
(
'templatedata-modal-button-addmap'
),
icon
:
'add'
,
framed
:
false
,
flags
:
[
'progressive'
]
}
);
this
.
newMapNameInput
=
new
OO
.
ui
.
TextInputWidget
(
{
value
:
''
,
placeholder
:
mw
.
msg
(
'templatedata-modal-placeholder-prompt-map-name'
),
classes
:
[
'mw-templateData-template-map-prompter'
]
}
);
this
.
cancelAddMapButton
=
new
OO
.
ui
.
ButtonWidget
(
{
label
:
mw
.
msg
(
'templatedata-modal-button-cancel'
),
framed
:
false
,
flags
:
[
'destructive'
]
}
);
this
.
saveAddMapButton
=
new
OO
.
ui
.
ButtonWidget
(
{
label
:
mw
.
msg
(
'templatedata-modal-button-done'
),
framed
:
false
,
flags
:
[
'primary'
,
'progressive'
]
}
);
this
.
mapsGroup
=
new
OO
.
ui
.
OutlineSelectWidget
(
{
classes
:
[
'mw-templateData-template-map-group'
]
}
);
const
addNewMapButtonPanel
=
new
OO
.
ui
.
PanelLayout
(
{
classes
:
[
'mw-templateData-template-add-map-button-panel'
],
padded
:
true
,
expanded
:
true
}
);
const
mapsListPanel
=
new
OO
.
ui
.
PanelLayout
(
{
expanded
:
true
,
scrollable
:
true
}
);
const
mapsListMenuLayout
=
new
OO
.
ui
.
MenuLayout
(
{
classes
:
[
'mw-templateData-template-map-list-menu-panel'
],
menuPosition
:
'top'
,
expanded
:
true
,
contentPanel
:
mapsListPanel
,
menuPanel
:
addNewMapButtonPanel
}
);
const
mapsContentPanel
=
new
OO
.
ui
.
PanelLayout
(
{
padded
:
true
,
expanded
:
true
}
);
const
templateMapsMenuLayout
=
new
OO
.
ui
.
MenuLayout
(
{
contentPanel
:
mapsContentPanel
,
menuPanel
:
mapsListMenuLayout
}
);
// Param list panel (main)
this
.
languageDropdownWidget
=
new
OO
.
ui
.
DropdownWidget
();
this
.
languagePanelButton
=
new
OO
.
ui
.
ButtonWidget
(
{
label
:
mw
.
msg
(
'templatedata-modal-button-add-language'
),
flags
:
[
'progressive'
]
}
);
const
languageActionFieldLayout
=
new
OO
.
ui
.
ActionFieldLayout
(
this
.
languageDropdownWidget
,
this
.
languagePanelButton
,
{
align
:
'left'
,
label
:
mw
.
msg
(
'templatedata-modal-title-language'
)
}
);
this
.
descriptionInput
=
new
OO
.
ui
.
MultilineTextInputWidget
(
{
autosize
:
true
}
);
this
.
templateDescriptionFieldset
=
new
OO
.
ui
.
FieldsetLayout
(
{
items
:
[
this
.
descriptionInput
]
}
);
// Add Maps panel button
this
.
mapsPanelButton
=
new
OO
.
ui
.
ButtonWidget
(
{
label
:
mw
.
msg
(
'templatedata-modal-button-map'
),
classes
:
[
'mw-templateData-maps-panel-button'
]
}
);
this
.
paramListNoticeMessage
=
new
OO
.
ui
.
MessageWidget
();
this
.
paramListNoticeMessage
.
toggle
(
false
);
this
.
paramSelect
=
new
ParamSelectWidget
();
this
.
paramImport
=
new
ParamImportWidget
();
const
templateParamsFieldset
=
new
OO
.
ui
.
FieldsetLayout
(
{
label
:
mw
.
msg
(
'templatedata-modal-title-templateparams'
),
items
:
[
this
.
paramSelect
,
this
.
paramImport
]
}
);
this
.
templateFormatSelectWidget
=
new
OO
.
ui
.
ButtonSelectWidget
();
this
.
templateFormatSelectWidget
.
addItems
(
[
new
OO
.
ui
.
ButtonOptionWidget
(
{
data
:
null
,
label
:
mw
.
msg
(
'templatedata-modal-format-null'
)
}
),
new
OO
.
ui
.
ButtonOptionWidget
(
{
data
:
'inline'
,
icon
:
'template-format-inline'
,
label
:
mw
.
msg
(
'templatedata-modal-format-inline'
)
}
),
new
OO
.
ui
.
ButtonOptionWidget
(
{
data
:
'block'
,
icon
:
'template-format-block'
,
label
:
mw
.
msg
(
'templatedata-modal-format-block'
)
}
),
new
OO
.
ui
.
ButtonOptionWidget
(
{
data
:
'custom'
,
icon
:
'settings'
,
label
:
mw
.
msg
(
'templatedata-modal-format-custom'
)
}
)
]
);
this
.
templateFormatInputWidget
=
new
OO
.
ui
.
TextInputWidget
(
{
placeholder
:
mw
.
msg
(
'templatedata-modal-format-placeholder'
)
}
);
const
templateFormatFieldSet
=
new
OO
.
ui
.
FieldsetLayout
(
{
label
:
mw
.
msg
(
'templatedata-modal-title-templateformat'
),
items
:
[
new
OO
.
ui
.
FieldLayout
(
this
.
templateFormatSelectWidget
),
new
OO
.
ui
.
FieldLayout
(
this
.
templateFormatInputWidget
,
{
align
:
'top'
,
label
:
mw
.
msg
(
'templatedata-modal-title-templateformatstring'
)
}
)
]
}
);
// Param details panel
this
.
$paramDetailsContainer
=
$
(
'<div>'
)
.
addClass
(
'tdg-templateDataDialog-paramDetails'
);
this
.
listParamsPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-listParamsPanel'
)
.
append
(
this
.
paramListNoticeMessage
.
$element
,
languageActionFieldLayout
.
$element
,
this
.
templateDescriptionFieldset
.
$element
,
new
OO
.
ui
.
FieldLayout
(
this
.
mapsPanelButton
).
$element
,
templateFormatFieldSet
.
$element
,
templateParamsFieldset
.
$element
);
this
.
paramEditNoticeMessage
=
new
OO
.
ui
.
MessageWidget
();
this
.
paramEditNoticeMessage
.
toggle
(
false
);
// Edit panel
this
.
editParamPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-editParamPanel'
)
.
append
(
this
.
paramEditNoticeMessage
.
$element
,
this
.
$paramDetailsContainer
);
// Language panel
this
.
languagePanel
.
$element
.
addClass
(
'tdg-templateDataDialog-languagePanel'
)
.
append
(
this
.
newLanguageSearch
.
$element
);
this
.
addParamPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-addParamPanel'
)
.
append
(
addParamFieldlayout
.
$element
);
// Maps panel
mapsListPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-mapsListPanel'
)
.
append
(
this
.
mapsGroup
.
$element
);
this
.
newMapNameInput
.
$element
.
hide
();
this
.
cancelAddMapButton
.
$element
.
hide
();
this
.
saveAddMapButton
.
$element
.
hide
();
addNewMapButtonPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-addNewMapButtonPanel'
)
.
append
(
this
.
addNewMapButton
.
$element
,
this
.
newMapNameInput
.
$element
,
this
.
cancelAddMapButton
.
$element
,
this
.
saveAddMapButton
.
$element
);
mapsContentPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-mapsContentPanel'
)
.
append
(
this
.
removeMapButton
.
$element
,
this
.
templateMapsInput
.
$element
);
this
.
editMapsPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-editMapsPanel'
)
.
append
(
templateMapsMenuLayout
.
$element
);
this
.
panels
.
addItems
(
[
this
.
listParamsPanel
,
this
.
editParamPanel
,
this
.
languagePanel
,
this
.
addParamPanel
,
this
.
editMapsPanel
]
);
this
.
panels
.
setItem
(
this
.
listParamsPanel
);
this
.
panels
.
$element
.
addClass
(
'tdg-templateDataDialog-panels'
);
// Build param details panel
this
.
$paramDetailsContainer
.
append
(
this
.
createParamDetails
()
);
// Initialization
this
.
$body
.
append
(
this
.
noticeMessage
.
$element
,
this
.
panels
.
$element
);
// Events
this
.
newLanguageSearch
.
getResults
().
connect
(
this
,
{
choose
:
'onNewLanguageSearchResultsChoose'
}
);
this
.
newParamInput
.
connect
(
this
,
{
change
:
'onAddParamInputChange'
,
enter
:
'onAddParamButtonClick'
}
);
this
.
addParamButton
.
connect
(
this
,
{
click
:
'onAddParamButtonClick'
}
);
this
.
descriptionInput
.
connect
(
this
,
{
change
:
'onDescriptionInputChange'
}
);
this
.
languagePanelButton
.
connect
(
this
,
{
click
:
'onLanguagePanelButton'
}
);
this
.
languageDropdownWidget
.
getMenu
().
connect
(
this
,
{
select
:
'onLanguageDropdownWidgetSelect'
}
);
this
.
mapsPanelButton
.
connect
(
this
,
{
click
:
'onMapsPanelButton'
}
);
this
.
addNewMapButton
.
connect
(
this
,
{
click
:
'onAddNewMapClick'
}
);
this
.
cancelAddMapButton
.
connect
(
this
,
{
click
:
'onCancelAddingMap'
}
);
this
.
saveAddMapButton
.
connect
(
this
,
{
click
:
'onEmbedNewMap'
}
);
this
.
newMapNameInput
.
connect
(
this
,
{
enter
:
'onEmbedNewMap'
}
);
this
.
mapsGroup
.
connect
(
this
,
{
select
:
'onMapsGroupSelect'
}
);
this
.
removeMapButton
.
connect
(
this
,
{
click
:
'onMapItemRemove'
}
);
this
.
templateMapsInput
.
connect
(
this
,
{
change
:
'onMapInfoChange'
}
);
this
.
paramSelect
.
connect
(
this
,
{
choose
:
'onParamSelectChoose'
,
reorder
:
'onParamSelectReorder'
}
);
this
.
paramImport
.
connect
(
this
,
{
click
:
'importParametersFromTemplateCode'
}
);
this
.
templateFormatSelectWidget
.
connect
(
this
,
{
choose
:
'onTemplateFormatSelectWidgetChoose'
}
);
this
.
templateFormatInputWidget
.
connect
(
this
,
{
change
:
'onTemplateFormatInputWidgetChange'
,
enter
:
'onTemplateFormatInputWidgetEnter'
}
);
};
/**
* Respond to model change of description event
*
* @param {string} description New description
*/
Dialog
.
prototype
.
onModelChangeDescription
=
function
(
description
)
{
this
.
descriptionInput
.
setValue
(
description
);
};
/**
* Respond to model change of map info event
*
* @param {Object|undefined} map
*/
Dialog
.
prototype
.
onModelChangeMapInfo
=
function
(
map
)
{
const
selectedItem
=
this
.
mapsGroup
.
findSelectedItem
();
map
=
map
||
{};
this
.
mapsCache
=
OO
.
copy
(
map
);
if
(
selectedItem
)
{
this
.
templateMapsInput
.
setValue
(
this
.
stringifyObject
(
map
[
selectedItem
.
label
]
)
);
}
};
/**
* Respond to add param input change.
*
* @param {string} value New parameter name
*/
Dialog
.
prototype
.
onAddParamInputChange
=
function
(
value
)
{
const
allProps
=
Model
.
static
.
getAllProperties
(
true
);
value
=
value
.
trim
();
const
invalid
=
!
value
||
allProps
.
name
.
restrict
.
test
(
value
);
const
used
=
this
.
model
.
isParamExists
(
value
)
&&
!
this
.
model
.
isParamDeleted
(
value
);
this
.
addParamButton
.
setDisabled
(
invalid
||
used
);
};
/**
* Respond to change of param order from the model
*
* @param {string[]} paramOrderArray The array of keys in order
*/
Dialog
.
prototype
.
onModelChangeParamOrder
=
function
()
{
// Refresh the parameter widget
this
.
repopulateParamSelectWidget
();
};
/**
* Respond to change of param property from the model
*
* @param {string} paramKey Parameter key
* @param {string} prop Property name
* @param {Mixed} value
* @param {string} language
*/
Dialog
.
prototype
.
onModelChangeProperty
=
function
(
paramKey
,
prop
,
value
)
{
// Refresh the parameter widget
if
(
paramKey
===
this
.
selectedParamKey
&&
prop
===
'name'
)
{
this
.
selectedParamKey
=
value
;
}
};
/**
* Respond to a change in the model
*/
Dialog
.
prototype
.
onModelChange
=
function
()
{
this
.
modified
=
true
;
this
.
updateActions
();
};
/**
* Set action abilities according to whether the model is modified
*/
Dialog
.
prototype
.
updateActions
=
function
()
{
this
.
actions
.
setAbilities
(
{
apply
:
this
.
modified
}
);
};
/**
* Respond to param order widget reorder event
*
* @param {OO.ui.OptionWidget} item Item reordered
* @param {number} newIndex New index of the item
*/
Dialog
.
prototype
.
onParamSelectReorder
=
function
(
item
,
newIndex
)
{
if
(
!
this
.
paramsReordered
)
{
Metrics
.
logEvent
(
'parameter-reorder'
);
}
this
.
paramsReordered
=
true
;
this
.
model
.
reorderParamOrderKey
(
item
.
getData
(),
newIndex
);
};
/**
* Respond to description input change event
*
* @param {string} value Description value
*/
Dialog
.
prototype
.
onDescriptionInputChange
=
function
(
value
)
{
if
(
!
this
.
descriptionChanged
)
{
Metrics
.
logEvent
(
'template-description-change'
);
}
this
.
descriptionChanged
=
true
;
if
(
this
.
model
.
getTemplateDescription
(
this
.
language
)
!==
value
)
{
this
.
model
.
setTemplateDescription
(
value
,
this
.
language
);
}
};
/**
* Create items for the returned maps and add them to the maps group
*
* @param {Object|undefined} mapsObject
*/
Dialog
.
prototype
.
populateMapsItems
=
function
(
mapsObject
)
{
mapsObject
=
mapsObject
||
{};
const
mapKeysList
=
Object
.
keys
(
mapsObject
);
const
items
=
mapKeysList
.
map
(
(
mapKey
)
=>
new
OO
.
ui
.
OutlineOptionWidget
(
{
label
:
mapKey
}
)
);
this
.
mapsGroup
.
clearItems
();
this
.
mapsGroup
.
addItems
(
items
);
// Maps is not empty anymore
this
.
updateActions
();
};
/**
* Respond to edit maps input change event
*
* @param {string} value map info value
*/
Dialog
.
prototype
.
onMapInfoChange
=
function
(
value
)
{
const
selectedItem
=
this
.
mapsGroup
.
findSelectedItem
();
// Update map Info
this
.
model
.
maps
=
this
.
model
.
getMapInfo
()
||
{};
if
(
selectedItem
)
{
if
(
this
.
model
.
getMapInfo
()[
selectedItem
.
label
]
!==
value
)
{
// Disable Done button in case of invalid JSON
try
{
// This parsing method keeps only the last key/value pair if duplicate keys are defined, and does not throw an error.
// Our model will be updated with a valid maps object, but the user may lose their input if it has duplicate key.
this
.
mapsCache
[
selectedItem
.
label
]
=
JSON
.
parse
(
value
);
this
.
actions
.
setAbilities
(
{
done
:
true
}
);
}
catch
(
err
)
{
// Otherwise disable the done button if maps object is populated
this
.
actions
.
setAbilities
(
{
done
:
false
}
);
}
finally
{
if
(
this
.
mapsGroup
.
items
.
length
===
0
)
{
this
.
actions
.
setAbilities
(
{
done
:
true
}
);
this
.
removeMapButton
.
setDisabled
(
true
);
}
}
}
}
};
/**
* Handle click event for Add new map button
*/
Dialog
.
prototype
.
onAddNewMapClick
=
function
()
{
// Add new text input in maps elements to prompt the map name
this
.
newMapNameInput
.
$element
.
show
();
this
.
cancelAddMapButton
.
$element
.
show
();
this
.
saveAddMapButton
.
$element
.
show
();
this
.
addNewMapButton
.
$element
.
hide
();
this
.
newMapNameInput
.
setValue
(
''
);
this
.
newMapNameInput
.
focus
();
this
.
mapsGroup
.
selectItem
(
null
);
// Text-area show "adding a new map.." message in templateMapsInput and disable the input.
this
.
templateMapsInput
.
setDisabled
(
true
);
this
.
templateMapsInput
.
setValue
(
mw
.
msg
(
'templatedata-modal-placeholder-add-new-map-input'
)
);
// Disable the removing functionality for maps
this
.
removeMapButton
.
setDisabled
(
true
);
// move the list panel down as add new map expanded
this
.
editMapsPanel
.
$element
.
addClass
(
'tdg-templateDataDialog-addingNewMap'
);
};
/**
* Handle clicking cancel button (for add new map panel)
*
* @param {OO.ui.OutlineOptionWidget} [highlightNext] item to be highlighted after adding a new map canceled/done
*/
Dialog
.
prototype
.
onCancelAddingMap
=
function
(
highlightNext
)
{
// Remove the text-area input, cancel button, and show add new map button
this
.
newMapNameInput
.
$element
.
hide
();
this
.
cancelAddMapButton
.
$element
.
hide
();
this
.
saveAddMapButton
.
$element
.
hide
();
this
.
addNewMapButton
.
$element
.
show
();
// move the list panel up back as add new map shrank
this
.
editMapsPanel
.
$element
.
removeClass
(
'tdg-templateDataDialog-addingNewMap'
);
this
.
removeMapButton
.
setDisabled
(
false
);
this
.
mapsGroup
.
selectItem
(
highlightNext
||
this
.
mapsGroup
.
findFirstSelectableItem
()
);
};
/**
* Handle clicking Enter event for promptMapName
*
* @param {jQuery.Event} response response from Enter action on promptMapName
*/
Dialog
.
prototype
.
onEmbedNewMap
=
function
(
response
)
{
const
mapNameValue
=
response
?
response
.
target
.
value
:
this
.
newMapNameInput
.
getValue
();
this
.
mapsCache
=
this
.
mapsCache
||
{};
// Create a new empty map in maps object
this
.
mapsCache
[
mapNameValue
]
=
{};
const
newlyAddedMap
=
new
OO
.
ui
.
OutlineOptionWidget
(
{
label
:
mapNameValue
}
);
// Add the new map item and select it
if
(
mapNameValue
.
length
!==
0
)
{
this
.
mapsGroup
.
addItems
(
[
newlyAddedMap
],
0
);
}
else
{
delete
this
.
mapsCache
[
mapNameValue
];
}
this
.
onCancelAddingMap
(
newlyAddedMap
);
};
/**
* Handle click event for the remove button
*/
Dialog
.
prototype
.
onMapItemRemove
=
function
()
{
const
item
=
this
.
mapsGroup
.
findSelectedItem
();
if
(
item
)
{
this
.
mapsGroup
.
removeItems
(
[
item
]
);
// Remove the highlighted map from maps object
delete
this
.
mapsCache
[
item
.
label
];
}
// Highlight another item, or show the search panel if the maps group is now empty
this
.
onMapsGroupSelect
();
};
/**
* Respond to a map group being selected
*/
Dialog
.
prototype
.
onMapsGroupSelect
=
function
()
{
// Highlight new item
const
item
=
this
.
mapsGroup
.
findSelectedItem
();
if
(
!
item
)
{
this
.
templateMapsInput
.
setDisabled
(
true
);
this
.
templateMapsInput
.
setValue
(
''
);
}
else
{
// Cancel the process of adding a map, Cannot call onCancelAddingMap because these two functions
// cannot be called recursively
// Remove the text-area input, cancel button, and show add new map button
this
.
newMapNameInput
.
$element
.
hide
();
this
.
cancelAddMapButton
.
$element
.
hide
();
this
.
saveAddMapButton
.
$element
.
hide
();
this
.
addNewMapButton
.
$element
.
show
();
// move the list panel up back as add new map shrank
this
.
editMapsPanel
.
$element
.
removeClass
(
'tdg-templateDataDialog-addingNewMap'
);
this
.
removeMapButton
.
setDisabled
(
$
.
isEmptyObject
(
this
.
mapsCache
)
);
this
.
mapsGroup
.
selectItem
(
item
);
this
.
templateMapsInput
.
setDisabled
(
false
);
// Scroll item into view in menu
OO
.
ui
.
Element
.
static
.
scrollIntoView
(
item
.
$element
[
0
]
);
// Populate the mapsContentPanel
this
.
mapsCache
=
this
.
mapsCache
||
{};
const
currentMapInfo
=
this
.
mapsCache
[
item
.
label
];
this
.
templateMapsInput
.
setValue
(
this
.
stringifyObject
(
currentMapInfo
)
);
}
};
/**
* Stringify objects in the dialog with space of 4, mainly maps objects
*
* @param {Object} object maps object
* @return {string} serialized form
*/
Dialog
.
prototype
.
stringifyObject
=
function
(
object
)
{
return
JSON
.
stringify
(
object
,
null
,
4
);
};
/**
* Respond to add language button click
*/
Dialog
.
prototype
.
onLanguagePanelButton
=
function
()
{
this
.
switchPanels
(
this
.
languagePanel
);
};
/**
* Respond to language select widget select event
*
* @param {OO.ui.OptionWidget} item Selected item
*/
Dialog
.
prototype
.
onLanguageDropdownWidgetSelect
=
function
(
item
)
{
const
language
=
item
?
item
.
getData
()
:
this
.
language
;
// Change current language
if
(
language
!==
this
.
language
)
{
this
.
language
=
language
;
// Update description label
this
.
templateDescriptionFieldset
.
setLabel
(
mw
.
msg
(
'templatedata-modal-title-templatedesc'
,
this
.
language
)
);
// Update description value
this
.
descriptionInput
.
setValue
(
this
.
model
.
getTemplateDescription
(
language
)
)
.
$input
.
attr
(
{
lang
:
mw
.
language
.
bcp47
(
language
),
dir
:
'auto'
}
);
// Update all param descriptions in the param select widget
this
.
repopulateParamSelectWidget
();
// Update the parameter detail page
this
.
updateParamDetailsLanguage
();
this
.
emit
(
'change-language'
,
this
.
language
);
}
};
/**
* Handle choose events from the new language search widget
*
* @param {OO.ui.OptionWidget} item Chosen item
*/
Dialog
.
prototype
.
onNewLanguageSearchResultsChoose
=
function
(
item
)
{
const
newLanguage
=
item
.
getData
().
code
;
if
(
newLanguage
)
{
if
(
this
.
availableLanguages
.
indexOf
(
newLanguage
)
===
-
1
)
{
// Add new language
this
.
availableLanguages
.
push
(
newLanguage
);
const
languageButton
=
new
OO
.
ui
.
MenuOptionWidget
(
{
data
:
newLanguage
,
label
:
$
.
uls
.
data
.
getAutonym
(
newLanguage
)
}
);
this
.
languageDropdownWidget
.
getMenu
().
addItems
(
[
languageButton
]
);
}
// Select the new item
this
.
languageDropdownWidget
.
getMenu
().
selectItemByData
(
newLanguage
);
}
// Go to the main panel
this
.
switchPanels
();
};
/**
* Respond to edit maps button click
*/
Dialog
.
prototype
.
onMapsPanelButton
=
function
()
{
const
item
=
this
.
mapsGroup
.
findSelectedItem
()
||
this
.
mapsGroup
.
findFirstSelectableItem
();
this
.
switchPanels
(
this
.
editMapsPanel
);
// Select first item
this
.
mapsGroup
.
selectItem
(
item
);
};
/**
* Respond to add parameter button
*/
Dialog
.
prototype
.
onAddParamButtonClick
=
function
()
{
if
(
this
.
addParamButton
.
isDisabled
()
)
{
return
;
}
const
newParamKey
=
this
.
newParamInput
.
getValue
().
trim
();
if
(
this
.
model
.
isParamDeleted
(
newParamKey
)
)
{
this
.
model
.
emptyParamData
(
newParamKey
);
}
else
if
(
!
this
.
model
.
isParamExists
(
newParamKey
)
)
{
this
.
model
.
addParam
(
newParamKey
);
this
.
addParamToSelectWidget
(
newParamKey
);
}
// Reset the input
this
.
newParamInput
.
setValue
(
''
);
// Go back to list
this
.
switchPanels
();
};
/**
* Respond to choose event from the param select widget
*
* @param {OO.ui.OptionWidget} item Parameter item
*/
Dialog
.
prototype
.
onParamSelectChoose
=
function
(
item
)
{
const
paramKey
=
item
.
getData
();
this
.
selectedParamKey
=
paramKey
;
// The panel with the `propInputs` widgets must be made visible before changing their value.
// Otherwise the autosize feature of MultilineTextInputWidget doesn't work.
this
.
switchPanels
(
this
.
editParamPanel
);
// Fill in parameter detail
this
.
getParameterDetails
(
paramKey
);
};
/**
* Respond to choose event from the template format select widget
*
* @param {OO.ui.OptionWidget} item Format item
*/
Dialog
.
prototype
.
onTemplateFormatSelectWidgetChoose
=
function
(
item
)
{
const
format
=
item
.
getData
(),
shortcuts
=
{
inline
:
'{{_|_=_}}'
,
block
:
'{{_\n| _ = _\n}}'
};
if
(
format
!==
'custom'
)
{
this
.
model
.
setTemplateFormat
(
format
);
this
.
templateFormatInputWidget
.
setDisabled
(
true
);
if
(
format
!==
null
)
{
this
.
templateFormatInputWidget
.
setValue
(
this
.
formatToDisplay
(
shortcuts
[
format
]
)
);
}
}
else
{
this
.
templateFormatInputWidget
.
setDisabled
(
false
);
this
.
onTemplateFormatInputWidgetChange
(
this
.
templateFormatInputWidget
.
getValue
()
);
}
};
Dialog
.
prototype
.
formatToDisplay
=
function
(
s
)
{
// Use '↵' (\u21b5) as a fancy newline (which doesn't start a new line).
return
s
.
replace
(
/\n/g
,
'\u21b5'
);
};
Dialog
.
prototype
.
displayToFormat
=
function
(
s
)
{
// Allow user to type \n or \\n (literal backslash, n) for a new line.
return
s
.
replace
(
/\n|\\n|\u21b5/g
,
'\n'
);
};
/**
* Respond to change event from the template format input widget
*
* @param {string} value Input widget value
*/
Dialog
.
prototype
.
onTemplateFormatInputWidgetChange
=
function
(
value
)
{
const
item
=
this
.
templateFormatSelectWidget
.
findSelectedItem
();
if
(
item
.
getData
()
===
'custom'
)
{
// Convert literal newlines or backslash-n to our fancy character
// replacement.
const
normalized
=
this
.
formatToDisplay
(
this
.
displayToFormat
(
value
)
);
if
(
normalized
!==
value
)
{
this
.
templateFormatInputWidget
.
setValue
(
normalized
);
// Will recurse to actually set value in model.
}
else
{
this
.
model
.
setTemplateFormat
(
this
.
displayToFormat
(
value
.
trim
()
)
);
}
}
};
/**
* Respond to enter event from the template format input widget
*/
Dialog
.
prototype
.
onTemplateFormatInputWidgetEnter
=
function
()
{
/* Synthesize a '\n' when enter is pressed. */
this
.
templateFormatInputWidget
.
insertContent
(
this
.
formatToDisplay
(
'\n'
)
);
};
Dialog
.
prototype
.
onParamPropertyInputChange
=
function
(
propName
,
value
)
{
let
$errors
=
$
(
[]
);
const
prop
=
Model
.
static
.
getAllProperties
(
true
)[
propName
],
propInput
=
this
.
propInputs
[
propName
];
if
(
propName
===
'type'
)
{
const
selected
=
propInput
.
getMenu
().
findSelectedItem
();
value
=
selected
?
selected
.
getData
()
:
prop
.
default
;
}
else
if
(
prop
.
type
===
'array'
)
{
value
=
propInput
.
getValue
();
}
if
(
propName
===
'type'
)
{
this
.
toggleSuggestedValues
(
value
);
}
if
(
propName
===
'name'
)
{
const
invalid
=
!
value
||
prop
.
restrict
.
test
(
value
);
const
changed
=
value
!==
this
.
selectedParamKey
;
if
(
invalid
)
{
$errors
=
$errors
.
add
(
$
(
'<p>'
).
text
(
mw
.
msg
(
'templatedata-modal-errormsg'
,
'|'
,
'='
,
'}}'
)
)
);
}
else
if
(
changed
&&
this
.
model
.
getAllParamNames
().
indexOf
(
value
)
!==
-
1
)
{
// We're changing the name. Make sure it doesn't conflict.
$errors
=
$errors
.
add
(
$
(
'<p>'
).
text
(
mw
.
msg
(
'templatedata-modal-errormsg-duplicate-name'
)
)
);
}
}
propInput
.
$element
.
toggleClass
(
'tdg-editscreen-input-error'
,
!!
$errors
.
length
);
// Check if there is a dependent input to activate
const
dependentField
=
prop
.
textValue
;
if
(
dependentField
&&
this
.
propFieldLayout
[
dependentField
]
)
{
// The textValue property depends on this property
// toggle its view
this
.
propFieldLayout
[
dependentField
].
toggle
(
!!
value
);
this
.
propInputs
[
dependentField
].
setValue
(
this
.
model
.
getParamProperty
(
this
.
selectedParamKey
,
dependentField
)
);
}
// Validate
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-global-selector
const
anyInputError
=
!!
$
(
'.tdg-templateDataDialog-paramInput.tdg-editscreen-input-error'
).
length
;
// Disable the 'done' button if there are any errors in the inputs
this
.
actions
.
setAbilities
(
{
done
:
!
anyInputError
}
);
if
(
$errors
.
length
)
{
this
.
toggleNoticeMessage
(
'edit'
,
true
,
'error'
,
$errors
);
}
else
{
this
.
toggleNoticeMessage
(
'edit'
,
false
);
this
.
model
.
setParamProperty
(
this
.
selectedParamKey
,
propName
,
value
,
this
.
language
);
}
// If we're changing the aliases and the name has an error, poke its change
// handler in case that error was because of a duplicate name with its own
// aliases.
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-class-state
if
(
propName
===
'aliases'
&&
this
.
propInputs
.
name
.
$element
.
hasClass
(
'tdg-editscreen-input-error'
)
)
{
this
.
onParamPropertyInputChange
(
'name'
,
this
.
propInputs
.
name
.
getValue
()
);
}
this
.
trackPropertyChange
(
propName
);
};
Dialog
.
prototype
.
toggleSuggestedValues
=
function
(
type
)
{
const
suggestedValuesAllowedTypes
=
[
'content'
,
'line'
,
'number'
,
'string'
,
'unbalanced-wikitext'
,
'unknown'
];
// Don't show the suggested values field when the feature flag is
// disabled, or for inapplicable types.
this
.
propFieldLayout
.
suggestedvalues
.
toggle
(
suggestedValuesAllowedTypes
.
indexOf
(
type
)
!==
-
1
);
};
/**
* Set the parameter details in the detail panel.
*
* @param {string} paramKey
*/
Dialog
.
prototype
.
getParameterDetails
=
function
(
paramKey
)
{
const
paramData
=
this
.
model
.
getParamData
(
paramKey
);
const
allProps
=
Model
.
static
.
getAllProperties
(
true
);
this
.
stopParameterInputTracking
();
for
(
const
prop
in
this
.
propInputs
)
{
this
.
changeParamPropertyInput
(
paramKey
,
prop
,
paramData
[
prop
],
this
.
language
);
// Show/hide dependents
if
(
allProps
[
prop
].
textValue
)
{
this
.
propFieldLayout
[
allProps
[
prop
].
textValue
].
toggle
(
!!
paramData
[
prop
]
);
}
}
// Update suggested values field visibility
this
.
toggleSuggestedValues
(
paramData
.
type
||
allProps
.
type
.
default
);
let
status
;
// This accepts one of the three booleans only if the other two are false
if
(
paramData
.
deprecated
)
{
status
=
!
paramData
.
required
&&
!
paramData
.
suggested
&&
'deprecated'
;
}
else
if
(
paramData
.
required
)
{
status
=
!
paramData
.
deprecated
&&
!
paramData
.
suggested
&&
'required'
;
}
else
if
(
paramData
.
suggested
)
{
status
=
!
paramData
.
deprecated
&&
!
paramData
.
required
&&
'suggested'
;
}
else
{
status
=
'optional'
;
}
// Status is false at this point when more than one was set to true
this
.
propFieldLayout
.
status
.
toggle
(
status
);
this
.
propFieldLayout
.
deprecated
.
toggle
(
!
status
);
this
.
propFieldLayout
.
required
.
toggle
(
!
status
);
this
.
propFieldLayout
.
suggested
.
toggle
(
!
status
);
if
(
!
status
)
{
// No unambiguous status found, can't use the dropdown
this
.
propInputs
.
status
.
getMenu
().
disconnect
(
this
);
}
else
{
this
.
changeParamPropertyInput
(
paramKey
,
'status'
,
status
);
this
.
propInputs
.
status
.
getMenu
().
connect
(
this
,
{
choose
:
function
(
item
)
{
const
selected
=
item
.
getData
();
// Forward selection from the dropdown to the hidden checkboxes, these get saved
this
.
propInputs
.
deprecated
.
setSelected
(
selected
===
'deprecated'
);
this
.
propInputs
.
required
.
setSelected
(
selected
===
'required'
);
this
.
propInputs
.
suggested
.
setSelected
(
selected
===
'suggested'
);
}
}
);
}
this
.
startParameterInputTracking
(
paramData
);
};
Dialog
.
prototype
.
stopParameterInputTracking
=
function
()
{
this
.
paramPropertyChangeTracking
=
{};
};
/**
* Temporary metrics to understand how properties are edited, see T260343.
*
* @param {Object} paramValues parameter property values at dialog open time
*/
Dialog
.
prototype
.
startParameterInputTracking
=
function
(
paramValues
)
{
this
.
paramPropertyChangeTracking
=
{};
for
(
const
prop
in
this
.
propInputs
)
{
// Set to true, unless one of the exceptions applies.
this
.
paramPropertyChangeTracking
[
prop
]
=
!
(
// Setting type when we already have a specific type.
(
prop
===
'type'
&&
paramValues
[
prop
]
!==
undefined
&&
paramValues
[
prop
]
!==
'unknown'
)
||
// Setting priority but already required, suggested, or deprecated.
(
(
prop
===
'required'
||
prop
===
'suggested'
||
prop
===
'deprecated'
)
&&
(
paramValues
.
required
||
paramValues
.
suggested
||
paramValues
.
deprecated
)
)
||
// Fields ignored by tracking.
(
prop
===
'name'
||
prop
===
'aliases'
||
prop
===
'autovalue'
||
prop
===
'deprecatedValue'
)
);
}
};
Dialog
.
prototype
.
trackPropertyChange
=
function
(
property
)
{
const
eventKey
=
(
property
===
'required'
||
property
===
'suggested'
||
property
===
'deprecated'
)
?
'parameter-priority-change'
:
'parameter-'
+
property
+
'-change'
;
if
(
this
.
paramPropertyChangeTracking
[
property
]
)
{
Metrics
.
logEvent
(
eventKey
);
}
this
.
paramPropertyChangeTracking
[
property
]
=
false
;
// These properties form a conceptual group; suppress additional events.
if
(
property
===
'required'
||
property
===
'suggested'
||
property
===
'deprecated'
)
{
this
.
paramPropertyChangeTracking
.
required
=
this
.
paramPropertyChangeTracking
.
suggested
=
this
.
paramPropertyChangeTracking
.
deprecated
=
false
;
}
};
/**
* Reset contents on reload
*/
Dialog
.
prototype
.
reset
=
function
()
{
this
.
language
=
null
;
this
.
availableLanguages
=
[];
if
(
this
.
paramSelect
)
{
this
.
paramSelect
.
clearItems
();
this
.
selectedParamKey
=
''
;
}
if
(
this
.
languageDropdownWidget
)
{
this
.
languageDropdownWidget
.
getMenu
().
clearItems
();
}
};
/**
* Empty and repopulate the parameter select widget.
*/
Dialog
.
prototype
.
repopulateParamSelectWidget
=
function
()
{
if
(
!
this
.
isSetup
)
{
return
;
}
const
paramList
=
this
.
model
.
getParams
(),
paramOrder
=
this
.
model
.
getTemplateParamOrder
();
this
.
paramSelect
.
clearItems
();
// Update all param descriptions in the param select widget
for
(
const
i
in
paramOrder
)
{
const
paramKey
=
paramList
[
paramOrder
[
i
]
];
if
(
paramKey
&&
!
paramKey
.
deleted
)
{
this
.
addParamToSelectWidget
(
paramOrder
[
i
]
);
}
}
// Check if there are potential parameters to add
// from the template source code
const
missingParams
=
this
.
model
.
getMissingParams
();
this
.
paramImport
.
toggle
(
!!
missingParams
.
length
)
.
buildParamLabel
(
missingParams
);
};
/**
* Change parameter property
*
* @param {string} paramKey Parameter key
* @param {string} propName Property name
* @param {Mixed} [value] Property value
* @param {string} [lang] Language
*/
Dialog
.
prototype
.
changeParamPropertyInput
=
function
(
paramKey
,
propName
,
value
,
lang
)
{
const
prop
=
Model
.
static
.
getAllProperties
(
true
)[
propName
];
let
propInput
=
this
.
propInputs
[
propName
];
switch
(
prop
.
type
)
{
case
'select'
:
propInput
=
propInput
.
getMenu
();
propInput
.
selectItem
(
propInput
.
findItemFromData
(
value
||
prop
.
default
)
);
break
;
case
'boolean'
:
propInput
.
setSelected
(
!!
value
);
break
;
case
'array'
:
value
=
value
||
[];
propInput
.
setValue
(
value
.
map
(
// TagMultiselectWidget accepts nothing but strings or objects with a .data property
(
v
)
=>
v
&&
v
.
data
?
v
:
String
(
v
)
)
);
break
;
default
:
if
(
typeof
value
===
'object'
)
{
value
=
value
[
lang
||
this
.
language
];
}
propInput
.
setValue
(
value
||
''
);
}
};
/**
* Add parameter to the list
*
* @param {string} paramKey Parameter key in the model
*/
Dialog
.
prototype
.
addParamToSelectWidget
=
function
(
paramKey
)
{
const
data
=
this
.
model
.
getParamData
(
paramKey
);
this
.
paramSelect
.
addItems
(
[
new
ParamWidget
(
{
key
:
paramKey
,
label
:
this
.
model
.
getParamValue
(
paramKey
,
'label'
,
this
.
language
),
aliases
:
data
.
aliases
,
description
:
this
.
model
.
getParamValue
(
paramKey
,
'description'
,
this
.
language
)
}
)
// Forward keyboard-triggered events from the OptionWidget to the SelectWidget
.
connect
(
this
.
paramSelect
,
{
choose
:
[
'emit'
,
'choose'
]
}
)
]
);
};
/**
* Create the information page about individual parameters
*
* @return {jQuery} Editable details page for the parameter
*/
Dialog
.
prototype
.
createParamDetails
=
function
()
{
const
paramProperties
=
Model
.
static
.
getAllProperties
(
true
);
// Fieldset
const
paramFieldset
=
new
OO
.
ui
.
FieldsetLayout
();
for
(
const
propName
in
paramProperties
)
{
const
prop
=
paramProperties
[
propName
];
const
config
=
{};
let
propInput
;
// Create the property inputs
switch
(
prop
.
type
)
{
case
'select'
:
{
propInput
=
new
OO
.
ui
.
DropdownWidget
(
config
);
const
items
=
[];
for
(
const
i
in
prop
.
children
)
{
items
.
push
(
new
OO
.
ui
.
MenuOptionWidget
(
{
data
:
prop
.
children
[
i
],
// The following messages are used here:
// * templatedata-doc-param-status-optional
// * templatedata-doc-param-status-deprecated
// * templatedata-doc-param-status-required
// * templatedata-doc-param-status-suggested
// * templatedata-doc-param-type-boolean, templatedata-doc-param-type-content,
// * templatedata-doc-param-type-date, templatedata-doc-param-type-line,
// * templatedata-doc-param-type-number, templatedata-doc-param-type-string,
// * templatedata-doc-param-type-unbalanced-wikitext, templatedata-doc-param-type-unknown,
// * templatedata-doc-param-type-url, templatedata-doc-param-type-wiki-file-name,
// * templatedata-doc-param-type-wiki-page-name, templatedata-doc-param-type-wiki-template-name,
// * templatedata-doc-param-type-wiki-user-name
label
:
mw
.
msg
(
'templatedata-doc-param-'
+
propName
+
'-'
+
prop
.
children
[
i
]
)
}
)
);
}
propInput
.
getMenu
().
addItems
(
items
);
break
;
}
case
'boolean'
:
propInput
=
new
OO
.
ui
.
CheckboxInputWidget
(
config
);
break
;
case
'array'
:
config
.
allowArbitrary
=
true
;
config
.
placeholder
=
mw
.
msg
(
'templatedata-modal-placeholder-multiselect'
);
propInput
=
new
OO
.
ui
.
TagMultiselectWidget
(
config
);
break
;
default
:
config
.
autosize
=
true
;
if
(
!
prop
.
multiline
)
{
config
.
rows
=
1
;
config
.
allowLinebreaks
=
false
;
}
propInput
=
new
OO
.
ui
.
MultilineTextInputWidget
(
config
);
break
;
}
this
.
propInputs
[
propName
]
=
propInput
;
// The following classes are used here:
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-aliases
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-autovalue
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-default
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-deprecated
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-deprecatedValue
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-description
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-example
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-label
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-name
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-required
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-suggested
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-suggestedvalues
// * tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-type
propInput
.
$element
.
addClass
(
'tdg-templateDataDialog-paramInput tdg-templateDataDialog-paramList-'
+
propName
);
this
.
propFieldLayout
[
propName
]
=
new
OO
.
ui
.
FieldLayout
(
propInput
,
{
align
:
'left'
,
// The following messages are used here:
// * templatedata-modal-table-param-aliases
// * templatedata-modal-table-param-autovalue
// * templatedata-modal-table-param-default
// * templatedata-modal-table-param-deprecated
// * templatedata-modal-table-param-deprecatedValue
// * templatedata-modal-table-param-description
// * templatedata-modal-table-param-example
// * templatedata-modal-table-param-label
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-suggestedvalues
// * templatedata-modal-table-param-type
label
:
mw
.
msg
(
'templatedata-modal-table-param-'
+
propName
)
}
);
// Event
if
(
propInput
instanceof
OO
.
ui
.
DropdownWidget
)
{
propInput
.
getMenu
().
connect
(
this
,
{
choose
:
[
'onParamPropertyInputChange'
,
propName
]
}
);
}
else
{
propInput
.
connect
(
this
,
{
change
:
[
'onParamPropertyInputChange'
,
propName
]
}
);
}
// Append to parameter section
paramFieldset
.
$element
.
append
(
this
.
propFieldLayout
[
propName
].
$element
);
}
return
paramFieldset
.
$element
;
};
/**
* Update the labels for parameter property inputs that include language, so
* they show the currently used language.
*/
Dialog
.
prototype
.
updateParamDetailsLanguage
=
function
()
{
const
languageProps
=
Model
.
static
.
getPropertiesWithLanguage
();
for
(
let
i
=
0
;
i
<
languageProps
.
length
;
i
++
)
{
const
prop
=
languageProps
[
i
];
// The following messages are used here:
// * templatedata-modal-table-param-aliases
// * templatedata-modal-table-param-autovalue
// * templatedata-modal-table-param-default
// * templatedata-modal-table-param-deprecated
// * templatedata-modal-table-param-deprecatedValue
// * templatedata-modal-table-param-description
// * templatedata-modal-table-param-example
// * templatedata-modal-table-param-label
// * templatedata-modal-table-param-name
// * templatedata-modal-table-param-required
// * templatedata-modal-table-param-suggested
// * templatedata-modal-table-param-suggestedvalues
// * templatedata-modal-table-param-type
const
label
=
mw
.
msg
(
'templatedata-modal-table-param-'
+
prop
,
this
.
language
);
this
.
propFieldLayout
[
prop
].
setLabel
(
label
);
this
.
propInputs
[
prop
]
.
$input
.
attr
(
{
lang
:
mw
.
language
.
bcp47
(
this
.
language
),
dir
:
'auto'
}
);
}
};
/**
* Override getBodyHeight to create a tall dialog relative to the screen.
*
* @return {number} Body height
*/
Dialog
.
prototype
.
getBodyHeight
=
function
()
{
return
window
.
innerHeight
-
200
;
};
/**
* Show or hide the notice message in the dialog with a set message.
*
* Hides all other notices messages when called, not just the one specified.
*
* @param {string} [type='list'] Which notice label to show: 'list', 'edit' or 'global'
* @param {boolean} [isShowing=false] Show or hide the message
* @param {string} [noticeMessageType='notice'] Message type: 'notice', 'error', 'warning', 'success'
* @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} [noticeMessageLabel] The message to display
*/
Dialog
.
prototype
.
toggleNoticeMessage
=
function
(
type
,
isShowing
,
noticeMessageType
,
noticeMessageLabel
)
{
// Hide all
this
.
noticeMessage
.
toggle
(
false
);
this
.
paramEditNoticeMessage
.
toggle
(
false
);
this
.
paramListNoticeMessage
.
toggle
(
false
);
if
(
noticeMessageLabel
)
{
// See which error to display
let
noticeReference
;
if
(
type
===
'global'
)
{
noticeReference
=
this
.
noticeMessage
;
}
else
if
(
type
===
'edit'
)
{
noticeReference
=
this
.
paramEditNoticeMessage
;
}
else
{
noticeReference
=
this
.
paramListNoticeMessage
;
}
// FIXME: Don't read model information from the DOM
// eslint-disable-next-line no-jquery/no-sizzle
isShowing
=
isShowing
||
!
noticeReference
.
$element
.
is
(
':visible'
);
noticeReference
.
setLabel
(
noticeMessageLabel
);
noticeReference
.
setType
(
noticeMessageType
);
noticeReference
.
toggle
(
isShowing
);
}
};
/**
* Import parameters from the source code.
*/
Dialog
.
prototype
.
importParametersFromTemplateCode
=
function
()
{
const
response
=
this
.
model
.
importSourceCodeParameters
();
// Repopulate the list
this
.
repopulateParamSelectWidget
();
let
$message
=
$
(
[]
),
state
=
'success'
;
if
(
response
.
imported
.
length
===
0
)
{
$message
=
$
(
'<p>'
).
text
(
mw
.
msg
(
'templatedata-modal-errormsg-import-noparams'
)
);
state
=
'error'
;
}
else
{
$message
=
$message
.
add
(
$
(
'<p>'
).
text
(
mw
.
msg
(
'templatedata-modal-notice-import-numparams'
,
response
.
imported
.
length
,
response
.
imported
.
join
(
mw
.
msg
(
'comma-separator'
)
)
)
)
);
}
this
.
toggleNoticeMessage
(
'list'
,
true
,
state
,
$message
);
};
/**
* Get a process for setting up a window for use.
*
* @param {Object} data Dialog opening data
* @param {Model} data.model
* @param {OO.ui.Element} data.editNoticeMessage
* @return {OO.ui.Process} Setup process
*/
Dialog
.
prototype
.
getSetupProcess
=
function
(
data
)
{
return
Dialog
.
super
.
prototype
.
getSetupProcess
.
call
(
this
,
data
)
.
next
(
()
=>
{
this
.
isSetup
=
false
;
this
.
reset
();
// The dialog must be supplied with a reference to a model
this
.
model
=
data
.
model
;
this
.
modified
=
false
;
// Hide the panels and display a spinner
this
.
$spinner
.
show
();
this
.
panels
.
$element
.
hide
();
this
.
toggleNoticeMessage
(
'global'
,
false
);
this
.
toggleNoticeMessage
(
'list'
,
false
);
// Start with parameter list
this
.
switchPanels
();
// Events
this
.
model
.
connect
(
this
,
{
'change-description'
:
'onModelChangeDescription'
,
'change-map'
:
'onModelChangeMapInfo'
,
'change-paramOrder'
:
'onModelChangeParamOrder'
,
'change-property'
:
'onModelChangeProperty'
,
change
:
'onModelChange'
}
);
// Setup the dialog
this
.
setupDetailsFromModel
();
this
.
newLanguageSearch
.
addResults
();
// Bring in the editNoticeMessage from the main page
this
.
listParamsPanel
.
$element
.
prepend
(
data
.
editNoticeMessage
.
$element
);
this
.
availableLanguages
=
this
.
model
.
getExistingLanguageCodes
().
slice
();
const
defaultLanguage
=
this
.
model
.
getDefaultLanguage
();
if
(
this
.
availableLanguages
.
indexOf
(
defaultLanguage
)
===
-
1
)
{
this
.
availableLanguages
.
unshift
(
defaultLanguage
);
}
const
items
=
this
.
availableLanguages
.
map
(
(
lang
)
=>
new
OO
.
ui
.
MenuOptionWidget
(
{
data
:
lang
,
label
:
$
.
uls
.
data
.
getAutonym
(
lang
)
}
)
);
this
.
languageDropdownWidget
.
getMenu
()
.
addItems
(
items
)
// Trigger the initial language choice
.
selectItemByData
(
defaultLanguage
);
this
.
isSetup
=
true
;
this
.
repopulateParamSelectWidget
();
// Show the panel
this
.
$spinner
.
hide
();
this
.
panels
.
$element
.
show
();
this
.
actions
.
setAbilities
(
{
apply
:
false
}
);
}
);
};
/**
* Set up the list of parameters from the model. This should happen
* after initialization of the model.
*/
Dialog
.
prototype
.
setupDetailsFromModel
=
function
()
{
// Set up description
this
.
descriptionInput
.
setValue
(
this
.
model
.
getTemplateDescription
(
this
.
language
)
);
// set up maps
this
.
populateMapsItems
(
this
.
model
.
getMapInfo
()
);
this
.
mapsCache
=
OO
.
copy
(
this
.
model
.
getMapInfo
()
);
this
.
onMapsGroupSelect
();
if
(
this
.
model
.
getMapInfo
()
!==
undefined
)
{
const
firstMapItem
=
Object
.
keys
(
this
.
model
.
getMapInfo
()
)[
0
];
this
.
templateMapsInput
.
setValue
(
this
.
stringifyObject
(
this
.
model
.
getMapInfo
()[
firstMapItem
]
)
);
}
else
{
this
.
templateMapsInput
.
setValue
(
''
);
this
.
templateMapsInput
.
setDisabled
(
true
);
}
// Set up format
const
format
=
this
.
model
.
getTemplateFormat
();
if
(
format
===
'inline'
||
format
===
'block'
||
format
===
null
)
{
this
.
templateFormatSelectWidget
.
selectItemByData
(
format
);
this
.
templateFormatInputWidget
.
setDisabled
(
true
);
}
else
{
this
.
templateFormatSelectWidget
.
selectItemByData
(
'custom'
);
this
.
templateFormatInputWidget
.
setValue
(
this
.
formatToDisplay
(
format
)
);
this
.
templateFormatInputWidget
.
setDisabled
(
false
);
}
// Repopulate the parameter list
this
.
repopulateParamSelectWidget
();
Metrics
.
logEvent
(
this
.
model
.
getOriginalTemplateDataObject
()
?
'dialog-open-edit'
:
'dialog-open-create'
);
};
/**
* Switch between stack layout panels
*
* @param {OO.ui.PanelLayout} [panel] Panel to switch to, defaults to the first panel
*/
Dialog
.
prototype
.
switchPanels
=
function
(
panel
)
{
panel
=
panel
||
this
.
listParamsPanel
;
this
.
panels
.
setItem
(
panel
);
this
.
listParamsPanel
.
$element
.
toggle
(
panel
===
this
.
listParamsPanel
);
this
.
editParamPanel
.
$element
.
toggle
(
panel
===
this
.
editParamPanel
);
this
.
languagePanel
.
$element
.
toggle
(
panel
===
this
.
languagePanel
);
this
.
addParamPanel
.
$element
.
toggle
(
panel
===
this
.
addParamPanel
);
this
.
editMapsPanel
.
$element
.
toggle
(
panel
===
this
.
editMapsPanel
);
switch
(
panel
)
{
case
this
.
listParamsPanel
:
this
.
actions
.
setMode
(
'list'
);
// Reset message
this
.
toggleNoticeMessage
(
'list'
,
false
);
// Deselect parameter
this
.
paramSelect
.
selectItem
(
null
);
// Repopulate the list to account for any changes
if
(
this
.
model
)
{
this
.
repopulateParamSelectWidget
();
}
break
;
case
this
.
editParamPanel
:
this
.
actions
.
setMode
(
'edit'
);
// Deselect parameter
this
.
paramSelect
.
selectItem
(
null
);
this
.
editParamPanel
.
focus
();
break
;
case
this
.
addParamPanel
:
this
.
actions
.
setMode
(
'add'
);
this
.
newParamInput
.
focus
();
break
;
case
this
.
editMapsPanel
:
this
.
actions
.
setMode
(
'maps'
);
this
.
templateMapsInput
.
adjustSize
(
true
).
focus
();
break
;
case
this
.
languagePanel
:
this
.
actions
.
setMode
(
'language'
);
this
.
newLanguageSearch
.
query
.
focus
();
break
;
}
};
/**
* Get a process for taking action.
*
* @param {string} [action] Symbolic name of action
* @return {OO.ui.Process} Action process
*/
Dialog
.
prototype
.
getActionProcess
=
function
(
action
)
{
if
(
action
===
'add'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
this
.
switchPanels
(
this
.
addParamPanel
);
}
);
}
if
(
action
===
'done'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
// setMapInfo with the value and keep the done button active
this
.
model
.
setMapInfo
(
this
.
mapsCache
);
this
.
model
.
originalMaps
=
OO
.
copy
(
this
.
mapsCache
);
this
.
switchPanels
();
}
);
}
if
(
action
===
'back'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
this
.
switchPanels
();
}
);
}
if
(
action
===
'maps'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
this
.
switchPanels
(
this
.
editMapsPanel
);
}
);
}
if
(
action
===
'cancel'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
this
.
mapsCache
=
OO
.
copy
(
this
.
model
.
getOriginalMapsInfo
()
);
this
.
model
.
restoreOriginalMaps
();
this
.
populateMapsItems
(
this
.
mapsCache
);
this
.
onCancelAddingMap
();
this
.
switchPanels
();
}
);
}
if
(
action
===
'delete'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
this
.
model
.
deleteParam
(
this
.
selectedParamKey
);
this
.
switchPanels
();
}
);
}
if
(
action
===
'apply'
)
{
return
new
OO
.
ui
.
Process
(
()
=>
{
Metrics
.
logEvent
(
this
.
model
.
getOriginalTemplateDataObject
()
?
'save-page-edit'
:
'save-page-create'
);
this
.
emit
(
'apply'
,
this
.
model
.
outputTemplateData
()
);
this
.
close
(
{
action
:
action
}
);
}
);
}
if
(
!
action
&&
this
.
modified
)
{
return
new
OO
.
ui
.
Process
(
()
=>
OO
.
ui
.
confirm
(
mw
.
msg
(
'templatedata-modal-confirmcancel'
)
)
.
then
(
(
result
)
=>
{
if
(
result
)
{
this
.
close
();
}
else
{
return
$
.
Deferred
().
resolve
().
promise
();
}
}
)
);
}
// Fallback to parent handler
return
Dialog
.
super
.
prototype
.
getActionProcess
.
call
(
this
,
action
);
};
module
.
exports
=
Dialog
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, May 16, 20:48 (1 d, 10 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f1/6c/63ca9d1b68fec15534193ab330e1
Default Alt Text
Dialog.js (49 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment