Skip to content

Instantly share code, notes, and snippets.

@weiserr
Last active October 31, 2025 07:46
Show Gist options
  • Select an option

  • Save weiserr/bc0745d3145b29f02b1d to your computer and use it in GitHub Desktop.

Select an option

Save weiserr/bc0745d3145b29f02b1d to your computer and use it in GitHub Desktop.

Revisions

  1. weiserr revised this gist May 13, 2025. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,10 @@
    // ==UserScript==
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @include https://www.postfinance.ch/ap/ra/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 13
    // @version 14
    // @grant none
    // ==/UserScript==

    @@ -77,12 +77,12 @@ function sanitizeRow(row) {
    }

    function waitForElem(selector) {
    if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    if (document.URL == "https://www.postfinance.ch/ap/ra/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    createGenerateButton();
    }

    const observer = new MutationObserver((mutations) => {
    if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    if (document.URL == "https://www.postfinance.ch/ap/ra/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    createGenerateButton();
    }
    });
  2. weiserr revised this gist Feb 22, 2025. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 12
    // @version 13
    // @grant none
    // ==/UserScript==

    @@ -44,7 +44,10 @@ function createCSV() {
    const rows = Array.from(document.querySelector('[data-cy="movements-table"] > tbody').querySelectorAll("tr"));

    // process the content
    const result = rows.map((row, index) => index > 0 ? sanitizeRow(row) : createHeader());
    let result = rows.map((row, index) => sanitizeRow(row));

    // add the header
    result.unshift(createHeader());

    // join the results into a single string
    return result.join("\n");
  3. weiserr revised this gist Aug 23, 2022. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 11
    // @version 12
    // @grant none
    // ==/UserScript==

    @@ -59,8 +59,8 @@ function sanitizeRow(row) {
    const payee = row.querySelector('[data-cy="shortText"] > span:nth-child(2)').textContent.replace(/\"/gi, '').replace(/\n/gi, ' ').replace(/,/gi, '; ').trim();
    const category = '';
    const memo = '';
    const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)').textContent.replace(/[^\d\.]/g, '').trim();
    const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)').textContent.trim().replace(/[^\d\.]/g, '');
    const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)')?.textContent?.replace(/[^\d\.]/g, '')?.trim() ?? '';
    const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)')?.textContent?.replace(/[^\d\.]/g, '')?.trim() ?? '';

    let line = [];
    line.push(date);
  4. weiserr revised this gist Aug 23, 2022. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 10
    // @version 11
    // @grant none
    // ==/UserScript==

    @@ -59,8 +59,8 @@ function sanitizeRow(row) {
    const payee = row.querySelector('[data-cy="shortText"] > span:nth-child(2)').textContent.replace(/\"/gi, '').replace(/\n/gi, ' ').replace(/,/gi, '; ').trim();
    const category = '';
    const memo = '';
    const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2)').textContent.replace(/[^\d\.]/g, '').trim();
    const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2)').textContent.trim().replace(/[^\d\.]/g, '');
    const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)').textContent.replace(/[^\d\.]/g, '').trim();
    const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2) > fpui-amount > span:nth-child(1)').textContent.trim().replace(/[^\d\.]/g, '');

    let line = [];
    line.push(date);
  5. weiserr revised this gist Mar 14, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 9
    // @version 10
    // @grant none
    // ==/UserScript==

  6. weiserr revised this gist Mar 14, 2022. 2 changed files with 15 additions and 25 deletions.
    5 changes: 0 additions & 5 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -12,10 +12,5 @@ If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postf
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:

    1. Reload the _Bewegungen_ page by hitting _F5_ (see the limitation note below for _why_ this is required)
    1. Click on the _YNAB Export_ button.
    1. Hit the import button in the **YNAB** account view.

    ## Limitations

    **Please note:** The page reload highlighted in the usage notes is necessary albeit this script is configured to be executed for _Konto -> Bewegungen_. It seems that URL changes performed via the usage of the [History API](https://developer.mozilla.org/docs/Web/API/History_API) are not apparent to **Tampermonkey**. There might be a fix possible by monitoring a larger portion of the **PostFinance** application, however due to security and maintenance reasons I want to keep the exposure surface of this script low. For now a simple page reload seems to be a well acceptable "work around".
    35 changes: 15 additions & 20 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    // ==UserScript==
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/*
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 9
    @@ -12,11 +12,11 @@
    function createGenerateButton() {
    let input = document.createElement("button");
    input.type = "button";
    input.className = "fpui-button text-sm fpui-button--primary fpui-button--normal";
    input.className = "ynab-button fpui-button text-sm fpui-button--primary fpui-button--normal";
    input.textContent = "YNAP Export";
    input.onclick = createExportButton;

    document.querySelectorAll('fpui-actions').forEach(elem => elem.appendChild(input))
    document.querySelectorAll('fpui-actions').forEach(elem => {if(elem.querySelectorAll(".ynab-button").length < 1) {elem.appendChild(input)}});
    }

    function createExportButton() {
    @@ -74,26 +74,21 @@ function sanitizeRow(row) {
    }

    function waitForElem(selector) {
    return new Promise((resolve) => {
    if (document.querySelector(selector)) {
    return resolve(document.querySelector(selector));
    if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    createGenerateButton();
    }

    const observer = new MutationObserver((mutations) => {
    if (document.URL == "https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview" && document.querySelector(selector)) {
    createGenerateButton();
    }
    });

    const observer = new MutationObserver((mutations) => {
    if (document.querySelector(selector)) {
    resolve(document.querySelector(selector));
    observer.disconnect();
    }
    });

    observer.observe(document.body, {
    childList: true,
    subtree: true,
    });
    observer.observe(document.body, {
    childList: true,
    subtree: true,
    });
    }


    waitForElem('fpui-actions').then(_ => {
    createGenerateButton();
    });
    waitForElem('fpui-actions');
  7. weiserr revised this gist Mar 8, 2022. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -12,5 +12,10 @@ If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postf
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:

    1. Reload the _Bewegungen_ page by hitting _F5_ (see the limitation note below for _why_ this is required)
    1. Click on the _YNAB Export_ button.
    1. Hit the import button in the **YNAB** account view.
    1. Hit the import button in the **YNAB** account view.

    ## Limitations

    **Please note:** The page reload highlighted in the usage notes is necessary albeit this script is configured to be executed for _Konto -> Bewegungen_. It seems that URL changes performed via the usage of the [History API](https://developer.mozilla.org/docs/Web/API/History_API) are not apparent to **Tampermonkey**. There might be a fix possible by monitoring a larger portion of the **PostFinance** application, however due to security and maintenance reasons I want to keep the exposure surface of this script low. For now a simple page reload seems to be a well acceptable "work around".
  8. weiserr revised this gist Mar 8, 2022. 2 changed files with 34 additions and 90 deletions.
    3 changes: 1 addition & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -12,6 +12,5 @@ If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postf
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:

    1. Click on the regular CSV Export. (You can cancel the download)
    1. Now click on the _YNAB Export_ button.
    1. Click on the _YNAB Export_ button.
    1. Hit the import button in the **YNAB** account view.
    121 changes: 33 additions & 88 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -1,23 +1,22 @@
    // ==UserScript==
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @include https://www.postfinance.ch/ap/ba/ob/html/finance/assets/movements-overview
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 8
    // @version 9
    // @grant none
    // @require https://code.jquery.com/jquery-3.3.1.slim.js
    // ==/UserScript==

    // Function definitions
    function createGenerateButton() {
    let input = document.createElement("input");
    let input = document.createElement("button");
    input.type = "button";
    input.value = "YNAB Export";
    input.className = "btn";
    input.style.setProperty('margin-left', '8px');
    input.className = "fpui-button text-sm fpui-button--primary fpui-button--normal";
    input.textContent = "YNAP Export";
    input.onclick = createExportButton;

    $("input[name='data']")[0].parentNode.append(input);
    document.querySelectorAll('fpui-actions').forEach(elem => elem.appendChild(input))
    }

    function createExportButton() {
    @@ -42,10 +41,10 @@ function createExportButton() {

    function createCSV() {
    // acquire the data computed by the Post
    let result = JSON.parse($("input[name='data']")[0].value)
    const rows = Array.from(document.querySelector('[data-cy="movements-table"] > tbody').querySelectorAll("tr"));

    // process the content
    result = result.filter(row => row.length === 6).map((row, index) => index > 0 ? sanitizeRow(row) : createHeader());
    const result = rows.map((row, index) => index > 0 ? sanitizeRow(row) : createHeader());

    // join the results into a single string
    return result.join("\n");
    @@ -56,12 +55,12 @@ function createHeader() {
    }

    function sanitizeRow(row) {
    const date = row[0].replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
    const payee = row[1].replace(/\"/gi, '').replace(/\r/gi, '').replace(/,/gi, ';');
    const date = row.querySelector('[data-cy="date"] > span:nth-child(2)').textContent;
    const payee = row.querySelector('[data-cy="shortText"] > span:nth-child(2)').textContent.replace(/\"/gi, '').replace(/\n/gi, ' ').replace(/,/gi, '; ').trim();
    const category = '';
    const memo = '';
    const outflow = row[3].replace(/-(.*)/, '$1-');
    const inflow = row[2];
    const outflow = row.querySelector('[data-cy="debit"] > span:nth-child(2)').textContent.replace(/[^\d\.]/g, '').trim();
    const inflow = row.querySelector('[data-cy="credit"] > span:nth-child(2)').textContent.trim().replace(/[^\d\.]/g, '');

    let line = [];
    line.push(date);
    @@ -74,81 +73,27 @@ function sanitizeRow(row) {
    return line.join(',');
    }

    /* waitForKeyElements(): A handy, utility function that
    * does what it says.
    */
    function waitForKeyElements(
    selectorTxt, /* Required: The jQuery selector string that
    specifies the desired element(s).
    */
    actionFunction, /* Required: The code to run when elements are
    found. It is passed a jNode to the matched
    element.
    */
    bWaitOnce, /* Optional: If false, will continue to scan for
    new elements even after the first match is
    found.
    */
    iframeSelector /* Optional: If set, identifies the iframe to
    search.
    */
    ) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
    targetNodes = $(selectorTxt);
    else
    targetNodes = $(iframeSelector).contents()
    .find(selectorTxt);

    if (targetNodes && targetNodes.length > 0) {
    /*--- Found target node(s). Go through each and act if they
    are new.
    */
    targetNodes.each(function () {
    var jThis = $(this);
    var alreadyFound = jThis.data('alreadyFound') || false;

    if (!alreadyFound) {
    //--- Call the payload function.
    actionFunction(jThis);
    jThis.data('alreadyFound', true);
    function waitForElem(selector) {
    return new Promise((resolve) => {
    if (document.querySelector(selector)) {
    return resolve(document.querySelector(selector));
    }

    const observer = new MutationObserver((mutations) => {
    if (document.querySelector(selector)) {
    resolve(document.querySelector(selector));
    observer.disconnect();
    }
    });
    btargetsFound = true;
    }
    else {
    btargetsFound = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj = waitForKeyElements.controlObj || {};
    var controlKey = selectorTxt.replace(/[^\w]/g, "_");
    var timeControl = controlObj[controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound && bWaitOnce && timeControl) {
    //--- The only condition where we need to clear the timer.
    clearInterval(timeControl);
    delete controlObj[controlKey]
    }
    else {
    //--- Set a timer, if needed.
    if (!timeControl) {
    timeControl = setInterval(function () {
    waitForKeyElements(selectorTxt,
    actionFunction,
    bWaitOnce,
    iframeSelector
    );
    },
    500
    );
    controlObj[controlKey] = timeControl;
    }
    }
    waitForKeyElements.controlObj = controlObj;

    observer.observe(document.body, {
    childList: true,
    subtree: true,
    });
    });
    }

    // Main entry point
    waitForKeyElements("input[name='data']", createGenerateButton);

    waitForElem('fpui-actions').then(_ => {
    createGenerateButton();
    });
  9. weiserr revised this gist Dec 28, 2018. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -8,10 +8,10 @@ If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postf
    **Tampermonkey** should ask you whether or not you would like to install this script.

    ## Usage
    > Tip: if you have a lot of transaction make sure to increase the shown amount from `25` to `100` somewhere in the settings.
    > Tip: if you have a lot of transactions make sure to increase the shown amount from `25` to `100` somewhere in the settings.
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:

    1. Click on the regular CSV Export.
    1. You can simply abort it.
    1. Now click on the _YNAB Export_ button.
    1. Click on the regular CSV Export. (You can cancel the download)
    1. Now click on the _YNAB Export_ button.
    1. Hit the import button in the **YNAB** account view.
  10. weiserr revised this gist Dec 28, 2018. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,7 @@ If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postf

    ## Usage
    > Tip: if you have a lot of transaction make sure to increase the shown amount from `25` to `100` somewhere in the settings.
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:

    1. Click on the regular CSV Export.
  11. weiserr revised this gist Dec 28, 2018. 1 changed file with 16 additions and 0 deletions.
    16 changes: 16 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    # PostFinance YNAB 4 Export
    This scripts allows the export of **PostFinance** account flow data for **YNAB 4** using **Tampermonkey**.
    The export is not based on the **CSV** generated by the Post as it does not adhere to the standard but makes use of the data loaded
    on the client side for doing said **CSV** export.

    ## Installation
    If **Tampermonkey** is installed you can click on the _RAW view_ of the ```postfinance-ynab.user.js``` file of this **Gist**.
    **Tampermonkey** should ask you whether or not you would like to install this script.

    ## Usage
    > Tip: if you have a lot of transaction make sure to increase the shown amount from `25` to `100` somewhere in the settings.
    Navigate to the account flow data in **PostFinance** (_Konto -> Bewegungen_) and:
    1. Click on the regular CSV Export.
    1. You can simply abort it.
    1. Now click on the _YNAB Export_ button.
  12. weiserr revised this gist Dec 24, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 7
    // @version 8
    // @grant none
    // @require https://code.jquery.com/jquery-3.3.1.slim.js
    // ==/UserScript==
  13. weiserr revised this gist Dec 24, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -57,7 +57,7 @@ function createHeader() {

    function sanitizeRow(row) {
    const date = row[0].replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
    const payee = row[1].replace(/\"/gi, '').replace(/\r/gi, '');
    const payee = row[1].replace(/\"/gi, '').replace(/\r/gi, '').replace(/,/gi, ';');
    const category = '';
    const memo = '';
    const outflow = row[3].replace(/-(.*)/, '$1-');
  14. weiserr revised this gist Jul 17, 2018. No changes.
  15. weiserr revised this gist Jul 17, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -4,8 +4,9 @@
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 6
    // @version 7
    // @grant none
    // @require https://code.jquery.com/jquery-3.3.1.slim.js
    // ==/UserScript==
    // Function definitions
    function createGenerateButton() {
  16. weiserr revised this gist Jul 17, 2018. No changes.
  17. weiserr revised this gist Jul 17, 2018. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,9 @@
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @version 5
    // @updateURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @downloadURL https://gist.github.com/weiserr/bc0745d3145b29f02b1d/raw/postfinance-ynab.user.js
    // @version 6
    // @grant none
    // ==/UserScript==
    // Function definitions
  18. weiserr revised this gist Jul 17, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @version 4
    // @version 5
    // @grant none
    // ==/UserScript==
    // Function definitions
    @@ -54,7 +54,7 @@ function createHeader() {

    function sanitizeRow(row) {
    const date = row[0].replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
    const payee = row[1].replace(/\"/gi, '');
    const payee = row[1].replace(/\"/gi, '').replace(/\r/gi, '');
    const category = '';
    const memo = '';
    const outflow = row[3].replace(/-(.*)/, '$1-');
  19. weiserr revised this gist Sep 16, 2017. 1 changed file with 10 additions and 16 deletions.
    26 changes: 10 additions & 16 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -2,27 +2,19 @@
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @version 3
    // @version 4
    // @grant none
    // ==/UserScript==
    // Function definitions
    function initializeYnabExport() {
    // adapt the name of the standard export button
    $("button", $("input[name='data']")[0].parentNode)[0].textContent = "1. Export";

    // add the generation button
    createGenerateButton();
    }

    function createGenerateButton() {
    let input = document.createElement("input");
    input.type = "button";
    input.value = "2. YNAB Generate";
    input.value = "YNAB Export";
    input.className = "btn";
    input.style.setProperty('margin-left', '8px');
    input.onclick = createExportButton;

    $("input[name='filename']")[0].parentNode.append(input);
    $("input[name='data']")[0].parentNode.append(input);
    }

    function createExportButton() {
    @@ -33,14 +25,16 @@ function createExportButton() {
    const day = date.getDate();
    const filename = "ynab-" + year + "-" + month + "-" + day + ".csv";

    // create a temporary download link
    let input = document.createElement("a");
    input.setAttribute('href', 'data:text/csv;charset=UTF-8,' + encodeURIComponent(csv));
    input.innerHTML = "3. YNAB Export";
    input.className = "btn";
    input.style.setProperty('margin-left', '8px');
    input.setAttribute('download', filename);
    input.style.display = 'none';

    $("input[name='filename']")[0].parentNode.append(input);
    // ...and click it to trigger the download
    document.body.appendChild(input);
    input.click();
    document.body.removeChild(input);
    }

    function createCSV() {
    @@ -154,4 +148,4 @@ function waitForKeyElements(
    }

    // Main entry point
    waitForKeyElements("input[name='data']", initializeYnabExport);
    waitForKeyElements("input[name='data']", createGenerateButton);
  20. weiserr revised this gist Sep 16, 2017. 1 changed file with 4 additions and 5 deletions.
    9 changes: 4 additions & 5 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -58,14 +58,13 @@ function createHeader() {
    return 'Date,Payee,Category,Memo,Outflow,Inflow';
    }

    // TODO: properly sanitize and sort the values
    function sanitizeRow(row) {
    const date = row[0];
    const payee = row[1];
    const date = row[0].replace(/(.*)-(.*)-(.*)/, '$3.$2.$1');
    const payee = row[1].replace(/\"/gi, '');
    const category = '';
    const memo = '';
    const outflow = row[3];
    const inflow = '';
    const outflow = row[3].replace(/-(.*)/, '$1-');
    const inflow = row[2];

    let line = [];
    line.push(date);
  21. weiserr revised this gist Sep 16, 2017. 1 changed file with 92 additions and 87 deletions.
    179 changes: 92 additions & 87 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -6,76 +6,82 @@
    // @grant none
    // ==/UserScript==
    // Function definitions
    function createAndAppendButton() {
    var input=document.createElement("input");
    input.type="button";
    input.value="Generate CSV Link";
    input.className="efinance-button";
    input.onclick = provideCSVLink;
    $("p.buttons").append(input);
    function initializeYnabExport() {
    // adapt the name of the standard export button
    $("button", $("input[name='data']")[0].parentNode)[0].textContent = "1. Export";

    // add the generation button
    createGenerateButton();
    }

    function provideCSVLink() {
    var input=document.createElement("a");
    var csv = createCSV();
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var filename = "ynab-" + year + "-" + month + "-" + day + ".csv";

    input.setAttribute('href','data:text/csv;charset=UTF-8,' + encodeURIComponent(csv));
    input.innerHTML = "Export to CSV";
    input.className="efinance-button";
    input.setAttribute('download',filename);

    $("p.buttons").append(input);
    function createGenerateButton() {
    let input = document.createElement("input");
    input.type = "button";
    input.value = "2. YNAB Generate";
    input.className = "btn";
    input.style.setProperty('margin-left', '8px');
    input.onclick = createExportButton;

    $("input[name='filename']")[0].parentNode.append(input);
    }

    function createExportButton() {
    const csv = createCSV();
    const date = new Date();
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const filename = "ynab-" + year + "-" + month + "-" + day + ".csv";

    let input = document.createElement("a");
    input.setAttribute('href', 'data:text/csv;charset=UTF-8,' + encodeURIComponent(csv));
    input.innerHTML = "3. YNAB Export";
    input.className = "btn";
    input.style.setProperty('margin-left', '8px');
    input.setAttribute('download', filename);

    $("input[name='filename']")[0].parentNode.append(input);
    }

    function createCSV() {
    var result = [];
    var lines = $("tbody").children("#detail_page tr");

    // push the header
    result.push('Date,Payee,Category,Memo,Outflow,Inflow');

    lines.each(function(){appendCreditDebitLine($(this),result)});

    return result.join("\n");
    // acquire the data computed by the Post
    let result = JSON.parse($("input[name='data']")[0].value)

    // process the content
    result = result.filter(row => row.length === 6).map((row, index) => index > 0 ? sanitizeRow(row) : createHeader());

    // join the results into a single string
    return result.join("\n");
    }

    // Date,Payee,Category,Memo,Outflow,Inflow
    function appendCreditDebitLine(context, lines) {
    var line = [];
    var cells = context.children("td");
    var date = sanitize(cells.get(5));
    var payee = "";
    var category = "";
    var memo = sanitize(cells.get(2));
    var outflow = sanitize(cells.get(4));
    var inflow = sanitize(cells.get(3));


    // create a CSV line
    line.push(date);
    line.push(payee);
    line.push(category);
    line.push(memo);
    line.push(outflow);
    line.push(inflow);

    // append the CSV line
    lines.push(line.join(","));
    function createHeader() {
    return 'Date,Payee,Category,Memo,Outflow,Inflow';
    }

    function sanitize(cell) {
    return cell.innerHTML.replace(/<br>/gi," ").replace(/&nbsp;/gi,"").replace(/'/gi,'').trimLeft().trimRight()
    // TODO: properly sanitize and sort the values
    function sanitizeRow(row) {
    const date = row[0];
    const payee = row[1];
    const category = '';
    const memo = '';
    const outflow = row[3];
    const inflow = '';

    let line = [];
    line.push(date);
    line.push(payee);
    line.push(category);
    line.push(memo);
    line.push(outflow);
    line.push(inflow);

    return line.join(',');
    }

    /* waitForKeyElements(): A handy, utility function that
    * does what it says.
    */
    function waitForKeyElements (
    function waitForKeyElements(
    selectorTxt, /* Required: The jQuery selector string that
    specifies the desired element(s).
    */
    @@ -90,64 +96,63 @@ function waitForKeyElements (
    iframeSelector /* Optional: If set, identifies the iframe to
    search.
    */
    )
    {
    ) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
    targetNodes = $(selectorTxt);
    targetNodes = $(selectorTxt);
    else
    targetNodes = $(iframeSelector).contents ()
    .find (selectorTxt);
    targetNodes = $(iframeSelector).contents()
    .find(selectorTxt);

    if (targetNodes && targetNodes.length > 0) {
    if (targetNodes && targetNodes.length > 0) {
    /*--- Found target node(s). Go through each and act if they
    are new.
    */
    targetNodes.each ( function () {
    var jThis = $(this);
    var alreadyFound = jThis.data ('alreadyFound') || false;
    targetNodes.each(function () {
    var jThis = $(this);
    var alreadyFound = jThis.data('alreadyFound') || false;

    if (!alreadyFound) {
    //--- Call the payload function.
    actionFunction (jThis);
    jThis.data ('alreadyFound', true);
    actionFunction(jThis);
    jThis.data('alreadyFound', true);
    }
    } );
    btargetsFound = true;
    });
    btargetsFound = true;
    }
    else {
    btargetsFound = false;
    btargetsFound = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj = waitForKeyElements.controlObj || {};
    var controlKey = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl = controlObj [controlKey];
    var controlObj = waitForKeyElements.controlObj || {};
    var controlKey = selectorTxt.replace(/[^\w]/g, "_");
    var timeControl = controlObj[controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound && bWaitOnce && timeControl) {
    if (btargetsFound && bWaitOnce && timeControl) {
    //--- The only condition where we need to clear the timer.
    clearInterval (timeControl);
    delete controlObj [controlKey]
    clearInterval(timeControl);
    delete controlObj[controlKey]
    }
    else {
    //--- Set a timer, if needed.
    if ( ! timeControl) {
    timeControl = setInterval ( function () {
    waitForKeyElements ( selectorTxt,
    actionFunction,
    bWaitOnce,
    iframeSelector
    );
    },
    if (!timeControl) {
    timeControl = setInterval(function () {
    waitForKeyElements(selectorTxt,
    actionFunction,
    bWaitOnce,
    iframeSelector
    );
    },
    500
    );
    controlObj [controlKey] = timeControl;
    controlObj[controlKey] = timeControl;
    }
    }
    waitForKeyElements.controlObj = controlObj;
    waitForKeyElements.controlObj = controlObj;
    }

    // Main entry point
    waitForKeyElements ("p.buttons", createAndAppendButton);
    waitForKeyElements("input[name='data']", initializeYnabExport);
  22. weiserr created this gist Dec 28, 2015.
    153 changes: 153 additions & 0 deletions postfinance-ynab.user.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,153 @@
    // ==UserScript==
    // @name Postfinance CSV Export
    // @namespace http://death-knight.com
    // @include https://www.postfinance.ch/ap/ba/fp/html/e-finance/assets
    // @version 3
    // @grant none
    // ==/UserScript==
    // Function definitions
    function createAndAppendButton() {
    var input=document.createElement("input");
    input.type="button";
    input.value="Generate CSV Link";
    input.className="efinance-button";
    input.onclick = provideCSVLink;
    $("p.buttons").append(input);
    }

    function provideCSVLink() {
    var input=document.createElement("a");
    var csv = createCSV();
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    var day = date.getDate();
    var filename = "ynab-" + year + "-" + month + "-" + day + ".csv";

    input.setAttribute('href','data:text/csv;charset=UTF-8,' + encodeURIComponent(csv));
    input.innerHTML = "Export to CSV";
    input.className="efinance-button";
    input.setAttribute('download',filename);

    $("p.buttons").append(input);
    }

    function createCSV() {
    var result = [];
    var lines = $("tbody").children("#detail_page tr");

    // push the header
    result.push('Date,Payee,Category,Memo,Outflow,Inflow');

    lines.each(function(){appendCreditDebitLine($(this),result)});

    return result.join("\n");
    }

    // Date,Payee,Category,Memo,Outflow,Inflow
    function appendCreditDebitLine(context, lines) {
    var line = [];
    var cells = context.children("td");
    var date = sanitize(cells.get(5));
    var payee = "";
    var category = "";
    var memo = sanitize(cells.get(2));
    var outflow = sanitize(cells.get(4));
    var inflow = sanitize(cells.get(3));


    // create a CSV line
    line.push(date);
    line.push(payee);
    line.push(category);
    line.push(memo);
    line.push(outflow);
    line.push(inflow);

    // append the CSV line
    lines.push(line.join(","));
    }

    function sanitize(cell) {
    return cell.innerHTML.replace(/<br>/gi," ").replace(/&nbsp;/gi,"").replace(/'/gi,'').trimLeft().trimRight()
    }

    /* waitForKeyElements(): A handy, utility function that
    * does what it says.
    */
    function waitForKeyElements (
    selectorTxt, /* Required: The jQuery selector string that
    specifies the desired element(s).
    */
    actionFunction, /* Required: The code to run when elements are
    found. It is passed a jNode to the matched
    element.
    */
    bWaitOnce, /* Optional: If false, will continue to scan for
    new elements even after the first match is
    found.
    */
    iframeSelector /* Optional: If set, identifies the iframe to
    search.
    */
    )
    {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
    targetNodes = $(selectorTxt);
    else
    targetNodes = $(iframeSelector).contents ()
    .find (selectorTxt);

    if (targetNodes && targetNodes.length > 0) {
    /*--- Found target node(s). Go through each and act if they
    are new.
    */
    targetNodes.each ( function () {
    var jThis = $(this);
    var alreadyFound = jThis.data ('alreadyFound') || false;

    if (!alreadyFound) {
    //--- Call the payload function.
    actionFunction (jThis);
    jThis.data ('alreadyFound', true);
    }
    } );
    btargetsFound = true;
    }
    else {
    btargetsFound = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj = waitForKeyElements.controlObj || {};
    var controlKey = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound && bWaitOnce && timeControl) {
    //--- The only condition where we need to clear the timer.
    clearInterval (timeControl);
    delete controlObj [controlKey]
    }
    else {
    //--- Set a timer, if needed.
    if ( ! timeControl) {
    timeControl = setInterval ( function () {
    waitForKeyElements ( selectorTxt,
    actionFunction,
    bWaitOnce,
    iframeSelector
    );
    },
    500
    );
    controlObj [controlKey] = timeControl;
    }
    }
    waitForKeyElements.controlObj = controlObj;
    }

    // Main entry point
    waitForKeyElements ("p.buttons", createAndAppendButton);