(function(){

// This is for grouping buttons into a bar // takes an array of `L.easyButton`s and // then the usual `.addTo(map)` L.Control.EasyBar = L.Control.extend({

options: {
  position:       'topleft',  // part of leaflet's defaults
  id:             null,       // an id to tag the Bar with
  leafletClasses: true        // use leaflet classes?
},

initialize: function(buttons, options){

  if(options){
    L.Util.setOptions( this, options );
  }

  this._buildContainer();
  this._buttons = [];

  for(var i = 0; i < buttons.length; i++){
    buttons[i]._bar = this;
    buttons[i]._container = buttons[i].button;
    this._buttons.push(buttons[i]);
    this.container.appendChild(buttons[i].button);
  }

},

_buildContainer: function(){
  this._container = this.container = L.DomUtil.create('div', '');
  this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
  this.options.id && (this.container.id = this.options.id);
},

enable: function(){
  L.DomUtil.addClass(this.container, 'enabled');
  L.DomUtil.removeClass(this.container, 'disabled');
  this.container.setAttribute('aria-hidden', 'false');
  return this;
},

disable: function(){
  L.DomUtil.addClass(this.container, 'disabled');
  L.DomUtil.removeClass(this.container, 'enabled');
  this.container.setAttribute('aria-hidden', 'true');
  return this;
},

onAdd: function () {
  return this.container;
},

addTo: function (map) {
  this._map = map;

  for(var i = 0; i < this._buttons.length; i++){
    this._buttons[i]._map = map;
  }

  var container = this._container = this.onAdd(map),
      pos = this.getPosition(),
      corner = map._controlCorners[pos];

  L.DomUtil.addClass(container, 'leaflet-control');

  if (pos.indexOf('bottom') !== -1) {
    corner.insertBefore(container, corner.firstChild);
  } else {
    corner.appendChild(container);
  }

  return this;
}

});

L.easyBar = function(){

var args = [L.Control.EasyBar];
for(var i = 0; i < arguments.length; i++){
  args.push( arguments[i] );
}
return new (Function.prototype.bind.apply(L.Control.EasyBar, args));

};

// L.EasyButton is the actual buttons // can be called without being grouped into a bar L.Control.EasyButton = L.Control.extend({

options: {
  position:  'topleft',       // part of leaflet's defaults

  id:        null,            // an id to tag the button with

  type:      'replace',       // [(replace|animate)]
                              // replace swaps out elements
                              // animate changes classes with all elements inserted

  states:    [],              // state names look like this
                              // {
                              //   stateName: 'untracked',
                              //   onClick: function(){ handle_nav_manually(); };
                              //   title: 'click to make inactive',
                              //   icon: 'fa-circle',    // wrapped with <a>
                              // }

  leafletClasses:   true,     // use leaflet styles for the button
  tagName:          'button',
},

initialize: function(icon, onClick, title, id){

  // clear the states manually
  this.options.states = [];

  // add id to options
  if(id != null){
    this.options.id = id;
  }

  // storage between state functions
  this.storage = {};

  // is the last item an object?
  if( typeof arguments[arguments.length-1] === 'object' ){

    // if so, it should be the options
    L.Util.setOptions( this, arguments[arguments.length-1] );
  }

  // if there aren't any states in options
  // use the early params
  if( this.options.states.length === 0 &&
      typeof icon  === 'string' &&
      typeof onClick === 'function'){

    // turn the options object into a state
    this.options.states.push({
      icon: icon,
      onClick: onClick,
      title: typeof title === 'string' ? title : ''
    });
  }

  // curate and move user's states into
  // the _states for internal use
  this._states = [];

  for(var i = 0; i < this.options.states.length; i++){
    this._states.push( new State(this.options.states[i], this) );
  }

  this._buildButton();

  this._activateState(this._states[0]);

},

_buildButton: function(){

  this.button = L.DomUtil.create(this.options.tagName, '');

  if (this.options.tagName === 'button') {
      this.button.setAttribute('type', 'button');
  }

  if (this.options.id ){
    this.button.id = this.options.id;
  }

  if (this.options.leafletClasses){
    L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
  }

  // don't let double clicks and mousedown get to the map
  L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
  L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
  L.DomEvent.addListener(this.button, 'mouseup', L.DomEvent.stop);

  // take care of normal clicks
  L.DomEvent.addListener(this.button,'click', function(e){
    L.DomEvent.stop(e);
    this._currentState.onClick(this, this._map ? this._map : null );
    this._map && this._map.getContainer().focus();
  }, this);

  // prep the contents of the control
  if(this.options.type == 'replace'){
    this.button.appendChild(this._currentState.icon);
  } else {
    for(var i=0;i<this._states.length;i++){
      this.button.appendChild(this._states[i].icon);
    }
  }
},

_currentState: {
  // placeholder content
  stateName: 'unnamed',
  icon: (function(){ return document.createElement('span'); })()
},

_states: null, // populated on init

state: function(newState){

  // when called with no args, it's a getter
  if (arguments.length === 0) {
    return this._currentState.stateName;
  }

  // activate by name
  if(typeof newState == 'string'){

    this._activateStateNamed(newState);

  // activate by index
  } else if (typeof newState == 'number'){

    this._activateState(this._states[newState]);
  }

  return this;
},

_activateStateNamed: function(stateName){
  for(var i = 0; i < this._states.length; i++){
    if( this._states[i].stateName == stateName ){
      this._activateState( this._states[i] );
    }
  }
},

_activateState: function(newState){

  if( newState === this._currentState ){

    // don't touch the dom if it'll just be the same after
    return;

  } else {

    // swap out elements... if you're into that kind of thing
    if( this.options.type == 'replace' ){
      this.button.appendChild(newState.icon);
      this.button.removeChild(this._currentState.icon);
    }

    if( newState.title ){
      this.button.title = newState.title;
    } else {
      this.button.removeAttribute('title');
    }

    // update classes for animations
    for(var i=0;i<this._states.length;i++){
      L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
      L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
    }

    // update classes for animations
    L.DomUtil.removeClass(this.button, this._currentState.stateName + '-active');
    L.DomUtil.addClass(this.button, newState.stateName + '-active');

    // update the record
    this._currentState = newState;

  }
},

enable: function(){
  L.DomUtil.addClass(this.button, 'enabled');
  L.DomUtil.removeClass(this.button, 'disabled');
  this.button.setAttribute('aria-hidden', 'false');
  return this;
},

disable: function(){
  L.DomUtil.addClass(this.button, 'disabled');
  L.DomUtil.removeClass(this.button, 'enabled');
  this.button.setAttribute('aria-hidden', 'true');
  return this;
},

onAdd: function(map){
  var bar = L.easyBar([this], {
    position: this.options.position,
    leafletClasses: this.options.leafletClasses
  });
  this._anonymousBar = bar;
  this._container = bar.container;
  return this._anonymousBar.container;
},

removeFrom: function (map) {
  if (this._map === map)
    this.remove();
  return this;
},

});

L.easyButton = function(/* args will pass automatically */){

var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
return new (Function.prototype.bind.apply(L.Control.EasyButton, args));

};

/*************************

*
* util functions
*
*************************/

// constructor for states so only curated // states end up getting called function State(template, easyButton){

this.title = template.title;
this.stateName = template.stateName ? template.stateName : 'unnamed-state';

// build the wrapper
this.icon = L.DomUtil.create('span', '');

L.DomUtil.addClass(this.icon, 'button-state state-' + this.stateName.replace(/(^\s*|\s*$)/g,''));
this.icon.innerHTML = buildIcon(template.icon);
this.onClick = L.Util.bind(template.onClick?template.onClick:function(){}, easyButton);

}

function buildIcon(ambiguousIconString) {

var tmpIcon;

// does this look like html? (i.e. not a class)
if( ambiguousIconString.match(/[&;=<>"']/) ){

  // if so, the user should have put in html
  // so move forward as such
  tmpIcon = ambiguousIconString;

// then it wasn't html, so
// it's a class list, figure out what kind
} else {
    ambiguousIconString = ambiguousIconString.replace(/(^\s*|\s*$)/g,'');
    tmpIcon = L.DomUtil.create('span', '');

    if( ambiguousIconString.indexOf('fa-') === 0 ){
      L.DomUtil.addClass(tmpIcon, 'fa '  + ambiguousIconString)
    } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
      L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
    } else {
      L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
    }

    // make this a string so that it's easy to set innerHTML below
    tmpIcon = tmpIcon.outerHTML;
}

return tmpIcon;

}

})();