/*! Menu - v0.1.4 - 2017-03-18
* https://github.com/filamentgroup/menu
* Copyright (c) 2017 Scott Jehl; Licensed MIT */
window.jQuery = window.jQuery || window.shoestring;
(function( $, w ) {
"use strict";
var componentName = "Menu",
at = {
ariaHidden: "aria-hidden"
},
selectClass = "menu-selected",
focusables = "a,input,[tabindex]";
var menu = function( element ){
if( !element ){
throw new Error( "Element required to initialize object" );
}
this.element = element;
this.$element = $( element );
this.opened = true;
};
menu.prototype.fill = function( items, selectedText ) {
var html = "";
$.each( items, function( i, item ){
html += "
" + item + "";
});
this.$element.find( "ol,ul" ).html( html );
};
menu.prototype.moveSelected = function( placement, focus ){
var $items = this.$element.find( "li" ),
$selected = $items.filter( "." + selectClass ),
$nextSelected;
if( !$selected.length || placement === "start" ){
$nextSelected = $items.eq( 0 );
}
else if( placement === "next" ){
$nextSelected = $selected.next();
if( !$nextSelected.length ){
$nextSelected = $items.eq( 0 );
}
}
else {
$nextSelected = $selected.prev();
if( !$nextSelected.length ){
$nextSelected = $items.eq( $items.length - 1 );
}
}
$selected.removeClass( selectClass );
$nextSelected.addClass( selectClass );
if( focus || $( w.document.activeElement ).closest( $selected ).length ){
if( $nextSelected.is( focusables ) ){
$nextSelected[ 0 ].focus();
}
else{
var $focusChild = $nextSelected.find( focusables );
if( $focusChild.length ){
$focusChild[ 0 ].focus();
}
}
}
};
menu.prototype.getSelectedElement = function(){
return this.$element.find( "li." + selectClass );
};
menu.prototype.selectActive = function(){
var trigger = this.$element.data( componentName + "-trigger" );
var $selected = this.getSelectedElement();
if( trigger && $( trigger ).is( "input" ) ){
trigger.value = $selected.text();
}
$selected.trigger( componentName + ":select" );
this.close();
return $selected.text();
};
menu.prototype.keycodes = {
38 : function(e) {
this.moveSelected( "prev" );
e.preventDefault();
},
40 : function(e){
this.moveSelected( "next" );
e.preventDefault();
},
13 : function(){
// return the selected value
return this.selectActive();
},
9 : function(e){
this.moveSelected( e.shiftKey ? "prev" : "next" );
e.preventDefault();
},
27 : function(){
this.close();
}
};
menu.prototype.keyDown = function( e ){
var fn = this.keycodes[e.keyCode] || function(){};
return fn.call( this, e );
};
menu.prototype._bindKeyHandling = function(){
var self = this;
this.$element
.bind( "keydown", function( e ){
self.keyDown( e );
} )
.bind( "mouseover", function( e ){
self.$element.find( "." + selectClass ).removeClass( selectClass );
$( e.target ).closest( "li" ).addClass( selectClass );
})
.bind( "mouseleave", function( e ){
$( e.target ).closest( "li" ).removeClass( selectClass );
})
.bind( "click", function(){
self.selectActive();
});
};
menu.prototype.open = function( trigger, sendFocus ){
if( this.opened ){
return;
}
this.$element.attr( at.ariaHidden, false );
this.$element.data( componentName + "-trigger", trigger );
this.opened = true;
this.moveSelected( "start", sendFocus );
this.$element.trigger( componentName + ":open" );
};
menu.prototype.close = function(){
if( !this.opened ){
return;
}
this.$element.attr( at.ariaHidden, true );
this.opened = false;
var $trigger = this.$element.data( componentName + "-trigger" );
if( $trigger ){
$trigger.focus();
}
this.$element.trigger( componentName + ":close" );
};
menu.prototype.toggle = function( trigger, sendFocus ){
this[ this.opened ? "close" : "open" ]( trigger, sendFocus );
};
menu.prototype.init = function(){
// prevent re-init
if( this.$element.data( componentName ) ) {
return;
}
this.$element.data( componentName, this );
this.$element.attr( "role", "menu" );
this.close();
var self = this;
$( document ).bind( "mouseup", function(event){
// only close the menu if the click is outside the menu element
if( ! $(event.target).closest( self.$element[0] ).length ){
self.close();
}
});
this._bindKeyHandling();
this.$element.trigger( componentName + ":init" );
};
menu.prototype.isOpen = function(){
return this.opened;
};
(w.componentNamespace = w.componentNamespace || w)[ componentName ] = menu;
}( jQuery, this ));