define(['jquery', 'lodash', 'ui/autoscroll', 'ui/hider', 'ui/documentViewer/providers/pdfViewer/pdfjs/searchEngine', 'handlebars', 'lib/handlebars/helpers'], function ($$1, _, autoscroll, hider, searchEngineFactory, Handlebars, Helpers0) { 'use strict';

    $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    autoscroll = autoscroll && Object.prototype.hasOwnProperty.call(autoscroll, 'default') ? autoscroll['default'] : autoscroll;
    hider = hider && Object.prototype.hasOwnProperty.call(hider, 'default') ? hider['default'] : hider;
    searchEngineFactory = searchEngineFactory && Object.prototype.hasOwnProperty.call(searchEngineFactory, 'default') ? searchEngineFactory['default'] : searchEngineFactory;
    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, options, helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, self=this;

    function program1(depth0,data) {
      
      
      return "checked ";
      }

      buffer += "<button class=\"small search\" data-control=\"pdf-search\" title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search", options) : helperMissing.call(depth0, "__", "Search", options)))
        + "\"><span class=\"icon icon-find\"></span></button>\n<div class=\"pdf-find-bar hidden\">\n    <label for=\"pdf-search-query\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Search", options) : helperMissing.call(depth0, "__", "Search", options)))
        + ":</label>\n    <input type=\"text\" id=\"pdf-search-query\" data-control=\"pdf-search-query\" placeholder=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Type your search", options) : helperMissing.call(depth0, "__", "Type your search", options)))
        + "\"/>\n    <button class=\"small search\" data-control=\"pdf-search-prev\" title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Previous match", options) : helperMissing.call(depth0, "__", "Previous match", options)))
        + "\"><span class=\"icon icon-left\"></span></button>\n    <button class=\"small search\" data-control=\"pdf-search-next\" title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Next match", options) : helperMissing.call(depth0, "__", "Next match", options)))
        + "\"><span class=\"icon icon-right\"></span></button>\n    <input type=\"checkbox\" id=\"highlight-all\" data-control=\"highlight-all\" value=\"1\" ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.highlightAll), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "/><label for=\"highlight-all\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Highlight all", options) : helperMissing.call(depth0, "__", "Highlight all", options)))
        + "</label>\n    <input type=\"checkbox\" id=\"case-sensitive-search\" data-control=\"case-sensitive-search\" value=\"1\" ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.caseSensitive), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "/><label for=\"case-sensitive-search\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Case sensitive", options) : helperMissing.call(depth0, "__", "Case sensitive", options)))
        + "</label>\n    <span class=\"pdf-search-info\" data-control=\"pdf-search-info\">\n        <span class=\"pdf-search-position hidden\" data-control=\"pdf-search-position\">\n            <span class=\"pdf-search-index\" data-control=\"pdf-search-index\"></span>/<span class=\"pdf-search-count\" data-control=\"pdf-search-count\"></span>\n        </span>\n        <span class=\"pdf-search-loop hidden\" data-control=\"pdf-search-loop-begin\">\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "End of document reached. Continuing from the beginning.", options) : helperMissing.call(depth0, "__", "End of document reached. Continuing from the beginning.", options)))
        + "\n        </span>\n        <span class=\"pdf-search-loop hidden\" data-control=\"pdf-search-loop-end\">\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Start of document reached. Continuing from the end.", options) : helperMissing.call(depth0, "__", "Start of document reached. Continuing from the end.", options)))
        + "\n        </span>\n    </span>\n</div>\n";
      return buffer;
      });
    function findBarTpl(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) 2016 (original work) Open Assessment Technologies SA ;
     */

    /**
     * The keypress delay before performing a search
     * @type {Number}
     */
    var searchThrottle = 250;

    /**
     * Enable/disable an element
     * @param {jQuery} $element
     * @param {Boolean} enabled
     */
    function toggleState($element, enabled) {
      if (enabled) {
        $element.removeAttr('disabled');
      } else {
        $element.attr('disabled', true);
      }
    }

    /**
     * Creates a find bar to search and display terms in the PDF document
     * @param {Object} config
     * @param {Object} config.events - The events hub
     * @param {Object} config.areaBroker - The areaBroker that gives access to the UI area
     * @param {Object} config.textManager - The textManager component that gives access to the text content
     * @param {Boolean} [config.caseSensitive] - Use a case sensitive search when the search feature is available
     * @param {Boolean} [config.highlightAll] - Highlight all matches to see all of them at a glance
     */
    function pdfjsFindBarFactory(config) {
      var throttledSearchStart = _.throttle(checkQuery, searchThrottle);
      var events = null;
      var broker = null;
      var searchEngine = null;
      var query = null;
      var pageNum = 0;
      var enabled = true;
      var loopBegin = false;
      var loopEnd = false;
      var navigating = false;
      var controls;

      /**
       * Search for the provided query, then trigger an update request
       */
      function doSearch() {
        loopBegin = false;
        loopEnd = false;
        if (query) {
          if (searchEngine) {
            /**
             * Notifies a search is processing
             * @event searching
             * @param {String} query
             */
            events.trigger('searching', query);
            searchEngine.search(query, pageNum).then(function (page) {
              /**
               * Notifies the search has ended
               * @event searchdone
               * @param {String} query
               * @param {Number} page
               */
              events.trigger('searchdone', query, page);
              if (page) {
                jumpTo(page);
              } else {
                refresh();
              }
            }).catch(function (err) {
              /**
               * Notifies the search error
               * @event error
               * @param {Object} err
               */
              events.trigger('error', err);
            });
          }
        } else {
          if (searchEngine) {
            searchEngine.clearMatches();
          }
          refresh();
        }
        updateControls();
      }

      /**
       * Displays the search matches on the rendered page
       * @param {Number} page - The page number of the rendered page
       * @returns {Promise}
       */
      function updateMatches(page) {
        var textManager = searchEngine && searchEngine.getTextManager();
        if (textManager && textManager.getDocument()) {
          /**
           * Notifies the matches are updating
           * @event matchesupdating
           * @param {Number} page
           */
          events.trigger('matchesupdating', page);
          return searchEngine.updateMatches(page).then(matchUpdated);
        }
      }

      /**
       * Requests a page change
       * @param {Number} page
       */
      function jumpTo(page) {
        /**
         * @event setpage
         * @param {Number} page
         */
        events.trigger('setpage', page);
      }

      /**
       * Requests a page refresh
       */
      function refresh() {
        /**
         * @event refresh
         */
        events.trigger('refresh');
      }

      /**
       * Finalizes the matches update
       * @param {Number} page
       */
      function matchUpdated(page) {
        if (navigating) {
          navigating = false;
          focusOnMatch();
        }
        updateControls();

        /**
         * Notifies the matches have been displayed
         * @event matchesupdated
         * @param {Number} page
         */
        events.trigger('matchesupdated', page);
      }

      /**
       * Select the current match and displays the page that contains it
       */
      function jumpToMatch() {
        var match;
        if (searchEngine) {
          match = searchEngine.getCurrentMatch();
          if (match) {
            navigating = true;
            if (pageNum !== match.page) {
              jumpTo(match.page);
            } else {
              updateSelection();
            }
          }
        }
      }

      /**
       * Moves between matches
       * @param {Number} direction
       */
      function moveBy(direction) {
        loopBegin = false;
        loopEnd = false;
        if (searchEngine) {
          if (direction < 0) {
            loopEnd = searchEngine.previousMatch();
          } else {
            loopBegin = searchEngine.nextMatch();
          }
          jumpToMatch();
        }
        updateControls();
        focusOnInput();
      }

      /**
       * Gets the selected match element
       * @returns {jQuery}
       */
      function getSelectectMatchElement() {
        return broker.getContentArea().find('.highlight.selected');
      }

      /**
       * Gets the current match element
       * @returns {jQuery}
       */
      function getCurrentMatchElement() {
        var match = searchEngine.getCurrentMatch();
        return broker.getContentArea().find('[data-match="' + match.index + '"]');
      }

      /**
       * Updates the selection to reflect the current match
       */
      function updateSelection() {
        if (broker && searchEngine) {
          getSelectectMatchElement().removeClass('selected');
          getCurrentMatchElement().addClass('selected');
          matchUpdated(pageNum);
        }
      }

      /**
       * Gives the focus to the selected match
       */
      function focusOnMatch() {
        if (broker && searchEngine) {
          autoscroll(getSelectectMatchElement(), broker.getContentArea());
        }
      }

      /**
       * Gives the focus to the query input
       */
      function focusOnInput() {
        controls.$searchQuery.focus();
      }

      /**
       * Reads the caseSensitive option and update the config
       */
      function readCaseSensitiveOption() {
        config.caseSensitive = controls.$caseSensitive.is(':checked');
      }

      /**
       * Reads the highlightAll option and apply the state
       */
      function applyHighlightAllOption() {
        if (broker) {
          broker.getContentArea().toggleClass('highlight-all', controls.$highlightAll.is(':checked'));
        }
      }

      /**
       * Checks if a change has been made in the current query, then performs a new search if needed
       */
      function checkQuery() {
        var typedQuery = controls.$searchQuery.val();
        if (typedQuery !== query) {
          query = typedQuery;
          navigating = true;
          doSearch();
        }
      }

      /**
       * Fetches the required controls
       * @param {jQuery} $container
       * @returns {Object}
       */
      function fetchControls($container) {
        return {
          $searchBar: $$1('.pdf-find-bar', $container),
          $searchButton: $$1('[data-control="pdf-search"]', $container),
          $searchQuery: $$1('[data-control="pdf-search-query"]', $container),
          $prevMatch: $$1('[data-control="pdf-search-prev"]', $container),
          $nextMatch: $$1('[data-control="pdf-search-next"]', $container),
          $caseSensitive: $$1('[data-control="case-sensitive-search"]', $container),
          $highlightAll: $$1('[data-control="highlight-all"]', $container),
          $matchIndex: $$1('[data-control="pdf-search-index"]', $container),
          $matchCount: $$1('[data-control="pdf-search-count"]', $container),
          $searchPosition: $$1('[data-control="pdf-search-position"]', $container),
          $searchLoopBegin: $$1('[data-control="pdf-search-loop-begin"]', $container),
          $searchLoopEnd: $$1('[data-control="pdf-search-loop-end"]', $container)
        };
      }

      /**
       * Will update the displayed controls
       */
      function updateControls() {
        var matchCount = 0;
        var matchIndex = 0;
        var match;
        if (searchEngine) {
          matchCount = searchEngine.getMatchCount();
          match = searchEngine.getCurrentMatch();
          matchIndex = match && match.overall;
        }
        if (!enabled && !hider.isHidden(controls.$searchBar)) {
          hider.hide(controls.$searchBar);
        }
        toggleState(controls.$searchButton, enabled);
        toggleState(controls.$searchQuery, enabled);
        toggleState(controls.$caseSensitive, enabled);
        toggleState(controls.$highlightAll, enabled);
        toggleState(controls.$prevMatch, enabled && matchCount > 1);
        toggleState(controls.$nextMatch, enabled && matchCount > 1);
        controls.$matchIndex.text(matchIndex);
        controls.$matchCount.text(matchCount);
        hider.toggle(controls.$searchPosition, enabled && matchCount > 0);
        hider.toggle(controls.$searchLoopBegin, enabled && loopBegin);
        hider.toggle(controls.$searchLoopEnd, enabled && loopEnd);
      }
      config = config || {};
      if (!_.isPlainObject(config.events)) {
        throw new TypeError('You must provide an events hub! [config.events is missing]');
      }
      if (!_.isPlainObject(config.areaBroker)) {
        throw new TypeError('You must provide an areaBroker to give access to the UI! [config.areaBroker is missing]');
      }
      if (!_.isPlainObject(config.textManager)) {
        throw new TypeError('You must provide a textManager to give access to the PDF text content! [config.textManager is missing]');
      }
      searchEngine = searchEngineFactory(config);
      broker = config.areaBroker;
      events = config.events;
      broker.getActionsArea().prepend(findBarTpl(config));
      controls = fetchControls(broker.getBarArea());
      readCaseSensitiveOption();
      applyHighlightAllOption();
      controls.$searchButton.on('click', function () {
        hider.toggle(controls.$searchBar);
        if (!hider.isHidden(controls.$searchBar)) {
          focusOnInput();
        }
      });
      controls.$prevMatch.on('click', function () {
        moveBy(-1);
      });
      controls.$nextMatch.on('click', function () {
        moveBy(1);
      });
      controls.$caseSensitive.on('change', function () {
        navigating = true;
        readCaseSensitiveOption();
        doSearch();
        focusOnInput();
      });
      controls.$highlightAll.on('change', function () {
        applyHighlightAllOption();
        focusOnInput();
      });
      controls.$searchQuery.on('keypress', function (event) {
        switch (event.keyCode) {
          case 27:
            hider.hide(controls.$searchBar);
            break;
          default:
            throttledSearchStart();
        }
      });
      events.on('enable.findbar', function () {
        enabled = true;
        updateControls();
      }).on('disable.findbar', function () {
        enabled = false;
        updateControls();
      }).on('pagechange.findbar', function (page) {
        pageNum = page;
        updateControls();
      }).on('allrendered.findbar', function (page) {
        pageNum = page;
        updateMatches(page);
        updateControls();
      })
      /**
       * Notifies the component is initialized
       * @event init.findbar
       */.trigger('init.findbar');
      return {
        /**
         * Gets the search engine instance
         * @returns {Object}
         */
        getSearchEngine: function getSearchEngine() {
          return searchEngine;
        },
        /**
         * Destroys the search engine and frees the resources
         */
        destroy: function destroy() {
          if (searchEngine) {
            searchEngine.destroy();
          }
          if (controls.$searchButton) {
            controls.$searchButton.remove();
          }
          if (controls.$searchBar) {
            controls.$searchBar.remove();
          }
          controls = {};
          config = null;
          searchEngine = null;
          broker = null;

          /**
           * Notifies the component is destroying
           * @event destroy.findbar
           */
          events.trigger('destroy.findbar');
          events.off('.findbar');
          events = null;
        }
      };
    }

    return pdfjsFindBarFactory;

});
