// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
// Uses AMD or browser globals to create a jQuery plugin.
(function (factory) {
if (typeof define === 'function' && define.amd) {
//TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
// (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
// relying on jquery being loaded globally* in order to preserve plugins.
define([], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function () {
var _l = window._l || function( s ){ return s; };
/** searchInput: (jQuery plugin)
* Creates a search input, a clear button, and loading indicator
* within the selected node.
*
* When the user either presses return or enters some minimal number
* of characters, a callback is called. Pressing ESC when the input
* is focused will clear the input and call a separate callback.
*/
function searchInput( parentNode, options ){
//TODO: consolidate with tool menu functionality, use there
var KEYCODE_ESC = 27,
KEYCODE_RETURN = 13,
$parentNode = $( parentNode ),
firstSearch = true,
defaults = {
initialVal : '',
name : 'search',
placeholder : 'search',
classes : '',
onclear : function(){},
onfirstsearch : null,
onsearch : function( inputVal ){},
minSearchLen : 0,
escWillClear : true,
oninit : function(){}
};
// .................................................................... input rendering and events
// visually clear the search, trigger an event, and call the callback
function clearSearchInput( event ){
var $input = $( this ).parent().children( 'input' );
//console.debug( this, 'clear', $input );
$input.focus().val( '' ).trigger( 'clear:searchInput' );
options.onclear();
}
// search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
function search( event, searchTerms ){
//console.debug( this, 'searching', searchTerms );
//TODO: I don't think this is classic jq custom event form? search.searchInput?
$( this ).trigger( 'search:searchInput', searchTerms );
if( typeof options.onfirstsearch === 'function' && firstSearch ){
firstSearch = false;
options.onfirstsearch( searchTerms );
} else {
options.onsearch( searchTerms );
}
}
// .................................................................... input rendering and events
function inputTemplate(){
// class search-query is bootstrap 2.3 style that now lives in base.less
return [ '' ].join( '' );
}
// the search input that responds to keyboard events and displays the search value
function $input(){
return $( inputTemplate() )
// select all text on a focus
.focus( function( event ){
$( this ).select();
})
// attach behaviors to esc, return if desired, search on some min len string
.keyup( function( event ){
event.preventDefault();
event.stopPropagation();
//TODO: doesn't work
if( !$( this ).val() ){ $( this ).blur(); }
// esc key will clear if desired
if( event.which === KEYCODE_ESC && options.escWillClear ){
clearSearchInput.call( this, event );
} else {
var searchTerms = $( this ).val();
// return key or the search string len > minSearchLen (if not 0) triggers search
if( ( event.which === KEYCODE_RETURN )
|| ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
search.call( this, event, searchTerms );
} else if( !searchTerms.length ){
clearSearchInput.call( this, event );
}
}
})
.on( 'change', function( event ){
search.call( this, event, $( this ).val() );
})
.val( options.initialVal );
}
// .................................................................... clear button rendering and events
// a button for clearing the search bar, placed on the right hand side
function $clearBtn(){
return $([ '' ].join('') )
.tooltip({ placement: 'bottom' })
.click( function( event ){
clearSearchInput.call( this, event );
});
}
// .................................................................... loadingIndicator rendering
// a button for clearing the search bar, placed on the right hand side
function $loadingIndicator(){
return $([ '' ].join('') )
.hide().tooltip({ placement: 'bottom' });
}
// .................................................................... commands
// visually swap the load, clear buttons
function toggleLoadingIndicator(){
$parentNode.find( '.search-loading' ).toggle();
$parentNode.find( '.search-clear' ).toggle();
}
// .................................................................... init
// string command (not constructor)
if( jQuery.type( options ) === 'string' ){
if( options === 'toggle-loading' ){
toggleLoadingIndicator();
}
return $parentNode;
}
// initial render
if( jQuery.type( options ) === 'object' ){
options = jQuery.extend( true, {}, defaults, options );
}
//NOTE: prepended
return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
}
// as jq plugin
jQuery.fn.extend({
searchInput : function $searchInput( options ){
return this.each( function(){
return searchInput( this, options );
});
}
});
}));