HSBC - Better Account History

Better readability, forever transactions retention and a chart display

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name           HSBC - Better Account History
// @description    Better readability, forever transactions retention and a chart display
// @namespace      http://github.com/jobwat/
// @author         Joseph Boiteau
// @version        2014.11.15
// @homepage       https://github.com/jobwat/hsbc-account-history-rerender.user.js
// @include        https://*.hsbc.com/*
// @include        https://*.hsbc.com.au/*
// @include        https://*.hsbc.co.uk/*
// @grant          none
// @require        https://code.jquery.com/jquery-1.11.1.min.js
// @require        http://cdnjs.cloudflare.com/ajax/libs/flot/0.8.2/jquery.flot.min.js
// @require        http://cdnjs.cloudflare.com/ajax/libs/flot/0.8.2/jquery.flot.time.min.js
// ==/UserScript==

// this script is only for the "Account History" page
if(document.getElementsByTagName('title')[0].text.match("Account History")){

  // centering function used in final display
  // Thx Tony L. - http://stackoverflow.com/questions/210717/what-is-the-best-way-to-center-a-div-on-the-screen-using-jquery
  $.fn.center = function () {
    this.css("position","absolute");
    this.css("top", ( $(window).height() - this.height() ) / 2+$(window).scrollTop() + "px");
    this.css("left", ( $(window).width() - this.width() ) / 2+$(window).scrollLeft() + "px");
    return this;
  };

  // set some style (no need to have wide spaces around lines)
  $('<style type="text/css">table.hsbcTableStyle07 tr td{ padding:0px 5px 0px 5px; line-height: 1em; white-space: nowrap; } </style>').prependTo($('head'));

  var graphData = [];   // array containing values for the graph
  var the_table = $("table.hsbcTableStyle07");  // the interesting table object of the page

  // We will first go through all lines of the table
  // Get their content, load our objects and rewrite all, but lighter

  clearTable = function(){
    trs = $('tr', the_table);
    trs.each(function(index, tr){ if(index!==0 && index!=(trs.length-1)){ $(tr).addClass('to_del'); } });
    $('tr.to_del', the_table).remove();
  };

  Line = function() {
    this.timestamp = undefined;
  };
  Line.prototype.populate = function(line_data_array) {
    this.timestamp = line_data_array.timestamp;
    this.details = line_data_array.details;
    this.debit = line_data_array.debit;
    this.credit = line_data_array.credit;
    this.balance = line_data_array.balance;
  };
  Line.prototype.setDate = function(dateString) {
    this.timestamp = new Date(dateString).getTime();
  };
  Line.prototype.hasDate = function() {
    return !(this.timestamp === undefined || isNaN(this.timestamp));
  };
  Line.prototype.getDate = function(){ // reformat the date column
    if(this.hasDate()){
      date = new Date(this.timestamp);
      return '<span style="float:right">'+weekdayArray[date.getDay()]+', '+date.getDate()+' '+monthArray[date.getMonth()].slice(0,3)+' '+date.getFullYear()+'</span>';
    }
    else{
      return undefined;
    }
  };
  Line.prototype.setDetails = function(detailsHTML) { // remove some non-useful data from details column and make it 1 line only
    tmp=detailsHTML.split('<br>');
    this.details = this.cleanStrings(tmp[0] + ' - ' + tmp[2] + ' - ' + tmp[3] + ' ' + tmp[4]);
  };
  Line.prototype.getDetails = function(){
    return this.details;
  };
  Line.prototype.setDebit = function(debit){
    this.debit = this.cleanAmounts(debit);
  };
  Line.prototype.getDebit = function(){
    return this.displayDigits(this.emptyIfZero(this.debit));
  };
  Line.prototype.setCredit = function(credit){
    this.credit = this.cleanAmounts(credit);
  };
  Line.prototype.getCredit = function(){
    return this.displayDigits(this.emptyIfZero(this.credit));
  };
  Line.prototype.setBalance = function(balance){
    this.balance = this.cleanAmounts(balance);
  };
  Line.prototype.getBalance = function(){
    return this.displayDigits(this.balance);
  };
  Line.prototype.cleanAmounts = function(value){
    tmp = parseFloat(this.cleanStrings(value).replace(/[^0-9\.]/g,''));
    if(isNaN(tmp)) return 0;
    else return tmp;
  };
  Line.prototype.cleanStrings = function(value){
    return value.replace(/[\t\n\r   ]+/g,' ');
  };
  Line.prototype.displayDigits = function(value){
   return value.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
  };
  Line.prototype.emptyIfZero = function(value){
    if(value===0) return '&nbsp;';
    else return value+'&nbsp;';
  };

  History = function(){
    this.lines = [];
  };
  History.prototype.add = function(line){
    this.lines.push(line);
  };
  History.prototype.toJSON = function(){
    return JSON.stringify(this.lines);
  };
  History.prototype.merge = function(anotherHistory){
    var merged = $.merge(this.lines, anotherHistory.lines);
    merged.sort(function(a,b){
      if(a.timestamp < b.timestamp) return 1;
      else if(a.timestamp > b.timestamp) return -1;
      else{
        if(a.balance == b.balance + b.credit) return 1;
        if(a.balance == b.balance - b.debit) return 1;
        return -1;
      }
    });

    var ind = 1;
    while(ind < merged.length){
      if(merged[ind-1].timestamp == merged[ind].timestamp && merged[ind-1].details == merged[ind].details){
        merged.splice(ind, 1);
      }
      else
        ind++;
    }

    this.lines = merged;
  };
  History.prototype.testMerge = function(){
    console.log('before merge: ' + this.lines.length + 'lines');
    history.merge(this);
    console.log('after merge: ' + this.lines.length + 'lines');
  };
  History.prototype.displayAll = function(){
    clearTable();
    last_tr = $('tr', the_table).last(); // the greyed line with sorting arrows at the bottom

    $(this.lines).each(function(index, line_data_array){
      var line = new Line();
      line.populate(line_data_array);
      graphData.push([line.timestamp, line.balance]);
      /*jshint multistr: true */
      tr = $('<tr class="hsbcTableRow0'+((index%2===0)?'3':'4')+'">\
        <td class="hsbcTableColumn03" headers="header1">'+line.getDate()+'</td>\
        <td class="" headers="header2">'+line.getDetails()+'</td>\
        <td class="hsbcTableColumn03" headers="header3">'+line.getDebit()+'</td>\
        <td class="hsbcTableColumn03" headers="header4">'+line.getCredit()+'</td>\
        <td class="hsbcTableColumn03" headers="header5">'+line.getBalance()+'</td>\
        <td class="hsbcTableColumn03" headers="header6">&nbsp;</td>\
      </tr>');
      tr.insertBefore(last_tr);
    });
  };

  var previous_history = new History();
  var account_history = new History();

  // recover the account name
  var account_name = $('#LongSelection1 option[value="'+$('#LongSelection1').val()+'"]').text().replace(/[^a-zA-Z]/g, '');
  console.log('account_name: ', account_name);

  // recover existing history
  previous_history_JSON = localStorage.getItem(account_name);
  if (previous_history_JSON){
    previous_history.lines = JSON.parse(previous_history_JSON);
    console.log('previous history from localStorage: ', previous_history.lines.length);
  }
  else{
    previous_history.lines = [];
  }

  // Drop the right panel containing mostly ads
  $('.containerRightTools').remove();

  // loop through table lines
  var prev_line = new Line();
  $.each($("tr", the_table), function(TRind, TRval) { // each tr

    line = new Line();

    var mytd = $(this).children("td").each(function(index, td){ // each td

      if(td.headers=="header1"){ // the date
        line.setDate(td.innerHTML);
        if(line.hasDate()){ td.innerHTML=line.getDate(); }
        else { return false; }
      }
      else if(td.headers=="header2"){ // the details
        line.setDetails(td.innerHTML);
        if(line.getDetails() == prev_line.getDetails()){ line.details = prev_line.getDetails() + ' (2x)'; }
        td.innerHTML = line.getDetails();
      }
      else if(td.headers=="header3"){ // debit
        line.setDebit(td.innerHTML);
        td.innerHTML = line.getDebit();
      }
      else if(td.headers=="header4"){ // credit
        line.setCredit(td.innerHTML);
        td.innerHTML = line.getCredit();
      }
      else if(td.headers=="header5"){ // balance
        line.setBalance(td.innerHTML);
        td.innerHTML = line.getBalance();
      }
      else if(td.headers=="header6"){ // empty last column
        td.innerHTML = account_history.lines.length + 1;
      }
    });

    if(line.getDate()!==undefined && line.getBalance()!==undefined){ // the first and last tr of the array are the sorting arrows
      account_history.add(line);
    }

    prev_line = line;

  });
  console.log('lines parsed: ', account_history.lines.length);

  // merging previous history with actual one and save it locally
  //account_history.merge(previous_history);

  // saving new merged history to localStorage
  localStorage.setItem(account_name, JSON.stringify(account_history.lines));
  console.log('lines saved after merge: ', account_history.lines.length);

  // rewrite the whole thing
  account_history.displayAll();

  // 2e part: draw a chart

  // add a div somewhere in the DOM
  $('<div id="chartwrap" style="border: 1px solid #AAA; background-color:white; position: absolute;"></div>').appendTo("body");
  $('<div id="placeholder" style="width:650px; height:300px;"></div>').appendTo("#chartwrap");

  // draw the chart in its div
  // TODO: do way better ! watch here: http://people.iola.dk/olau/flot/examples/stacking.html
  $.plot("#placeholder", [graphData], { xaxis: { mode: "time" } });

  toggleChartDisplay = function(show_or_hide){
      if($("#chartwrap").is(':hidden')) $("#chartwrap").center();
      $("#chartwrap").toggle(show_or_hide);
  };

  // display a button to toggle chart visibility
  $('<a class="hsbcLinkStyle06">Display chart</a>')
    .prependTo($('td.hsbcTableColumn03')[0])
    .css('cursor', 'pointer')
    .click(toggleChartDisplay);

  // click on the chart hides it
  $('#placeholder').click(toggleChartDisplay);

  // start with Chart hidden
  toggleChartDisplay(false);
}