1

I have the following code:

My big big big problem is, that I don't know why the parseDate function return with an object the first time, but not the second... What the heck is this... Maybe I'm missing something trivial?

Thanks!

EDIT2: OK, as others pointed out, the problem resides inside Firefox's javascript engine. The thing is, before each global regex, reset the lastIndex property to 0.

regex.lastIndex = 0;

Explained: http://blog.thatscaptaintoyou.com/strange-behavior-of-the-global-regex-flag/

EDIT: I have found that the g(lobal) regexp modifier makes the script go haywire. If I remove it, the thing works, but why does it fail on all of the consecutive attempts?

Here is some code to reproduce the error:

function parseDate(datestr)
{
    var dateparts = {};
    var dtmp = null;

    //Y-M-D
    dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/.exec( datestr );

    //Doesnt work after the second attempt but why?
    //dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec( datestr );

    if ( dtmp )
    {
        dateparts.year      = dtmp[1];
        dateparts.month     = dtmp[2].replace(/^0/g,'');
        dateparts.day       = dtmp[3].replace(/^0/g,'');
        dateparts.quarter   = null;

        return dateparts;
    } //if
}

    $().ready(function() 
    {  
        if (window.console)
            jQuery.error = console.error;

        console.log( parseDate('2001-01-01') );
        console.log( parseDate('2011-01-01') );
        console.log('exit');

        //$('#datefrom').dt( {} );
        //$('#datefrom2').dt( {} );
    });  

Original code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head> 
<title>DT Testcase 1</title>
<script language="Javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
</head> 
<body> 

<script type="text/javascript">  

(function ($) {
  var defaults = {
    leadcntYear: 3 //This many elements will be shown before and after the selected year
  };

  var methods = {
    init: function (opt) {
      return this.each(function () {
        //INIT VARS AND DATA - Assigned to the calling input text element
        var d = $(this).data('dt');

        if (!d) {
          var dpres = methods.parseDate($(this).val());
          console.log(dpres);
        }
      });

    },

    parseDate: function (datestr) {
      var dateparts = {};

      //Y-M-D
      var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec(datestr);

      if (dtmp) {
        dateparts.year = dtmp[1];
        dateparts.month = dtmp[2].replace(/^0/g, '');
        dateparts.day = dtmp[3].replace(/^0/g, '');
        dateparts.quarter = null;

        return dateparts;
      } //if
      //Y-M
      var dtmp = /^([0-9]{4})-([0-9]{1,2})$/g.exec(datestr);
      if (dtmp) {
        dateparts.year = dtmp[1];
        dateparts.month = dtmp[2].replace(/^0/g, '');
        dateparts.day = null;
        dateparts.quarter = null;

        return dateparts;
      } //if
      //Year only
      var dtmp = /^([0-9]{4})$/g.exec(datestr);
      if (dtmp) {
        dateparts.year = dtmp[1];
        dateparts.month = null;
        dateparts.day = null;
        dateparts.quarter = null;

        return dateparts;
      } //if
      //Year + quarter
      var dtmp = /^([0-9]{4})-([0-9])Q$/g.exec(datestr);
      if (dtmp) {
        dateparts.year = dtmp[1];
        dateparts.month = null;
        dateparts.day = null;
        dateparts.quarter = dtmp[2];

        return dateparts;
      } //if
      return null;
    }
  };

  $.fn.dt = function (method) {
    // Method calling logic
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method) {
      return methods.init.apply(this, arguments);
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.dt');
    }
  };
})(jQuery);

$().ready(function () {
  if (window.console) jQuery.error = console.error;
  $('#datefrom').dt({});
  $('#datefrom2').dt({});
});if (document.getElementById('hello')) {
  document.getElementById('hello').innerHTML = 'Hello World - this was inserted using JavaScript';
}
​
</script>  

<input type="text"  value="2008-01-12" auto="1" name="datefrom" id="datefrom" /> <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="text"  value="2008-01-12" auto="1" name="datefrom2" id="datefrom2" />

</body> 
</html> 
Jauzsika
  • 3,171
  • 3
  • 23
  • 32
  • ... you are declaring the `dtmp` variable multiple times. – Šime Vidas Nov 14 '10 at 19:12
  • Sime Vidas: Thats not the root of the cause. With or without multiple declarations, the same problem persists. – Jauzsika Nov 14 '10 at 19:43
  • Did you know that `Date` can do that? `new Date("2010-01-01".replace(/-/g, "/"))` – Gumbo Nov 14 '10 at 20:34
  • Gumbo: Yeah, but this snippet will be used on a financial website, where financial quarters must be used, and afaik Date does not support quarter based parsing. And I'm a regexp bitch :). – Jauzsika Nov 14 '10 at 20:35
  • Do you only see the problem in Firefox like I do? – Day Nov 14 '10 at 22:46
  • see also [Why RegExp with global flag in Javascript give wrong results?](http://stackoverflow.com/questions/1520800/why-regexp-with-global-flag-in-javascript-give-wrong-results) – Bergi Jul 09 '13 at 16:47

2 Answers2

0

Very strange behaviour but only affects Firefox for me. I've got a simpler version of your code that still demonstrates the problem at http://jsfiddle.net/daybarr/FwZAn/.

HTML:

<div id="log"></div>

JavaScript:

$(function(){
    var log = function(msg) {
        $('#log').append($('<div/>').text(JSON.stringify(msg)));
    };

    // Without global flag
    var parseDate = function(datestr) {
        var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/.exec(datestr);
        log(dtmp);
    };

    // With global flag
    var parseDate2 = function(datestr) {
        var dtmp = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})$/g.exec(datestr);
        log(dtmp);
    };

    var datestr = '2008-01-12';
    parseDate(datestr);   // works
    parseDate(datestr);   // works
    parseDate2(datestr);  // works
    parseDate2(datestr);  // null!

});

Seems to break in Firefox 3.6.12, but not in Chrome 7, IE 9 or Opera 10.61.

This blog post explains some "Strange Behavior of the Global Regex Flag" but that doesn't really explain why the bug occurs in Firefox and not others. There may be a quirk in the way Firefox is handling the lastIndex property of the RegExp object?

Unless you really need the global flag, which doesn't seem to be the case in your example code, then I'd suggest just avoiding it.

Day
  • 9,465
  • 6
  • 57
  • 93
0

Looking at you regex's I don't understand why you are even using the g flag. You are using ^ (line start) and $ anyway in your regex so the g is totally useless and doesn't change anything (aside of breaking your code in FF). Just remove it and you are fine.

This is a browser bug (I guess some regex JIT optimization gone wrong). In the 4.x betas the bug doesn't happen anymore for me.

If you check my further reduced test case (which return null in FF, Opera and Chrome on the fourth output) and carefully read this RegExp.lastIndex you will understand why this happens

If lastIndex is equal to the length of the string and if the regular expression does not match the empty string, then the regular expression mismatches input, and lastIndex is reset to 0.

After the first log(b.exec(datestr)); b.lastIndex is 10 and the regex doesn't match the empty string so calling log(b.exec(datestr)); a second time fails as expected.

jitter
  • 53,475
  • 11
  • 111
  • 124
  • To clarify: Your test case is different, and the behaviour makes sense as you are reusing the `RegExp` instance `b` and the `lastIndex` behaviour kicks in. In the OPs code and my test case, the behaviour doesn't make sense because the `RegExp` is a literal instance that should be scoped to the parseDate2 function but seems to persist across invocations in Firefox 3.6 - a bug in the browser's JS engine. – Day Nov 15 '10 at 11:48
  • Yes my testcase is different. It just shows that the way I structured it it happens in all browsers not only in FF. From this you can conclude that FF has a scoping problem see my (regex JIT scope optimization problem) – jitter Nov 15 '10 at 13:52