text: /**
   * Insight search plugin
   * @author PPOffice { @link https://github.com/ppoffice }
   */
  (function ($, CONFIG) {
      var $main = $('.ins-search');
      var $input = $main.find('.ins-search-input');
      var $wrapper = $main.find('.ins-section-wrapper');
      var $container = $main.find('.ins-section-container');
      $main.parent().remove('.ins-search');
      $('body').append($main);
  
      function section (title) {
          return $('<section>').addClass('ins-section')
              .append($('<header>').addClass('ins-section-header').text(title));
      }
  
      function searchItem (icon, title, slug, preview, url) {
          return $('<div>').addClass('ins-selectable').addClass('ins-search-item')
              .append($('<header>').append($('<i>').addClass('fa').addClass('fa-' + icon)).append(title != null && title != '' ? title : CONFIG.TRANSLATION['UNTITLED'])
                  .append(slug ? $('<span>').addClass('ins-slug').text(slug) : null))
              .append(preview ? $('<p>').addClass('ins-search-preview').text(preview) : null)
              .attr('data-url', url);
      }
  
      function sectionFactory (type, array) {
          var sectionTitle;
          var $searchItems;
          if (array.length === 0) return null;
          sectionTitle = CONFIG.TRANSLATION[type];
          switch (type) {
              case 'POSTS':
              case 'PAGES':
                  $searchItems = array.map(function (item) {
                      // Use config.root instead of permalink to fix url issue
                      return searchItem('file', item.title, null, item.text.slice(0, 150), CONFIG.ROOT_URL + item.path);
                  });
                  break;
              case 'CATEGORIES':
              case 'TAGS':
                  $searchItems = array.map(function (item) {
                      return searchItem(type === 'CATEGORIES' ? 'folder' : 'tag', item.name, item.slug, null, item.permalink);
                  });
                  break;
              default:
                  return null;
          }
          return section(sectionTitle).append($searchItems);
      }
  
      function extractToSet (json, key) {
          var values = {};
          var entries = json.pages.concat(json.posts);
          entries.forEach(function (entry) {
              if (entry[key]) {
                  entry[key].forEach(function (value) {
                      values[value.name] = value;
                  });
              }
          });
          var result = [];
          for (var key in values) {
              result.push(values[key]);
          }
          return result;
      }
  
      function parseKeywords (keywords) {
          return keywords.split(' ').filter(function (keyword) {
              return !!keyword;
          }).map(function (keyword) {
              return keyword.toUpperCase();
          });
      }
  
      /**
       * Judge if a given post/page/category/tag contains all of the keywords.
       * @param Object            obj     Object to be weighted
       * @param Array<String>     fields  Object's fields to find matches
       */
      function filter (keywords, obj, fields) {
          var result = false;
          var keywordArray = parseKeywords(keywords);
          var containKeywords = keywordArray.filter(function (keyword) {
              var containFields = fields.filter(function (field) {
                  if (!obj.hasOwnProperty(field))
                      return false;
                  if (obj[field].toUpperCase().indexOf(keyword) > -1)
                      return true;
              });
              if (containFields.length > 0)
                  return true;
              return false;
          });
          return containKeywords.length === keywordArray.length;
      }
  
      function filterFactory (keywords) {
          return {
              POST: function (obj) {
                  return filter(keywords, obj, ['title', 'text']);
              },
              PAGE: function (obj) {
                  return filter(keywords, obj, ['title', 'text']);
              },
              CATEGORY: function (obj) {
                  return filter(keywords, obj, ['name', 'slug']);
              },
              TAG: function (obj) {
                  return filter(keywords, obj, ['name', 'slug']);
              }
          };
      }
  
      /**
       * Calculate the weight of a matched post/page/category/tag.
       * @param Object            obj     Object to be weighted
       * @param Array<String>     fields  Object's fields to find matches
       * @param Array<Integer>    weights Weight of every field
       */
      function weight (keywords, obj, fields, weights) {
          var value = 0;
          parseKeywords(keywords).forEach(function (keyword) {
              var pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive
              fields.forEach(function (field, index) {
                  if (obj.hasOwnProperty(field)) {
                      var matches = obj[field].match(pattern);
                      value += matches ? matches.length * weights[index] : 0;
                  }
              });
          });
          return value;
      }
  
      function weightFactory (keywords) {
          return {
              POST: function (obj) {
                  return weight(keywords, obj, ['title', 'text'], [3, 1]);
              },
              PAGE: function (obj) {
                  return weight(keywords, obj, ['title', 'text'], [3, 1]);
              },
              CATEGORY: function (obj) {
                  return weight(keywords, obj, ['name', 'slug'], [1, 1]);
              },
              TAG: function (obj) {
                  return weight(keywords, obj, ['name', 'slug'], [1, 1]);
              }
          };
      }
  
      function search (json, keywords) {
          var WEIGHTS = weightFactory(keywords);
          var FILTERS = filterFactory(keywords);
          var posts = json.posts;
          var pages = json.pages;
          var tags = extractToSet(json, 'tags');
          var categories = extractToSet(json, 'categories');
          return {
              posts: posts.filter(FILTERS.POST).sort(function (a, b) { return WEIGHTS.POST(b) - WEIGHTS.POST(a); }).slice(0, 5),
              pages: pages.filter(FILTERS.PAGE).sort(function (a, b) { return WEIGHTS.PAGE(b) - WEIGHTS.PAGE(a); }).slice(0, 5),
              categories: categories.filter(FILTERS.CATEGORY).sort(function (a, b) { return WEIGHTS.CATEGORY(b) - WEIGHTS.CATEGORY(a); }).slice(0, 5),
              tags: tags.filter(FILTERS.TAG).sort(function (a, b) { return WEIGHTS.TAG(b) - WEIGHTS.TAG(a); }).slice(0, 5)
          };
      }
  
      function searchResultToDOM (searchResult) {
          $container.empty();
          for (var key in searchResult) {
              $container.append(sectionFactory(key.toUpperCase(), searchResult[key]));
          }
      }
  
      function scrollTo ($item) {
          if ($item.length === 0) return;
          var wrapperHeight = $wrapper[0].clientHeight;
          var itemTop = $item.position().top - $wrapper.scrollTop();
          var itemBottom = $item[0].clientHeight + $item.position().top;
          if (itemBottom > wrapperHeight + $wrapper.scrollTop()) {
              $wrapper.scrollTop(itemBottom - $wrapper[0].clientHeight);
          }
          if (itemTop < 0) {
              $wrapper.scrollTop($item.position().top);
          }
      }
  
      function selectItemByDiff (value) {
          var $items = $.makeArray($container.find('.ins-selectable'));
          var prevPosition = -1;
          $items.forEach(function (item, index) {
              if ($(item).hasClass('active')) {
                  prevPosition = index;
                  return;
              }
          });
          var nextPosition = ($items.length + prevPosition + value) % $items.length;
          $($items[prevPosition]).removeClass('active');
          $($items[nextPosition]).addClass('active');
          scrollTo($($items[nextPosition]));
      }
  
      function gotoLink ($item) {
          if ($item && $item.length) {
              location.href = $item.attr('data-url');
          }
      }
  
      $.getJSON(CONFIG.CONTENT_URL, function (json) {
          if (location.hash.trim() === '#ins-search') {
              $main.addClass('show');
          }
          $input.on('input', function () {
              var keywords = $(this).val();
              searchResultToDOM(search(json, keywords));
          });
          $input.trigger('input');
      });
  
  
      $(document).on('click focus', '.search-form-submit', function () {
          event.preventDefault();
          $main.addClass('show');
          $main.find('.ins-search-input').focus();
      }).on('click', '.ins-search-item', function () {
          gotoLink($(this));
      }).on('click', '.ins-close', function () {
          $main.removeClass('show');
      }).on('keydown', function (e) {
          if (!$main.hasClass('show')) return;
          switch (e.keyCode) {
              case 27: // ESC
                  $main.removeClass('show'); break;
              case 38: // UP
                  selectItemByDiff(-1); break;
              case 40: // DOWN
                  selectItemByDiff(1); break;
              case 13: //ENTER
                  gotoLink($container.find('.ins-selectable.active').eq(0)); break;
          }
      });
  })(jQuery, window.INSIGHT_CONFIG);