mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-09-23 10:21:11 +00:00
Tooling: Begin to use webpack for JS code generation
Generate changeListeners via webpack We now use a build folder to build the JavaScript for our ResourceLoader modules. This is the first change in a line of changes. A source map is provided for debug support. Bug: T156333 Change-Id: I771843d1ddb4b50adedc3fa53b30c2f1d8a76acb
This commit is contained in:
parent
fa0426e008
commit
49df4b9572
|
@ -3,6 +3,7 @@
|
|||
"env": {
|
||||
"browser": true,
|
||||
"jquery": true,
|
||||
"commonjs": true,
|
||||
"qunit": true
|
||||
},
|
||||
"globals": {
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
/node_modules/
|
||||
/vendor/
|
||||
/composer.lock
|
||||
resources/**/*.map
|
||||
|
|
|
@ -23,8 +23,10 @@ module.exports = function ( grunt ) {
|
|||
]
|
||||
},
|
||||
all: [
|
||||
'build/**',
|
||||
'resources/ext.popups/*.js',
|
||||
'resources/ext.popups/**/*.js',
|
||||
'!resources/ext.popups/changeListeners/*.js',
|
||||
'!docs/**',
|
||||
'!node_modules/**'
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
( function ( mw, $ ) {
|
||||
( function ( $ ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the event logging change listener.
|
||||
|
@ -12,7 +12,7 @@
|
|||
* @param {mw.eventLog.Schema} schema
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.eventLogging = function ( boundActions, schema ) {
|
||||
module.exports = function ( boundActions, schema ) {
|
||||
return function ( _, state ) {
|
||||
var eventLogging = state.eventLogging,
|
||||
event = eventLogging.event;
|
||||
|
@ -25,4 +25,4 @@
|
|||
};
|
||||
};
|
||||
|
||||
}( mediaWiki, jQuery ) );
|
||||
}( jQuery ) );
|
|
@ -51,7 +51,7 @@
|
|||
* @param {Object} boundActions
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.footerLink = function ( boundActions ) {
|
||||
module.exports = function ( boundActions ) {
|
||||
var $footerLink;
|
||||
|
||||
return function ( prevState, state ) {
|
10
build/ext.popups/changeListeners/index.js
Normal file
10
build/ext.popups/changeListeners/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
( function ( mw ) {
|
||||
mw.popups.changeListeners = {
|
||||
footerLink: require( './footerLink' ),
|
||||
eventLogging: require( './eventLogging' ),
|
||||
linkTitle: require( './linkTitle' ),
|
||||
render: require( './render' ),
|
||||
settings: require( './settings' ),
|
||||
syncUserSettings: require( './syncUserSettings' )
|
||||
};
|
||||
}( mediaWiki ) );
|
|
@ -1,4 +1,4 @@
|
|||
( function ( mw, $ ) {
|
||||
( function ( $ ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the link title change listener.
|
||||
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.linkTitle = function () {
|
||||
module.exports = function () {
|
||||
var title;
|
||||
|
||||
/**
|
||||
|
@ -62,4 +62,4 @@
|
|||
};
|
||||
};
|
||||
|
||||
}( mediaWiki, jQuery ) );
|
||||
}( jQuery ) );
|
|
@ -6,7 +6,7 @@
|
|||
* @param {ext.popups.PreviewBehavior} previewBehavior
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.render = function ( previewBehavior ) {
|
||||
module.exports = function ( previewBehavior ) {
|
||||
var preview;
|
||||
|
||||
return function ( prevState, state ) {
|
44
build/ext.popups/changeListeners/settings.js
Normal file
44
build/ext.popups/changeListeners/settings.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Creates an instance of the settings change listener.
|
||||
*
|
||||
* @param {Object} boundActions
|
||||
* @param {Object} render function that renders a jQuery el with the settings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( boundActions, render ) {
|
||||
var settings;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( !prevState ) {
|
||||
// Nothing to do on initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// Update global modal visibility
|
||||
if (
|
||||
prevState.settings.shouldShow === false &&
|
||||
state.settings.shouldShow === true
|
||||
) {
|
||||
// Lazily instantiate the settings UI
|
||||
if ( !settings ) {
|
||||
settings = render( boundActions );
|
||||
settings.appendTo( document.body );
|
||||
}
|
||||
|
||||
// Update the UI settings with the current settings
|
||||
settings.setEnabled( state.preview.enabled );
|
||||
|
||||
settings.show();
|
||||
} else if (
|
||||
prevState.settings.shouldShow === true &&
|
||||
state.settings.shouldShow === false
|
||||
) {
|
||||
settings.hide();
|
||||
}
|
||||
|
||||
// Update help visibility
|
||||
if ( prevState.settings.showHelp !== state.settings.showHelp ) {
|
||||
settings.toggleHelp( state.settings.showHelp );
|
||||
}
|
||||
};
|
||||
};
|
60
build/ext.popups/changeListeners/syncUserSettings.js
Normal file
60
build/ext.popups/changeListeners/syncUserSettings.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Creates an instance of the user settings sync change listener.
|
||||
*
|
||||
* This change listener syncs certain parts of the state tree to user
|
||||
* settings when they change.
|
||||
*
|
||||
* Used for:
|
||||
*
|
||||
* * Enabled state: If the previews are enabled or disabled.
|
||||
* * Preview count: When the user dwells on a link for long enough that
|
||||
* a preview is shown, then their preview count will be incremented (see
|
||||
* `mw.popups.reducers.eventLogging`, and is persisted to local storage.
|
||||
*
|
||||
* @param {ext.popups.UserSettings} userSettings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( userSettings ) {
|
||||
|
||||
return function ( prevState, state ) {
|
||||
|
||||
syncIfChanged(
|
||||
prevState, state, 'eventLogging', 'previewCount',
|
||||
userSettings.setPreviewCount
|
||||
);
|
||||
syncIfChanged(
|
||||
prevState, state, 'preview', 'enabled',
|
||||
userSettings.setIsEnabled
|
||||
);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a state tree, reducer and property, safely return the value of the
|
||||
* property if the reducer and property exist
|
||||
* @param {Object} state tree
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @return {*}
|
||||
*/
|
||||
function get( state, reducer, prop ) {
|
||||
return state[ reducer ] && state[ reducer ][ prop ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a sync function if the property prop on the property reducer on
|
||||
* the state trees has changed value.
|
||||
* @param {Object} prevState
|
||||
* @param {Object} state
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @param {Function} sync function to be called with the newest value if
|
||||
* changed
|
||||
*/
|
||||
function syncIfChanged( prevState, state, reducer, prop, sync ) {
|
||||
var current = get( state, reducer, prop );
|
||||
if ( prevState && ( get( prevState, reducer, prop ) !== current ) ) {
|
||||
sync( current );
|
||||
}
|
||||
}
|
63
doc/adr/0004-use-webpack.md
Normal file
63
doc/adr/0004-use-webpack.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# 4. Use webpack
|
||||
|
||||
Date: 02/02/2017
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Discussed by entire team, but predominately Sam Smith, Joaquin Hernandez and
|
||||
Jon Robson.
|
||||
|
||||
As our JavaScript becomes more complex we are making it increasingly difficult
|
||||
to maintain dependencies via extension.json. Dependencies and file order have
|
||||
to be managed and every new file creation requires an edit to extension.json.
|
||||
This slows down development. In Vagrant for instance NTFS file systems
|
||||
experience slowdown when loading many files.
|
||||
|
||||
There are many tools that bundle JavaScript out there that can do this for us.
|
||||
|
||||
** Pros **
|
||||
* mw.popups no longer needs to be exposed as a global object
|
||||
* Dependency management is no longer a manual process but automated by webpack
|
||||
* Would allow us to explore template pre-compiling
|
||||
* More reliable debug via source map support
|
||||
* For non-MediaWiki developers it should be easier to understand our
|
||||
development workflow.
|
||||
|
||||
**Cons**
|
||||
* There is now a build step. New developers to the extension may try to
|
||||
directly edit the distribution files.
|
||||
* Likely to be more merge conflicts, but this could be addressed by additional
|
||||
tooling (e.g. post-merge build step)
|
||||
|
||||
## Decision
|
||||
|
||||
There are various bundlers to choose from, but Webpack was chosen on the basis
|
||||
that
|
||||
1) It was easy to switch to another
|
||||
2) It is popular and well maintained.
|
||||
3) Many members of the team are familiar with it.
|
||||
|
||||
https://medium.com/@tomchentw/why-webpack-is-awesome-9691044b6b8e#.mi0mmz75y
|
||||
provides a good write up.
|
||||
|
||||
## Consequences
|
||||
|
||||
While we migrate directory structure is likely to go through a series of
|
||||
changes. Specifically template loading is likely to change in future.
|
||||
|
||||
New JavaScript files should import and export other files via commonjs and
|
||||
not rely on global variables.
|
||||
|
||||
extension.json still needs to be updated to point to modules in MediaWiki
|
||||
core.
|
||||
|
||||
Care should be taken when including node module libraries to ensure they
|
||||
are not loaded by other extensions.
|
||||
|
||||
Developers working on the repository are now required to run `npm run build`
|
||||
in a pre-commit hook to ensure that the right JavaScript is sent to users.
|
||||
|
|
@ -97,12 +97,7 @@
|
|||
"resources/ext.popups/reducers/settings.js",
|
||||
|
||||
"resources/ext.popups/changeListener.js",
|
||||
"resources/ext.popups/changeListeners/footerLink.js",
|
||||
"resources/ext.popups/changeListeners/linkTitle.js",
|
||||
"resources/ext.popups/changeListeners/render.js",
|
||||
"resources/ext.popups/changeListeners/eventLogging.js",
|
||||
"resources/ext.popups/changeListeners/syncUserSettings.js",
|
||||
"resources/ext.popups/changeListeners/settings.js",
|
||||
"resources/ext.popups/changeListeners/index.js",
|
||||
|
||||
"resources/ext.popups/settingsDialog.js",
|
||||
"resources/ext.popups/pageVisibility.js",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"test": "grunt lint",
|
||||
"doc": "jsduck"
|
||||
},
|
||||
|
@ -14,6 +15,7 @@
|
|||
"grunt-eslint": "19.0.0",
|
||||
"grunt-jsonlint": "1.1.0",
|
||||
"grunt-stylelint": "^0.6.0",
|
||||
"stylelint-config-wikimedia": "0.3.0"
|
||||
"stylelint-config-wikimedia": "0.3.0",
|
||||
"webpack": "^1.14.0"
|
||||
}
|
||||
}
|
||||
|
|
391
resources/ext.popups/changeListeners/index.js
Normal file
391
resources/ext.popups/changeListeners/index.js
Normal file
|
@ -0,0 +1,391 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId])
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ exports: {},
|
||||
/******/ id: moduleId,
|
||||
/******/ loaded: false
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.loaded = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
( function ( mw ) {
|
||||
mw.popups.changeListeners = {
|
||||
footerLink: __webpack_require__( 1 ),
|
||||
eventLogging: __webpack_require__( 2 ),
|
||||
linkTitle: __webpack_require__( 3 ),
|
||||
render: __webpack_require__( 4 ),
|
||||
settings: __webpack_require__( 5 ),
|
||||
syncUserSettings: __webpack_require__( 6 )
|
||||
};
|
||||
}( mediaWiki ) );
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 1 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
( function ( mw, $ ) {
|
||||
|
||||
/**
|
||||
* Creates the link element and appends it to the footer element.
|
||||
*
|
||||
* The following elements are considered to be the footer element (highest
|
||||
* priority to lowest):
|
||||
*
|
||||
* # `#footer-places`
|
||||
* # `#f-list`
|
||||
* # The parent element of `#footer li`, which is either an `ol` or `ul`.
|
||||
*
|
||||
* @return {jQuery} The link element
|
||||
*/
|
||||
function createFooterLink() {
|
||||
var $link = $( '<li>' ).append(
|
||||
$( '<a>' )
|
||||
.attr( 'href', '#' )
|
||||
.text( mw.message( 'popups-settings-enable' ).text() )
|
||||
),
|
||||
$footer;
|
||||
|
||||
// As yet, we don't know whether the link should be visible.
|
||||
$link.hide();
|
||||
|
||||
// From https://en.wikipedia.org/wiki/MediaWiki:Gadget-ReferenceTooltips.js,
|
||||
// which was written by Yair rand <https://en.wikipedia.org/wiki/User:Yair_rand>.
|
||||
$footer = $( '#footer-places, #f-list' );
|
||||
|
||||
if ( $footer.length === 0 ) {
|
||||
$footer = $( '#footer li' ).parent();
|
||||
}
|
||||
|
||||
$footer.append( $link );
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the footer link change listener.
|
||||
*
|
||||
* The change listener covers the following behaviour:
|
||||
*
|
||||
* * The "Enable previews" link (the "link") is appended to the footer menu
|
||||
* (see `createFooterLink` above).
|
||||
* * When Page Previews are disabled, then the link is shown; otherwise, the
|
||||
* link is hidden.
|
||||
* * When the user clicks the link, then the `showSettings` bound action
|
||||
* creator is called.
|
||||
*
|
||||
* @param {Object} boundActions
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( boundActions ) {
|
||||
var $footerLink;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( $footerLink === undefined ) {
|
||||
$footerLink = createFooterLink();
|
||||
$footerLink.click( function ( e ) {
|
||||
e.preventDefault();
|
||||
boundActions.showSettings();
|
||||
} );
|
||||
}
|
||||
|
||||
if ( state.settings.shouldShowFooterLink ) {
|
||||
$footerLink.show();
|
||||
} else {
|
||||
$footerLink.hide();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 2 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
( function ( $ ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the event logging change listener.
|
||||
*
|
||||
* When an event is enqueued to be logged it'll be logged using the schema.
|
||||
* Since it's the responsibility of EventLogging (and the UA) to deliver
|
||||
* logged events, the `EVENT_LOGGED` is immediately dispatched rather than
|
||||
* waiting for some indicator of completion.
|
||||
*
|
||||
* @param {Object} boundActions
|
||||
* @param {mw.eventLog.Schema} schema
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( boundActions, schema ) {
|
||||
return function ( _, state ) {
|
||||
var eventLogging = state.eventLogging,
|
||||
event = eventLogging.event;
|
||||
|
||||
if ( event ) {
|
||||
schema.log( $.extend( true, {}, eventLogging.baseData, event ) );
|
||||
|
||||
boundActions.eventLogged();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}( jQuery ) );
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 3 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
( function ( $ ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the link title change listener.
|
||||
*
|
||||
* While the user dwells on a link, then it becomes the active link. The
|
||||
* change listener will remove a link's `title` attribute while it's the
|
||||
* active link.
|
||||
*
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function () {
|
||||
var title;
|
||||
|
||||
/**
|
||||
* Destroys the title attribute of the element, storing its value in local
|
||||
* state so that it can be restored later (see `restoreTitleAttr`).
|
||||
*
|
||||
* @param {Element} el
|
||||
*/
|
||||
function destroyTitleAttr( el ) {
|
||||
var $el = $( el );
|
||||
|
||||
// Has the user dwelled on a link? If we've already removed its title
|
||||
// attribute, then NOOP.
|
||||
if ( title ) {
|
||||
return;
|
||||
}
|
||||
|
||||
title = $el.attr( 'title' );
|
||||
|
||||
$el.attr( 'title', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the title attribute of the element.
|
||||
*
|
||||
* @param {Element} el
|
||||
*/
|
||||
function restoreTitleAttr( el ) {
|
||||
$( el ).attr( 'title', title );
|
||||
|
||||
title = undefined;
|
||||
}
|
||||
|
||||
return function ( prevState, state ) {
|
||||
var hasPrevActiveLink = prevState && prevState.preview.activeLink;
|
||||
|
||||
if ( hasPrevActiveLink ) {
|
||||
|
||||
// Has the user dwelled on a link immediately after abandoning another
|
||||
// (remembering that the ABANDON_END action is delayed by
|
||||
// ~10e2 ms).
|
||||
if ( prevState.preview.activeLink !== state.preview.activeLink ) {
|
||||
restoreTitleAttr( prevState.preview.activeLink );
|
||||
}
|
||||
}
|
||||
|
||||
if ( state.preview.activeLink ) {
|
||||
destroyTitleAttr( state.preview.activeLink );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}( jQuery ) );
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 4 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
( function ( mw ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the render change listener.
|
||||
*
|
||||
* @param {ext.popups.PreviewBehavior} previewBehavior
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( previewBehavior ) {
|
||||
var preview;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( state.preview.shouldShow && !preview ) {
|
||||
preview = mw.popups.renderer.render( state.preview.fetchResponse );
|
||||
preview.show( state.preview.activeEvent, previewBehavior );
|
||||
} else if ( !state.preview.shouldShow && preview ) {
|
||||
preview.hide();
|
||||
preview = undefined;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}( mediaWiki ) );
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 5 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the settings change listener.
|
||||
*
|
||||
* @param {Object} boundActions
|
||||
* @param {Object} render function that renders a jQuery el with the settings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( boundActions, render ) {
|
||||
var settings;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( !prevState ) {
|
||||
// Nothing to do on initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// Update global modal visibility
|
||||
if (
|
||||
prevState.settings.shouldShow === false &&
|
||||
state.settings.shouldShow === true
|
||||
) {
|
||||
// Lazily instantiate the settings UI
|
||||
if ( !settings ) {
|
||||
settings = render( boundActions );
|
||||
settings.appendTo( document.body );
|
||||
}
|
||||
|
||||
// Update the UI settings with the current settings
|
||||
settings.setEnabled( state.preview.enabled );
|
||||
|
||||
settings.show();
|
||||
} else if (
|
||||
prevState.settings.shouldShow === true &&
|
||||
state.settings.shouldShow === false
|
||||
) {
|
||||
settings.hide();
|
||||
}
|
||||
|
||||
// Update help visibility
|
||||
if ( prevState.settings.showHelp !== state.settings.showHelp ) {
|
||||
settings.toggleHelp( state.settings.showHelp );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 6 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the user settings sync change listener.
|
||||
*
|
||||
* This change listener syncs certain parts of the state tree to user
|
||||
* settings when they change.
|
||||
*
|
||||
* Used for:
|
||||
*
|
||||
* * Enabled state: If the previews are enabled or disabled.
|
||||
* * Preview count: When the user dwells on a link for long enough that
|
||||
* a preview is shown, then their preview count will be incremented (see
|
||||
* `mw.popups.reducers.eventLogging`, and is persisted to local storage.
|
||||
*
|
||||
* @param {ext.popups.UserSettings} userSettings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
module.exports = function ( userSettings ) {
|
||||
|
||||
return function ( prevState, state ) {
|
||||
|
||||
syncIfChanged(
|
||||
prevState, state, 'eventLogging', 'previewCount',
|
||||
userSettings.setPreviewCount
|
||||
);
|
||||
syncIfChanged(
|
||||
prevState, state, 'preview', 'enabled',
|
||||
userSettings.setIsEnabled
|
||||
);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a state tree, reducer and property, safely return the value of the
|
||||
* property if the reducer and property exist
|
||||
* @param {Object} state tree
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @return {*}
|
||||
*/
|
||||
function get( state, reducer, prop ) {
|
||||
return state[ reducer ] && state[ reducer ][ prop ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a sync function if the property prop on the property reducer on
|
||||
* the state trees has changed value.
|
||||
* @param {Object} prevState
|
||||
* @param {Object} state
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @param {Function} sync function to be called with the newest value if
|
||||
* changed
|
||||
*/
|
||||
function syncIfChanged( prevState, state, reducer, prop, sync ) {
|
||||
var current = get( state, reducer, prop );
|
||||
if ( prevState && ( get( prevState, reducer, prop ) !== current ) ) {
|
||||
sync( current );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ }
|
||||
/******/ ]);
|
||||
//# sourceMappingURL=index.js.map
|
|
@ -1,48 +0,0 @@
|
|||
( function ( mw ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the settings change listener.
|
||||
*
|
||||
* @param {Object} boundActions
|
||||
* @param {Object} render function that renders a jQuery el with the settings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.settings = function ( boundActions, render ) {
|
||||
var settings;
|
||||
|
||||
return function ( prevState, state ) {
|
||||
if ( !prevState ) {
|
||||
// Nothing to do on initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// Update global modal visibility
|
||||
if (
|
||||
prevState.settings.shouldShow === false &&
|
||||
state.settings.shouldShow === true
|
||||
) {
|
||||
// Lazily instantiate the settings UI
|
||||
if ( !settings ) {
|
||||
settings = render( boundActions );
|
||||
settings.appendTo( document.body );
|
||||
}
|
||||
|
||||
// Update the UI settings with the current settings
|
||||
settings.setEnabled( state.preview.enabled );
|
||||
|
||||
settings.show();
|
||||
} else if (
|
||||
prevState.settings.shouldShow === true &&
|
||||
state.settings.shouldShow === false
|
||||
) {
|
||||
settings.hide();
|
||||
}
|
||||
|
||||
// Update help visibility
|
||||
if ( prevState.settings.showHelp !== state.settings.showHelp ) {
|
||||
settings.toggleHelp( state.settings.showHelp );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}( mediaWiki ) );
|
|
@ -1,64 +0,0 @@
|
|||
( function ( mw ) {
|
||||
|
||||
/**
|
||||
* Creates an instance of the user settings sync change listener.
|
||||
*
|
||||
* This change listener syncs certain parts of the state tree to user
|
||||
* settings when they change.
|
||||
*
|
||||
* Used for:
|
||||
*
|
||||
* * Enabled state: If the previews are enabled or disabled.
|
||||
* * Preview count: When the user dwells on a link for long enough that
|
||||
* a preview is shown, then their preview count will be incremented (see
|
||||
* `mw.popups.reducers.eventLogging`, and is persisted to local storage.
|
||||
*
|
||||
* @param {ext.popups.UserSettings} userSettings
|
||||
* @return {ext.popups.ChangeListener}
|
||||
*/
|
||||
mw.popups.changeListeners.syncUserSettings = function ( userSettings ) {
|
||||
|
||||
return function ( prevState, state ) {
|
||||
|
||||
syncIfChanged(
|
||||
prevState, state, 'eventLogging', 'previewCount',
|
||||
userSettings.setPreviewCount
|
||||
);
|
||||
syncIfChanged(
|
||||
prevState, state, 'preview', 'enabled',
|
||||
userSettings.setIsEnabled
|
||||
);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a state tree, reducer and property, safely return the value of the
|
||||
* property if the reducer and property exist
|
||||
* @param {Object} state tree
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @return {*}
|
||||
*/
|
||||
function get( state, reducer, prop ) {
|
||||
return state[ reducer ] && state[ reducer ][ prop ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a sync function if the property prop on the property reducer on
|
||||
* the state trees has changed value.
|
||||
* @param {Object} prevState
|
||||
* @param {Object} state
|
||||
* @param {String} reducer key to access on the state tree
|
||||
* @param {String} prop key to access on the reducer key of the state tree
|
||||
* @param {Function} sync function to be called with the newest value if
|
||||
* changed
|
||||
*/
|
||||
function syncIfChanged( prevState, state, reducer, prop, sync ) {
|
||||
var current = get( state, reducer, prop );
|
||||
if ( prevState && ( get( prevState, reducer, prop ) !== current ) ) {
|
||||
sync( current );
|
||||
}
|
||||
}
|
||||
|
||||
}( mediaWiki ) );
|
19
webpack.config.js
Normal file
19
webpack.config.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
var path = require( 'path' );
|
||||
var webpack = require( 'webpack' );
|
||||
var PUBLIC_PATH = '/w/extensions/Popups';
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
// The absolute path to the output directory.
|
||||
path: path.resolve( __dirname, 'resources/' ),
|
||||
devtoolModuleFilenameTemplate: PUBLIC_PATH + '/[resource-path]',
|
||||
|
||||
// Write each chunk (entries, here) to a file named after the entry, e.g.
|
||||
// the "index" entry gets written to index.js.
|
||||
filename: '/[name]/index.js'
|
||||
},
|
||||
entry: {
|
||||
'ext.popups/changeListeners': './build/ext.popups/changeListeners/index.js'
|
||||
},
|
||||
devtool: 'source-map'
|
||||
};
|
Loading…
Reference in a new issue