/*  dateextract.js, version 0.2
 *  (c) 2006 Shinichi Tomita <shinichi.tomita@hotmail.com>
 *
 *  dateextract.js is freely distributable under the terms of an MIT-style license.
 *  For details, see the dateextract web site: http://code.google.com/p/dateextract/
 *
/*--------------------------------------------------------------------------*/

var DateExtractor = function() {}

DateExtractor.prototype = {

  weekStartDay : 1,

  weekEndDay : 6,

  extractRules : {

    time : [
    
      { regexp   : /^(([1-9１-９][0-9０-９]*)時間)?(半|([1-9１-９][0-9０-９]*)分)?(前|後)((から|[\s　]*[\-,、〜～ー－][\s　]*)(([1-9１-９][0-9０-９]*)(時間|[hHｈＨ]))?(半|([1-9１-９][0-9０-９]*)(分間?|[mMｍＭ]))?)?/,
        callback : function(context, matched) {
          var h1 = matched[2] ? this.toNumericValue(matched[2]) : 0;
          var m1 = matched[4] ? this.toNumericValue(matched[4]) : 
                   matched[3]=='半' ? 30 : 0;
          var el = matched[5];
          var d = new Date();
          context.d1.setHours(d.getHours() +  h1 * (el=='後' ? 1 : -1));
          context.d1.setMinutes(d.getMinutes() + m1 * (el=='後' ? 1 : -1));
          if (matched[8]) {
            var h2 = matched[9] ? this.toNumericValue(matched[9]) : 1;
            var m2 = matched[12] ? this.toNumericValue(matched[12]) :
                     matched[11]=='半' ? 30 : 0;
            context.d2 = context.d2 || new Date(context.d1);
            context.d2.setHours(context.d1.getHours() + h2);
            context.d2.setMinutes(context.d1.getMinutes() + m2);
          }
          return true;
        }
      }
      ,

      { regexp   : /((A\.?M\.?\s*|a\.?m\.?\s*|午前)|(P\.?M\.?\s*|p\.?m\.?\s*|午後))?([0-9０-９]{1,2})[:：時](半|(([0-9０-９]{1,2})分?))?((から|[\s　]*[\(（][\s　]*)(([1-9１-９][0-9０-９]*)(時間|[hHｈＨ]))?(半|(([1-9１-９][0-9０-９]*)(分間?|[mMｍＭ])))?[\)）]?[\s　]*)/,
        callback : function(context, matched) {
          var pm1 = matched[3];
          var h1 = matched[4] ? this.toNumericValue(matched[4]) : 0;
          var m1 = matched[7] ? this.toNumericValue(matched[7]) :
                   matched[5]=='半' ? 30 : 0;
          if (pm1) h1+=12;
          context.d1.setHours(h1);
          context.d1.setMinutes(m1);
          if (matched[8]) {
            if (!matched[11] && !matched[13]) return false;
            var h2 = matched[11] ? this.toNumericValue(matched[11]) : 0;
            var m2 = matched[15] ? this.toNumericValue(matched[15]) :
                     matched[13]=='半' ? 30 : 0;
            context.d2 = context.d2 || new Date(context.d1);
            context.d2.setHours(h1+h2);
            context.d2.setMinutes(m1+m2);
          }
          return true;
        }
      }
      ,
      
      { regexp   : /((A\.?M\.?\s*|a\.?m\.?\s*|午前)|(P\.?M\.?\s*|p\.?m\.?\s*|午後))?([0-9０-９]{1,2})[:：時](半|(([0-9０-９]{1,2})分?))?((から|[\s　]*[\-〜～ー－][\s　]*)((A\.?M\.?\s*|a\.?m\.?\s*|午前)|(P\.?M\.?\s*|p\.?m\.?\s*|午後))?([0-9０-９]{1,2})[:：時](半|(([0-9０-９]{1,2})分?))?(まで)?)?/,
        callback : function(context, matched) {
          var pm1 = matched[3];
          var h1 = matched[4] ? this.toNumericValue(matched[4]) : 0;
          var m1 = matched[7] ? this.toNumericValue(matched[7]) :
                   matched[5]=='半' ? 30 : 0;
          if (pm1) h1+=12;
          context.d1.setHours(h1);
          context.d1.setMinutes(m1);
          if (matched[8]) {
            var pm2 = matched[12];
            var h2 = matched[13] ? this.toNumericValue(matched[13]) : h1;
            var m2 = matched[16] ? this.toNumericValue(matched[16]) : 
                     matched[14]=='半' ? 30 : m1;
            if (pm2) h2+=12;
            context.d2 = context.d2 || new Date(context.d1);
            context.d2.setHours(h2);
            context.d2.setMinutes(m2);
          }
          return true;
        }
      }

    ]

    ,

    date : [

      { regexp   : /([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週間|ヶ月)(前|後)(から([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週|ヶ月)間)?/,
        callback : function(context, matched) {
          var d1 = new Date();
          var n1 = this.toNumericValue(matched[1]);
          var el = matched[3]=='後' ? 1 : -1;
          if (matched[2]=='日') {
            d1.setDate(d1.getDate() + n1 * el);
          } else if (matched[2]=='週間') {
            d1.setDate(d1.getDate() + n1 * 7 * el);
          } else {
            d1.setMonth(d1.getMonth() + n1 * el);
          }
          var y1 = d1.getFullYear();
          var m1 = d1.getMonth();
          var dd1 = d1.getDate();
          context.d1.setFullYear(y1);
          context.d1.setMonth(m1);
          context.d1.setDate(dd1);
          if (matched[4]) {
            var d2 = new Date(d1);
            var n2 = this.toNumericValue(matched[5]);
            if (matched[6]=='日') {
              d2.setDate(d2.getDate() + n2);
            } else if (matched[6]=='週') {
              d2.setDate(d2.getDate() + n2 * 7);
            } else {
              d2.setMonth(d2.getMonth() + n2);
            }
            context.d2 = context.d2 || new Date(d2);
            context.d2.setFullYear(d2.getFullYear());
            context.d2.setMonth(d2.getMonth());
            context.d2.setDate(d2.getDate());
            context.d2.setHours(0);
            context.d2.setMinutes(0);
          }
          return true;
        }
      }
      ,
      
      { regexp   : /(一昨日)|(昨日)|(今日|本日)|(明日)|(明後日)(から([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週|ヶ月)間)?/,
        callback : function(context, matched) {
          var dd = new Date().getDate();
          dd += matched[1] ? -2 :
                matched[2] ? -1 :
                matched[3] ? 0 :
                matched[4] ? 1 :
                matched[5] ? 2 : 
                0;
          context.d1.setDate(dd);
          if (matched[6]) {
            var d2 = new Date(context.d1);
            var n2 = this.toNumericValue(matched[7]);
            if (matched[8]=='日') {
              d2.setDate(d2.getDate() + n2);
            } else if (matched[8]=='週') {
              d2.setDate(d2.getDate() + n2 * 7);
            } else {
              d2.setMonth(d2.getMonth() + n2);
            }
            context.d2 = context.d2 || new Date(d2);
            context.d2.setFullYear(d2.getFullYear());
            context.d2.setMonth(d2.getMonth());
            context.d2.setDate(d2.getDate());
            context.d2.setHours(0);
            context.d2.setMinutes(0);
          }
          return true;
        }
      }
      ,

      { regexp   : /((先々週)|(先週)|(今週)|(来週|翌週)|(再来週|翌々週))(の?((([初始]め?|頭)|(末))|(([日月火水木金土])曜日?)))?(から([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週|ヶ月)間)?/,
        callback : function(context, matched) {
          var d = new Date();
          var w = matched[2] ? -2 :
                  matched[3] ? -1 :
                  matched[4] ? 0 :
                  matched[5] ? 1 :
                  matched[6] ? 2 : 
                  undefined;
          var dd = d.getDate();
          var dy = matched[13] ? '日月火水木金土'.indexOf(matched[13]) :
                   matched[10] ? this.weekStartDay : 
                   matched[11] ? this.weekEndDay : d.getDay();
          if (typeof w != 'undefined') {
            dd += 7 * w;
            dd += dy - d.getDay();
          } else { 
            dd += (dy - d.getDay() + 6) % 7 + 1;
          }
          context.d1.setDate(dd);
          if (matched[14]) {
            var d2 = new Date(context.d1);
            var n2 = this.toNumericValue(matched[15]);
            if (matched[16]=='日') {
              d2.setDate(d2.getDate() + n2);
            } else if (matched[16]=='週') {
              d2.setDate(d2.getDate() + n2 * 7);
            } else {
              d2.setMonth(d2.getMonth() + n2);
            }
            context.d2 = context.d2 || new Date(d2);
            context.d2.setFullYear(d2.getFullYear());
            context.d2.setMonth(d2.getMonth());
            context.d2.setDate(d2.getDate());
            context.d2.setHours(0);
            context.d2.setMinutes(0);
          }
          return true;
        }
      }
      ,

      { regexp   : /((先々月)|(先月)|(今月)|(来月|翌月)|(再来月|翌々月))(の?((([初始]め?|頭)|(末))|(第([1-5１-５一二三四五])(([日月火水木金土])曜日?))))?(から([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週|ヶ月)間)?/,
        callback : function(context, matched) {
          var d = new Date();
          var y,dd;
          var m = d.getMonth();
          m += matched[2] ? -2 :
               matched[3] ? -1 :
               matched[4] ? 0 :
               matched[5] ? 1 :
               matched[6] ? 2 : 
               0;
          if (matched[10]) {
            d.setMonth(m);
            d.setDate(1);
          } else if (matched[11]) {
            d.setMonth(m+1);
            d.setDate(0);
          } else if (matched[15]) {
            var num = this.toNumericValue(matched[13]);
            var dy = '日月火水木金土'.indexOf(matched[15]);
            d.setMonth(m);
            d.setDate(1);
            dd = 1 + (num-1)*7;
            dd += (dy - d.getDay() + 7) % 7;
            d.setDate(dd);
          }
          y = d.getFullYear();
          m = d.getMonth();
          dd = d.getDate();
          context.d1.setFullYear(y);
          context.d1.setMonth(m);
          context.d1.setDate(dd);
          if (matched[16]) {
            var d2 = new Date(context.d1);
            var n2 = this.toNumericValue(matched[17]);
            if (matched[18]=='日') {
              d2.setDate(d2.getDate() + n2);
            } else if (matched[18]=='週') {
              d2.setDate(d2.getDate() + n2 * 7);
            } else {
              d2.setMonth(d2.getMonth() + n2);
            }
            context.d2 = context.d2 || new Date(d2);
            context.d2.setFullYear(d2.getFullYear());
            context.d2.setMonth(d2.getMonth());
            context.d2.setDate(d2.getDate());
            context.d2.setHours(0);
            context.d2.setMinutes(0);
          }
          return true;
        }
      }
      ,

      { regexp   : /([日月火水木金土])曜日?/,
        callback : function(context, matched) {
          var d = new Date();
          var dy = '日月火水木金土'.indexOf(matched[1]);
          dd = d.getDate();
          dd += (dy - d.getDay() + 7) % 7;
          d.setDate(dd);
          var y = d.getFullYear();
          var m = d.getMonth();
          dd = d.getDate();
          context.d1.setFullYear(y);
          context.d1.setMonth(m);
          context.d1.setDate(dd);
          return true;
        }
      }
      ,

      { regexp   : /((([1-2１２][0-9０-９]{3})[\/／年])?([01０１]?[0-9０-９])[\/／月])?([0-3０-３]?[0-9０-９])(日)?((から([1-9１-９][0-9０-９]*|[一二三四五六七八九])(日|週|ヶ月)間)|((から|[\s　]*[\-,、〜ー－][\s　]*)((([1-2１２][0-9０-９]{3})[\/／年])?([01０１]?[0-9０-９])[\/／月])?([0-3０-３]?[0-9０-９])(日)?))?/,
        callback : function(context, matched) {
          if (!matched[1] && !matched[6]) return false;
          var d = new Date();
          var y1 = matched[3] ? this.toNumericValue(matched[3]) : d.getFullYear();
          var m1 = matched[4] ? this.toNumericValue(matched[4])-1 : d.getMonth();
          var dd1 = matched[5] ? this.toNumericValue(matched[5]) : d.getDate();
          context.d1.setFullYear(y1);
          context.d1.setMonth(m1);
          context.d1.setDate(dd1);
          if (matched[11]) {
            var y2 = matched[13] ? 
                     this.toNumericValue(matched[13]) : 
                     context.d1.getFullYear();
            var m2 = matched[15] ? 
                     this.toNumericValue(matched[15])-1 : 
                     context.d1.getMonth();
            var dd2 = matched[17] ? 
                      this.toNumericValue(matched[17]) : 
                      context.d1.getDate();
            context.d2 = context.d2 || new Date(context.d1);
            context.d2.setFullYear(y2);
            context.d2.setMonth(m2);
            context.d2.setDate(dd2);
          } else if (matched[7]) {
            var d2 = new Date(context.d1);
            var n2 = this.toNumericValue(matched[9]);
            if (matched[10]=='日') {
              d2.setDate(d2.getDate() + n2);
            } else if (matched[10]=='週') {
              d2.setDate(d2.getDate() + n2 * 7);
            } else {
              d2.setMonth(d2.getMonth() + n2);
            }
            context.d2 = context.d2 || new Date(d2);
            context.d2.setFullYear(d2.getFullYear());
            context.d2.setMonth(d2.getMonth());
            context.d2.setDate(d2.getDate());
            context.d2.setHours(0);
            context.d2.setMinutes(0);
          }
          return true;
        }
      }

    ]
  }

  ,

  toNumericValue : function(str) {
    var numstr1 = '０１２３４５６７８９';
    var numstr2 = '０一二三四五六七八九';
    str = str.replace(/[０-９]/g, function(matchText) {
      return numstr1.indexOf(matchText);
    }).replace(/[一二三四五六七八九]/g, function(matchText) {
      return numstr2.indexOf(matchText);
    })
    return parseInt(str, 10)
  }
  ,

  extractDate : function(str) {
    var ctx = {};
    ctx.d1 = new Date();
    ctx.d1.setHours(0);
    ctx.d1.setMinutes(0);
    ctx.d1.setSeconds(0);
    ctx.d1.setMilliseconds(0);

    var rules = this.extractRules['date'];
    for (var i=0; i<rules.length; i++) {
      var m = str.match(rules[i].regexp);
      if (m) {
        if (rules[i].callback.call(this, ctx, m)) {
          str = str.replace(rules[i], '')
                   .replace(/^\s*/, '');
          break;
        }
      }
    }

    rules = this.extractRules['time'];
    for (var i=0; i<rules.length; i++) {
      var m = str.match(rules[i].regexp);
      if (m) {
        if (rules[i].callback.call(this, ctx, m)) {
          str = str.replace(rules[i], '')
                   .replace(/^\s*/, '');
          break;
        }
      }
    }

    var dd = [ ctx.d1 ];
    if (ctx.d2) dd.push(ctx.d2);
    return dd;

  }

}
