mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/WikiEditor
synced 2024-11-30 11:05:51 +00:00
dbb6583bbb
The main idea is to make the code shorter and easier to read. One notable change is to use .test() for boolean tests instead of the actual .match() function. Change-Id: Ic43442b75f839906b644d4586c907601f4d5d521
836 lines
27 KiB
JavaScript
836 lines
27 KiB
JavaScript
/**
|
|
* Toolbar module for wikiEditor
|
|
*/
|
|
( function () {
|
|
|
|
var toolbarModule = {
|
|
|
|
/**
|
|
* API accessible functions
|
|
*/
|
|
api: {
|
|
addToToolbar: function ( context, data ) {
|
|
|
|
for ( var type in data ) {
|
|
var i;
|
|
switch ( type ) {
|
|
case 'sections':
|
|
var $sections = context.modules.toolbar.$toolbar.find( 'div.sections' );
|
|
var $tabs = context.modules.toolbar.$toolbar.find( 'div.tabs' );
|
|
for ( var section in data[ type ] ) {
|
|
if ( section === 'main' || section === 'secondary' ) {
|
|
// Section
|
|
context.modules.toolbar.$toolbar.prepend(
|
|
toolbarModule.fn.buildSection(
|
|
context, section, data[ type ][ section ]
|
|
)
|
|
);
|
|
continue;
|
|
}
|
|
// Section
|
|
$sections.append(
|
|
toolbarModule.fn.buildSection( context, section, data[ type ][ section ] )
|
|
);
|
|
// Tab
|
|
$tabs.append(
|
|
toolbarModule.fn.buildTab( context, section, data[ type ][ section ] )
|
|
);
|
|
}
|
|
break;
|
|
case 'groups':
|
|
if ( !( 'section' in data ) ) {
|
|
continue;
|
|
}
|
|
var $section = context.modules.toolbar.$toolbar.find( 'div[rel="' + data.section + '"].section' );
|
|
for ( var group in data[ type ] ) {
|
|
// Group
|
|
$section.append(
|
|
toolbarModule.fn.buildGroup( context, group, data[ type ][ group ] )
|
|
);
|
|
}
|
|
break;
|
|
case 'tools':
|
|
if ( !( 'section' in data && 'group' in data ) ) {
|
|
continue;
|
|
}
|
|
var $group = context.modules.toolbar.$toolbar.find(
|
|
'div[rel="' + data.section + '"].section ' +
|
|
'div[rel="' + data.group + '"].group'
|
|
);
|
|
for ( var tool in data[ type ] ) {
|
|
// Tool
|
|
$group.append( toolbarModule.fn.buildTool( context, tool, data[ type ][ tool ] ) );
|
|
}
|
|
if ( $group.children().length ) {
|
|
$group.removeClass( 'empty' );
|
|
}
|
|
break;
|
|
case 'pages':
|
|
if ( !( 'section' in data ) ) {
|
|
continue;
|
|
}
|
|
var $pages = context.modules.toolbar.$toolbar.find(
|
|
'div[rel="' + data.section + '"].section .pages'
|
|
);
|
|
var $index = context.modules.toolbar.$toolbar.find(
|
|
'div[rel="' + data.section + '"].section .index'
|
|
);
|
|
for ( var page in data[ type ] ) {
|
|
// Page
|
|
$pages.append( toolbarModule.fn.buildPage( context, page, data[ type ][ page ] ) );
|
|
// Index
|
|
$index.append(
|
|
toolbarModule.fn.buildBookmark( context, page, data[ type ][ page ] )
|
|
);
|
|
}
|
|
toolbarModule.fn.updateBookletSelection( context, data.section, $pages, $index );
|
|
break;
|
|
case 'rows':
|
|
if ( !( 'section' in data && 'page' in data ) ) {
|
|
continue;
|
|
}
|
|
var $table = context.modules.toolbar.$toolbar.find(
|
|
'div[rel="' + data.section + '"].section ' +
|
|
'div[rel="' + data.page + '"].page table'
|
|
);
|
|
for ( i = 0; i < data.rows.length; i++ ) {
|
|
// Row
|
|
$table.append( toolbarModule.fn.buildRow( context, data.rows[ i ] ) );
|
|
}
|
|
break;
|
|
case 'characters':
|
|
if ( !( 'section' in data && 'page' in data ) ) {
|
|
continue;
|
|
}
|
|
var $characters = context.modules.toolbar.$toolbar.find(
|
|
'div[rel="' + data.section + '"].section ' +
|
|
'div[rel="' + data.page + '"].page div'
|
|
);
|
|
var actions = $characters.data( 'actions' );
|
|
for ( i = 0; i < data.characters.length; i++ ) {
|
|
// Character
|
|
$characters.append(
|
|
$( toolbarModule.fn.buildCharacter( data.characters[ i ], actions ) )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click', function ( e ) {
|
|
toolbarModule.fn.doAction( $( this ).parent().data( 'context' ),
|
|
$( this ).parent().data( 'actions' )[ $( this ).attr( 'rel' ) ] );
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
);
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
},
|
|
removeFromToolbar: function ( context, data ) {
|
|
if ( typeof data.section === 'string' ) {
|
|
// Section
|
|
var tab = 'div.tabs span[rel="' + data.section + '"].tab';
|
|
var target = 'div[rel="' + data.section + '"].section';
|
|
var group = null;
|
|
if ( typeof data.group === 'string' ) {
|
|
// Toolbar group
|
|
target += ' div[rel="' + data.group + '"].group';
|
|
if ( typeof data.tool === 'string' ) {
|
|
// Save for later checking if empty
|
|
group = target;
|
|
// Tool
|
|
target = target + ' [rel="' + data.tool + '"].tool';
|
|
}
|
|
} else if ( typeof data.page === 'string' ) {
|
|
// Booklet page
|
|
var index = target + ' div.index div[rel="' + data.page + '"]';
|
|
target += ' div.pages div[rel="' + data.page + '"].page';
|
|
if ( typeof data.character === 'string' ) {
|
|
// Character
|
|
target += ' span[rel="' + data.character + '"]';
|
|
} else if ( typeof data.row === 'number' ) {
|
|
// Table row
|
|
target += ' table tr:not(:has(th)):eq(' + data.row + ')';
|
|
} else {
|
|
// Just a page, remove the index too!
|
|
context.modules.toolbar.$toolbar.find( index ).remove();
|
|
toolbarModule.fn.updateBookletSelection(
|
|
context,
|
|
data.section,
|
|
context.modules.toolbar.$toolbar.find( target ),
|
|
context.modules.toolbar.$toolbar.find( index )
|
|
);
|
|
}
|
|
} else {
|
|
// Just a section, remove the tab too!
|
|
context.modules.toolbar.$toolbar.find( tab ).remove();
|
|
}
|
|
context.modules.toolbar.$toolbar.find( target ).remove();
|
|
// Hide empty groups
|
|
if ( group ) {
|
|
var $group = context.modules.toolbar.$toolbar.find( group );
|
|
if ( $group.children().length === 0 ) {
|
|
$group.addClass( 'empty' );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Internally used functions
|
|
*/
|
|
fn: {
|
|
/**
|
|
* Creates a toolbar module within a wikiEditor
|
|
*
|
|
* @param {Object} context Context object of editor to create module in
|
|
* @param {Object} config Configuration object to create module from
|
|
*/
|
|
create: function ( context, config ) {
|
|
if ( '$toolbar' in context.modules.toolbar ) {
|
|
return;
|
|
}
|
|
context.modules.toolbar.$toolbar = $( '<div>' )
|
|
.addClass( 'wikiEditor-ui-toolbar' )
|
|
.attr( 'id', 'wikiEditor-ui-toolbar' );
|
|
toolbarModule.fn.build( context, config );
|
|
context.$ui.find( '.wikiEditor-ui-top' ).append( context.modules.toolbar.$toolbar );
|
|
},
|
|
/**
|
|
* Performs an operation based on parameters
|
|
*
|
|
* @param {Object} context
|
|
* @param {Object} action
|
|
*/
|
|
doAction: function ( context, action ) {
|
|
switch ( action.type ) {
|
|
case 'replace':
|
|
case 'encapsulate':
|
|
if ( context.$textarea.prop( 'readonly' ) ) {
|
|
break;
|
|
}
|
|
var parts = {
|
|
pre: $.wikiEditor.autoMsg( action.options, 'pre' ),
|
|
peri: $.wikiEditor.autoMsg( action.options, 'peri' ),
|
|
post: $.wikiEditor.autoMsg( action.options, 'post' )
|
|
};
|
|
var replace = action.type === 'replace';
|
|
if ( 'regex' in action.options && 'regexReplace' in action.options ) {
|
|
var selection = context.$textarea.textSelection( 'getSelection' );
|
|
if ( selection !== '' && action.options.regex.test( selection ) ) {
|
|
parts.peri = selection.replace( action.options.regex,
|
|
action.options.regexReplace );
|
|
parts.pre = parts.post = '';
|
|
replace = true;
|
|
}
|
|
}
|
|
context.$textarea.textSelection(
|
|
'encapsulateSelection',
|
|
$.extend( {}, action.options, parts, { replace: replace } )
|
|
);
|
|
break;
|
|
case 'callback':
|
|
if ( typeof action.execute === 'function' ) {
|
|
action.execute( context );
|
|
}
|
|
break;
|
|
case 'dialog':
|
|
context.fn.saveSelection();
|
|
context.$textarea.wikiEditor( 'openDialog', action.module );
|
|
break;
|
|
default: break;
|
|
}
|
|
},
|
|
buildGroup: function ( context, id, group ) {
|
|
var $group = $( '<div>' ).attr( { class: 'group group-' + id, rel: id } ),
|
|
label = $.wikiEditor.autoMsg( group, 'label' );
|
|
if ( label ) {
|
|
$( '<span>' ).addClass( 'label' ).text( label ).appendTo( $group );
|
|
}
|
|
var empty = true;
|
|
if ( 'tools' in group ) {
|
|
for ( var tool in group.tools ) {
|
|
var $tool = toolbarModule.fn.buildTool( context, tool, group.tools[ tool ] );
|
|
if ( $tool ) {
|
|
// Consider a group with only hidden tools empty as well
|
|
// .is( ':visible' ) always returns false because tool is not attached to the DOM yet
|
|
empty = empty && $tool.css( 'display' ) === 'none';
|
|
$group.append( $tool );
|
|
}
|
|
}
|
|
}
|
|
if ( empty ) {
|
|
$group.addClass( 'empty' );
|
|
}
|
|
return $group;
|
|
},
|
|
buildTool: function ( context, id, tool ) {
|
|
if ( 'filters' in tool ) {
|
|
for ( var i = 0; i < tool.filters.length; i++ ) {
|
|
if ( $( tool.filters[ i ] ).length === 0 ) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
var label = $.wikiEditor.autoMsg( tool, 'label' );
|
|
switch ( tool.type ) {
|
|
case 'button':
|
|
case 'toggle':
|
|
var $button;
|
|
if ( tool.oouiIcon ) {
|
|
var config = {
|
|
framed: false,
|
|
classes: [ 'tool' ],
|
|
icon: tool.oouiIcon,
|
|
title: label
|
|
};
|
|
var oouiButton;
|
|
if ( tool.type === 'button' ) {
|
|
oouiButton = new OO.ui.ButtonWidget( config );
|
|
} else if ( tool.type === 'toggle' ) {
|
|
oouiButton = new OO.ui.ToggleButtonWidget( config );
|
|
}
|
|
$button = oouiButton.$element
|
|
.attr( 'rel', id )
|
|
.data( 'ooui', oouiButton );
|
|
} else {
|
|
$button = $( '<a>' )
|
|
.attr( {
|
|
tabindex: 0,
|
|
title: label,
|
|
rel: id,
|
|
role: 'button',
|
|
class: 'tool tool-button'
|
|
} )
|
|
.text( label );
|
|
if ( tool.icon ) {
|
|
var icon = $.wikiEditor.autoIcon(
|
|
tool.icon,
|
|
$.wikiEditor.imgPath + 'toolbar/'
|
|
);
|
|
$button.css( 'background-image', 'url(' + icon + ')' );
|
|
}
|
|
}
|
|
$button.data( 'setActive', function ( active ) {
|
|
$button.toggleClass( 'tool-active', active );
|
|
|
|
// OOUI button
|
|
if ( $button.data( 'ooui' ) && tool.type === 'toggle' ) {
|
|
$button.data( 'ooui' ).setValue( active );
|
|
// Use progressive icon in WMUI theme
|
|
if ( OO.ui.WikimediaUITheme && OO.ui.theme instanceof OO.ui.WikimediaUITheme ) {
|
|
// Wait for updateElementClasses to run
|
|
setTimeout( function () {
|
|
$button.data( 'ooui' ).$icon.toggleClass( 'oo-ui-image-progressive', active );
|
|
} );
|
|
}
|
|
}
|
|
} );
|
|
if ( 'action' in tool ) {
|
|
$button
|
|
.data( 'action', tool.action )
|
|
.data( 'context', context )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} );
|
|
if ( $button.data( 'ooui' ) ) {
|
|
$button.data( 'ooui' ).on( 'click', function () {
|
|
toolbarModule.fn.doAction(
|
|
context, tool.action
|
|
);
|
|
} );
|
|
} else {
|
|
$button.on( 'click keydown', function ( e ) {
|
|
if (
|
|
e.type === 'click' ||
|
|
e.type === 'keydown' && e.key === 'Enter'
|
|
) {
|
|
toolbarModule.fn.doAction(
|
|
context, tool.action
|
|
);
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
} );
|
|
}
|
|
}
|
|
return $button;
|
|
case 'select':
|
|
var menuId = 'menu-' + Date.now();
|
|
var $select = $( '<div>' )
|
|
.attr( { rel: id, class: 'tool tool-select' } );
|
|
var $options = $( '<div>' ).addClass( 'options' );
|
|
if ( 'list' in tool ) {
|
|
for ( var option in tool.list ) {
|
|
var optionLabel = $.wikiEditor.autoMsg( tool.list[ option ], 'label' );
|
|
$options.append(
|
|
$( '<a>' )
|
|
.data( 'action', tool.list[ option ].action )
|
|
.data( 'context', context )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click keydown', function ( e ) {
|
|
if (
|
|
e.type === 'click' ||
|
|
e.type === 'keydown' && e.key === 'Enter'
|
|
) {
|
|
toolbarModule.fn.doAction(
|
|
$( this ).data( 'context' ), $( this ).data( 'action' ), $( this )
|
|
);
|
|
// Hide the dropdown
|
|
$( this ).closest( '.tool-select' ).removeClass( 'options-shown' );
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
} )
|
|
.text( optionLabel )
|
|
.addClass( 'option' )
|
|
.attr( { rel: option, tabindex: 0, role: 'menuitem' } )
|
|
);
|
|
}
|
|
}
|
|
$select.append( $( '<a>' )
|
|
.addClass( 'label' )
|
|
.text( label )
|
|
.data( 'options', $options )
|
|
.attr( { role: 'button', tabindex: 0, 'aria-expanded': false, 'aria-controls': menuId, 'aria-haspopup': 'menu' } )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click keydown', function ( e ) {
|
|
if (
|
|
e.type === 'click' ||
|
|
e.type === 'keydown' && e.key === 'Enter'
|
|
) {
|
|
var $opts = $( this ).data( 'options' );
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
|
var canShowOptions = !$opts.closest( '.tool-select' ).hasClass( 'options-shown' );
|
|
$opts.closest( '.tool-select' ).toggleClass( 'options-shown', canShowOptions );
|
|
$( this ).attr( 'aria-expanded', canShowOptions.toString() );
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
} )
|
|
);
|
|
$select.append( $( '<div>' ).addClass( 'menu' ).append( $options ) );
|
|
return $select;
|
|
case 'element':
|
|
// A raw 'element' type can be {htmlString|Element|Text|Array|jQuery|OO.ui.HTMLSnippet|function}.
|
|
var $element;
|
|
if ( tool.element instanceof OO.ui.HtmlSnippet ) {
|
|
$element = tool.element.toString();
|
|
} else if ( typeof tool.element === 'function' ) {
|
|
$element = tool.element( context );
|
|
} else {
|
|
$element = tool.element;
|
|
}
|
|
return $( '<div>' )
|
|
.attr( { rel: id, class: 'tool tool-element' } )
|
|
.append( $element );
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
buildBookmark: function ( context, id, page ) {
|
|
var label = $.wikiEditor.autoMsg( page, 'label' );
|
|
return $( '<div>' )
|
|
.text( label )
|
|
.attr( {
|
|
rel: id,
|
|
role: 'option'
|
|
} )
|
|
.data( 'context', context )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click', function ( event ) {
|
|
$( this ).parent().parent().find( '.page' ).hide();
|
|
$( this ).parent().parent().find( '.page-' + $( this ).attr( 'rel' ) ).show().trigger( 'loadPage' );
|
|
$( this )
|
|
.addClass( 'current' )
|
|
.siblings().removeClass( 'current' );
|
|
var section = $( this ).parent().parent().attr( 'rel' );
|
|
$.cookie(
|
|
'wikiEditor-' + $( this ).data( 'context' ).instance + '-booklet-' + section + '-page',
|
|
$( this ).attr( 'rel' ),
|
|
{ expires: 30, path: '/' }
|
|
);
|
|
// No dragging!
|
|
event.preventDefault();
|
|
return false;
|
|
} );
|
|
},
|
|
buildPage: function ( context, id, page, deferLoad ) {
|
|
var $page = $( '<div>' ).attr( {
|
|
class: 'page page-' + id,
|
|
rel: id
|
|
} );
|
|
if ( deferLoad ) {
|
|
$page.one( 'loadPage', function () {
|
|
toolbarModule.fn.reallyBuildPage( context, id, page, $page );
|
|
} );
|
|
} else {
|
|
toolbarModule.fn.reallyBuildPage( context, id, page, $page );
|
|
}
|
|
return $page;
|
|
},
|
|
reallyBuildPage: function ( context, id, page, $page ) {
|
|
var i;
|
|
switch ( page.layout ) {
|
|
case 'table':
|
|
$page.addClass( 'page-table' );
|
|
var html = '';
|
|
if ( 'headings' in page ) {
|
|
html += toolbarModule.fn.buildHeading( context, page.headings );
|
|
}
|
|
if ( 'rows' in page ) {
|
|
for ( i = 0; i < page.rows.length; i++ ) {
|
|
html += toolbarModule.fn.buildRow( context, page.rows[ i ] );
|
|
}
|
|
}
|
|
$page.html( '<table class="table-' + id + '">' + html + '</table>' );
|
|
break;
|
|
case 'characters':
|
|
$page.addClass( 'page-characters' );
|
|
var $characters = $( '<div>' ).data( 'context', context ).data( 'actions', {} );
|
|
var actions = $characters.data( 'actions' );
|
|
if ( 'language' in page ) {
|
|
$characters.attr( 'lang', page.language );
|
|
}
|
|
if ( 'direction' in page ) {
|
|
$characters.attr( 'dir', page.direction );
|
|
} else {
|
|
// By default it should be explicit ltr for all scripts.
|
|
// Without this some conjoined ltr characters look
|
|
// weird in rtl wikis.
|
|
$characters.attr( 'dir', 'ltr' );
|
|
}
|
|
if ( 'characters' in page ) {
|
|
html = '';
|
|
for ( i = 0; i < page.characters.length; i++ ) {
|
|
html += toolbarModule.fn.buildCharacter( page.characters[ i ], actions );
|
|
}
|
|
$characters
|
|
.html( html )
|
|
.children()
|
|
.attr( 'role', 'option' )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click', function ( e ) {
|
|
toolbarModule.fn.doAction(
|
|
$( this ).parent().data( 'context' ),
|
|
$( this ).parent().data( 'actions' )[ $( this ).attr( 'rel' ) ],
|
|
$( this )
|
|
);
|
|
e.preventDefault();
|
|
return false;
|
|
} );
|
|
}
|
|
$page.append( $characters );
|
|
break;
|
|
}
|
|
},
|
|
buildHeading: function ( context, headings ) {
|
|
var html = '';
|
|
for ( var i = 0; i < headings.length; i++ ) {
|
|
html += '<th>' + $.wikiEditor.autoSafeMsg( headings[ i ], [ 'html', 'text' ] ) + '</th>';
|
|
}
|
|
return '<tr>' + html + '</tr>';
|
|
},
|
|
buildRow: function ( context, row ) {
|
|
var html = '';
|
|
for ( var cell in row ) {
|
|
// FIXME: This currently needs to use the "unsafe" .text() message because it embeds raw HTML
|
|
// in the messages (as used exclusively by the 'help' toolbar panel).
|
|
html += '<td class="cell cell-' + cell + '"><span>' +
|
|
$.wikiEditor.autoMsg( row[ cell ], [ 'html', 'text' ] ) + '</span></td>';
|
|
}
|
|
return '<tr>' + html + '</tr>';
|
|
},
|
|
buildCharacter: function ( character, actions ) {
|
|
if ( typeof character === 'string' ) {
|
|
character = {
|
|
label: character,
|
|
action: {
|
|
type: 'replace',
|
|
options: {
|
|
peri: character,
|
|
selectPeri: false
|
|
}
|
|
}
|
|
};
|
|
// In some cases the label for the character isn't the same as the
|
|
// character that gets inserted (e.g. Hebrew vowels)
|
|
} else if ( character && 0 in character && 1 in character ) {
|
|
character = {
|
|
label: character[ 0 ],
|
|
action: {
|
|
type: 'replace',
|
|
options: {
|
|
peri: character[ 1 ],
|
|
selectPeri: false
|
|
}
|
|
}
|
|
};
|
|
}
|
|
if ( character && 'action' in character && 'label' in character ) {
|
|
actions[ character.label ] = character.action;
|
|
// eslint-disable-next-line mediawiki/msg-doc
|
|
var title = character.titleMsg ? mw.msg( character.titleMsg ) : character.title;
|
|
return mw.html.element(
|
|
'span',
|
|
{ rel: character.label, title: title || false },
|
|
character.label
|
|
);
|
|
}
|
|
mw.log( 'A character for the toolbar was undefined. This is not supposed to happen. Double check the config.' );
|
|
// bug 31673; also an additional fix for bug 24208...
|
|
return '';
|
|
},
|
|
buildTab: function ( context, id, section ) {
|
|
var selected = $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section' );
|
|
// Re-save cookie
|
|
if ( selected !== null ) {
|
|
$.cookie( 'wikiEditor-' + context.instance + '-toolbar-section', selected, { expires: 30, path: '/' } );
|
|
}
|
|
var $link =
|
|
$( '<a>' )
|
|
.addClass( selected === id ? 'current' : null )
|
|
.attr( {
|
|
tabindex: 0,
|
|
role: 'button',
|
|
'aria-expanded': ( selected === id ).toString(),
|
|
'aria-controls': 'wikiEditor-section-' + id
|
|
} )
|
|
.text( $.wikiEditor.autoMsg( section, 'label' ) )
|
|
.data( 'context', context )
|
|
.on( 'mouseup', function () {
|
|
$( this ).trigger( 'blur' );
|
|
} )
|
|
.on( 'mousedown', function ( e ) {
|
|
// No dragging!
|
|
e.preventDefault();
|
|
return false;
|
|
} )
|
|
.on( 'click keydown', function ( e ) {
|
|
if (
|
|
e.type !== 'click' &&
|
|
( e.type !== 'keydown' || e.key !== 'Enter' )
|
|
) {
|
|
return;
|
|
}
|
|
// We have to set aria-pressed over here, as NVDA wont recognize it
|
|
// if we do it in the below .each as it seems
|
|
$( this ).attr( 'aria-pressed', 'true' );
|
|
$( '.tab > a' ).each( function ( i, elem ) {
|
|
if ( elem !== e.target ) {
|
|
$( elem ).attr( 'aria-pressed', 'false' );
|
|
}
|
|
} );
|
|
var $sections = $( this ).data( 'context' ).$ui.find( '.sections' );
|
|
var $section = $sections.find( '.section-' + $( this ).parent().attr( 'rel' ) );
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
|
var show = !$section.hasClass( 'section-visible' );
|
|
$sections.find( '.section-visible' )
|
|
.removeClass( 'section-visible' )
|
|
.addClass( 'section-hidden' );
|
|
|
|
$( this )
|
|
.attr( 'aria-expanded', 'false' )
|
|
.parent().parent().find( 'a' ).removeClass( 'current' );
|
|
if ( show ) {
|
|
$section
|
|
.removeClass( 'section-hidden' )
|
|
.attr( 'aria-expanded', 'true' )
|
|
.addClass( 'section-visible' );
|
|
|
|
$( this ).attr( 'aria-expanded', 'true' )
|
|
.addClass( 'current' );
|
|
}
|
|
|
|
// Save the currently visible section
|
|
$.cookie(
|
|
'wikiEditor-' + $( this ).data( 'context' ).instance + '-toolbar-section',
|
|
show ? $section.attr( 'rel' ) : null,
|
|
{ expires: 30, path: '/' }
|
|
);
|
|
|
|
e.preventDefault();
|
|
return false;
|
|
} );
|
|
return $( '<span>' )
|
|
.attr( {
|
|
class: 'tab tab-' + id,
|
|
rel: id
|
|
} )
|
|
.append( $link );
|
|
},
|
|
buildSection: function ( context, id, section ) {
|
|
var $section = $( '<div>' ).attr( {
|
|
class: section.type + ' section section-' + id,
|
|
rel: id,
|
|
id: 'wikiEditor-section-' + id
|
|
} );
|
|
var selected = $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section' );
|
|
var show = selected === id;
|
|
|
|
toolbarModule.fn.reallyBuildSection( context, id, section, $section, section.deferLoad );
|
|
|
|
// Show or hide section
|
|
if ( id !== 'main' && id !== 'secondary' ) {
|
|
$section
|
|
.attr( 'aria-expanded', show.toString() )
|
|
.addClass( show ? 'section-visible' : 'section-hidden' );
|
|
}
|
|
return $section;
|
|
},
|
|
reallyBuildSection: function ( context, id, section, $section, deferLoad ) {
|
|
context.$textarea.trigger( 'wikiEditor-toolbar-buildSection-' + $section.attr( 'rel' ), [ section ] );
|
|
switch ( section.type ) {
|
|
case 'toolbar':
|
|
if ( 'groups' in section ) {
|
|
for ( var group in section.groups ) {
|
|
$section.append(
|
|
toolbarModule.fn.buildGroup( context, group, section.groups[ group ] )
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
case 'booklet':
|
|
var $pages = $( '<div>' )
|
|
.addClass( 'pages' )
|
|
.attr( {
|
|
tabindex: '0',
|
|
role: 'listbox'
|
|
} )
|
|
.on( 'keydown', function ( event ) {
|
|
var $selected = $pages.children().filter( function () {
|
|
return $( this ).css( 'display' ) !== 'none';
|
|
} );
|
|
$.wikiEditor.modules.toolbar.fn.handleKeyDown( $selected.children().first(), event, $pages );
|
|
} );
|
|
var $index = $( '<div>' )
|
|
.addClass( 'index' )
|
|
.attr( {
|
|
tabindex: '0',
|
|
role: 'listbox'
|
|
} )
|
|
.on( 'keydown', function ( event ) {
|
|
$.wikiEditor.modules.toolbar.fn.handleKeyDown( $index, event, $index );
|
|
} );
|
|
if ( 'pages' in section ) {
|
|
for ( var page in section.pages ) {
|
|
$pages.append(
|
|
toolbarModule.fn.buildPage( context, page, section.pages[ page ], deferLoad )
|
|
);
|
|
$index.append(
|
|
toolbarModule.fn.buildBookmark( context, page, section.pages[ page ] )
|
|
);
|
|
}
|
|
}
|
|
$section.append( $index, $pages );
|
|
toolbarModule.fn.updateBookletSelection( context, id, $pages, $index );
|
|
break;
|
|
}
|
|
},
|
|
updateBookletSelection: function ( context, id, $pages, $index ) {
|
|
var cookie = 'wikiEditor-' + context.instance + '-booklet-' + id + '-page',
|
|
selected = $.cookie( cookie );
|
|
// Re-save cookie
|
|
if ( selected !== null ) {
|
|
$.cookie( cookie, selected, { expires: 30, path: '/' } );
|
|
}
|
|
var $selectedIndex = $index.find( '*[rel="' + selected + '"]' );
|
|
if ( $selectedIndex.length === 0 ) {
|
|
$selectedIndex = $index.children().eq( 0 );
|
|
selected = $selectedIndex.attr( 'rel' );
|
|
}
|
|
$pages.children().hide();
|
|
$pages.find( '*[rel="' + selected + '"]' ).show().trigger( 'loadPage' );
|
|
$index.children().removeClass( 'current' );
|
|
$selectedIndex.addClass( 'current' );
|
|
},
|
|
build: function ( context, config ) {
|
|
var $tabs = $( '<div>' ).addClass( 'tabs' ).appendTo( context.modules.toolbar.$toolbar ),
|
|
$sections = $( '<div>' ).addClass( 'sections' ).appendTo( context.modules.toolbar.$toolbar );
|
|
context.modules.toolbar.$toolbar.append( $( '<div>' ).css( 'clear', 'both' ) );
|
|
for ( var section in config ) {
|
|
if ( section === 'main' || section === 'secondary' ) {
|
|
context.modules.toolbar.$toolbar.prepend(
|
|
toolbarModule.fn.buildSection( context, section, config[ section ] )
|
|
);
|
|
} else {
|
|
$sections.append( toolbarModule.fn.buildSection( context, section, config[ section ] ) );
|
|
$tabs.append( toolbarModule.fn.buildTab( context, section, config[ section ] ) );
|
|
}
|
|
}
|
|
setTimeout( function () {
|
|
context.$textarea.trigger( 'wikiEditor-toolbar-doneInitialSections' );
|
|
// Use hook for attaching new toolbar tools to avoid race conditions
|
|
mw.hook( 'wikiEditor.toolbarReady' ).fire( context.$textarea );
|
|
} );
|
|
},
|
|
handleKeyDown: function ( $element, event, $parent ) {
|
|
var $currentItem = $element.find( '.wikiEditor-character-highlighted' ),
|
|
optionOffset = $parent.find( '.wikiEditor-character-highlighted' ).offset(),
|
|
optionTop = optionOffset ? optionOffset.top : 0,
|
|
selectTop = $parent.offset().top;
|
|
|
|
var $nextItem;
|
|
switch ( event.keyCode ) {
|
|
// Up arrow
|
|
case 38:
|
|
if ( $currentItem.length ) {
|
|
$currentItem.removeClass( 'wikiEditor-character-highlighted' );
|
|
$nextItem = $currentItem.prev();
|
|
$nextItem = $nextItem.length ? $nextItem : $currentItem;
|
|
$nextItem.addClass( 'wikiEditor-character-highlighted' );
|
|
} else {
|
|
$element.children().first().addClass( 'wikiEditor-character-highlighted' );
|
|
}
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
break;
|
|
// Down arrow
|
|
case 40:
|
|
if ( $currentItem.length ) {
|
|
$currentItem.removeClass( 'wikiEditor-character-highlighted' );
|
|
$nextItem = $currentItem.next();
|
|
$nextItem = $nextItem.length ? $nextItem : $currentItem;
|
|
$nextItem.addClass( 'wikiEditor-character-highlighted' );
|
|
} else {
|
|
$element.children().first().addClass( 'wikiEditor-character-highlighted' );
|
|
}
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
break;
|
|
// Enter
|
|
case 13:
|
|
$currentItem.trigger( 'click' );
|
|
break;
|
|
}
|
|
$parent.scrollTop( $parent.scrollTop() + ( optionTop - selectTop ) );
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = toolbarModule;
|
|
|
|
}() );
|