2017-12-28 00:02:01 +01:00
/*global SelectBox, gettext, interpolate, quickElement, SelectFilter*/
/ *
SelectFilter2 - Turns a multiple - select box into a filter interface .
Requires jQuery , core . js , and SelectBox . js .
* /
( function ( $ ) {
'use strict' ;
function findForm ( node ) {
// returns the node of the form containing the given node
if ( node . tagName . toLowerCase ( ) !== 'form' ) {
return findForm ( node . parentNode ) ;
}
return node ;
}
window . SelectFilter = {
init : function ( field _id , field _name , is _stacked ) {
if ( field _id . match ( /__prefix__/ ) ) {
// Don't initialize on empty forms.
return ;
}
var from _box = document . getElementById ( field _id ) ;
from _box . id += '_from' ; // change its ID
from _box . className = 'filtered' ;
var ps = from _box . parentNode . getElementsByTagName ( 'p' ) ;
for ( var i = 0 ; i < ps . length ; i ++ ) {
if ( ps [ i ] . className . indexOf ( "info" ) !== - 1 ) {
// Remove <p class="info">, because it just gets in the way.
from _box . parentNode . removeChild ( ps [ i ] ) ;
} else if ( ps [ i ] . className . indexOf ( "help" ) !== - 1 ) {
// Move help text up to the top so it isn't below the select
// boxes or wrapped off on the side to the right of the add
// button:
from _box . parentNode . insertBefore ( ps [ i ] , from _box . parentNode . firstChild ) ;
}
}
// <div class="selector"> or <div class="selector stacked">
var selector _div = quickElement ( 'div' , from _box . parentNode ) ;
selector _div . className = is _stacked ? 'selector stacked' : 'selector' ;
// <div class="selector-available">
var selector _available = quickElement ( 'div' , selector _div ) ;
selector _available . className = 'selector-available' ;
var title _available = quickElement ( 'h2' , selector _available , interpolate ( gettext ( 'Available %s' ) + ' ' , [ field _name ] ) ) ;
quickElement (
'span' , title _available , '' ,
'class' , 'help help-tooltip help-icon' ,
'title' , interpolate (
gettext (
'This is the list of available %s. You may choose some by ' +
'selecting them in the box below and then clicking the ' +
'"Choose" arrow between the two boxes.'
) ,
[ field _name ]
)
) ;
var filter _p = quickElement ( 'p' , selector _available , '' , 'id' , field _id + '_filter' ) ;
filter _p . className = 'selector-filter' ;
var search _filter _label = quickElement ( 'label' , filter _p , '' , 'for' , field _id + '_input' ) ;
quickElement (
'span' , search _filter _label , '' ,
'class' , 'help-tooltip search-label-icon' ,
'title' , interpolate ( gettext ( "Type into this box to filter down the list of available %s." ) , [ field _name ] )
) ;
filter _p . appendChild ( document . createTextNode ( ' ' ) ) ;
var filter _input = quickElement ( 'input' , filter _p , '' , 'type' , 'text' , 'placeholder' , gettext ( "Filter" ) ) ;
filter _input . id = field _id + '_input' ;
selector _available . appendChild ( from _box ) ;
var choose _all = quickElement ( 'a' , selector _available , gettext ( 'Choose all' ) , 'title' , interpolate ( gettext ( 'Click to choose all %s at once.' ) , [ field _name ] ) , 'href' , '#' , 'id' , field _id + '_add_all_link' ) ;
choose _all . className = 'selector-chooseall' ;
// <ul class="selector-chooser">
var selector _chooser = quickElement ( 'ul' , selector _div ) ;
selector _chooser . className = 'selector-chooser' ;
var add _link = quickElement ( 'a' , quickElement ( 'li' , selector _chooser ) , gettext ( 'Choose' ) , 'title' , gettext ( 'Choose' ) , 'href' , '#' , 'id' , field _id + '_add_link' ) ;
add _link . className = 'selector-add' ;
var remove _link = quickElement ( 'a' , quickElement ( 'li' , selector _chooser ) , gettext ( 'Remove' ) , 'title' , gettext ( 'Remove' ) , 'href' , '#' , 'id' , field _id + '_remove_link' ) ;
remove _link . className = 'selector-remove' ;
// <div class="selector-chosen">
var selector _chosen = quickElement ( 'div' , selector _div ) ;
selector _chosen . className = 'selector-chosen' ;
var title _chosen = quickElement ( 'h2' , selector _chosen , interpolate ( gettext ( 'Chosen %s' ) + ' ' , [ field _name ] ) ) ;
quickElement (
'span' , title _chosen , '' ,
'class' , 'help help-tooltip help-icon' ,
'title' , interpolate (
gettext (
'This is the list of chosen %s. You may remove some by ' +
'selecting them in the box below and then clicking the ' +
'"Remove" arrow between the two boxes.'
) ,
[ field _name ]
)
) ;
var to _box = quickElement ( 'select' , selector _chosen , '' , 'id' , field _id + '_to' , 'multiple' , 'multiple' , 'size' , from _box . size , 'name' , from _box . getAttribute ( 'name' ) ) ;
to _box . className = 'filtered' ;
var clear _all = quickElement ( 'a' , selector _chosen , gettext ( 'Remove all' ) , 'title' , interpolate ( gettext ( 'Click to remove all chosen %s at once.' ) , [ field _name ] ) , 'href' , '#' , 'id' , field _id + '_remove_all_link' ) ;
clear _all . className = 'selector-clearall' ;
from _box . setAttribute ( 'name' , from _box . getAttribute ( 'name' ) + '_old' ) ;
// Set up the JavaScript event handlers for the select box filter interface
var move _selection = function ( e , elem , move _func , from , to ) {
if ( elem . className . indexOf ( 'active' ) !== - 1 ) {
move _func ( from , to ) ;
SelectFilter . refresh _icons ( field _id ) ;
}
e . preventDefault ( ) ;
} ;
choose _all . addEventListener ( 'click' , function ( e ) {
move _selection ( e , this , SelectBox . move _all , field _id + '_from' , field _id + '_to' ) ;
} ) ;
add _link . addEventListener ( 'click' , function ( e ) {
move _selection ( e , this , SelectBox . move , field _id + '_from' , field _id + '_to' ) ;
} ) ;
remove _link . addEventListener ( 'click' , function ( e ) {
move _selection ( e , this , SelectBox . move , field _id + '_to' , field _id + '_from' ) ;
} ) ;
clear _all . addEventListener ( 'click' , function ( e ) {
move _selection ( e , this , SelectBox . move _all , field _id + '_to' , field _id + '_from' ) ;
} ) ;
filter _input . addEventListener ( 'keypress' , function ( e ) {
SelectFilter . filter _key _press ( e , field _id ) ;
} ) ;
filter _input . addEventListener ( 'keyup' , function ( e ) {
SelectFilter . filter _key _up ( e , field _id ) ;
} ) ;
filter _input . addEventListener ( 'keydown' , function ( e ) {
SelectFilter . filter _key _down ( e , field _id ) ;
} ) ;
selector _div . addEventListener ( 'change' , function ( e ) {
if ( e . target . tagName === 'SELECT' ) {
SelectFilter . refresh _icons ( field _id ) ;
}
} ) ;
selector _div . addEventListener ( 'dblclick' , function ( e ) {
if ( e . target . tagName === 'OPTION' ) {
if ( e . target . closest ( 'select' ) . id === field _id + '_to' ) {
SelectBox . move ( field _id + '_to' , field _id + '_from' ) ;
} else {
SelectBox . move ( field _id + '_from' , field _id + '_to' ) ;
}
SelectFilter . refresh _icons ( field _id ) ;
}
} ) ;
findForm ( from _box ) . addEventListener ( 'submit' , function ( ) {
SelectBox . select _all ( field _id + '_to' ) ;
} ) ;
SelectBox . init ( field _id + '_from' ) ;
SelectBox . init ( field _id + '_to' ) ;
// Move selected from_box options to to_box
SelectBox . move ( field _id + '_from' , field _id + '_to' ) ;
if ( ! is _stacked ) {
// In horizontal mode, give the same height to the two boxes.
2020-02-15 17:56:36 +01:00
var j _from _box = $ ( '#' + field _id + '_from' ) ;
var j _to _box = $ ( '#' + field _id + '_to' ) ;
j _to _box . height ( $ ( filter _p ) . outerHeight ( ) + j _from _box . outerHeight ( ) ) ;
2017-12-28 00:02:01 +01:00
}
// Initial icon refresh
SelectFilter . refresh _icons ( field _id ) ;
} ,
any _selected : function ( field ) {
var any _selected = false ;
try {
// Temporarily add the required attribute and check validity.
// This is much faster in WebKit browsers than the fallback.
field . attr ( 'required' , 'required' ) ;
any _selected = field . is ( ':valid' ) ;
field . removeAttr ( 'required' ) ;
} catch ( e ) {
// Browsers that don't support :valid (IE < 10)
any _selected = field . find ( 'option:selected' ) . length > 0 ;
}
return any _selected ;
} ,
refresh _icons : function ( field _id ) {
var from = $ ( '#' + field _id + '_from' ) ;
var to = $ ( '#' + field _id + '_to' ) ;
// Active if at least one item is selected
$ ( '#' + field _id + '_add_link' ) . toggleClass ( 'active' , SelectFilter . any _selected ( from ) ) ;
$ ( '#' + field _id + '_remove_link' ) . toggleClass ( 'active' , SelectFilter . any _selected ( to ) ) ;
// Active if the corresponding box isn't empty
$ ( '#' + field _id + '_add_all_link' ) . toggleClass ( 'active' , from . find ( 'option' ) . length > 0 ) ;
$ ( '#' + field _id + '_remove_all_link' ) . toggleClass ( 'active' , to . find ( 'option' ) . length > 0 ) ;
} ,
filter _key _press : function ( event , field _id ) {
var from = document . getElementById ( field _id + '_from' ) ;
// don't submit form if user pressed Enter
if ( ( event . which && event . which === 13 ) || ( event . keyCode && event . keyCode === 13 ) ) {
from . selectedIndex = 0 ;
SelectBox . move ( field _id + '_from' , field _id + '_to' ) ;
from . selectedIndex = 0 ;
event . preventDefault ( ) ;
return false ;
}
} ,
filter _key _up : function ( event , field _id ) {
var from = document . getElementById ( field _id + '_from' ) ;
var temp = from . selectedIndex ;
SelectBox . filter ( field _id + '_from' , document . getElementById ( field _id + '_input' ) . value ) ;
from . selectedIndex = temp ;
return true ;
} ,
filter _key _down : function ( event , field _id ) {
var from = document . getElementById ( field _id + '_from' ) ;
// right arrow -- move across
if ( ( event . which && event . which === 39 ) || ( event . keyCode && event . keyCode === 39 ) ) {
var old _index = from . selectedIndex ;
SelectBox . move ( field _id + '_from' , field _id + '_to' ) ;
from . selectedIndex = ( old _index === from . length ) ? from . length - 1 : old _index ;
return false ;
}
// down arrow -- wrap around
if ( ( event . which && event . which === 40 ) || ( event . keyCode && event . keyCode === 40 ) ) {
from . selectedIndex = ( from . length === from . selectedIndex + 1 ) ? 0 : from . selectedIndex + 1 ;
}
// up arrow -- wrap around
if ( ( event . which && event . which === 38 ) || ( event . keyCode && event . keyCode === 38 ) ) {
from . selectedIndex = ( from . selectedIndex === 0 ) ? from . length - 1 : from . selectedIndex - 1 ;
}
return true ;
}
} ;
window . addEventListener ( 'load' , function ( e ) {
$ ( 'select.selectfilter, select.selectfilterstacked' ) . each ( function ( ) {
var $el = $ ( this ) ,
data = $el . data ( ) ;
SelectFilter . init ( $el . attr ( 'id' ) , data . fieldName , parseInt ( data . isStacked , 10 ) ) ;
} ) ;
} ) ;
} ) ( django . jQuery ) ;