/*!

* froala_editor v2.3.3 (https://www.froala.com/wysiwyg-editor)
* License https://froala.com/wysiwyg-editor/terms/
* Copyright 2014-2016 Froala Labs
*/

(function (factory) {

if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
    // Node/CommonJS
    module.exports = function( root, jQuery ) {
        if ( jQuery === undefined ) {
            // require('jQuery') returns a factory that requires window to
            // build a jQuery instance, we normalize how we use modules
            // that require this pattern but the window provided is a noop
            // if it's defined (how jquery works)
            if ( typeof window !== 'undefined' ) {
                jQuery = require('jquery');
            }
            else {
                jQuery = require('jquery')(root);
            }
        }
        factory(jQuery);
        return jQuery;
    };
} else {
    // Browser globals
    factory(jQuery);
}

}(function ($) {

'use strict';

// Extend defaults.
$.extend($.FE.DEFAULTS, {
  lineBreakerTags: ['table', 'hr', 'form', 'dl', 'span.fr-video'],
  lineBreakerOffset: 15
});

$.FE.PLUGINS.lineBreaker = function (editor) {
  var $line_breaker;
  var mouseDownFlag;
  var mouseMoveTimer;

  /*
   * Show line breaker.
   * Compute top, left, width and show the line breaker.
   * tag1 and tag2 are the tags between which the line breaker must be showed.
   * If tag1 is null then tag2 is the first tag in the editor.
   * If tag2 is null then tag1 is the last tag in the editor.
   */
  function _show ($tag1, $tag2) {
    // Line breaker's possition and width.
    var breakerTop;
    var breakerLeft;
    var breakerWidth;
    var parent_tag;
    var parent_top;
    var parent_bottom;
    var tag_top;
    var tag_bottom;

    // Mouse is over the first tag in the editor. Show line breaker above tag2.
    if ($tag1 == null) {
      // Compute line breaker's possition and width.
      parent_tag = $tag2.parent();
      parent_top = parent_tag.offset().top;
      tag_top = $tag2.offset().top;

      breakerTop = tag_top - Math.min((tag_top - parent_top) / 2, editor.opts.lineBreakerOffset);
      breakerWidth = parent_tag.outerWidth();
      breakerLeft = parent_tag.offset().left;

    // Mouse is over the last tag in the editor. Show line breaker below tag1.
    } else if ($tag2 == null) {
      // Compute line breaker's possition and width.
      parent_tag = $tag1.parent();
      parent_bottom = parent_tag.offset().top + parent_tag.outerHeight();
      tag_bottom = $tag1.offset().top + $tag1.outerHeight();

      breakerTop = tag_bottom + Math.min((parent_bottom - tag_bottom) / 2, editor.opts.lineBreakerOffset);
      breakerWidth = parent_tag.outerWidth();
      breakerLeft = parent_tag.offset().left;

    // Mouse is between the 2 tags.
    } else {
      // Compute line breaker's possition and width.
      parent_tag = $tag1.parent();
      var tag1_bottom = $tag1.offset().top + $tag1.height();
      var tag2_top = $tag2.offset().top;

      // Tags may be on the same line, so there is no need for line breaker.
      if (tag1_bottom > tag2_top) {
        return false;
      }

      breakerTop = (tag1_bottom + tag2_top) / 2;
      breakerWidth = parent_tag.outerWidth();
      breakerLeft = parent_tag.offset().left;
    }

    if (editor.opts.iframe) {
      breakerLeft += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
      breakerTop += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
    }

    editor.$box.append($line_breaker);

    // Set line breaker's top, left and width.
    $line_breaker.css('top', breakerTop - editor.win.pageYOffset);
    $line_breaker.css('left', breakerLeft - editor.win.pageXOffset);
    $line_breaker.css('width', breakerWidth);

    $line_breaker.data('tag1', $tag1);
    $line_breaker.data('tag2', $tag2);

    // Show the line breaker.
    $line_breaker.addClass('fr-visible').data('instance', editor);
  }

  /*
   * Check tag siblings.
   * The line breaker hould appear if there is no sibling or if the sibling is also in the line breaker tags list.
   */
  function _checkTagSiblings ($tag, mouseY) {
    // Tag's Y top and bottom coordinate.
    var tag_top = $tag.offset().top;
    var tag_bottom = $tag.offset().top + $tag.outerHeight();
    var $sibling;
    var tag;

    // Only if the mouse is close enough to the bottom or top edges.
    if (Math.abs(tag_bottom - mouseY) <= editor.opts.lineBreakerOffset ||
        Math.abs(mouseY - tag_top) <= editor.opts.lineBreakerOffset) {

      // Mouse is near bottom check for next sibling.
      if (Math.abs(tag_bottom - mouseY) < Math.abs(mouseY - tag_top)) {
        tag = $tag.get(0);

        var next_node = tag.nextSibling;
        while (next_node && next_node.nodeType == Node.TEXT_NODE && next_node.textContent.length === 0) {
          next_node = next_node.nextSibling;
        }

        // Tag has next sibling.
        if (next_node) {
          $sibling = _checkTag(next_node);

          // Sibling is in the line breaker tags list.
          if ($sibling) {
            // Show line breaker.
            _show($tag, $sibling);
            return true;
          }

        // No next sibling.
        } else {
          // Show line breaker
          _show($tag, null);
          return true;
        }
      }

      // Mouse is near top check for prev sibling.
      else {
        tag = $tag.get(0);

        // No prev sibling.
        if (!tag.previousSibling) {
          // Show line breaker
          _show(null, $tag);
          return true;

        // Tag has prev sibling.
        } else {
          $sibling = _checkTag(tag.previousSibling);

          // Sibling is in the line breaker tags list.
          if ($sibling) {
            // Show line breaker.
            _show($sibling, $tag);
            return true;
          }
        }
      }
    }

    $line_breaker.removeClass('fr-visible').removeData('instance');
  }

  /*
   * Check if tag is in the line breaker list and in the editor as well.
   * Returns the tag from the line breaker list or false if the tag is not in the list.
   */
  function _checkTag (tag) {
    if (tag) {
      var $tag = $(tag);

      // Make sure tag is inside the editor.
      if (editor.$el.find($tag).length === 0) return null;

      // Tag is in the line breaker tags list.
      if (tag.nodeType != Node.TEXT_NODE && $tag.is(editor.opts.lineBreakerTags.join(','))) {
        return $tag;
      }

      // Tag's parent is in the line breaker tags list.
      else if ($tag.parents(editor.opts.lineBreakerTags.join(',')).length > 0) {
        tag = $tag.parents(editor.opts.lineBreakerTags.join(',')).get(0);

        return $(tag);
      }
    }

    return null;
  }

  /*
   * Get the tag under the mouse cursor.
   */
  function _tagUnder (e) {
    mouseMoveTimer = null;

    // The tag for which the line breaker should be showed.
    var $tag = null;

    // The tag under the mouse cursor.
    var tag_under = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset);
    var i;
    var tag_above;
    var tag_below;

    // Tag is the editor element. Look for closest tag above and bellow.
    if (tag_under && (tag_under.tagName == 'HTML' || tag_under.tagName == 'BODY' || editor.node.isElement(tag_under))) {
      // Look 1px up and 1 down until a tag is found or the line breaker offset is reached.
      for (i = 1; i <= editor.opts.lineBreakerOffset; i++) {
        // Look for tag above.
        tag_above = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset - i);

        // We found a tag above.
        if (tag_above && !editor.node.isElement(tag_above) && tag_above != editor.$wp.get(0) && $(tag_above).parents(editor.$wp).length) {
          $tag = _checkTag(tag_above);
          break;
        }

        // Look for tag below.
        tag_below = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset + i);

        // We found a tag bellow.
        if (tag_below && !editor.node.isElement(tag_below) && tag_below != editor.$wp.get(0) && $(tag_below).parents(editor.$wp).length) {
          $tag = _checkTag(tag_below);
          break;
        }
      }

    // Tag is not the editor element.
    } else {
      // Check if the tag is in the line breaker list.
      $tag = _checkTag(tag_under);
    }

    // Check tag siblings.
    if ($tag) {
      _checkTagSiblings($tag, e.pageY);
    }
    else if (editor.core.sameInstance($line_breaker)) {
      $line_breaker.removeClass('fr-visible').removeData('instance');
    }
  }

  /*
   * Set mouse timer to improve performance.
   */
  function _mouseTimer (e) {
    if ($line_breaker.hasClass('fr-visible') && !editor.core.sameInstance($line_breaker)) return false;

    if (editor.popups.areVisible() || editor.$el.get(0).querySelectorAll('.fr-selected-cell').length) {
      $line_breaker.removeClass('fr-visible');
      return true;
    }

    if (mouseDownFlag === false) {
      if (mouseMoveTimer) {
        clearTimeout(mouseMoveTimer);
      }

      mouseMoveTimer = setTimeout(_tagUnder, 30, e);
    }
  }

  /*
   * Hide line breaker and prevent timer from showing it again.
   */
  function _hide () {
    if (mouseMoveTimer) {
      clearTimeout(mouseMoveTimer);
    }

    if ($line_breaker.hasClass('fr-visible')) {
      $line_breaker.removeClass('fr-visible').removeData('instance');
    }
  }

  /*
   * Notify that mouse is down and prevent line breaker from showing.
   * This may happen either for selection or for drag.
   */
  function _mouseDown () {
    mouseDownFlag = true;
    _hide();
  }

  /*
   * Notify that mouse is no longer pressed.
   */
  function _mouseUp () {
    mouseDownFlag = false;
  }

  /*
   * Add new line between the tags.
   */
  function _doLineBreak (e) {
    if (!editor.core.sameInstance($line_breaker)) return true;

    e.preventDefault();

    // Hide the line breaker.
    $line_breaker.removeClass('fr-visible').removeData('instance');

    // Tags between which that line break needs to be done.
    var $tag1 = $line_breaker.data('tag1');
    var $tag2 = $line_breaker.data('tag2');

    // P, DIV or none.
    var default_tag = editor.html.defaultTag();

    // The line break needs to be done before the first element in the editor.
    if ($tag1 == null) {
      // If the tag is in a TD tag then just add <br> no matter what the default_tag is.
      if (default_tag && $tag2.parent().get(0).tagName != 'TD') {
        $tag2.before('<' + default_tag + '>' + $.FE.MARKERS + '<br></' + default_tag + '>')
      }
      else {
        $tag2.before($.FE.MARKERS + '<br>');
      }

    // The line break needs to be done either after the last element in the editor or between the 2 tags.
    // Either way the line break is after the first tag.
    } else {
      // If the tag is in a TD tag then just add <br> no matter what the default_tag is.
      if (default_tag && $tag1.parent().get(0).tagName != 'TD' && $tag1.parents(default_tag).length === 0) {
        $tag1.after('<' + default_tag + '>' + $.FE.MARKERS + '<br></' + default_tag + '>')
      }
      else {
        $tag1.after($.FE.MARKERS + '<br>');
      }
    }

    // Cursor is now at the beginning of the new line.
    editor.selection.restore();
  }

  /*
   * Initialize the line breaker.
   */
  function _initLineBreaker () {
    // Append line breaker HTML to editor wrapper.
    if (!editor.shared.$line_breaker) {
      editor.shared.$line_breaker = $('<div class="fr-line-breaker"><a class="fr-floating-btn" role="button" tabindex="-1" title="' + editor.language.translate('Break') + '"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect x="21" y="11" width="2" height="8"/><rect x="14" y="17" width="7" height="2"/><path d="M14.000,14.000 L14.000,22.013 L9.000,18.031 L14.000,14.000 Z"/></svg></a></div>');
    }

    $line_breaker = editor.shared.$line_breaker;

    // Editor shared destroy.
    editor.events.on('shared.destroy', function () {
      $line_breaker.html('').removeData().remove();
      $line_breaker = null;
    }, true);

    // Editor destroy.
    editor.events.on('destroy', function () {
      $line_breaker.removeData('instance').removeClass('fr-visible').appendTo('body');
      clearTimeout(mouseMoveTimer);
    }, true)

    editor.events.$on($line_breaker, 'mouseleave', _hide, true);

    editor.events.$on($line_breaker, 'mousemove', function (e) {
      e.stopPropagation();
    }, true)

    // Add new line break.
    editor.events.$on($line_breaker, 'mousedown', 'a', function (e) {
      e.stopPropagation();
    }, true);
    editor.events.$on($line_breaker, 'click', 'a', _doLineBreak, true);
  }

  /*
   * Tear up.
   */
  function _init () {
    if (!editor.$wp) return false;

    _initLineBreaker();

    // Remember if mouse is clicked so the line breaker does not appear.
    mouseDownFlag = false;

    // Check tags under the mouse to see if the line breaker needs to be shown.
    editor.events.$on(editor.$win, 'mousemove', _mouseTimer);

    // Hide the line breaker if the page is scrolled.
    editor.events.$on($(editor.win), 'scroll', _hide);

    // Hide the line breaker on cell edit.
    editor.events.on('popups.show.table.edit', _hide);

    // Prevent line breaker from showing while selecting text or dragging images.
    editor.events.$on($(editor.win), 'mousedown', _mouseDown);

    // Mouse is not pressed anymore, line breaker may be shown.
    editor.events.$on($(editor.win), 'mouseup', _mouseUp);
  }

  return {
    _init: _init
  }
};

}));