Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5955721
campaignManager.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
campaignManager.js
View Options
/**
* JS for campaign editor (handled by Special:CentralNotice)
*
* This file is part of the CentralNotice Extension to MediaWiki
* https://www.mediawiki.org/wiki/Extension:CentralNotice
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
(
function
()
{
var
stepSize
=
1
,
mixinDefs
=
mw
.
config
.
get
(
'wgCentralNoticeCampaignMixins'
),
mixinParamsTemplate
=
mw
.
template
.
get
(
'ext.centralNotice.adminUi.campaignManager'
,
'campaignMixinParamControls.mustache'
),
$form
,
$submitBtn
,
MixinCustomUiController
,
MixinCustomWidget
,
ErrorStateTracker
,
mixinCustomUiControllerFactory
=
new
OO
.
Factory
(),
errorStateTracker
,
eventBus
,
assignedBanners
,
BUCKET_LABELS
=
[
'A'
,
'B'
,
'C'
,
'D'
];
// TODO Fix for configs with more buckets
/* Event bus */
/**
* eventBus: A simple object for subscribing to and emitting events.
*/
/**
* @event bucket-change
*
* The control for the number of buckets in the campaign has changed.
* @param {number} numBuckets
*/
/**
* @event assigned-banners-change
*
* The controls for banner assignment (bucket assignments and banner removal
* checkboxes) have changed. Note: This event does not fire when a checkbox for
* adding a banner is checked.
*/
/**
* @event error-state
*
* An error state is set or removed
* @param {string} errorKey
* @param {boolean} state true to set error, false to clear one.
*/
eventBus
=
new
OO
.
EventEmitter
();
/* MixinCustomUiController */
// Note: the following code uses two completely distinct meanings of
// "mixin". One is "campaign mixin", bits of JS code that can run in the
// browsers of users in specific campaigns. The other is the OOjs concept
// of mixin, that is, a bit of functionality that can be added to a
// javascript class. For example, the MixinCustomWidget class provides a
// bit of UI for a campaign mixin, and it mixes in, in the OOjs sense,
// functionality from OO.ui.mixin.GroupElement.
/**
* Base class for custom campaign mixin UI controllers.
*
* Provides facilities for setting hidden form input elements for mixin parameter
* values. This lets custom mixins provide interactive interfaces that are not input
* elements, and send data to the server via these hidden inputs.
*
* Note: Subclasses are expected to be singletons.
*
* @abstract
* @class MixinCustomUiController
* @constructor
*/
MixinCustomUiController
=
function
()
{
// Declare the abstract property here, but don't force it to null, in case the
// subclass decides to set it before calling the constructor.
/**
* The element of the corresponding MixinCustomWidget.
*
* @abstract
* @property {jQuery}
*/
this
.
$widgetElement
=
this
.
$widgetElement
||
null
;
};
OO
.
initClass
(
MixinCustomUiController
);
/**
* The name of the campaign mixin that this control group sets the parameters for.
*
* @abstract
* @inheritable
* @static
* @property {string}
*/
MixinCustomUiController
.
static
.
name
=
null
;
/**
* Initialize the controller with the data provided.
*
* @method
* @abstract
* @param {Object} data Object in which properties and their values are mixin
* parameter names and values. Format should coordinate with the format sent
* to the client via the 'mixin-param-values' data value on the checkbox that
* enables the mixin.
*/
MixinCustomUiController
.
prototype
.
init
=
null
;
/**
* Set the (string-encoded) value of a mixin parameter via a hidden input element.
*
* @param {string} name The name of the parameter.
* @param {string} value The value (formatted as appropriate for form submission).
*/
MixinCustomUiController
.
prototype
.
setParam
=
function
(
name
,
value
)
{
var
$input
=
this
.
getParamInputEl
(
name
,
true
);
$input
.
val
(
value
);
};
/**
* Remove a mixin parameter's hidden input element, if it exists.
*
* @param {string} name The name of the parameter
*/
MixinCustomUiController
.
prototype
.
removeParam
=
function
(
name
)
{
var
$input
=
this
.
getParamInputEl
(
name
,
false
);
if
(
$input
.
length
)
{
$input
.
remove
();
}
};
/**
* Get the hidden input element for a mixin parameter, if it exists. If requested,
* create it if it doesn't exist.
*
* @private
* @param {string} name The name of the parameter
* @param {boolean} create Create the element if it doesn't exist
* @return {jQuery|null}
*/
MixinCustomUiController
.
prototype
.
getParamInputEl
=
function
(
name
,
create
)
{
var
inputName
=
makeNoticeMixinControlName
(
this
.
constructor
.
static
.
name
,
name
),
$input
=
$form
.
find
(
'input[name="'
+
inputName
+
'"]'
);
if
(
create
&&
!
(
$input
.
length
)
)
{
$input
=
$
(
'<input>'
).
attr
(
{
name
:
inputName
,
type
:
'hidden'
}
);
$form
.
append
(
$input
);
}
return
$input
;
};
/* MixinCustomWidget */
/**
* Base class for custom campaign mixin widgets.
*
* @abstract
* @class MixinCustomWidget
* @extends OO.ui.Widget
* @mixes OO.ui.mixin.GroupWidget
* @constructor
*
* @param {MixinCustomUiController} controller
* @param {Object} [config] Configuration options
*/
MixinCustomWidget
=
function
(
controller
,
config
)
{
var
$element
=
$
(
'<fieldset>'
),
$group
=
$
(
'<div>'
);
// Set up config with elements, CSS class and id. This should coordinate with
// makeMixinParamControlSet() (below) and
// templates/campaignMixinParamControls.mustache (used for the automatic creation
// of mixin param controls).
config
=
Object
.
assign
(
{
$element
:
$element
,
// This works because controller classes are singletons.
id
:
mixinParamControlsId
(
controller
.
constructor
.
static
.
name
)
},
config
);
$group
.
addClass
(
'campaignMixinControls'
);
$element
.
append
(
$group
);
// Call parent constructor
MixinCustomWidget
.
super
.
call
(
this
,
config
);
// Call mixin constructor
OO
.
ui
.
mixin
.
GroupElement
.
call
(
this
,
Object
.
assign
(
{},
config
,
{
$group
:
$group
}
)
);
};
OO
.
inheritClass
(
MixinCustomWidget
,
OO
.
ui
.
Widget
);
OO
.
mixinClass
(
MixinCustomWidget
,
OO
.
ui
.
mixin
.
GroupElement
);
/* Event handlers (non-OOUI) and related logic */
/**
* Simple object for keeping track of validation errors.
*
* @class ErrorStateTracker
* @constructor
*/
ErrorStateTracker
=
function
()
{
this
.
errors
=
{};
this
.
messageBoxes
=
{};
};
/**
* Set or clear an error state.
*
* @param {string} errorKey A unique key identifying this error
* @param {boolean} state true sets an error for this key, and false clear it
* @param {Element} messageBox
*/
ErrorStateTracker
.
prototype
.
setErrorState
=
function
(
errorKey
,
state
,
messageBox
)
{
if
(
state
)
{
this
.
errors
[
errorKey
]
=
true
;
this
.
messageBoxes
[
errorKey
]
=
messageBox
;
}
else
{
delete
this
.
errors
[
errorKey
];
delete
this
.
messageBoxes
[
errorKey
];
}
};
/**
* Gets the message box relating to an error
*
* @param {string} errorKey A unique key identifying this error
* @return {Element}
*/
ErrorStateTracker
.
prototype
.
getMessageBox
=
function
(
errorKey
)
{
return
this
.
messageBoxes
[
errorKey
];
};
/**
* Is one or more error currently set?
*
* @return {boolean}
*/
ErrorStateTracker
.
prototype
.
hasErrorState
=
function
()
{
return
Object
.
keys
(
this
.
errors
).
length
>
0
;
};
// General error state tracker for the page
errorStateTracker
=
new
ErrorStateTracker
();
// Connect handler for error-state events
eventBus
.
on
(
'error-state'
,
function
(
errorKey
,
state
,
messageBox
)
{
// Pass state on to errorStateTracker
errorStateTracker
.
setErrorState
(
errorKey
,
state
,
messageBox
);
// Update the submit button
if
(
errorStateTracker
.
hasErrorState
()
)
{
$submitBtn
.
prop
(
'disabled'
,
true
);
}
else
{
$submitBtn
.
prop
(
'disabled'
,
false
);
}
}
);
function
updateThrottle
()
{
if
(
$
(
'#throttle-enabled'
).
prop
(
'checked'
)
)
{
$
(
'.cn-throttle-amount'
).
show
();
}
else
{
$
(
'.cn-throttle-amount'
).
hide
();
}
}
function
updateWeightColumn
()
{
if
(
$
(
'#balanced'
).
prop
(
'checked'
)
)
{
$
(
'.cn-weight'
).
hide
();
}
else
{
$
(
'.cn-weight'
).
show
();
}
}
function
updateBuckets
()
{
var
i
,
isBucketDisabled
,
numBuckets
=
getNumBuckets
(),
maxNumBuckets
=
mw
.
config
.
get
(
'wgNoticeNumberOfBuckets'
),
$bucketSelectors
=
$
(
'select.bucketSelector'
),
$bucketSelectorUnassigned
=
$bucketSelectors
.
not
(
'.bucketSelectorForAssignedBanners'
);
// Change selected value of bucket selectors to only available buckets
$bucketSelectors
.
each
(
function
()
{
var
$selector
=
$
(
this
),
selectedVal
=
$selector
.
val
();
$selector
.
val
(
selectedVal
%
numBuckets
);
}
);
// If only one bucket is available, disable the selectors for unassigned banners,
// and enable them if more than one bucket is available.
// (If we disable selectors for assigned banners, then they won't send their
// values when the form is submitted. In that case, if the number of buckets were
// changed from a value greater than 1 to 1, the selectors would show all banners
// on bucket 0, but for any banners that were moved to bucket 0, the change would
// not be submitted to the server.)
if
(
numBuckets
===
1
)
{
$bucketSelectorUnassigned
.
prop
(
'disabled'
,
true
);
}
else
{
$bucketSelectors
.
prop
(
'disabled'
,
false
);
}
// Enable or disable bucket options in drop-downs, as appropriate
for
(
i
=
0
;
i
<
maxNumBuckets
;
i
++
)
{
isBucketDisabled
=
(
i
>=
numBuckets
);
$bucketSelectors
.
find
(
'option[value='
+
i
+
']'
)
.
prop
(
'disabled'
,
isBucketDisabled
);
}
// Broadcast bucket change event
eventBus
.
emit
(
'bucket-change'
,
numBuckets
);
// It's important to update assigned banners *after* emitting bucket-change so
// widgets first can adjust to the new bucket number.
updateAssignedBanners
();
}
function
getNumBuckets
()
{
return
+
$
(
'select#buckets'
).
val
();
}
/**
* Hide or display campaign mixin parameter controls based on checkbox state.
* The mixin name and any existing parameter values are received as data
* properties on the checkbox.
*/
function
showOrHideCampaignMixinControls
()
{
var
$checkBox
=
$
(
this
),
mixinName
=
$checkBox
.
data
(
'mixin-name'
),
paramValues
,
$paramControlSet
=
$
(
'#'
+
mixinParamControlsId
(
mixinName
)
),
mixinCustomUiController
,
$paramControls
;
if
(
$checkBox
.
prop
(
'checked'
)
)
{
// If the controls don't exist yet, create them
if
(
$paramControlSet
.
length
===
0
)
{
paramValues
=
$checkBox
.
data
(
'mixin-param-values'
);
// If the mixin uses a custom UI to set params, instantiate that
if
(
mixinDefs
[
mixinName
].
customAdminUIControlsModule
)
{
mixinCustomUiController
=
mixinCustomUiControllerFactory
.
create
(
mixinName
);
mixinCustomUiController
.
init
(
paramValues
);
$paramControlSet
=
mixinCustomUiController
.
$widgetElement
;
}
else
{
// Otherwise, create generic controls using a template
$paramControlSet
=
makeMixinParamControlSet
(
mixinName
,
paramValues
);
// Hook up handler for verification
$paramControls
=
$paramControlSet
.
find
(
'input'
);
$paramControls
.
on
(
'keyup keydown change mouseup cut paste focus blur'
,
mw
.
util
.
debounce
(
verifyParamControl
,
100
)
);
}
// Attach the controls
$checkBox
.
parent
(
'div'
).
append
(
$paramControlSet
);
}
else
{
$paramControlSet
.
show
();
}
}
else
if
(
$paramControlSet
.
length
!==
0
)
{
$paramControlSet
.
hide
();
}
}
function
mixinParamControlsId
(
mixinName
)
{
return
'notice-mixin-'
+
mixinName
+
'-paramControls'
;
}
function
makeNoticeMixinControlName
(
mixinName
,
paramName
)
{
return
'notice-mixin-'
+
mixinName
+
'-'
+
paramName
;
}
function
makeMixinParamControlSet
(
mixinName
,
paramValues
)
{
var
paramDefs
=
mixinDefs
[
mixinName
].
parameters
,
templateVars
=
{
divId
:
mixinParamControlsId
(
mixinName
),
params
:
[]
};
paramValues
=
paramValues
||
{};
// eslint-disable-next-line no-jquery/no-each-util
$
.
each
(
paramDefs
,
function
(
paramName
,
paramDef
)
{
var
paramTemplateVars
=
{
// eslint-disable-next-line mediawiki/msg-doc
labelMsg
:
mw
.
message
(
paramDef
.
labelMsg
).
text
(),
inputName
:
makeNoticeMixinControlName
(
mixinName
,
paramName
),
dataType
:
paramDef
.
type
,
minVal
:
paramDef
.
minVal
,
maxVal
:
paramDef
.
maxVal
,
step
:
paramDef
.
step
};
switch
(
paramDef
.
type
)
{
case
'string'
:
paramTemplateVars
.
inputType
=
'text'
;
paramTemplateVars
.
inputSizeFlagAndVar
=
{
inputSize
:
30
};
break
;
case
'integer'
:
case
'float'
:
paramTemplateVars
.
inputType
=
'text'
;
paramTemplateVars
.
inputSizeFlagAndVar
=
{
inputSize
:
5
};
break
;
case
'boolean'
:
paramTemplateVars
.
inputType
=
'checkbox'
;
paramTemplateVars
.
inputValue
=
paramName
;
break
;
case
'json'
:
throw
new
Error
(
'json parameter type requires custom admin UI module.'
);
default
:
throw
new
Error
(
'Invalid parameter definition type: '
+
paramDef
.
type
);
}
// If parameter value was not provided, set a default
if
(
!
(
paramName
in
paramValues
)
)
{
if
(
typeof
paramDef
.
defaultValue
!==
'undefined'
)
{
paramValues
[
paramName
]
=
paramDef
.
defaultValue
;
}
else
{
switch
(
paramDef
.
type
)
{
case
'string'
:
paramValues
[
paramName
]
=
''
;
break
;
case
'integer'
:
case
'float'
:
paramValues
[
paramName
]
=
'0'
;
break
;
case
'boolean'
:
paramValues
[
paramName
]
=
false
;
break
;
default
:
throw
new
Error
(
'Invalid parameter definition type: '
+
paramDef
.
type
);
}
}
}
// Set form control values
switch
(
paramDef
.
type
)
{
case
'string'
:
case
'integer'
:
case
'float'
:
paramTemplateVars
.
inputValue
=
paramValues
[
paramName
];
break
;
case
'boolean'
:
if
(
paramValues
[
paramName
]
)
{
paramTemplateVars
.
checkedFlagAndVar
=
{
checked
:
'checked'
};
}
break
;
default
:
throw
new
Error
(
'Invalid parameter definition type: '
+
paramDef
.
type
);
}
if
(
paramDef
.
helpMsg
)
{
// eslint-disable-next-line mediawiki/msg-doc
paramTemplateVars
.
help
=
mw
.
message
(
paramDef
.
helpMsg
).
text
();
}
templateVars
.
params
.
push
(
paramTemplateVars
);
}
);
return
$
(
mixinParamsTemplate
.
render
(
templateVars
)
);
}
function
verifyParamControl
()
{
var
$input
=
$
(
this
),
val
=
$input
.
val
().
trim
();
switch
(
$input
.
data
(
'data-type'
)
)
{
case
'integer'
:
if
(
/^-?\d+$/
.
test
(
val
)
)
{
setValidationError
(
false
,
$input
);
}
else
{
setValidationError
(
true
,
$input
,
'centralnotice-notice-mixins-int-required'
);
}
break
;
case
'float'
:
if
(
/^-?\d+\.?\d*$|^-?\d*\.?\d+$/
.
test
(
val
)
)
{
setValidationError
(
false
,
$input
);
}
else
{
setValidationError
(
true
,
$input
,
'centralnotice-notice-mixins-float-required'
);
}
break
;
}
if
(
!
isNaN
(
$input
.
data
(
'min-val'
)
)
&&
Number
(
val
)
<
Number
(
$input
.
data
(
'min-val'
)
)
)
{
setValidationError
(
true
,
$input
,
'centralnotice-notice-mixins-out-of-bound'
);
}
if
(
!
isNaN
(
$input
.
data
(
'max-val'
)
)
&&
Number
(
val
)
>
Number
(
$input
.
data
(
'max-val'
)
)
)
{
setValidationError
(
true
,
$input
,
'centralnotice-notice-mixins-out-of-bound'
);
}
}
function
setValidationError
(
error
,
$input
,
msgKey
)
{
var
errorKey
=
$input
.
attr
(
'name'
),
messageBox
=
errorStateTracker
.
getMessageBox
(
errorKey
);
if
(
error
)
{
if
(
!
messageBox
)
{
// eslint-disable-next-line mediawiki/msg-doc
messageBox
=
mw
.
util
.
messageBox
(
mw
.
message
(
msgKey
).
text
(),
'alert'
);
$input
.
closest
(
'p'
).
before
(
messageBox
);
}
eventBus
.
emit
(
'error-state'
,
errorKey
,
true
,
messageBox
);
}
else
{
if
(
messageBox
)
{
messageBox
.
remove
();
}
eventBus
.
emit
(
'error-state'
,
errorKey
,
false
);
}
}
/**
* Create a by-bucket index of assigned banners using data received from the server.
*/
function
setUpAssignedBanners
()
{
var
assignedBannersFlat
,
i
;
// Create outer array and inner arrays for all possible buckets
assignedBanners
=
[];
for
(
i
=
0
;
i
<
mw
.
config
.
get
(
'wgNoticeNumberOfBuckets'
);
i
++
)
{
assignedBanners
[
i
]
=
[];
}
// Get the data sent from the server
// If there are no assigned banners, the assigned banner fieldset isn't included
// in the page. In that case, jQuery will return undefined from data()
assignedBannersFlat
=
$
(
'#centralnotice-assigned-banners'
).
data
(
'assigned-banners'
)
||
[];
// Fill up the index
for
(
i
=
0
;
i
<
assignedBannersFlat
.
length
;
i
++
)
{
assignedBanners
[
assignedBannersFlat
[
i
].
bucket
]
.
push
(
assignedBannersFlat
[
i
].
bannerName
);
}
}
/**
* Update the by-bucket index of assigned banners when a remove banner checkbox or a
* bucket selector for an assigned banner changes. Then, broadcast the
* assigned-banner-change event.
*/
function
updateAssignedBanners
()
{
var
$removeCheckboxes
=
$
(
'.bannerRemoveCheckbox'
),
$selectors
=
$
(
'.bucketSelectorForAssignedBanners'
),
removedBanners
=
[];
// Create an array with the names of banners whose remove checkbox is checked
$removeCheckboxes
.
each
(
function
()
{
var
$this
=
$
(
this
);
if
(
$this
.
prop
(
'checked'
)
)
{
removedBanners
.
push
(
$this
.
val
()
);
}
}
);
// Iterate over the bucket selectors for assigned banners
$selectors
.
each
(
function
()
{
var
i
,
bannerIdx
,
$this
=
$
(
this
),
assignedBucket
=
+
$this
.
val
(),
bannerName
=
$this
.
data
(
'banner-name'
),
removed
=
(
removedBanners
.
indexOf
(
bannerName
)
!==
-
1
);
// Iterate over all buckets, adding banners to the index or removeing them,
// as needed. (assignedBanners has elements for all possible buckets.)
// TODO Make the order of banners the same as the order displayed in the UI
for
(
i
=
0
;
i
<
assignedBanners
.
length
;
i
++
)
{
bannerIdx
=
assignedBanners
[
i
].
indexOf
(
bannerName
);
// If the box is checked to remove, just ensure the banner is not there
if
(
removed
)
{
if
(
bannerIdx
!==
-
1
)
{
assignedBanners
[
i
].
splice
(
bannerIdx
,
1
);
}
continue
;
}
// If the banner is assigned to this bucket but not in the array, add it.
if
(
i
===
assignedBucket
&&
bannerIdx
===
-
1
)
{
assignedBanners
[
i
].
push
(
bannerName
);
continue
;
}
// If the banner isn't assigned to this bucket but it is in the array,
// remove it.
if
(
i
!==
assignedBucket
&&
bannerIdx
!==
-
1
)
{
assignedBanners
[
i
].
splice
(
bannerIdx
,
1
);
}
}
}
);
// Broadcast the event
eventBus
.
emit
(
'assigned-banners-change'
);
}
/**
* Get an array of banners assigned to a specific bucket.
*
* @param {number} bucket
* @return {Array}
*/
function
getAssignedBanners
(
bucket
)
{
return
assignedBanners
[
bucket
];
}
/**
* Get the human-friendly alphabetic label for a bucket number
*
* @param {number} bucket
* @return {string}
*/
function
getBucketLabel
(
bucket
)
{
return
BUCKET_LABELS
[
bucket
];
}
/* Exports */
module
.
exports
=
{
/**
* Base class for custom campaign mixin UI controllers.
*
* @see MixinCustomUiController
* @type {Function}
*/
MixinCustomUiController
:
MixinCustomUiController
,
/**
* Base class for custom campaign mixin widgets.
*
* @see MixinCustomWidget
* @type {Function}
*/
MixinCustomWidget
:
MixinCustomWidget
,
/**
* Simple object for keeping track of validation errors.
*
* @see ErrorStateTracker
* @type {Function}
*/
ErrorStateTracker
:
ErrorStateTracker
,
/**
* Factory for custom mixin UI controllers.
*
* @type {OO.Factory}
*/
mixinCustomUiControllerFactory
:
mixinCustomUiControllerFactory
,
/**
* Centralized object for emitting and subscribing to events.
*
* @type {OO.EventEmitter}
*/
eventBus
:
eventBus
,
/**
* Get the number of buckets currently set in the bucket input.
*
* @method
* @return {number}
*/
getNumBuckets
:
getNumBuckets
,
/**
* Get the human-friendly alphabetic label for a bucket number.
*
* @method
* @param {number} bucket
* @return {string}
*/
getBucketLabel
:
getBucketLabel
,
/**
* Get an array of banners assigned to a specific bucket.
*
* @method
* @param {number} bucket
* @return {Array}
*/
getAssignedBanners
:
getAssignedBanners
};
/* General setup */
/**
* Finalize setup: initialize slider, set handlers, set variables for jQuery elements
*/
function
initialize
()
{
var
$mixinCheckboxes
=
$
(
'input.noticeMixinCheck'
);
$
(
'#centralnotice-throttle-amount'
).
slider
(
{
range
:
'min'
,
min
:
0
,
max
:
100
,
value
:
$
(
'#centralnotice-throttle-cur'
).
val
(),
step
:
stepSize
,
slide
:
function
(
event
,
element
)
{
var
val
=
Number
(
element
.
value
),
rounded
=
Math
.
round
(
val
*
10
)
/
10
;
$
(
'#centralnotice-throttle-echo'
).
text
(
String
(
rounded
)
+
'%'
);
$
(
'#centralnotice-throttle-cur'
).
val
(
val
);
}
}
);
$submitBtn
=
$
(
'#noticeDetailSubmit'
);
$form
=
$
(
'#centralnotice-notice-detail'
);
updateThrottle
();
updateWeightColumn
();
setUpAssignedBanners
();
updateBuckets
();
$
(
'#throttle-enabled'
).
on
(
'click'
,
updateThrottle
);
$
(
'#balanced'
).
on
(
'click'
,
updateWeightColumn
);
$
(
'select#buckets'
).
on
(
'change'
,
updateBuckets
);
$
(
'.bucketSelectorForAssignedBanners, .bannerRemoveCheckbox'
)
.
on
(
'change'
,
updateAssignedBanners
);
$mixinCheckboxes
.
each
(
showOrHideCampaignMixinControls
);
$mixinCheckboxes
.
on
(
'change'
,
showOrHideCampaignMixinControls
);
}
// We have to wait for document ready and for custom controls modules to be loaded
// before initializing everything
$
(
function
()
{
// eslint-disable-next-line no-jquery/no-map-util
var
customControlsModules
=
$
.
map
(
mixinDefs
,
function
(
mixinDef
)
{
return
mixinDef
.
customAdminUIControlsModule
;
}
);
// Custom mixin control modules depend on this module so they can access base
// classes here when they declare subclasses. So, this module can't depend on
// them. However, we need those modules to be loaded when we first call
// showOrHideCampaignMixinControls() (from initialize(), above). Since the
// custom control modules are added server-side, the following call to
// mw.loader.using() should be quick.
mw
.
loader
.
using
(
customControlsModules
).
done
(
initialize
);
}
);
}()
);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 3, 03:55 (3 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
85/03/5cd0d85983b4fa7b9ce3e59f8191
Default Alt Text
campaignManager.js (22 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment