Файловый менеджер - Редактировать - /var/www/html/mediawiki.htmlform.zip
Ðазад
PK ! ��x� � htmlform.jsnu �Iw�� $( () => { /** * Fired on page load to enhance any HTML forms on the page. * * @event ~'htmlform.enhance' * @param {jQuery} document * @memberof Hooks */ mw.hook( 'htmlform.enhance' ).fire( $( document ) ); } ); mw.hook( 'htmlform.enhance' ).add( ( $root ) => { // Turn HTML5 form validation back on, in cases where it was disabled server-side (see // HTMLForm::needsJSForHtml5FormValidation()) because we need extra logic implemented in JS to // validate correctly. Currently, this is only used for forms containing fields with 'hide-if'. $root.find( '.mw-htmlform' ).removeAttr( 'novalidate' ); // Enable collapsible forms const $collapsible = $root.find( '.mw-htmlform-ooui .oo-ui-fieldsetLayout.mw-collapsible' ); if ( $collapsible.length ) { mw.loader.using( 'jquery.makeCollapsible' ).then( () => { $collapsible.makeCollapsible(); } ); } } ); // Collect other hook handlers require( './autocomplete.js' ); require( './autoinfuse.js' ); require( './cloner.js' ); require( './cond-state.js' ); require( './multiselect.js' ); require( './selectandother.js' ); require( './selectorother.js' ); require( './timezone.js' ); PK ! $�E�! �! cond-state.jsnu �Iw�� /* * HTMLForm enhancements: * Set up 'hide-if' and 'disable-if' behaviors for form fields that have them. */ /** * Helper function for conditional states to find the nearby form field. * * Find the closest match for the given name, "closest" being the minimum * level of parents to go to find a form field matching the given name or * ending in array keys matching the given name (e.g. "baz" matches * "foo[bar][baz]"). * * @ignore * @private * @param {jQuery} $root * @param {string} name * @return {jQuery|null} */ function conditionGetField( $root, name ) { const nameFilter = function () { return this.name === name; }; let $found = $root.find( '[name]' ).filter( nameFilter ); if ( !$found.length ) { // Field cloner can load from template dynamically and fire event on sub element $found = $root.closest( 'form' ).find( '[name]' ).filter( nameFilter ); } return $found.length ? $found : null; } /** * Helper function to get the OOUI widget containing the given field, if any. * * @ignore * @private * @param {jQuery} $field * @return {OO.ui.Widget|null} */ function getWidget( $field ) { const $widget = $field.closest( '.oo-ui-widget[data-ooui]' ); if ( $widget.length ) { return OO.ui.Widget.static.infuse( $widget ); } return null; } /** * Helper function for conditional states to return a test function and list of * dependent fields for a conditional states specification. * * @ignore * @private * @param {jQuery} $root * @param {Array} spec * @return {Array} * @return {Array} return.0 Dependent fields, array of jQuery objects * @return {Function} return.1 Test function */ function conditionParse( $root, spec ) { let v, fields, func; const op = spec[ 0 ]; let l = spec.length; switch ( op ) { case 'AND': case 'OR': case 'NAND': case 'NOR': { const funcs = []; fields = []; for ( let i = 1; i < l; i++ ) { if ( !Array.isArray( spec[ i ] ) ) { throw new Error( op + ' parameters must be arrays' ); } v = conditionParse( $root, spec[ i ] ); fields = fields.concat( v[ 0 ] ); funcs.push( v[ 1 ] ); } l = funcs.length; const valueChk = { AND: false, OR: true, NAND: false, NOR: true }; const valueRet = { AND: true, OR: false, NAND: false, NOR: true }; func = function () { for ( let j = 0; j < l; j++ ) { if ( valueChk[ op ] === funcs[ j ]() ) { return !valueRet[ op ]; } } return valueRet[ op ]; }; return [ fields, func ]; } case 'NOT': if ( l !== 2 ) { throw new Error( 'NOT takes exactly one parameter' ); } if ( !Array.isArray( spec[ 1 ] ) ) { throw new Error( 'NOT parameters must be arrays' ); } v = conditionParse( $root, spec[ 1 ] ); fields = v[ 0 ]; func = v[ 1 ]; return [ fields, function () { return !func(); } ]; case '===': case '!==': { if ( l !== 3 ) { throw new Error( op + ' takes exactly two parameters' ); } const $field = conditionGetField( $root, spec[ 1 ] ); if ( !$field ) { return [ [], function () { return false; } ]; } v = spec[ 2 ]; let widget; const getVal = function () { // When the value is requested for the first time, // determine if we need to treat this field as a OOUI widget. if ( widget === undefined ) { widget = getWidget( $field ); } if ( widget ) { if ( widget.supports( 'isSelected' ) ) { const selected = widget.isSelected(); return selected ? widget.getValue() : ''; } else { return widget.getValue(); } } else { if ( $field.prop( 'type' ) === 'radio' || $field.prop( 'type' ) === 'checkbox' ) { const $selected = $field.filter( ':checked' ); return $selected.length ? $selected.val() : ''; } else { return $field.val(); } } }; switch ( op ) { case '===': func = function () { return getVal() === v; }; break; case '!==': func = function () { return getVal() !== v; }; break; } return [ [ $field ], func ]; } default: throw new Error( 'Unrecognized operation \'' + op + '\'' ); } } /** * Helper function to get the list of ResourceLoader modules needed to infuse the OOUI widgets * containing the given fields. * * @ignore * @private * @param {jQuery} $fields * @return {string[]} */ function gatherOOUIModules( $fields ) { const $oouiFields = $fields.filter( '[data-ooui]' ); const modules = []; if ( $oouiFields.length ) { modules.push( 'mediawiki.htmlform.ooui' ); $oouiFields.each( function () { const data = $( this ).data( 'mw-modules' ); if ( data ) { // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer const extraModules = data.split( ',' ); modules.push( ...extraModules ); } } ); } return modules; } mw.hook( 'htmlform.enhance' ).add( ( $root ) => { const $exclude = $root.find( '.mw-htmlform-autoinfuse-lazy' ) .find( '.mw-htmlform-hide-if, .mw-htmlform-disable-if' ); const $fields = $root.find( '.mw-htmlform-hide-if, .mw-htmlform-disable-if' ).not( $exclude ); // Load modules for the fields we will hide/disable mw.loader.using( gatherOOUIModules( $fields ) ).done( () => { $fields.each( function () { const $el = $( this ); let spec, $elOrLayout, $form; if ( $el.is( '[data-ooui]' ) ) { // $elOrLayout should be a FieldLayout that mixes in mw.htmlform.Element $elOrLayout = OO.ui.FieldLayout.static.infuse( $el ); $form = $elOrLayout.$element.closest( 'form' ); spec = $elOrLayout.condState; } else { $elOrLayout = $el; $form = $el.closest( 'form' ); spec = $el.data( 'condState' ); } if ( !spec ) { return; } let fields = []; const test = {}; [ 'hide', 'disable' ].forEach( ( type ) => { if ( spec[ type ] ) { const v = conditionParse( $form, spec[ type ] ); fields = fields.concat( fields, v[ 0 ] ); test[ type ] = v[ 1 ]; } } ); const func = function () { const shouldHide = spec.hide ? test.hide() : false; const shouldDisable = shouldHide || ( spec.disable ? test.disable() : false ); if ( spec.hide ) { // The .toggle() method works mostly the same for jQuery objects and OO.ui.Widget $elOrLayout.toggle( !shouldHide ); } // Disable fields with either 'disable-if' or 'hide-if' rules // Hidden fields should be disabled to avoid users meet validation failure on these fields, // because disabled fields will not be submitted with the form. if ( $elOrLayout instanceof $ ) { // This also finds elements inside any nested fields (in case of HTMLFormFieldCloner), // which is problematic. But it works because: // * HTMLFormFieldCloner::createFieldsForKey() copies '*-if' rules to nested fields // * jQuery collections like $fields are in document order, so we register event // handlers for parents first // * Event handlers are fired in the order they were registered, so even if the handler // for parent messed up the child, the handle for child will run next and fix it $elOrLayout.find( 'input, textarea, select' ).each( function () { const $this = $( this ); if ( shouldDisable ) { if ( $this.data( 'was-disabled' ) === undefined ) { $this.data( 'was-disabled', $this.prop( 'disabled' ) ); } $this.prop( 'disabled', true ); } else { $this.prop( 'disabled', $this.data( 'was-disabled' ) ); } } ); } else { // $elOrLayout is a OO.ui.FieldLayout if ( shouldDisable ) { if ( $elOrLayout.wasDisabled === undefined ) { $elOrLayout.wasDisabled = $elOrLayout.fieldWidget.isDisabled(); } $elOrLayout.fieldWidget.setDisabled( true ); } else if ( $elOrLayout.wasDisabled !== undefined ) { $elOrLayout.fieldWidget.setDisabled( $elOrLayout.wasDisabled ); } } }; const oouiNodes = fields.map( // We expect undefined for non-OOUI nodes (T308626) ( $node ) => $node.closest( '.oo-ui-fieldLayout[data-ooui]' )[ 0 ] ).filter( // Remove undefined ( node ) => !!node ); // Load modules for the fields whose state we will check mw.loader.using( gatherOOUIModules( $( oouiNodes ) ) ).done( () => { for ( let i = 0; i < fields.length; i++ ) { const widget = getWidget( fields[ i ] ); if ( widget ) { fields[ i ] = widget; } // The .on() method works mostly the same for jQuery objects and OO.ui.Widget fields[ i ].on( 'change', func ); } func(); } ); } ); } ); } ); PK ! �R��� � autocomplete.jsnu �Iw�� /* * HTMLForm enhancements: * Set up autocomplete fields. */ mw.hook( 'htmlform.enhance' ).add( ( $root ) => { const $autocomplete = $root.find( '.mw-htmlform-autocomplete' ); if ( $autocomplete.length ) { mw.loader.using( 'jquery.suggestions', () => { $autocomplete.suggestions( { fetch: function ( val ) { const $el = $( this ); $el.suggestions( 'suggestions', $el.data( 'autocomplete' ).filter( ( v ) => v.indexOf( val ) === 0 ) ); } } ); } ); } } ); PK ! ��B�O O timezone.jsnu �Iw�� /* * HTMLForm enhancements: * Enable the "Fill in from browser" option for the timezone selector */ function minutesToHours( min ) { const tzHour = Math.floor( Math.abs( min ) / 60 ), tzMin = Math.abs( min ) % 60, tzString = ( ( min >= 0 ) ? '' : '-' ) + ( ( tzHour < 10 ) ? '0' : '' ) + tzHour + ':' + ( ( tzMin < 10 ) ? '0' : '' ) + tzMin; return tzString; } mw.hook( 'htmlform.enhance' ).add( ( $root ) => { mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', () => { $root.find( '.mw-htmlform-timezone-field' ).each( function () { // This is identical to OO.ui.infuse( ... ), but it makes the class name of the result known. const timezoneWidget = mw.widgets.SelectWithInputWidget.static.infuse( $( this ) ); function maybeGuessTimezone() { if ( timezoneWidget.dropdowninput.getValue() !== 'guess' ) { return; } // If available, get the named time zone from the browser. // (We also support older browsers where this API is not available.) let timeZone; try { // This may return undefined timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; } catch ( err ) { timeZone = null; } // Get the time offset const minuteDiff = -( new Date().getTimezoneOffset() ); let newValue; if ( timeZone ) { // Try to save both time zone and offset newValue = 'ZoneInfo|' + minuteDiff + '|' + timeZone; timezoneWidget.dropdowninput.setValue( newValue ); } if ( !timeZone || timezoneWidget.dropdowninput.getValue() !== newValue ) { // No time zone, or it's unknown to MediaWiki. Save only offset timezoneWidget.dropdowninput.setValue( 'other' ); timezoneWidget.textinput.setValue( minutesToHours( minuteDiff ) ); } } timezoneWidget.dropdowninput.on( 'change', maybeGuessTimezone ); maybeGuessTimezone(); } ); } ); } ); PK ! p��� � multiselect.jsnu �Iw�� /* * HTMLForm enhancements: * Convert multiselect fields from checkboxes to Chosen selector when requested. */ function convertCheckboxesWidgetToTags( fieldLayout ) { const checkboxesWidget = fieldLayout.fieldWidget; const checkboxesOptions = checkboxesWidget.checkboxMultiselectWidget.getItems(); const menuTagOptions = checkboxesOptions.map( ( option ) => new OO.ui.MenuOptionWidget( { data: option.getData(), label: option.getLabel(), disabled: option.disabled // Don't take the state of parent elements into account. } ) ); const fieldData = checkboxesWidget.data || {}; const menuTagWidget = new OO.ui.MenuTagMultiselectWidget( { $overlay: true, menu: { items: menuTagOptions }, disabled: checkboxesWidget.isDisabled(), placeholder: fieldData.placeholder || '' } ); menuTagWidget.setValue( checkboxesWidget.getValue() ); menuTagOptions.forEach( ( option ) => { if ( option.disabled ) { const tagItem = menuTagWidget.findItemFromData( option.getData() ); // When this disabled option is selected by default. if ( tagItem ) { tagItem.setFixed( true ); } } } ); // Data from TagMultiselectWidget will not be submitted with the form, so keep the original // CheckboxMultiselectInputWidget up-to-date. menuTagWidget.on( 'change', () => { checkboxesWidget.setValue( menuTagWidget.getValue() ); } ); // Synchronize the disable state for submission, and set the proper state of the label. menuTagWidget.on( 'disable', ( isDisabled ) => { checkboxesWidget.setDisabled( isDisabled ); } ); // Change the connected fieldWidget to the new one, so other scripts can infuse the layout // and make changes to this widget. fieldLayout.fieldWidget = menuTagWidget; // Hide original widget and add new one in its place. checkboxesWidget.toggle( false ); checkboxesWidget.$element.after( menuTagWidget.$element ); } mw.hook( 'htmlform.enhance' ).add( ( $root ) => { const $dropdowns = $root.find( '.mw-htmlform-dropdown:not(.oo-ui-widget)' ); if ( $dropdowns.length ) { $dropdowns.each( function () { const $el = $( this ); if ( $el.is( '[data-ooui]' ) ) { // Avoid kicks in multiple times and causing a mess if ( $el.find( '.oo-ui-menuTagMultiselectWidget' ).length ) { return; } // Load 'oojs-ui-widgets' for TagMultiselectWidget const modules = [ 'mediawiki.htmlform.ooui', 'oojs-ui-widgets' ]; const data = $el.data( 'mw-modules' ); if ( data ) { // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer const extraModules = data.split( ',' ); modules.push( ...extraModules ); } mw.loader.using( modules, () => { convertCheckboxesWidgetToTags( OO.ui.FieldLayout.static.infuse( $el ) ); } ); } } ); } } ); PK ! �; "_ _ cloner.jsnu �Iw�� /* * HTMLForm enhancements: * Add/remove cloner clones without having to resubmit the form. */ let cloneCounter = 0; /** * Appends a new row with fields to the cloner. * * @ignore * @param {jQuery} $createButton */ function appendToCloner( $createButton ) { const $ul = $createButton.prev( 'ul.mw-htmlform-cloner-ul' ), cloneRegex = new RegExp( mw.util.escapeRegExp( $ul.data( 'uniqueId' ) ), 'g' ), // Assume the ids that need to be made unique will start with 'ooui-php-'. See T274533 inputIdRegex = new RegExp( /(ooui-php-[0-9]*)/, 'gm' ); ++cloneCounter; const html = $ul.data( 'template' ) .replace( cloneRegex, 'clone' + cloneCounter ) .replace( inputIdRegex, '$1-clone' + cloneCounter ); const $li = $( '<li>' ) .addClass( 'mw-htmlform-cloner-li' ) .html( html ) .appendTo( $ul ); mw.hook( 'htmlform.enhance' ).fire( $li ); } mw.hook( 'htmlform.enhance' ).add( ( $root ) => { const $deleteElement = $root.find( '.mw-htmlform-cloner-delete-button' ), $createElement = $root.find( '.mw-htmlform-cloner-create-button' ); $deleteElement.each( function () { const $element = $( this ); // eslint-disable-next-line no-jquery/no-class-state if ( $element.hasClass( 'oo-ui-widget' ) ) { const deleteButton = OO.ui.infuse( $element ); deleteButton.on( 'click', () => { deleteButton.$element.closest( 'li.mw-htmlform-cloner-li' ).remove(); } ); } else { // eslint-disable-next-line no-jquery/no-sizzle $element.filter( ':input' ).on( 'click', function ( e ) { e.preventDefault(); $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove(); } ); } } ); $createElement.each( function () { const $element = $( this ); // eslint-disable-next-line no-jquery/no-class-state if ( $element.hasClass( 'oo-ui-widget' ) ) { const createButton = OO.ui.infuse( $element ); createButton.on( 'click', () => { appendToCloner( createButton.$element ); } ); } else { // eslint-disable-next-line no-jquery/no-sizzle $element.filter( ':input' ).on( 'click', function ( e ) { e.preventDefault(); appendToCloner( $( this ) ); } ); } } ); } ); PK ! �ϩ� autoinfuse.jsnu �Iw�� /* * HTMLForm enhancements: * Infuse some OOUI HTMLForm fields (those which benefit from always being infused). */ mw.hook( 'htmlform.enhance' ).add( ( $root ) => { let $oouiNodes = $root.find( '.mw-htmlform-autoinfuse' ); $oouiNodes = $oouiNodes.filter( function () { return !$( this ).closest( '.mw-htmlform-autoinfuse-lazy' ).length; } ); if ( $oouiNodes.length ) { // The modules are preloaded (added server-side in HTMLFormField, and the individual fields // which need extra ones), but this module doesn't depend on them. Wait until they're loaded. const modules = [ 'mediawiki.htmlform.ooui' ]; $oouiNodes.each( function () { const data = $( this ).data( 'mw-modules' ); if ( data ) { // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer const extraModules = data.split( ',' ); modules.push( ...extraModules ); } } ); mw.loader.using( modules ).done( () => { $oouiNodes.each( function () { OO.ui.infuse( this ); } ); } ); } } ); PK ! ���`� � selectandother.jsnu �Iw�� /* * HTMLForm enhancements: * Add a dynamic max length to the reason field of SelectAndOther. */ // cache the separator to avoid require on each keypress const colonSeparator = require( './contentMessages.json' ).colonSeparator; mw.hook( 'htmlform.enhance' ).add( ( $root ) => { // This checks the length together with the value from the select field // When the reason list is changed and the bytelimit is longer than the allowed, // nothing is done $root .find( '.mw-htmlform-select-and-other-field' ) .each( function () { const $this = $( this ), $widget = $this.closest( '.oo-ui-widget[data-ooui]' ); // find the reason list const $reasonList = $root.find( '#' + $this.data( 'id-select' ) ); if ( $widget.length ) { mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', () => { const widget = OO.ui.Widget.static.infuse( $widget ); const maxlengthUnit = widget.getData().maxlengthUnit; const lengthLimiter = maxlengthUnit === 'codepoints' ? 'visibleCodePointLimitWithDropdown' : 'visibleByteLimitWithDropdown'; mw.widgets[ lengthLimiter ]( widget.textinput, widget.dropdowninput ); } ); } else { // cache the current selection to avoid expensive lookup let currentValReasonList = $reasonList.val(); $reasonList.on( 'change', () => { currentValReasonList = $reasonList.val(); } ); // Select the function for the length limit const maxlengthUnit = $this.data( 'mw-maxlength-unit' ); const lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit'; $this[ lengthLimiter ]( ( input ) => { // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest let comment = currentValReasonList; if ( comment === 'other' ) { comment = input; } else if ( input !== '' ) { // Entry from drop down menu + additional comment comment += colonSeparator + input; } return comment; } ); } } ); } ); PK ! ���� � selectorother.jsnu �Iw�� /* * HTMLForm enhancements: * Animate the SelectOrOther fields, to only show the text field when 'other' is selected. */ /** * jQuery plugin to fade or snap to visible state. * * @param {boolean} [instantToggle=false] * @return {jQuery} */ $.fn.goIn = function ( instantToggle ) { if ( instantToggle === true ) { return this.show(); } return this.stop( true, true ).fadeIn(); }; /** * jQuery plugin to fade or snap to hiding state. * * @param {boolean} [instantToggle=false] * @return {jQuery} */ $.fn.goOut = function ( instantToggle ) { if ( instantToggle === true ) { return this.hide(); } return this.stop( true, true ).fadeOut(); }; mw.hook( 'htmlform.enhance' ).add( ( $root ) => { /** * @ignore * @param {boolean|jQuery.Event} instant */ function handleSelectOrOther( instant ) { const $select = $( this ).find( 'select' ); let $other = $( this ).find( 'input' ); $other = $other.add( $other.siblings( 'br' ) ); if ( $select.val() === 'other' ) { $other.goIn( instant ); } else { $other.goOut( instant ); } } // Exclude OOUI widgets, since they're infused and SelectWithInputWidget // is responsible for this logic. $root .on( 'change', '.mw-htmlform-select-or-other:not(.oo-ui-widget)', handleSelectOrOther ) .find( '.mw-htmlform-select-or-other:not(.oo-ui-widget)' ) .each( function () { handleSelectOrOther.call( this, true ); } ); } ); PK ! ��x� � htmlform.jsnu �Iw�� PK ! $�E�! �! � cond-state.jsnu �Iw�� PK ! �R��� � �&