<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=636737
-->
<head>
  <title>Test for Bug input.valueAsNumber</title>
  <script type="application/javascript" src="/MochiKit/packed.js"></script>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=636737">Mozilla Bug 636737</a>
<p id="display"></p>
<pre id="test">
<script type="application/javascript">

/** Test for Bug 636737 **/

/**
 * This test is checking .valueAsNumber.
 */

function checkAvailability()
{
  var testData =
  [
    ["text", false],
    ["password", false],
    ["search", false],
    ["tel", false],
    ["email", false],
    ["url", false],
    ["hidden", false],
    ["checkbox", false],
    ["radio", false],
    ["file", false],
    ["submit", false],
    ["image", false],
    ["reset", false],
    ["button", false],
    ["number", true],
    ["range", true],
    ["date", true],
    ["time", true],
    ["color", false],
    ["month", true],
    ["week", true],
    ["datetime-local", true],
  ];

  var element = document.createElement('input');

  for (let data of testData) {
    var exceptionCatched = false;
    element.type = data[0];
    try {
      element.valueAsNumber;
    } catch (e) {
      exceptionCatched = true;
    }
    is(exceptionCatched, false,
       "valueAsNumber shouldn't throw exception on getting");

    exceptionCatched = false;
    try {
      element.valueAsNumber = 42;
    } catch (e) {
      exceptionCatched = true;
    }
    is(exceptionCatched, !data[1], "valueAsNumber for " + data[0] +
                                   " availability is not correct");
  }
}

function checkNumberGet()
{
  var testData =
  [
    ["42", 42],
    ["-42", -42], // should work for negative values
    ["42.1234", 42.1234],
    ["123.123456789123", 123.123456789123], // double precision
    ["1e2", 100], // e should be usable
    ["2e1", 20],
    ["1e-1", 0.1], // value after e can be negative
    ["1E2", 100], // E can be used instead of e
    ["e", null],
    ["e2", null],
    ["1e0.1", null],
    ["", null], // the empty string is not a number
    ["foo", null],
    ["42,13", null], // comma can't be used as a decimal separator
  ];

  var element = document.createElement('input');
  element.type = "number";
  for (let data of testData) {
    element.value = data[0];

    // Given that NaN != NaN, we have to use null when the expected value is NaN.
    if (data[1] != null) {
      is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
         "floating point representation of the value");
    } else {
      ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
         "when the element value is not a number");
    }
  }
}

function checkNumberSet()
{
  var testData =
  [
    [42, "42"],
    [-42, "-42"], // should work for negative values
    [42.1234, "42.1234"],
    [123.123456789123, "123.123456789123"], // double precision
    [1e2, "100"], // e should be usable
    [2e1, "20"],
    [1e-1, "0.1"], // value after e can be negative
    [1E2, "100"], // E can be used instead of e
    // Setting a string will set NaN.
    ["foo", ""],
    // "" is converted to 0.
    ["", "0"],
    [42, "42"], // Keep this here, it is used by the next test.
    // Setting Infinity should throw and not change the current value.
    [Infinity, "42", true],
    [-Infinity, "42", true],
    // Setting NaN should change the value to the empty string.
    [NaN, ""],
  ];

  var element = document.createElement('input');
  element.type = "number";
  for (let data of testData) {
    var caught = false;
    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1],
         "valueAsNumber should be able to set the value");
    } catch (e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }
}

function checkRangeGet()
{
  // For type=range we should never get NaN since the user agent is required
  // to fix up the input's value to be something sensible.

  var min = -200;
  var max = 200;
  var defaultValue = min + (max - min)/2;

  var testData =
  [
    ["42", 42],
    ["-42", -42], // should work for negative values
    ["42.1234", 42.1234],
    ["123.123456789123", 123.123456789123], // double precision
    ["1e2", 100], // e should be usable
    ["2e1", 20],
    ["1e-1", 0.1], // value after e can be negative
    ["1E2", 100], // E can be used instead of e
    ["e", defaultValue],
    ["e2", defaultValue],
    ["1e0.1", defaultValue],
    ["", defaultValue],
    ["foo", defaultValue],
    ["42,13", defaultValue],
  ];

  var element = document.createElement('input');
  element.type = "range";
  element.setAttribute("min", min); // avoids out of range sanitization
  element.setAttribute("max", max);
  element.setAttribute("step", "any"); // avoids step mismatch sanitization
  for (let data of testData) {
    element.value = data[0];

    // Given that NaN != NaN, we have to use null when the expected value is NaN.
    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
       "floating point representation of the value");
  }
}

function checkRangeSet()
{
  var min = -200;
  var max = 200;
  var defaultValue = String(min + (max - min)/2);

  var testData =
  [
    [42, "42"],
    [-42, "-42"], // should work for negative values
    [42.1234, "42.1234"],
    [123.123456789123, "123.123456789123"], // double precision
    [1e2, "100"], // e should be usable
    [2e1, "20"],
    [1e-1, "0.1"], // value after e can be negative
    [1E2, "100"], // E can be used instead of e
    ["foo", defaultValue],
    ["", defaultValue],
    [42, "42"], // Keep this here, it is used by the next test.
    // Setting Infinity should throw and not change the current value.
    [Infinity, "42", true],
    [-Infinity, "42", true],
    // Setting NaN should change the value to the empty string.
    [NaN, defaultValue],
  ];

  var element = document.createElement('input');
  element.type = "range";
  element.setAttribute("min", min); // avoids out of range sanitization
  element.setAttribute("max", max);
  element.setAttribute("step", "any"); // avoids step mismatch sanitization
  for (let data of testData) {
    var caught = false;
    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1],
         "valueAsNumber should be able to set the value");
    } catch (e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }
}

function checkDateGet()
{
  var validData =
  [
    [ "2012-07-12", 1342051200000 ],
    [ "1970-01-01", 0 ],
    // We are supposed to support at least until this date.
    // (corresponding to the date object maximal value)
    [ "275760-09-13", 8640000000000000 ],
    // Minimum valid date (limited by the input element minimum valid value)
    [ "0001-01-01", -62135596800000 ],
    [ "2012-02-29", 1330473600000 ],
    [ "2011-02-28", 1298851200000 ],
  ];

  var invalidData =
  [
    "invaliddate",
    "",
    "275760-09-14",
    "999-12-31",
    "-001-12-31",
    "0000-01-01",
    "2011-02-29",
    "1901-13-31",
    "1901-12-32",
    "1901-00-12",
    "1901-01-00",
    "1900-02-29",
  ];

  var element = document.createElement('input');
  element.type = "date";
  for (let data of validData) {
    element.value = data[0];
    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
       "timestamp representing this date");
  }

  for (let data of invalidData) {
    element.value = data;
    ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
       "when the element value is not a valid date");
  }
}

function checkDateSet()
{
  var testData =
  [
    [ 1342051200000,     "2012-07-12" ],
    [ 0,                 "1970-01-01" ],
    // Maximum valid date (limited by the ecma date object range).
    [ 8640000000000000,  "275760-09-13" ],
    // Minimum valid date (limited by the input element minimum valid value)
    [ -62135596800000,   "0001-01-01" ],
    [ 1330473600000,     "2012-02-29" ],
    [ 1298851200000,     "2011-02-28" ],
    // "Values must be truncated to valid dates"
    [ 42.1234,           "1970-01-01" ],
    [ 123.123456789123,  "1970-01-01" ],
    [ 1e2,               "1970-01-01" ],
    [ 1E9,               "1970-01-12" ],
    [ 1e-1,              "1970-01-01" ],
    [ 2e10,              "1970-08-20" ],
    [ 1298851200010,     "2011-02-28" ],
    [ -1,                "1969-12-31" ],
    [ -86400000,         "1969-12-31" ],
    [ 86400000,          "1970-01-02" ],
    // Invalid numbers.
    // Those are implicitly converted to numbers
    [ "",                "1970-01-01" ],
    [ true,              "1970-01-01" ],
    [ false,             "1970-01-01" ],
    [ null,              "1970-01-01" ],
    // Those are converted to NaN, the corresponding date string is the empty string
    [ "invaliddatenumber", "" ],
    [ NaN,               "" ],
    [ undefined,         "" ],
    // Out of range, the corresponding date string is the empty string
    [ -62135596800001,   "" ],
    // Infinity will keep the current value and throw (so we need to set a current value).
    [ 1298851200010, "2011-02-28" ],
    [ Infinity, "2011-02-28", true ],
    [ -Infinity, "2011-02-28", true ],
  ];

  var element = document.createElement('input');
  element.type = "date";
  for (let data of testData) {
    var caught = false;

    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
    } catch(e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "the value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }

}

function checkTimeGet()
{
  var tests = [
    // Some invalid values to begin.
    { value: "", result: NaN },
    { value: "foobar", result: NaN },
    { value: "00:", result: NaN },
    { value: "24:00", result: NaN },
    { value: "00:99", result: NaN },
    { value: "00:00:", result: NaN },
    { value: "00:00:99", result: NaN },
    { value: "00:00:00:", result: NaN },
    { value: "00:00:00.", result: NaN },
    { value: "00:00:00.0000", result: NaN },
    // Some simple valid values.
    { value: "00:00", result: 0 },
    { value: "00:01", result: 60000 },
    { value: "01:00", result: 3600000 },
    { value: "01:01", result: 3660000 },
    { value: "13:37", result: 49020000 },
    // Valid values including seconds.
    { value: "00:00:01", result: 1000 },
    { value: "13:37:42", result: 49062000 },
    // Valid values including seconds fractions.
    { value: "00:00:00.001", result: 1 },
    { value: "00:00:00.123", result: 123 },
    { value: "00:00:00.100", result: 100 },
    { value: "00:00:00.000", result: 0 },
    { value: "20:17:31.142", result: 73051142 },
    // Highest possible value.
    { value: "23:59:59.999", result: 86399999 },
    // Some values with one or two digits for the fraction of seconds.
    { value: "00:00:00.1", result: 100 },
    { value: "00:00:00.14", result: 140 },
    { value: "13:37:42.7", result: 49062700 },
    { value: "23:31:12.23", result: 84672230 },
  ];

  var element = document.createElement('input');
  element.type = 'time';

  for (let test of tests) {
    element.value = test.value;
    if (isNaN(test.result)) {
      ok(isNaN(element.valueAsNumber),
         "invalid value should have .valueAsNumber return NaN");
    } else {
      is(element.valueAsNumber, test.result,
         ".valueAsNumber should return " + test.result);
    }
  }
}

function checkTimeSet()
{
  var tests = [
    // Some NaN values (should set to empty string).
    { value: NaN, result: "" },
    { value: "foobar", result: "" },
    { value: function() {}, result: "" },
    // Inifinity (should throw).
    { value: Infinity, throw: true },
    { value: -Infinity, throw: true },
    // "" converts to 0... JS is fun :)
    { value: "", result: "00:00" },
    // Simple tests.
    { value: 0, result: "00:00" },
    { value: 1, result: "00:00:00.001" },
    { value: 100, result: "00:00:00.100" },
    { value: 1000, result: "00:00:01" },
    { value: 60000, result: "00:01" },
    { value: 3600000, result: "01:00" },
    { value: 83622234, result: "23:13:42.234" },
    // Some edge cases.
    { value: 86400000, result: "00:00" },
    { value: 86400001, result: "00:00:00.001" },
    { value: 170022234, result: "23:13:42.234" },
    { value: 432000000, result: "00:00" },
    { value: -1, result: "23:59:59.999" },
    { value: -86400000, result: "00:00" },
    { value: -86400001, result: "23:59:59.999" },
    { value: -56789, result: "23:59:03.211" },
    { value: 0.9, result: "00:00" },
  ];

  var element = document.createElement('input');
  element.type = 'time';

  for (let test of tests) {
    try {
      var caught = false;
      element.valueAsNumber = test.value;
      is(element.value, test.result, "value should return " + test.result);
    } catch(e) {
      caught = true;
    }

    if (!test.throw) {
      test.throw = false;
    }

    is(caught, test.throw, "the test throwing status should be " + test.throw);
  }
}

function checkMonthGet()
{
  var validData =
  [
    [ "2016-07", 558       ],
    [ "1970-01", 0         ],
    [ "1969-12", -1        ],
    [ "0001-01", -23628    ],
    [ "10000-12", 96371    ],
    [ "275760-09", 3285488 ],
  ];

  var invalidData =
  [
    "invalidmonth",
    "0000-01",
    "2000-00",
    "2012-13",
    // Out of range.
    "275760-10",
  ];

  var element = document.createElement('input');
  element.type = "month";
  for (let data of validData) {
    element.value = data[0];
    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
       "integer value representing this month");
  }

  for (let data of invalidData) {
    element.value = data;
    ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
       "when the element value is not a valid month");
  }
}

function checkMonthSet()
{
  var testData =
  [
    [ 558,               "2016-07"   ],
    [ 0,                 "1970-01"   ],
    [ -1,                "1969-12"   ],
    [ 96371,             "10000-12"  ],
    [ 12,                "1971-01"   ],
    [ -12,               "1969-01"   ],
    // Maximum valid month (limited by the ecma date object range)
    [ 3285488,           "275760-09" ],
    // Minimum valid month (limited by the input element minimum valid value)
    [ -23628,            "0001-01"   ],
    // "Values must be truncated to valid months"
    [ 0.3,               "1970-01"   ],
    [ -1.1,              "1969-11"   ],
    [ 1e2,               "1978-05"   ],
    [ 1e-1,              "1970-01"   ],
    // Invalid numbers.
    // Those are implicitly converted to numbers
    [ "",                "1970-01"   ],
    [ true,              "1970-02"   ],
    [ false,             "1970-01"   ],
    [ null,              "1970-01"   ],
    // Those are converted to NaN, the corresponding month string is the empty string
    [ "invalidmonth",    ""          ],
    [ NaN,               ""          ],
    [ undefined,         ""          ],
    // Out of range, the corresponding month string is the empty string
    [ -23629,            ""          ],
    [ 3285489,           ""          ],
    // Infinity will keep the current value and throw (so we need to set a current value)
    [ 558,               "2016-07"   ],
    [ Infinity,          "2016-07", true ],
    [ -Infinity,         "2016-07", true ],
  ];

  var element = document.createElement('input');
  element.type = "month";
  for (let data of testData) {
    var caught = false;

    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
    } catch(e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "the value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }
}

function checkWeekGet()
{
  var validData =
  [
    // Common years starting on different days of week.
    [ "2007-W01", Date.UTC(2007, 0, 1)   ], // Mon
    [ "2013-W01", Date.UTC(2012, 11, 31) ], // Tue
    [ "2014-W01", Date.UTC(2013, 11, 30) ], // Wed
    [ "2015-W01", Date.UTC(2014, 11, 29) ], // Thu
    [ "2010-W01", Date.UTC(2010, 0, 4)   ], // Fri
    [ "2011-W01", Date.UTC(2011, 0, 3)   ], // Sat
    [ "2017-W01", Date.UTC(2017, 0, 2)   ], // Sun
    // Common years ending on different days of week.
    [ "2007-W52", Date.UTC(2007, 11, 24) ], // Mon
    [ "2013-W52", Date.UTC(2013, 11, 23) ], // Tue
    [ "2014-W52", Date.UTC(2014, 11, 22) ], // Wed
    [ "2015-W53", Date.UTC(2015, 11, 28) ], // Thu
    [ "2010-W52", Date.UTC(2010, 11, 27) ], // Fri
    [ "2011-W52", Date.UTC(2011, 11, 26) ], // Sat
    [ "2017-W52", Date.UTC(2017, 11, 25) ], // Sun
    // Leap years starting on different days of week.
    [ "1996-W01", Date.UTC(1996, 0, 1)   ], // Mon
    [ "2008-W01", Date.UTC(2007, 11, 31) ], // Tue
    [ "2020-W01", Date.UTC(2019, 11, 30) ], // Wed
    [ "2004-W01", Date.UTC(2003, 11, 29) ], // Thu
    [ "2016-W01", Date.UTC(2016, 0, 4)   ], // Fri
    [ "2000-W01", Date.UTC(2000, 0, 3)   ], // Sat
    [ "2012-W01", Date.UTC(2012, 0, 2)   ], // Sun
    // Leap years ending on different days of week.
    [ "2012-W52", Date.UTC(2012, 11, 24) ], // Mon
    [ "2024-W52", Date.UTC(2024, 11, 23) ], // Tue
    [ "1980-W52", Date.UTC(1980, 11, 22) ], // Wed
    [ "1992-W53", Date.UTC(1992, 11, 28) ], // Thu
    [ "2004-W53", Date.UTC(2004, 11, 27) ], // Fri
    [ "1988-W52", Date.UTC(1988, 11, 26) ], // Sat
    [ "2000-W52", Date.UTC(2000, 11, 25) ], // Sun
    // Other normal cases.
    [ "2015-W53", Date.UTC(2015, 11, 28)   ],
    [ "2016-W36", Date.UTC(2016, 8, 5)     ],
    [ "1970-W01", Date.UTC(1969, 11, 29)   ],
    [ "275760-W37", Date.UTC(275760, 8, 8) ],
  ];

  var invalidData =
  [
    "invalidweek",
    "0000-W01",
    "2016-W00",
    "2016-W53",
    // Out of range.
    "275760-W38",
  ];

  var element = document.createElement('input');
  element.type = "week";
  for (let data of validData) {
    element.value = data[0];
    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
       "integer value representing this week");
  }

  for (let data of invalidData) {
    element.value = data;
    ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
       "when the element value is not a valid week");
  }
}

function checkWeekSet()
{
  var testData =
  [
    // Common years starting on different days of week.
    [ Date.UTC(2007, 0, 1), "2007-W01"   ], // Mon
    [ Date.UTC(2013, 0, 1), "2013-W01"   ], // Tue
    [ Date.UTC(2014, 0, 1), "2014-W01"   ], // Wed
    [ Date.UTC(2015, 0, 1), "2015-W01"   ], // Thu
    [ Date.UTC(2010, 0, 1), "2009-W53"   ], // Fri
    [ Date.UTC(2011, 0, 1), "2010-W52"   ], // Sat
    [ Date.UTC(2017, 0, 1), "2016-W52"   ], // Sun
    // Common years ending on different days of week.
    [ Date.UTC(2007, 11, 31), "2008-W01" ], // Mon
    [ Date.UTC(2013, 11, 31), "2014-W01" ], // Tue
    [ Date.UTC(2014, 11, 31), "2015-W01" ], // Wed
    [ Date.UTC(2015, 11, 31), "2015-W53" ], // Thu
    [ Date.UTC(2010, 11, 31), "2010-W52" ], // Fri
    [ Date.UTC(2011, 11, 31), "2011-W52" ], // Sat
    [ Date.UTC(2017, 11, 31), "2017-W52" ], // Sun
    // Leap years starting on different days of week.
    [ Date.UTC(1996, 0, 1), "1996-W01"   ], // Mon
    [ Date.UTC(2008, 0, 1), "2008-W01"   ], // Tue
    [ Date.UTC(2020, 0, 1), "2020-W01"   ], // Wed
    [ Date.UTC(2004, 0, 1), "2004-W01"   ], // Thu
    [ Date.UTC(2016, 0, 1), "2015-W53"   ], // Fri
    [ Date.UTC(2000, 0, 1), "1999-W52"   ], // Sat
    [ Date.UTC(2012, 0, 1), "2011-W52"   ], // Sun
    // Leap years ending on different days of week.
    [ Date.UTC(2012, 11, 31), "2013-W01" ], // Mon
    [ Date.UTC(2024, 11, 31), "2025-W01" ], // Tue
    [ Date.UTC(1980, 11, 31), "1981-W01" ], // Wed
    [ Date.UTC(1992, 11, 31), "1992-W53" ], // Thu
    [ Date.UTC(2004, 11, 31), "2004-W53" ], // Fri
    [ Date.UTC(1988, 11, 31), "1988-W52" ], // Sat
    [ Date.UTC(2000, 11, 31), "2000-W52" ], // Sun
    // Other normal cases.
    [ Date.UTC(2008, 8, 26),  "2008-W39" ],
    [ Date.UTC(2016, 0, 4),   "2016-W01" ],
    [ Date.UTC(2016, 0, 10),  "2016-W01" ],
    [ Date.UTC(2016, 0, 11),  "2016-W02" ],
    // Maximum valid week (limited by the ecma date object range).
    [ 8640000000000000,  "275760-W37" ],
    // Minimum valid week (limited by the input element minimum valid value)
    [ -62135596800000,   "0001-W01" ],
    // "Values must be truncated to valid weeks"
    [ 0.3,               "1970-W01"  ],
    [ 1e-1,              "1970-W01"  ],
    [ -1.1,              "1970-W01"  ],
    [ -345600000,        "1969-W52"  ],
    // Invalid numbers.
    // Those are implicitly converted to numbers
    [ "",                "1970-W01" ],
    [ true,              "1970-W01" ],
    [ false,             "1970-W01" ],
    [ null,              "1970-W01" ],
    // Those are converted to NaN, the corresponding week string is the empty string
    [ "invalidweek",     "" ],
    [ NaN,               "" ],
    [ undefined,         "" ],
    // Infinity will keep the current value and throw (so we need to set a current value).
    [ Date.UTC(2016, 8, 8), "2016-W36" ],
    [ Infinity,             "2016-W36", true ],
    [ -Infinity,            "2016-W36", true ],
  ];

  var element = document.createElement('input');
  element.type = "week";
  for (let data of testData) {
    var caught = false;

    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1], "valueAsNumber should set the value to " +
                                 data[1]);
    } catch(e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "the value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }
}

function checkDatetimeLocalGet() {
  var validData =
  [
    // Simple cases.
    [ "2016-12-20T09:58",          Date.UTC(2016, 11, 20, 9, 58)          ],
    [ "2016-12-20T09:58:30",       Date.UTC(2016, 11, 20, 9, 58, 30)      ],
    [ "2016-12-20T09:58:30.123",   Date.UTC(2016, 11, 20, 9, 58, 30, 123) ],
    [ "2017-01-01T10:00",          Date.UTC(2017, 0, 1, 10, 0, 0)         ],
    [ "1969-12-31T12:00:00",       Date.UTC(1969, 11, 31, 12, 0, 0)       ],
    [ "1970-01-01T00:00",          0                                      ],
    // Leap years.
    [ "1804-02-29 12:34",          Date.UTC(1804, 1, 29, 12, 34, 0)       ],
    [ "2016-02-29T12:34",          Date.UTC(2016, 1, 29, 12, 34, 0)       ],
    [ "2016-12-31T12:34:56",       Date.UTC(2016, 11, 31, 12, 34, 56)     ],
    [ "2016-01-01T12:34:56.789",   Date.UTC(2016, 0, 1, 12, 34, 56, 789)  ],
    [ "2017-01-01 12:34:56.789",   Date.UTC(2017, 0, 1, 12, 34, 56, 789)  ],
    // Maximum valid datetime-local (limited by the ecma date object range).
    [ "275760-09-13T00:00",        8640000000000000                       ],
    // Minimum valid datetime-local (limited by the input element minimum valid value).
    [ "0001-01-01T00:00",          -62135596800000                        ],
  ];

  var invalidData =
  [
    "invaliddatetime-local",
    "0000-01-01T00:00",
    "2016-12-25T00:00Z",
    "2015-02-29T12:34",
    "1-1-1T12:00",
    // Out of range.
    "275760-09-13T12:00",
  ];

  var element = document.createElement('input');
  element.type = "datetime-local";
  for (let data of validData) {
    element.value = data[0];
    is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
       "integer value representing this datetime-local");
  }

  for (let data of invalidData) {
    element.value = data;
    ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN "  +
       "when the element value is not a valid datetime-local");
  }
}

function checkDatetimeLocalSet()
{
  var testData =
  [
    // Simple cases.
    [ Date.UTC(2016, 11, 20, 9, 58, 0),       "2016-12-20T09:58",       ],
    [ Date.UTC(2016, 11, 20, 9, 58, 30),      "2016-12-20T09:58:30"     ],
    [ Date.UTC(2016, 11, 20, 9, 58, 30, 123), "2016-12-20T09:58:30.123" ],
    [ Date.UTC(2017, 0, 1, 10, 0, 0),         "2017-01-01T10:00"        ],
    [ Date.UTC(1969, 11, 31, 12, 0, 0),       "1969-12-31T12:00"        ],
    [ 0,                                      "1970-01-01T00:00"        ],
    // Maximum valid week (limited by the ecma date object range).
    [ 8640000000000000,  "275760-09-13T00:00"      ],
    // Minimum valid datetime-local (limited by the input element minimum valid value).
    [ -62135596800000,   "0001-01-01T00:00"        ],
    // Leap years.
    [ Date.UTC(1804, 1, 29, 12, 34, 0),       "1804-02-29T12:34"        ],
    [ Date.UTC(2016, 1, 29, 12, 34, 0),       "2016-02-29T12:34"        ],
    [ Date.UTC(2016, 11, 31, 12, 34, 56),     "2016-12-31T12:34:56"     ],
    [ Date.UTC(2016, 0, 1, 12, 34, 56, 789),  "2016-01-01T12:34:56.789" ],
    [ Date.UTC(2017, 0, 1, 12, 34, 56, 789),  "2017-01-01T12:34:56.789" ],
    // "Values must be truncated to valid datetime-local"
    [ 0.3,               "1970-01-01T00:00"        ],
    [ 1e-1,              "1970-01-01T00:00"        ],
    [ -1  ,              "1969-12-31T23:59:59.999" ],
    [ -345600000,        "1969-12-28T00:00"        ],
    // Invalid numbers.
    // Those are implicitly converted to numbers
    [ "",                "1970-01-01T00:00"        ],
    [ true,              "1970-01-01T00:00:00.001" ],
    [ false,             "1970-01-01T00:00"        ],
    [ null,              "1970-01-01T00:00"        ],
    // Those are converted to NaN, the corresponding week string is the empty string
    [ "invaliddatetime-local", "" ],
    [ NaN,                     "" ],
    [ undefined,               "" ],
    // Infinity will keep the current value and throw (so we need to set a current value).
    [ Date.UTC(2016, 11, 27, 15, 10, 0), "2016-12-27T15:10"       ],
    [ Infinity,                          "2016-12-27T15:10", true ],
    [ -Infinity,                         "2016-12-27T15:10", true ],
  ];

  var element = document.createElement('input');
  element.type = "datetime-local";
  for (let data of testData) {
    var caught = false;

    try {
      element.valueAsNumber = data[0];
      is(element.value, data[1], "valueAsNumber should set the value to " +
                                 data[1]);
    } catch(e) {
      caught = true;
    }

    if (data[2]) {
      ok(caught, "valueAsNumber should have thrown");
      is(element.value, data[1], "the value should not have changed");
    } else {
      ok(!caught, "valueAsNumber should not have thrown");
    }
  }
}

checkAvailability();

// <input type='number'> test
checkNumberGet();
checkNumberSet();

// <input type='range'> test
checkRangeGet();
checkRangeSet();

// <input type='date'> test
checkDateGet();
checkDateSet();

// <input type='time'> test
checkTimeGet();
checkTimeSet();

// <input type='month'> test
checkMonthGet();
checkMonthSet();

// <input type='week'> test
checkWeekGet();
checkWeekSet();

// <input type='datetime-local'> test
checkDatetimeLocalGet();
checkDatetimeLocalSet();

</script>
</pre>
</body>
</html>