(function() {
  var __slice = [].slice;

  (function($) {
    var Bus, Leg, methods, tourbus, uniqueId, _addRule, _assemble, _busses, _dataProp, _include, _tours;
    tourbus = $.tourbus = function() {
      var args, method;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      method = args[0];
      if (methods.hasOwnProperty(method)) {
        args = args.slice(1);
      } else if (method instanceof $) {
        method = 'build';
      } else if (typeof method === 'string') {
        method = 'build';
        args[0] = $(args[0]);
      } else {
        $.error("Unknown method of $.tourbus --", args);
      }
      return methods[method].apply(this, args);
    };
    $.fn.tourbus = function() {
      var args;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return this.each(function() {
        args.unshift($(this));
        tourbus.apply(null, ['build'].concat(__slice.call(args)));
        return this;
      });
    };
    methods = {
      build: function(el, options) {
        var built;
        if (options == null) {
          options = {};
        }
        options = $.extend(true, {}, tourbus.defaults, options);
        built = [];
        if (!(el instanceof $)) {
          el = $(el);
        }
        el.each(function() {
          return built.push(_assemble(this, options));
        });
        if (built.length === 0) {
          $.error("" + el.selector + " was not found!");
        }
        if (built.length === 1) {
          return built[0];
        }
        return built;
      },
      destroyAll: function() {
        var bus, index, _results;
        _results = [];
        for (index in _busses) {
          bus = _busses[index];
          _results.push(bus.destroy());
        }
        return _results;
      },
      expose: function(global) {
        return global.tourbus = {
          Bus: Bus,
          Leg: Leg
        };
      }
    };
    tourbus.defaults = {
      debug: false,
      autoDepart: false,
      target: 'body',
      startAt: 0,
      onDepart: function() {
        return null;
      },
      onStop: function() {
        return null;
      },
      onLegStart: function() {
        return null;
      },
      onLegEnd: function() {
        return null;
      },
      leg: {
        scrollTo: null,
        scrollSpeed: 150,
        scrollContext: 100,
        orientation: 'bottom',
        align: 'left',
        width: 'auto',
        margin: 10,
        top: null,
        left: null,
        arrow: "35%"
      }
    };
    /* Internal
    */

    Bus = (function() {

      function Bus(el, options) {
        this.id = uniqueId();
        this.$target = $(options.target);
        this.$el = $(el);
        this.$el.data({
          tourbus: this
        });
        this.options = options;
        this.currentLegIndex = null;
        this.legs = null;
        this.legEls = this.$el.children('li');
        this.totalLegs = this.legEls.length;
        this._setupEvents();
        if (this.options.autoDepart) {
          this.$el.trigger('depart.tourbus');
        }
        this._log('built tourbus with el', el.toString(), 'and options', this.options);
      }

      Bus.prototype.depart = function() {
        this.running = true;
        this.options.onDepart(this);
        this._log('departing', this);
        this.legs = this._buildLegs();
        this.currentLegIndex = this.options.startAt;
        return this.showLeg();
      };

      Bus.prototype.stop = function() {
        if (!this.running) {
          return;
        }
        if (this.legs) {
          $.each(this.legs, $.proxy(this.hideLeg, this));
        }
        this.currentLegIndex = this.options.startAt;
        this.options.onStop(this);
        return this.running = false;
      };

      Bus.prototype.on = function(event, selector, fn) {
        return this.$target.on(event, selector, fn);
      };

      Bus.prototype.currentLeg = function() {
        if (this.currentLegIndex === null) {
          return null;
        }
        return this.legs[this.currentLegIndex];
      };

      Bus.prototype.showLeg = function(index) {
        var leg, preventDefault;
        if (index == null) {
          index = this.currentLegIndex;
        }
        leg = this.legs[index];
        this._log('showLeg:', leg);
        preventDefault = this.options.onLegStart(leg, this);
        if (preventDefault !== false) {
          return leg.show();
        }
      };

      Bus.prototype.hideLeg = function(index) {
        var leg, preventDefault;
        if (index == null) {
          index = this.currentLegIndex;
        }
        leg = this.legs[index];
        this._log('hideLeg:', leg);
        preventDefault = this.options.onLegEnd(leg, this);
        if (preventDefault !== false) {
          return leg.hide();
        }
      };

      Bus.prototype.repositionLegs = function() {
        if (this.legs) {
          return $.each(this.legs, function() {
            return this.reposition();
          });
        }
      };

      Bus.prototype.next = function() {
        this.hideLeg();
        this.currentLegIndex++;
        if (this.currentLegIndex > this.totalLegs - 1) {
          return this.stop();
        }
        return this.showLeg();
      };

      Bus.prototype.prev = function(cb) {
        this.hideLeg();
        this.currentLegIndex--;
        if (this.currentLegIndex < 0) {
          return this.stop();
        }
        return this.showLeg();
      };

      Bus.prototype.destroy = function() {
        if (this.legs) {
          $.each(this.legs, function() {
            return this.destroy();
          });
        }
        this.legs = null;
        delete _busses[this.id];
        return this._teardownEvents();
      };

      Bus.prototype._buildLegs = function() {
        var _this = this;
        if (this.legs) {
          $.each(this.legs, function(_, leg) {
            return leg.destroy();
          });
        }
        return $.map(this.legEls, function(legEl, i) {
          var $legEl, data, leg;
          $legEl = $(legEl);
          data = $legEl.data();
          leg = new Leg({
            content: $legEl.html(),
            target: data.el || 'body',
            bus: _this,
            index: i,
            rawData: data
          });
          leg.render();
          _this.$target.append(leg.$el);
          leg._position();
          leg.hide();
          return leg;
        });
      };

      Bus.prototype._log = function() {
        if (!this.options.debug) {
          return;
        }
        return console.log.apply(console, ["TOURBUS " + this.id + ":"].concat(__slice.call(arguments)));
      };

      Bus.prototype._setupEvents = function() {
        this.$el.on('depart.tourbus', $.proxy(this.depart, this));
        this.$el.on('stop.tourbus', $.proxy(this.stop, this));
        this.$el.on('next.tourbus', $.proxy(this.next, this));
        return this.$el.on('prev.tourbus', $.proxy(this.prev, this));
      };

      Bus.prototype._teardownEvents = function() {
        return this.$el.off('.tourbus');
      };

      return Bus;

    })();
    Leg = (function() {

      function Leg(options) {
        this.bus = options.bus;
        this.rawData = options.rawData;
        this.content = options.content;
        this.index = options.index;
        this.options = options;
        this.$target = $(options.target);
        if (this.$target.length === 0) {
          throw "" + this.$target.selector + " is not an element!";
        }
        this._setupOptions();
        this._configureElement();
        this._configureTarget();
        this._configureScroll();
        this._setupEvents();
        this.bus._log("leg " + this.index + " made with options", this.options);
      }

      Leg.prototype.render = function() {
        var arrowClass, html;
        arrowClass = this.options.orientation === 'centered' ? '' : 'tourbus-arrow';
        this.$el.addClass(" " + arrowClass + " tourbus-arrow-" + this.options.orientation + " ");
        html = "<div class='tourbus-leg-inner'>\n  " + this.content + "\n</div>";
        this.$el.css({
          width: this.options.width
        }).html(html);
        return this;
      };

      Leg.prototype.destroy = function() {
        this.$el.remove();
        return this._teardownEvents();
      };

      Leg.prototype.reposition = function() {
        this._configureTarget();
        return this._position();
      };

      Leg.prototype._position = function() {
        var css, keys, rule, selector;
        if (this.options.orientation !== 'centered') {
          rule = {};
          keys = {
            top: 'left',
            bottom: 'left',
            left: 'top',
            right: 'top'
          };
          if (typeof this.options.arrow === 'number') {
            this.options.arrow += 'px';
          }
          rule[keys[this.options.orientation]] = this.options.arrow;
          selector = "#" + this.id + ".tourbus-arrow";
          this.bus._log("adding rule for " + this.id, rule);
          _addRule("" + selector + ":before, " + selector + ":after", rule);
        }
        css = this._offsets();
        this.bus._log('setting offsets on leg', css);
        return this.$el.css(css);
      };

      Leg.prototype.show = function() {
        this.$el.css({
          visibility: 'visible',
          opacity: 1.0,
          zIndex: 9999
        });
        return this.scrollIntoView();
      };

      Leg.prototype.hide = function() {
        if (this.bus.options.debug) {
          return this.$el.css({
            visibility: 'visible',
            opacity: 0.4,
            zIndex: 0
          });
        } else {
          return this.$el.css({
            visibility: 'hidden'
          });
        }
      };

      Leg.prototype.scrollIntoView = function() {
        var scrollTarget;
        if (!this.willScroll) {
          return;
        }
        scrollTarget = _dataProp(this.options.scrollTo, this.$el);
        this.bus._log('scrolling to', scrollTarget, this.scrollSettings);
        return $.scrollTo(scrollTarget, this.scrollSettings);
      };

      Leg.prototype._setupOptions = function() {
        var globalOptions;
        globalOptions = this.bus.options.leg;
        this.options.top = _dataProp(this.rawData.top, globalOptions.top);
        this.options.left = _dataProp(this.rawData.left, globalOptions.left);
        this.options.scrollTo = _dataProp(this.rawData.scrollTo, globalOptions.scrollTo);
        this.options.scrollSpeed = _dataProp(this.rawData.scrollSpeed, globalOptions.scrollSpeed);
        this.options.scrollContext = _dataProp(this.rawData.scrollContext, globalOptions.scrollContext);
        this.options.margin = _dataProp(this.rawData.margin, globalOptions.margin);
        this.options.arrow = this.rawData.arrow || globalOptions.arrow;
        this.options.align = this.rawData.align || globalOptions.align;
        this.options.width = this.rawData.width || globalOptions.width;
        return this.options.orientation = this.rawData.orientation || globalOptions.orientation;
      };

      Leg.prototype._configureElement = function() {
        this.id = "tourbus-leg-id-" + this.bus.id + "-" + this.options.index;
        this.$el = $("<div class='tourbus-leg'></div>");
        this.el = this.$el[0];
        this.$el.attr({
          id: this.id
        });
        return this.$el.css({
          zIndex: 9999
        });
      };

      Leg.prototype._setupEvents = function() {
        this.$el.on('click', '.tourbus-next', $.proxy(this.bus.next, this.bus));
        this.$el.on('click', '.tourbus-prev', $.proxy(this.bus.prev, this.bus));
        return this.$el.on('click', '.tourbus-stop', $.proxy(this.bus.stop, this.bus));
      };

      Leg.prototype._teardownEvents = function() {
        return this.$el.off('click');
      };

      Leg.prototype._configureTarget = function() {
        this.targetOffset = this.$target.offset();
        if (_dataProp(this.options.top, false)) {
          this.targetOffset.top = this.options.top;
        }
        if (_dataProp(this.options.left, false)) {
          this.targetOffset.left = this.options.left;
        }
        this.targetWidth = this.$target.outerWidth();
        return this.targetHeight = this.$target.outerHeight();
      };

      Leg.prototype._configureScroll = function() {
        this.willScroll = $.fn.scrollTo && this.options.scrollTo !== false;
        return this.scrollSettings = {
          offset: -this.options.scrollContext,
          easing: 'linear',
          axis: 'y',
          duration: this.options.scrollSpeed
        };
      };

      Leg.prototype._offsets = function() {
        var dimension, elHalf, elHeight, elWidth, offsets, targetHalf, targetHeightOverride, validOrientations;
        elHeight = this.$el.height();
        elWidth = this.$el.width();
        offsets = {};
        switch (this.options.orientation) {
          case 'centered':
            targetHeightOverride = $(window).height();
            offsets.top = this.options.top;
            if (!_dataProp(offsets.top, false)) {
              offsets.top = (targetHeightOverride / 2) - (elHeight / 2);
            }
            offsets.left = (this.targetWidth / 2) - (elWidth / 2);
            break;
          case 'left':
            offsets.top = this.targetOffset.top;
            offsets.left = this.targetOffset.left - elWidth - this.options.margin;
            break;
          case 'right':
            offsets.top = this.targetOffset.top;
            offsets.left = this.targetOffset.left + this.targetWidth + this.options.margin;
            break;
          case 'top':
            offsets.top = this.targetOffset.top - elHeight - this.options.margin;
            offsets.left = this.targetOffset.left;
            break;
          case 'bottom':
            offsets.top = this.targetOffset.top + this.targetHeight + this.options.margin;
            offsets.left = this.targetOffset.left;
        }
        validOrientations = {
          top: ['left', 'right'],
          bottom: ['left', 'right'],
          left: ['top', 'bottom'],
          right: ['top', 'bottom']
        };
        if (_include(this.options.orientation, validOrientations[this.options.align])) {
          switch (this.options.align) {
            case 'right':
              offsets.left += this.targetWidth - elWidth;
              break;
            case 'bottom':
              offsets.top += this.targetHeight - elHeight;
          }
        } else if (this.options.align === 'center') {
          if (_include(this.options.orientation, validOrientations.left)) {
            targetHalf = this.targetWidth / 2;
            elHalf = elWidth / 2;
            dimension = 'left';
          } else {
            targetHalf = this.targetHeight / 2;
            elHalf = elHeight / 2;
            dimension = 'top';
          }
          if (targetHalf > elHalf) {
            offsets[dimension] += targetHalf - elHalf;
          } else {
            offsets[dimension] -= elHalf - targetHalf;
          }
        }
        return offsets;
      };

      return Leg;

    })();
    _tours = 0;
    uniqueId = function() {
      return _tours++;
    };
    _busses = {};
    _assemble = function() {
      var bus;
      bus = (function(func, args, ctor) {
        ctor.prototype = func.prototype;
        var child = new ctor, result = func.apply(child, args);
        return Object(result) === result ? result : child;
      })(Bus, arguments, function(){});
      _busses[bus.id] = bus;
      return bus;
    };
    _dataProp = function(possiblyFalsy, alternative) {
      if (possiblyFalsy === null || typeof possiblyFalsy === 'undefined') {
        return alternative;
      }
      return possiblyFalsy;
    };
    _include = function(value, array) {
      return $.inArray(value, array || []) !== -1;
    };
    return _addRule = (function(styleTag) {
      var sheet;
      styleTag.type = 'text/css';
      document.getElementsByTagName('head')[0].appendChild(styleTag);
      sheet = document.styleSheets[document.styleSheets.length - 1];
      return function(selector, css) {
        var key, propText;
        propText = $.map((function() {
          var _results;
          _results = [];
          for (key in css) {
            _results.push(key);
          }
          return _results;
        })(), function(p) {
          return "" + p + ":" + css[p];
        }).join(';');
        try {
          if (sheet.insertRule) {
            sheet.insertRule("" + selector + " { " + propText + " }", (sheet.cssRules || sheet.rules).length);
          } else {
            sheet.addRule(selector, propText);
          }
        } catch (_error) {}
      };
    })(document.createElement('style'));
  })(jQuery);

}).call(this);
