/*
   replay.js
*/
Function.prototype.applyTimeout = function(self, args, ms) {
  var func = this;
  return setTimeout(function () { func.apply(self, args) }, ms);
};
Function.prototype.applyEventWithDOM = function(element, type, args, ignoreEvent) {
  var self = this;
  var _chk = function(event, args, ignoreEvent){
    var ev = event || window.event;
    if (ignoreEvent != true)
      args[0] = ev;
    self.apply(self, args);
  }
  if (element.addEventListener)
    element.addEventListener(type, function(event){ _chk(event, args, ignoreEvent) }, false);
  else
    element.attachEvent('on'+type, function(event){ _chk(event, args, ignoreEvent) });
}
// 
var $ = function(id){
  return document.getElementById(id);
}
// X座標取得
var pointerX = function(event) {
  var mini = isIE ? -2 : 0;
  return event.pageX - isIE || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - isIE) || 0;
}
// Y座標取得
var pointerY = function(event) {
  var mini = isIE ? -2 : 0;
  return event.pageY - isIE || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - isIE) || 0;
}
// get xpath from target node
var getXpath = function(node) {
  var pathElement  = "";
  var searchIndex = function(nd) {
    for (var i = 0, r = 1, c = nd.parentNode.childNodes, len = c.length; i < len; i++) {
      if (c[i].nodeName == nd.nodeName &&
        c[i].nodeType == nd.nodeType) {
        if (c[i] == nd) return r;
        r++;
      }
    }
    return -1;
  }
  if (node.nodeType == 9)
    return "";
  else {
    var isTrue = false;
    var tagName = node.tagName.toLowerCase();
    if (isIE) {
      var attr = node.getAttributeNode('id');
      if (attr && attr.specified)
        isTrue = true;
    }
    else {
      if (node.hasAttribute('id'))
        isTrue = true;
    }
    if (isTrue)
      pathElement = 'id("'+node.getAttribute('id')+'")';
    else
      pathElement = arguments.callee(node.parentNode) + '/' + tagName + '['+searchIndex(node)+']';
  }
  return pathElement;
}
// send ajax request to php server
var ajaxRequest = function(type, url, data) {
  var xmlhttp = null;
  if (window.ActiveXObject)
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP") || new ActiveXObject("Microsoft.XMLHTTP");
  else if (window.XMLHttpRequest)
    xmlhttp = new XMLHttpRequest();
  xmlhttp.open('POST', url, true);
  xmlhttp.onreadystatechange = function() { 
    if (xmlhttp.readyState == 4)
      if (type == 'send') {
        //$('console').innerHTML = 'request send : ' + (new Date().getTime()) + '.<br />' + $('console').innerHTML;
      }
      else if(type == 'replay') {
        replayRecords = eval(xmlhttp.responseText);
        
        var option = replayRecords[0];
        replayRecords.splice(0, 1);
        
        if (!option['width'])
          option['width'] = wWidth;
        if (!option['height'])
          option['height'] = wHeight;
        
        //window.moveTo(0, 0);
        //window.resizeTo(option['width'], option['height']);
        //window.scrollTo(0,0);
        
        alert('***** replay start ***** \r\n' + 'session_key = ' + option['session_key'] + '\r\n' + 'location = ' + option['location'] + '\r\n' + 'host = ' + option['host'] + '\r\n' + 'window size = ' + option['width'] + ' * ' + option['height'] + '\r\n');
        
        // replay done!
        replayDone();
      }
  }
  xmlhttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
  //xmlhttp.setRequestHeader("If-Modified-Since", new Date().getTime());
  xmlhttp.send(data);
}

// configure
var dummy = '';
var mousePointer = [];
var wWidth = 0;
var wHeight = 0;
var sessionKey;
var start = ~~(new Date/1000);
var isIE = navigator.userAgent.indexOf('MSIE') != -1 ? true : false;
var isFF = navigator.userAgent.indexOf('Firefox') != -1 ? true : false;

var watchObjects = ['input', 'textarea', 'label', 'select']; // 'img', 
var records = [];
var recordMax = 50000;
var recordObstruct = false;
var recordObserver = null;

var replayRecords = [];
var replayObserver = null;

/*---------------------------------------------------------------------------------------------------------+/
/+ for Record
/+
/+---------------------------------------------------------------------------------------------------------*/
// recording start
var startRecorder = function() {
  var sendTerm = 1;
  
  recordObserver = setInterval(function(){
    var tom = new Date().getTime();
    var now = ~~(tom/1000);
    if (++sendTerm > 5) {
      var parseRecords = '';
      var backRecords = records;
      // clear cache
      records = [];
      for (var i in backRecords) {
        // t  -> type
        // el -> element
        // vl -> text value
        // tl -> time line
        parseRecords += "{'x':"+backRecords[i]['x']+",'y':"+backRecords[i]['y']+",'t':'"+backRecords[i]['t']+"','el':'"+backRecords[i]['el']+"','vl':'"+backRecords[i]['vl']+"','tl':'"+backRecords[i]['tl']+"'}, ";
      }
      // send request
      ajaxRequest('send', '/replay.php', 'mode=send&session_key=' + sessionKey + '&start=' + start + '&now=' + now + '&wWidth=' + wWidth + '&wHeight=' + wHeight + '&records=' + parseRecords);
      // reset interval
      sendTerm = 1;
    }
    else {
      var isPush = false;
      if (records.length <= 0) {
        mousePointer = [0,0];
        isPush = true;
      }
      else if (records.length > 0 && tom > records[records.length-1]['tl'] + 500)
        isPush = true;
      if (isPush == true)
        records.push({ 'x': mousePointer[0], 'y': mousePointer[1], 't': 'statics', 'el': '', 'vl': '', 'tl': tom });
    }
  }, 1000);
  
  // 
  addRecord.applyEventWithDOM(document.body, 'mousemove', [dummy, document.body]);
  
  // watching tags
  for (var i in watchObjects) {
    var objects = document.evaluate('//'+watchObjects[i], document.body, null, 7, null);
    // checker for kind of form text
    if (watchObjects[i] == 'input' || watchObjects[i] == 'textarea' || watchObjects[i] == 'select') {
      for (var j=0;j<objects.snapshotLength;j++) {
        var obj = objects.snapshotItem(j);
        addRecord.applyEventWithDOM(obj, 'change', [dummy, obj, true]);
        addRecord.applyEventWithDOM(obj, 'focus', [dummy, obj, true]); // isIE ? 'focus' : 'mousedown'
        addRecord.applyEventWithDOM(obj, 'blur', [dummy, obj, true]);
        // 氏ねIE(autocomplete使うとonChangeのイベントハンドラ取れない)
        if (isIE == true)
          obj.setAttribute('autocomplete', 'off');
      }
    }
    // checker for other all tags
    else {
      for (var j=0;j<objects.snapshotLength;j++) {
        var obj = objects.snapshotItem(j);
        addRecord.applyEventWithDOM(obj, 'click', [dummy, obj, true]);
        //addRecord.applyEventWithDOM(obj, 'dblclick', [dummy, obj, true]);
      }
    }
  }
  // window.onresize
  //addRecord.applyEventWithDOM(window, 'resize', [dummy, mouse]);
  // window.scroll
  addRecord.applyEventWithDOM(document.body, isFF ? 'DOMMouseScroll' : 'mousewheel', [dummy, dummy]);
}

// add record to records_array
var addRecord = function(event, self, ignore) {
  if ((recordObstruct == false || ignore == true) && recordMax > 0) {
    var xPath = '';
    var text = '';
    // イベントにより分岐
    switch (event.type) {
      // input[text],input[radio],input[checkbox],input[select],textarea
      case 'change':
        text = self.value;
        break;
//      case 'focus': case 'mousedown':
//        break;
//      case 'blur':
//        break;
//      case 'click':
//         break;
      case 'resize':
        text = '['+(window.innerWidth || document.body.clientWidth)+', '+(window.innerHeight || document.body.clientHeight)+']';
        break;
      case 'DOMMouseScroll': case 'mousewheel':
        text = '['+(document.body.scrollLeft || document.documentElement.scrollLeft)+', '+(document.body.scrollTop || document.documentElement.scrollTop)+']';
        break;
      default:
        break;
    }
    
    // replay時にobject参照するタイプは必須
    if (event.type != 'mousemove' && event.type != 'statics' && event.type != 'resize' && event.type != 'mousewheel' && event.type != 'DOMMouseScroll')
      xPath = getXpath(self);
    
    // cache
    mousePointer = [pointerX(event), pointerY(event)];
    
    // add records
    var time = new Date().getTime();
    records.push({'x': mousePointer[0], 'y': mousePointer[1], 't': event.type, 'el': xPath, 'vl': text, 'tl': time });
    
    setTimeout((function(){ recordObstruct = false; }), 100);
    recordObstruct = true;
    
    recordMax--;
    
    // IE氏ね(labelタグの画像クリック時にhtmlForをcheckedできない問題)
    if (event.type == 'click' && self && self.tagName.toLowerCase() == 'label') {
      if (isIE != true) {
        //event.returnValue  = false;
        //event.cancelBubble = true;
        if (event.preventDefault())
          event.preventDefault();
        if (event.stopPropagation())
          event.stopPropagation();
      }
      $(self.htmlFor).checked = !$(self.htmlFor).checked;
    }
  }
}

/*---------------------------------------------------------------------------------------------------------+/
/+ for Reaply
/+
/+---------------------------------------------------------------------------------------------------------*/
// スタイルをn秒後に戻すやつ
var playBackStyle = function(style, old) {
  this.style[style] = old;
}

// リプレイ実行
var replayDone = function() {
  var mouse = $('mouse');
  var console = $('console');
  
  console.innerHTML = '';
  
  var val = 10;
  mouse.style.filter = 'alpha(opacity=' + (val * 10) + ')';
  mouse.style.MozOpacity = val / 10;
  mouse.style.opacity = val / 10;
  // 
  if (!mouse.style.display || mouse.style.display == 'none')
    mouse.style.display = 'block';
  // reset textareas
  var selects = document.evaluate('//select', document.body, null, 7, null);
  var inputs = document.evaluate('//input', document.body, null, 7, null);
  var textareas = document.evaluate('//textarea', document.body, null, 7, null);
  for (var j=0;j<selects.snapshotLength;j++)
    selects.snapshotItem(j).getElementsByTagName('option')[0].selected = 'selected';
  for (var j=0;j<inputs.snapshotLength;j++) {
    var node = inputs.snapshotItem(j);
    if (node.type != 'submit')
      node.value = '';
    if (node.type == 'checkbox' || node.type == 'radio')
      node.checked = false;
  }
  for (var j=0;j<textareas.snapshotLength;j++)
    textareas.snapshotItem(j).value = '';
  // execute replay
  replayObserver = setInterval(function() {
    var content = replayRecords ? replayRecords[0] : null;
    if (content == null) {
      if (mouse.style.display == 'block') {
        var val = 3;
        mouse.style.filter = 'alpha(opacity=' + (val * 10) + ')';
        mouse.style.MozOpacity = val / 10;
        mouse.style.opacity = val / 10;
      }
      clearInterval(replayObserver); 
      replayObserver = null;
      
      console.innerHTML = '=== End of pointer ===<br />' + console.innerHTML;
    }
    else {
      // move mouse pointer
      if (content['x'] != 0 && content['y'] != 0) {
        mouse.style.top = content['y'] + 'px';
        mouse.style.left = content['x'] + 'px';
      }
      
      // get xPath
      var targetNode = content['el'] ? document.evaluate(content['el'], document.body, null, 7, null).snapshotItem(0) : null;
      
      // execute replay
      switch (content['t']) {
        case 'change':
          console.innerHTML = targetNode.name + 'の値が' + content['vl'].replace(/<br \/>/g, '\n') + 'に変更<br />' + console.innerHTML;
          console.scrollTop = 0;
          
          if (targetNode.tagName.toLowerCase() == 'input' && (targetNode.type == 'checkbox' || targetNode.type == 'radio'))
            targetNode.checked = !targetNode.checked;
          else if (targetNode.tagName.toLowerCase() == 'select') {
            var oops = targetNode.getElementsByTagName('option');
            for (var k=0,len=oops.length;k<len;k++) {
              if (oops[k].value === content['vl'])
                oops[k].selected = 'selected';
            }
          }
          else {
            var replaceText = content['vl'].replace(/<br \/>/g, '\n');
            targetNode.value = replaceText;
            
            var old = targetNode.style.border;
            targetNode.style.border = '1px #008800 solid';
            playBackStyle.applyTimeout(targetNode, ['border', old], 500);
          }
          
          break;
	case 'focus': case 'mousedown':
          console.innerHTML = targetNode.name + 'にフォーカスが当たった<br />' + console.innerHTML;
          console.scrollTop = 0;
          
          var old = targetNode.style.backgroundColor;
          targetNode.style.backgroundColor = '#90ee90';
          playBackStyle.applyTimeout(targetNode, ['backgroundColor', old], 500);
          
          break;
        case 'blur':
          console.innerHTML = targetNode.name + 'からフォーカスが外れた<br />' + console.innerHTML;
          console.scrollTop = 0;
          
          break;
        case 'click':
          if (targetNode.tagName.toLowerCase() == 'label') {
            $(targetNode.htmlFor).checked = !$(targetNode.htmlFor).checked;
          }
          else {
            targetNode.click;
            
            var old = targetNode.style.border;
            targetNode.style.border = '1px #CC0000 solid';
            playBackStyle.applyTimeout(targetNode, ['border', old], 500);
          }
          
          break;
        case 'resize':
          console.innerHTML = 'windowサイズ変更 : ' + content['vl'] + '<br />' + console.innerHTML;
          console.scrollTop = 0;
          
          var windowSize = eval(content['vl']);
          window.moveTo(0, 0);
          window.resizeTo(windowSize[0], wHeight - windowSize[1]);
          
          break;
	case 'DOMMouseScroll': case 'mousewheel':
          console.innerHTML = 'マウススクロール : ' + content['vl'] + '<br />' + console.innerHTML;
          console.scrollTop = 0;
          
          var windowPosition = eval(content['vl']);
          window.scrollTo(windowPosition[0], windowPosition[1]);
          
          break;
        default:
          break;
      }

      replayRecords.splice(0, 1);
    }
  }, 120);
}



