// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

goog.provide('goog.dom.selectionTest');
goog.setTestOnly('goog.dom.selectionTest');

goog.require('goog.dom');
goog.require('goog.dom.InputType');
goog.require('goog.dom.TagName');
goog.require('goog.dom.selection');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');

var input;
var hiddenInput;
var textarea;
var hiddenTextarea;

function setUp() {
  input = goog.dom.createDom(
      goog.dom.TagName.INPUT, {type: goog.dom.InputType.TEXT});
  textarea = goog.dom.createDom(goog.dom.TagName.TEXTAREA);
  hiddenInput = goog.dom.createDom(
      goog.dom.TagName.INPUT,
      {type: goog.dom.InputType.TEXT, style: 'display: none'});
  hiddenTextarea =
      goog.dom.createDom(goog.dom.TagName.TEXTAREA, {style: 'display: none'});

  document.body.appendChild(input);
  document.body.appendChild(textarea);
  document.body.appendChild(hiddenInput);
  document.body.appendChild(hiddenTextarea);
}

function tearDown() {
  goog.dom.removeChildren(document.body);
}


/**
 * Tests getStart routine in both input and textarea.
 */
function testGetStartInput() {
  getStartHelper(input, hiddenInput);
}

function testGetStartTextarea() {
  getStartHelper(textarea, hiddenTextarea);
}

/**
 * Tests that getStart routine does not error for elements that are neither
 * text inputs nor text areas.
 */
function testGetStartOther() {
  var button = goog.dom.createDom(goog.dom.TagName.BUTTON);
  var hiddenButton =
      goog.dom.createDom(goog.dom.TagName.BUTTON, {style: 'display: none'});
  document.body.appendChild(button);
  document.body.appendChild(hiddenButton);
  getStartHelper(button, hiddenButton);
}

function getStartHelper(field, hiddenField) {
  assertEquals(0, goog.dom.selection.getStart(field));
  assertEquals(0, goog.dom.selection.getStart(hiddenField));

  field.focus();
  assertEquals(0, goog.dom.selection.getStart(field));
}


/**
 * Tests the setText routine for both input and textarea
 * with a single line of text.
 */
function testSetTextInput() {
  setTextHelper(input);
}

function testSetTextTextarea() {
  setTextHelper(textarea);
}

function setTextHelper(field) {
  // Test one line string only
  select(field);
  assertEquals('', goog.dom.selection.getText(field));

  goog.dom.selection.setText(field, 'Get Behind Me Satan');
  assertEquals('Get Behind Me Satan', goog.dom.selection.getText(field));
}


/**
 * Tests the setText routine for textarea with multiple lines of text.
 */
function testSetTextMultipleLines() {
  select(textarea);
  assertEquals('', goog.dom.selection.getText(textarea));
  var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
  var message = isLegacyIE ? 'Get Behind Me\r\nSatan' : 'Get Behind Me\nSatan';
  goog.dom.selection.setText(textarea, message);
  assertEquals(message, goog.dom.selection.getText(textarea));

  // Select the text up to the point just after the \r\n combination
  // or \n in GECKO.
  var endOfNewline = isLegacyIE ? 15 : 14;
  var selectedMessage = message.substring(0, endOfNewline);
  goog.dom.selection.setStart(textarea, 0);
  goog.dom.selection.setEnd(textarea, endOfNewline);
  assertEquals(selectedMessage, goog.dom.selection.getText(textarea));

  selectedMessage = isLegacyIE ? '\r\n' : '\n';
  goog.dom.selection.setStart(textarea, 13);
  goog.dom.selection.setEnd(textarea, endOfNewline);
  assertEquals(selectedMessage, goog.dom.selection.getText(textarea));
}


/**
 * Tests the setCursor routine for both input and textarea.
 */
function testSetCursorInput() {
  setCursorHelper(input);
}

function testSetCursorTextarea() {
  setCursorHelper(textarea);
}

function setCursorHelper(field) {
  select(field);
  // try to set the cursor beyond the length of the content
  goog.dom.selection.setStart(field, 5);
  goog.dom.selection.setEnd(field, 15);
  assertEquals(0, goog.dom.selection.getStart(field));
  assertEquals(0, goog.dom.selection.getEnd(field));

  select(field);
  var message = 'Get Behind Me Satan';
  goog.dom.selection.setText(field, message);
  goog.dom.selection.setStart(field, 5);
  goog.dom.selection.setEnd(field, message.length);
  assertEquals(5, goog.dom.selection.getStart(field));
  assertEquals(message.length, goog.dom.selection.getEnd(field));

  // Set the end before the start, and see if getEnd returns the start
  // position itself.
  goog.dom.selection.setStart(field, 5);
  goog.dom.selection.setEnd(field, 3);
  assertEquals(3, goog.dom.selection.getEnd(field));
}


/**
 * Tests the getText and setText routines acting on selected text in
 * both input and textarea.
 */
function testGetAndSetSelectedTextInput() {
  getAndSetSelectedTextHelper(input);
}

function testGetAndSetSelectedTextTextarea() {
  getAndSetSelectedTextHelper(textarea);
}

function getAndSetSelectedTextHelper(field) {
  select(field);
  goog.dom.selection.setText(field, 'Get Behind Me Satan');

  // select 'Behind'
  goog.dom.selection.setStart(field, 4);
  goog.dom.selection.setEnd(field, 10);
  assertEquals('Behind', goog.dom.selection.getText(field));

  goog.dom.selection.setText(field, 'In Front Of');
  goog.dom.selection.setStart(field, 0);
  goog.dom.selection.setEnd(field, 100);
  assertEquals('Get In Front Of Me Satan', goog.dom.selection.getText(field));
}


/**
 * Test setStart on hidden input and hidden textarea.
 */
function testSetCursorOnHiddenInput() {
  setCursorOnHiddenInputHelper(hiddenInput);
}

function testSetCursorOnHiddenTextarea() {
  setCursorOnHiddenInputHelper(hiddenTextarea);
}

function setCursorOnHiddenInputHelper(hiddenField) {
  goog.dom.selection.setStart(hiddenField, 0);
  assertEquals(0, goog.dom.selection.getStart(hiddenField));
}


/**
 * Test setStart, setEnd, getStart and getEnd in textarea with text
 * containing line breaks.
 */
function testSetAndGetCursorWithLineBreaks() {
  select(textarea);
  var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
  var newline = isLegacyIE ? '\r\n' : '\n';
  var message = 'Hello' + newline + 'World';
  goog.dom.selection.setText(textarea, message);

  // Test setEnd and getEnd, by setting the cursor somewhere after the
  // \r\n combination.
  goog.dom.selection.setEnd(textarea, 9);
  assertEquals(9, goog.dom.selection.getEnd(textarea));

  // Test basic setStart and getStart
  goog.dom.selection.setStart(textarea, 10);
  assertEquals(10, goog.dom.selection.getStart(textarea));

  // Test setEnd and getEnd, by setting the cursor exactly after the
  // \r\n combination in IE or after \n in GECKO.
  var endOfNewline = isLegacyIE ? 7 : 6;
  checkSetAndGetTextarea(endOfNewline, endOfNewline);

  // Select a \r\n combination in IE or \n in GECKO and see if
  // getStart and getEnd work correctly.
  clearField(textarea);
  message = 'Hello' + newline + newline + 'World';
  goog.dom.selection.setText(textarea, message);
  var startOfNewline = isLegacyIE ? 7 : 6;
  endOfNewline = isLegacyIE ? 9 : 7;
  checkSetAndGetTextarea(startOfNewline, endOfNewline);

  // Select 2 \r\n combinations in IE or 2 \ns in GECKO and see if getStart
  // and getEnd work correctly.
  checkSetAndGetTextarea(5, endOfNewline);

  // Position cursor b/w 2 \r\n combinations in IE or 2 \ns in GECKO and see
  // if getStart and getEnd work correctly.
  clearField(textarea);
  message = 'Hello' + newline + newline + newline + newline + 'World';
  goog.dom.selection.setText(textarea, message);
  var middleOfNewlines = isLegacyIE ? 9 : 7;
  checkSetAndGetTextarea(middleOfNewlines, middleOfNewlines);

  // Position cursor at end of a textarea which ends with \r\n in IE or \n in
  // GECKO.
  if (!goog.userAgent.IE || !goog.userAgent.isVersionOrHigher('11')) {
    // TODO(johnlenz): investigate why this fails in IE 11.
    clearField(textarea);
    message = 'Hello' + newline + newline;
    goog.dom.selection.setText(textarea, message);
    var endOfTextarea = message.length;
    checkSetAndGetTextarea(endOfTextarea, endOfTextarea);
  }

  // Position cursor at the end of the 2 starting \r\ns in IE or \ns in GECKO
  // within a textarea.
  clearField(textarea);
  message = newline + newline + 'World';
  goog.dom.selection.setText(textarea, message);
  var endOfTwoNewlines = isLegacyIE ? 4 : 2;
  checkSetAndGetTextarea(endOfTwoNewlines, endOfTwoNewlines);

  // Position cursor at the end of the first \r\n in IE or \n in
  // GECKO within a textarea.
  endOfOneNewline = isLegacyIE ? 2 : 1;
  checkSetAndGetTextarea(endOfOneNewline, endOfOneNewline);
}


/**
 * Test to make sure there's no error when getting the range of an unselected
 * textarea. See bug 1274027.
 */
function testGetStartOnUnfocusedTextarea() {
  input.value = 'White Blood Cells';
  input.focus();
  goog.dom.selection.setCursorPosition(input, 5);

  assertEquals(
      'getStart on input should return where we put the cursor', 5,
      goog.dom.selection.getStart(input));

  assertEquals(
      'getStart on unfocused textarea should succeed without error', 0,
      goog.dom.selection.getStart(textarea));
}


/**
 * Test to make sure there's no error setting cursor position within a
 * textarea after a newline. This is problematic on IE because of the
 * '\r\n' vs '\n' issue.
 */
function testSetCursorPositionTextareaWithNewlines() {
  textarea.value = 'Hello\nWorld';
  textarea.focus();

  // Set the selection point between 'W' and 'o'. Position is computed this
  // way instead of being hard-coded because it's different in IE due to \r\n
  // vs \n.
  goog.dom.selection.setCursorPosition(textarea, textarea.value.length - 4);

  var isLegacyIE = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
  var linebreak = isLegacyIE ? '\r\n' : '\n';
  var expectedLeftString = 'Hello' + linebreak + 'W';

  assertEquals(
      'getStart on input should return after the newline',
      expectedLeftString.length, goog.dom.selection.getStart(textarea));
  assertEquals(
      'getEnd on input should return after the newline',
      expectedLeftString.length, goog.dom.selection.getEnd(textarea));

  goog.dom.selection.setEnd(textarea, textarea.value.length);
  assertEquals('orld', goog.dom.selection.getText(textarea));
}


/**
 * Helper function to clear the textfield contents.
 */
function clearField(field) {
  field.value = '';
}


/**
 * Helper function to set the start and end and assert the getter values.
 */
function checkSetAndGetTextarea(start, end) {
  goog.dom.selection.setStart(textarea, start);
  goog.dom.selection.setEnd(textarea, end);
  assertEquals(start, goog.dom.selection.getStart(textarea));
  assertEquals(end, goog.dom.selection.getEnd(textarea));
}


/**
 * Helper function to focus and select a field. In IE8, selected
 * fields need focus.
 */
function select(field) {
  field.focus();
  field.select();
}
