<commit_before>/**
 * vConsole Default Tab
 *
 * @author WechatFE
 */

import $ from '../lib/query.js';
import * as tool from '../lib/tool.js';
import VConsolePlugin from '../lib/plugin.js';
import tplTabbox from './tabbox.html';
import tplHeader from './header.html';
import tplItem from './item.html';

class VConsoleNetworkTab extends VConsolePlugin {

  constructor(...args) {
    super(...args);

    this.$tabbox = $.render(tplTabbox, {});
    this.$header = null;
    this.reqList = {}; // URL as key, request item as value
    this.domList = {}; // URL as key, dom item as value

    this.mockAjax();
  }

  onRenderTab(callback) {
    callback(this.$tabbox);
  }

  onAddTool(callback) {
    let that = this;
    let toolList = [{
      name: 'Clear',
      global: false,
      onClick: function(e) {
        that.clearLog();
      }
    }];
    callback(toolList);
  }

  onReady() {

    // header
    this.renderHeader();

    // expend group item
    $.delegate($.one('.vc-log', this.$tabbox), 'click', '.vc-group-preview', function(e) {
      let $group = this.parentNode;
      if ($.hasClass($group, 'vc-actived')) {
        $.removeClass($group, 'vc-actived');
      } else {
        $.addClass($group, 'vc-actived');
      }
      e.preventDefault();
    });

  }

  clearLog() {
    // remove list
    this.reqList = {};

    // remove dom
    for (let id in this.domList) {
      this.domList[id].remove();
      this.domList[id] = undefined;
    }
    this.domList = {};

    // update header
    this.renderHeader();
  }

  renderHeader() {
    let count = Object.keys(this.reqList).length,
        $header = $.render(tplHeader, {count: count}),
        $logbox = $.one('.vc-log', this.$tabbox);
    if (this.$header) {
      // update
      this.$header.parentNode.replaceChild($header, this.$header);
    } else {
      // add
      $logbox.parentNode.insertBefore($header, $logbox);
    }
    this.$header = $header;
  }

  /**
   * add or update a request item by request ID
   * @private
   * @param string id
   * @param object data
   */
  updateRequest(id, data) {
    // see whether add new item into list
    let preCount = Object.keys(this.reqList).length;

    // update item
    let item = this.reqList[id] || {};
    for (let key in data) {
      item[key] = data[key];
    }
    this.reqList[id] = item;
    console.log(item);
    
    // update dom
    let domData = {
      url: item.url,
      status: item.status,
      type: '-',
      costTime: item.costTime>0 ? item.costTime+'ms' : '-',
      header: item.header,
      response: tool.htmlEncode(item.response)
    };
    if (item.readyState <= 1) {
      domData.status = 'Pending';
    } else if (item.readyState < 4) {
      domData.status = 'Loading';
    }
    let $new = $.render(tplItem, domData),
        $old = this.domList[id];
    if (item.status >= 400) {
      $.addClass($.one('.vc-group-preview', $new), 'vc-table-row-error');
    }
    if ($old) {
      $old.parentNode.replaceChild($new, $old);
    } else {
      $.one('.vc-log', this.$tabbox).appendChild($new);
    }
    this.domList[id] = $new;

    // update header
    let curCount = Object.keys(this.reqList).length;
    if (curCount != preCount) {
      this.renderHeader();
    }
  }

  /**
   * mock ajax request
   * @private
   */
  mockAjax() {
    let _XMLHttpRequest = window.XMLHttpRequest;
    if (!_XMLHttpRequest) { return; }

    let that = this;
    let _open = window.XMLHttpRequest.prototype.open,
        _send = window.XMLHttpRequest.prototype.send;

    // mock open()
    window.XMLHttpRequest.prototype.open = function() {
      let XMLReq = this;
      let args = [].slice.call(arguments),
          url = args[1],
          id = that.getUniqueID();

      // may be used by other methods
      XMLReq._requestID = id;

      // mock onreadystatechange
      let _onreadystatechange = XMLReq.onreadystatechange || function() {};
      XMLReq.onreadystatechange = function() {

        let item = that.reqList[id] || {};

        // update status
        item.url = url;
        item.readyState = XMLReq.readyState;
        item.status = XMLReq.status;

        if (XMLReq.readyState == 0) {
          // UNSENT
          item.startTime = (+new Date());
        } else if (XMLReq.readyState == 1) {
          // OPENED
          item.startTime = (+new Date());
        } else if (XMLReq.readyState == 2) {
          // HEADERS_RECEIVED
          item.header = {};
          let header = XMLReq.getAllResponseHeaders() || '',
              headerArr = header.split("\n");
          // extract plain text to key-value format
          for (let line of headerArr) {
            if (!line) { continue; }
            let arr = line.split(': ');
            let key = arr[0],
                value = arr.slice(1).join(': ');
            item.header[key] = value;
          }
        } else if (XMLReq.readyState == 3) {
          // LOADING
        } else if (XMLReq.readyState == 4) {
          // DONE
          item.endTime = +new Date(),
          item.costTime = item.endTime - (item.startTime || item.endTime);
          item.response = XMLReq.response;
        }

        that.updateRequest(id, item);
        return _onreadystatechange.apply(XMLReq, arguments);
      };

      return _open.apply(XMLReq, args);
    };

  };

  /**
   * generate an unique id string (32)
   * @private
   * @return string
   */
  getUniqueID() {
    let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
    return id;
  }

} // END class


const tab = new VConsoleNetworkTab('network', 'Network');

export default tab;<commit_msg>remove debug log<commit_after>/**
 * vConsole Default Tab
 *
 * @author WechatFE
 */

import $ from '../lib/query.js';
import * as tool from '../lib/tool.js';
import VConsolePlugin from '../lib/plugin.js';
import tplTabbox from './tabbox.html';
import tplHeader from './header.html';
import tplItem from './item.html';

class VConsoleNetworkTab extends VConsolePlugin {

  constructor(...args) {
    super(...args);

    this.$tabbox = $.render(tplTabbox, {});
    this.$header = null;
    this.reqList = {}; // URL as key, request item as value
    this.domList = {}; // URL as key, dom item as value

    this.mockAjax();
  }

  onRenderTab(callback) {
    callback(this.$tabbox);
  }

  onAddTool(callback) {
    let that = this;
    let toolList = [{
      name: 'Clear',
      global: false,
      onClick: function(e) {
        that.clearLog();
      }
    }];
    callback(toolList);
  }

  onReady() {

    // header
    this.renderHeader();

    // expend group item
    $.delegate($.one('.vc-log', this.$tabbox), 'click', '.vc-group-preview', function(e) {
      let $group = this.parentNode;
      if ($.hasClass($group, 'vc-actived')) {
        $.removeClass($group, 'vc-actived');
      } else {
        $.addClass($group, 'vc-actived');
      }
      e.preventDefault();
    });

  }

  clearLog() {
    // remove list
    this.reqList = {};

    // remove dom
    for (let id in this.domList) {
      this.domList[id].remove();
      this.domList[id] = undefined;
    }
    this.domList = {};

    // update header
    this.renderHeader();
  }

  renderHeader() {
    let count = Object.keys(this.reqList).length,
        $header = $.render(tplHeader, {count: count}),
        $logbox = $.one('.vc-log', this.$tabbox);
    if (this.$header) {
      // update
      this.$header.parentNode.replaceChild($header, this.$header);
    } else {
      // add
      $logbox.parentNode.insertBefore($header, $logbox);
    }
    this.$header = $header;
  }

  /**
   * add or update a request item by request ID
   * @private
   * @param string id
   * @param object data
   */
  updateRequest(id, data) {
    // see whether add new item into list
    let preCount = Object.keys(this.reqList).length;

    // update item
    let item = this.reqList[id] || {};
    for (let key in data) {
      item[key] = data[key];
    }
    this.reqList[id] = item;
    // console.log(item);
    
    // update dom
    let domData = {
      url: item.url,
      status: item.status,
      type: '-',
      costTime: item.costTime>0 ? item.costTime+'ms' : '-',
      header: item.header,
      response: tool.htmlEncode(item.response)
    };
    if (item.readyState <= 1) {
      domData.status = 'Pending';
    } else if (item.readyState < 4) {
      domData.status = 'Loading';
    }
    let $new = $.render(tplItem, domData),
        $old = this.domList[id];
    if (item.status >= 400) {
      $.addClass($.one('.vc-group-preview', $new), 'vc-table-row-error');
    }
    if ($old) {
      $old.parentNode.replaceChild($new, $old);
    } else {
      $.one('.vc-log', this.$tabbox).appendChild($new);
    }
    this.domList[id] = $new;

    // update header
    let curCount = Object.keys(this.reqList).length;
    if (curCount != preCount) {
      this.renderHeader();
    }
  }

  /**
   * mock ajax request
   * @private
   */
  mockAjax() {
    let _XMLHttpRequest = window.XMLHttpRequest;
    if (!_XMLHttpRequest) { return; }

    let that = this;
    let _open = window.XMLHttpRequest.prototype.open,
        _send = window.XMLHttpRequest.prototype.send;

    // mock open()
    window.XMLHttpRequest.prototype.open = function() {
      let XMLReq = this;
      let args = [].slice.call(arguments),
          url = args[1],
          id = that.getUniqueID();

      // may be used by other methods
      XMLReq._requestID = id;

      // mock onreadystatechange
      let _onreadystatechange = XMLReq.onreadystatechange || function() {};
      XMLReq.onreadystatechange = function() {

        let item = that.reqList[id] || {};

        // update status
        item.url = url;
        item.readyState = XMLReq.readyState;
        item.status = XMLReq.status;

        if (XMLReq.readyState == 0) {
          // UNSENT
          item.startTime = (+new Date());
        } else if (XMLReq.readyState == 1) {
          // OPENED
          item.startTime = (+new Date());
        } else if (XMLReq.readyState == 2) {
          // HEADERS_RECEIVED
          item.header = {};
          let header = XMLReq.getAllResponseHeaders() || '',
              headerArr = header.split("\n");
          // extract plain text to key-value format
          for (let line of headerArr) {
            if (!line) { continue; }
            let arr = line.split(': ');
            let key = arr[0],
                value = arr.slice(1).join(': ');
            item.header[key] = value;
          }
        } else if (XMLReq.readyState == 3) {
          // LOADING
        } else if (XMLReq.readyState == 4) {
          // DONE
          item.endTime = +new Date(),
          item.costTime = item.endTime - (item.startTime || item.endTime);
          item.response = XMLReq.response;
        }

        that.updateRequest(id, item);
        return _onreadystatechange.apply(XMLReq, arguments);
      };

      return _open.apply(XMLReq, args);
    };

  };

  /**
   * generate an unique id string (32)
   * @private
   * @return string
   */
  getUniqueID() {
    let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
    return id;
  }

} // END class


const tab = new VConsoleNetworkTab('network', 'Network');

export default tab;
{'fraction_non_alphanumeric': 0.1576105886450714, 'fraction_numerical': 0.004528039010797631, 'mean_word_length': 2.0577742279020232, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4083684', 'n_tokens_mistral': 3754, 'n_tokens_neox': 3658, 'n_words': 819}