define(['jquery', 'lodash', 'handlebars', 'lib/handlebars/helpers', 'css!ui/contextualPopup/css/contextualPopup.css'], function ($$1, _, Handlebars, Helpers0, contextualPopup_css) { 'use strict';

    $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars;
    Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;

    if (!Helpers0.__initialized) {
      Helpers0(Handlebars);
      Helpers0.__initialized = true;
    }
    var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
      this.compilerInfo = [4,'>= 1.0.0'];
    helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
      var buffer = "", stack1, helper, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this, functionType="function";

    function program1(depth0,data) {
      
      var buffer = "", stack1;
      buffer += "\n            ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.controls)),stack1 == null || stack1 === false ? stack1 : stack1.done), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n            ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.controls)),stack1 == null || stack1 === false ? stack1 : stack1.cancel), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        ";
      return buffer;
      }
    function program2(depth0,data) {
      
      
      return "<button class=\"btn btn-info small done\">done</button>";
      }

    function program4(depth0,data) {
      
      var buffer = "", helper, options;
      buffer += "<a href=\"#\" class=\"btn cancel\" title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "cancel", options) : helperMissing.call(depth0, "__", "cancel", options)))
        + "\">cancel</a>";
      return buffer;
      }

      buffer += "<div class=\"contextual-popup\" style=\"top:"
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.popup)),stack1 == null || stack1 === false ? stack1 : stack1.top)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "px; left:"
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.popup)),stack1 == null || stack1 === false ? stack1 : stack1.left)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "px\">\n    <div class=\"arrow\" style=\"left:"
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.arrow)),stack1 == null || stack1 === false ? stack1 : stack1.left)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "px\"></div>\n    <div class=\"arrow-cover\" style=\"left:"
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.arrow)),stack1 == null || stack1 === false ? stack1 : stack1.leftCover)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "px\"></div>\n    <div class=\"popup-content\">";
      if (helper = helpers.content) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.content); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "</div>\n    <div class=\"footer\">\n        ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.controls), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    </div>\n</div>";
      return buffer;
      });
    function popupTpl(data, options, asString) {
      var html = Template(data, options);
      return (asString || true) ? html : $(html);
    }

    /*
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * as published by the Free Software Foundation; under version 2
     * of the License (non-upgradable).
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     *
     * Copyright (c) 2015 (original work) Open Assessment Technologies SA ;
     *
     */
    var _ns = '.contextual-popup';
    var _defaults = {
      controls: false,
      style: {},
      position: 'bottom',
      callbacks: {}
    };

    /**
     * Create an element selector reltive to the $anchor and contained in the $container
     *
     * @param {JQuery} $anchor
     * @param {JQuery} $container
     * @param {Object} options
     * @param {JQuery|String} [options.content] - the inital content of the popup
     * @param {Boolean} [options.controls] - add cancel/done button
     * @param {Function} [options.callbacks.beforeDone] - Triggered when a dialog is about to close. If returned <i>false</i>, the dialog will not close.
     * @param {Function} [options.callbacks.beforeCancel] - Triggered when a dialog is about to close. If returned <i>false</i>, the dialog will not close.
     * @param {Function} [options.callbacks.beforeDestroy] - Triggered when a dialog is about to destroy.
     * @returns {Object} the new selector instance
     */
    function create($anchor, $container, options) {
      let destroyed = false;
      options = _.defaults(options, _defaults);
      $anchor.data('contextual-popup-options', options);

      //anchor must be positioned in css
      const positions = _computePosition($anchor, $container);
      const $element = $$1(popupTpl({
        popup: positions.popup,
        arrow: positions.arrow,
        controls: options.controls
      }));

      //only one
      $anchor.find('.contextual-popup').remove();

      //attach the popup
      $element.addClass(options.position);
      $element.css('width', options.style.popupWidth);
      $anchor.append($element);
      $element.off(_ns).on('click' + _ns, '.done', function () {
        if (runCallback('beforeDone')) {
          _done($element);
        }
      }).on('click' + _ns, '.cancel', function () {
        if (runCallback('beforeCancel')) {
          _cancel($element);
        }
      });
      if (options.content) {
        setContent(options.content);
      }

      /**
       * Set the popup content
       * @param {JQuery|String} content
       * @returns {undefined}
       */
      function setContent(content) {
        if (content instanceof $$1 || _.isString(content)) {
          $element.find('.popup-content').empty().append(content);
        }
      }

      /**
       * Run callback function before action. If returned <i>false</i>, action will not be executed.
       * @param {string} name - callback name
       * @returns {boolean}
       */
      function runCallback(name) {
        var result = true;
        if (options.callbacks[name] && _.isFunction(options.callbacks[name])) {
          result = options.callbacks[name]();
        }
        return result;
      }
      const popup = {
        /**
         * Get the popup JQuery container
         *
         * @returns {jQuery}
         */
        getPopup: function getPopup() {
          return $element;
        },
        setContent: setContent,
        /**
         * Recalculates the position of the popup relative to the anchor
         * Useful after any changes in layout
         *
         * @returns {undefined}
         */
        reposition: function reposition() {
          var pos = _computePosition($anchor, $container);
          $element.css({
            top: pos.popup.top,
            left: pos.popup.left
          });
          $element.children('.arrow').css('left', pos.arrow.left);
          $element.children('.arrow-cover').css('left', pos.arrow.leftCover);
        },
        /**
         * Manually triggers "done"
         *
         * @returns {undefined}
         */
        done: function done() {
          _done($element);
        },
        /**
         * Manually triggers "cancel"
         *
         * @returns {undefined}
         */
        cancel: function cancel() {
          _cancel($element);
        },
        /**
         * Manually triggers "hide"
         *
         * @returns {undefined}
         */
        hide: function hide() {
          _hide($element);
        },
        /**
         * Manually triggers "show"
         *
         * @fires show.contextual-popup
         * @returns {undefined}
         */
        show: function show() {
          $element.show();
          $element.trigger('show' + _ns);
        },
        /**
         * Manually triggers "destroy"
         *
         * @fires destroy.contextual-popup
         * @returns {undefined}
         */
        destroy: function destroy() {
          if (!destroyed) {
            runCallback('beforeDestroy');
            destroyed = true;
            $element.remove();
            $element.trigger('destroy' + _ns);
          }
        },
        /**
         * Tells if the popup is currently hidden or visible
         * @returns {boolean}
         */
        isVisible: function () {
          return $element.is(':visible');
        }
      };
      //need to reposition the popup after this has been attached to the dom
      popup.reposition();
      return popup;
    }

    /**
     * Hide
     *
     * @fires hide.contextual-popup
     * @param {JQuery} $element
     */
    function _hide($element) {
      $element.hide();
      $element.trigger('hide' + _ns);
    }

    /**
     * Callback when the "done" button is clicked
     *
     * @fires done.contextual-popup
     * @param {JQuery} $element
     */
    function _done($element) {
      _hide($element);
      $element.trigger('done' + _ns);
    }

    /**
     * Callback when the "cancel" button is clicked
     *
     * @fires cancel.contextual-popup
     * @param {JQuery} $element
     */
    function _cancel($element) {
      _hide($element);
      $element.trigger('cancel' + _ns);
    }
    const _styleDefaults = {
      popupWidth: 500,
      arrowWidth: 6,
      marginTop: 15,
      marginLeft: 15
    };

    /**
     * Calculate the position of the popup and arrow relative to the anchor and container elements
     *
     * @param {JQuery} $anchor
     * @param {JQuery} $container
     * @returns {Object} - Object containing the positioning data
     */
    function _computePosition($anchor, $container) {
      var $contextualPopup = $anchor.children('.contextual-popup');
      var options = $anchor.data('contextual-popup-options');
      var styleOpts = _.defaults(options.style || {}, _styleDefaults);
      var popupWidth = styleOpts.popupWidth;
      var popupHeight = $contextualPopup.outerHeight();
      var arrowWidth = styleOpts.arrowWidth;
      var marginTop = styleOpts.marginTop;
      var marginLeft = styleOpts.marginLeft;
      var _anchor = {
        top: $anchor.offset().top,
        left: $anchor.offset().left,
        w: $anchor.innerWidth(),
        h: $anchor.innerHeight()
      };
      var _container = {
        top: $container.offset().top,
        left: $container.offset().left,
        w: $container.innerWidth()
      };
      var _popup = {
        top: _anchor.h + marginTop,
        left: -popupWidth / 2 + _anchor.w / 2,
        w: popupWidth
      };
      switch (options.position) {
        case 'top':
          _popup.top = -marginTop - popupHeight;
          $contextualPopup.removeClass('bottom').addClass('top');
          break;
        case 'bottom':
          _popup.top = _anchor.h + marginTop;
          $contextualPopup.removeClass('top').addClass('bottom');
          break;
      }
      const offset = _anchor.left - _container.left;
      //do we have enough space on the left ?
      if (offset + marginLeft + _anchor.w / 2 < _popup.w / 2) {
        _popup.left = -offset + marginLeft;
      } else if (_container.w - (offset + _anchor.w / 2 + marginLeft) < _popup.w / 2) {
        _popup.left = -offset + _container.w - marginLeft - _popup.w;
      }
      const _arrow = {
        left: -_popup.left + _anchor.w / 2 - arrowWidth,
        leftCover: -_popup.left + _anchor.w / 2 - arrowWidth - 6
      };
      return {
        popup: _popup,
        arrow: _arrow
      };
    }

    return create;

});
