Friday, June 15, 2018

git command line helper

Overview 

git command line are difficult to remember for somebody used to Visual Source Safe, Star Team or TFS. But these days you do not have the choice. I came up with this PowerShell Script I called pgit.ps1 that translate command I can remember into git command.

Source: pgit.ps1

PS C:\>pgit list [branch, url, tag, history, merge-history]
PS C:\>pgit create branch branchName
PS C:\>pgit switch branch branchName
PS C:\>pgit delete branch branchName
PS C:\>pgit push branch branchName
PS C:\>pgit rename branch oldBranchName newBranchName
PS C:\>pgit push branch branchName # push branch to the origin server
PS C:\>pgit status
PS C:\>pgit commit # will automatically stage the all the modified files and commit
PS C:\>pgit commit -m "message" # will automatically stage the all the modified files and commit
PS C:\>pgit undo [unstaged, unstaged-staged, last-commited, -untracked-file]

AT28C16 EEPROM Programmer

Overview


The old AT28C16 EEPROM is a 2K EEPROM with 2 parallel buses:
  • One 8 bits data bus 
  • One 12 bits address bus
It is therefore easier to control without software than a modern EEPROM or FLASH chip that require one serial bus.


With a 8 bit counter plugged into the EEPROM address bus, we can select the first 256 addresses and output the programmed 256 values (each 8 bits) at the speed of the clock. 

The programmed EEPROM can also be used as a memory containing some assembly code to execute, in case you want to design your own 4 or 8 bit processor.

It is also an easy way to manager a 2 digit counter display using 2 7-segment displays, see videos by Ben Eater
Following the video I build my own programmer with an Arduino Nano and 2 shift registers to add an extra 8 GPIOs.


I would suggest to use an Arduino MEGA to control directly the 8 data bit and 12 address bit ,
plus the Read/Write pin and Enable pin, for a total of 22 GPIOs needed.

Code


Some of my very ugly code to program the EEPROM as reference table for a 2 digit counter display using 2 7-segment displays. 


/*
 * https://raw.githubusercontent.com/beneater/eeprom-programmer/master/eeprom-programmer.ino
 * AT28C16
 * Image https://www.google.com/search?q=AT28C16&rlz=1C1CHBD_enUS700US700&source=lnms&tbm=isch&sa=X&ved=0ahUKEwityKHj-tLVAhXC64MKHXsgDmMQ_AUICigB&biw=1164&bih=580&dpr=1.1
*/
#define SHIFT_DATA 2
#define SHIFT_CLK 3
#define SHIFT_LATCH 4
#define EEPROM_D0 5
#define EEPROM_D7 12
#define WRITE_EN 13 // Active Low

#define OUT_EN_BIT 0x80 // 16 gpio extender 8..15 16..23 -> connect to gpio 23
#define READ_EN 12 // Active Low

#define DEFAULT_ERASE_VALUE 0x56

#define ADDR_OFFSET 0

void setOutEnable(bool state) {
  if(state)
    digitalWrite(READ_EN, LOW);
  else
    digitalWrite(READ_EN, true);
}

/*
 * Output the address bits and outputEnable signal using shift registers.
 */
void setAddress(int address, bool outputEnable) {
  
  shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, (address >> 8) | (outputEnable ? 0x00 : OUT_EN_BIT));
  shiftOut(SHIFT_DATA, SHIFT_CLK, MSBFIRST, address);

  digitalWrite(SHIFT_LATCH, LOW);
  delay(2);
  digitalWrite(SHIFT_LATCH, HIGH);
  delay(2);
  digitalWrite(SHIFT_LATCH, LOW);
  delay(40);
}

void SetDataPinInInputMode() 
{ 
  for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) 
  {
      pinMode(pin, INPUT);
      delay(25);
  }  
  delay(25);
}

void SetDataPinInOutputMode() {
  for (int pin = EEPROM_D0; pin <=EEPROM_D7; pin += 1) {
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
  }
  delay(25);
}
/*
 * Read a byte from the EEPROM at the specified address.
 */
byte __readEEPROM(int address) {

  //Serial.print("__readEEPROM [");Serial.print(address);Serial.println("]");delay(20);
  byte data = 0;
  for (int pin = EEPROM_D7; pin >= EEPROM_D0; pin -= 1) 
  {
    int pinVal = digitalRead(pin);    
    data = (data << 1) + pinVal;    
    Serial.print("pin:");Serial.print(pin);
    Serial.print(" pinVal:");Serial.print(pinVal);
    Serial.print(" data:");Serial.println(data);    
    delay(20);
  }
  return data;
}

byte readEEPROM(int address) {

  SetDataPinInInputMode();
  
  setAddress(address, /*outputEnable*/ true);   // 40 ms wait after address is set
  byte v0 =  __readEEPROM(address);  
  return v0;

  //////////////////////////
  
  byte v1 =  __readEEPROM(address);
  if(v0 == v1) 
  {
      return v0;
  }
  else
  {    
      //Serial.print("readEEPROM issue1 [");Serial.print(address);Serial.println("]");  delay(110);
      v0 =  __readEEPROM(address);
      delay(2);
      v1 =  __readEEPROM(address);
      if(v0 == v1) {
        Serial.print("readEEPROM pass1 [");Serial.print(address);Serial.println("]"); delay(110);
        return v0;
      }
      else {
        Serial.print("readEEPROM issue2 [");Serial.print(address);Serial.println("]");  
        delay(110);
        v0 =  __readEEPROM(address);
        return v0;
      }
  }
}

/*
 * Write a byte to the EEPROM at the specified address.
 */
void writeEEPROM(int address, byte data) 
{
  //data = 129;
  Serial.print("writeEEPROM ");Serial.print(address);
  Serial.print(" data:");Serial.print(data);
  Serial.println("");
  delay(20);
  
  setAddress(address, /*outputEnable*/ false);
  SetDataPinInOutputMode();
  
  for (int pin = EEPROM_D0; pin <= EEPROM_D7; pin += 1) 
  {
    byte bit = data & 1;
    digitalWrite(pin, bit);    
    
    Serial.print("pin:");Serial.print(pin);delay(20);
    Serial.print(" bit:");Serial.print(bit);delay(20);
    Serial.print(" data:");Serial.println(data);delay(20);    
    data = data >> 1;
  }
  delay(50);

  //PORTB = PORTB | B00100000;   // set pin 13 high
  //asm("nop\n nop\n nop\n nop\n nop\n nop\n");
 //PORTB = PORTB & B11011111;  // set pin 13 low
 
  //cli(); // disable global interrupts
  digitalWrite(WRITE_EN, LOW);
  //delayMicroseconds(1);
  cli(); sei();
  //_delay_us(300);
  //asm("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;");   
  digitalWrite(WRITE_EN, HIGH);
  //sei(); // re-enable global interrupts:  
  delay(100);
}


/*
 * Read the contents of the EEPROM and print them to the serial monitor.
 */
void printContents() {

  byte data[16];
  
  for (int base = 0; base <=16; base += 16) {
        
    for (int offset = 0; offset < 16; offset += 1) {
        data[offset] = readEEPROM(base + offset);
    }

    char buf[80];
    sprintf(buf, "%03x:  %02x %02x %02x %02x %02x %02x %02x %02x   %02x %02x %02x %02x %02x %02x %02x %02x",
            base, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
            data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]);

    Serial.println(buf);
  }
}

// 4-bit hex decoder for common cathode 7-segment display
// byte data[] = { 0x7e, 0x30, 0x6d, 0x79, 0x33, 0x5b, 0x5f, 0x70, 0x7f, 0x7b, 0x77, 0x1f, 0x4e, 0x3d, 0x4f, 0x47 };

// 4-bit hex decoder for common anode 7-segment display
  byte data[] = { 0x81, 0xcf, 0x92, 0x86, 0xcc, 0xa4, 0xa0, 0x8f, 0x80, 0x84, 0x88, 0xe0, 0xb1, 0xc2, 0xb0, 0xb8 };



void EraseEEPROM() {
  
  Serial.print("Erasing EEPROM");

  // We using a 7 bit address bus, so we can only set
  for (int address = 0; address < 128; address += 1) {
    
    writeEEPROM(address, DEFAULT_ERASE_VALUE);
    if (address % 10 == 0) Serial.print(".");
  }
  Serial.println(" done");
}

#define _7bit 16 // _7SegmentDisplayNumber2_Bit

byte _2_7SegmentDisplayTheFredWay[] = {     
    // Trigger via the first 4 bit a 74LS47N digit 0..9 on the first 7-segment display
    // and we use the bit 5(16)  to turn off the 2 segment to make a 1 on the second 7-SegmentDisplay
    // Active low
    0+_7bit, 1+_7bit, 2+_7bit, 3+_7bit, 4+_7bit, 5+_7bit, 6+_7bit, 7+_7bit, 8+_7bit, 9+_7bit,
    // Trigger via the first 4 bit a 74LS47N digit 0..5 on the first 7-segment display
    // and we use the bit 5(16) to turn on the 2 segment to make a 1 on the second 7-SegmentDisplay
    // Active low
    0,
    1,
    2,
    3,
    4,
    5,
    };

void InitEEPROMFor2SevenSegmentDisplayTheFredWay() {
  
  Serial.println("Init EEPROM For 2 7-SegmentDisplay the fred way");
  int len = sizeof(_2_7SegmentDisplayTheFredWay)/sizeof(_2_7SegmentDisplayTheFredWay[0]);
  Serial.print(len); Serial.println(" items");
  
  for (int a = 0; a < len; a++)
  {
    int v = a;
    
    if(a <= 9)
      v += _7bit;
    else
      v -= 10;
          
    Serial.print("Writing addr:"); Serial.print(a);
    Serial.print(" val:"); Serial.print(v);
    Serial.println(""); delay(100);
    
    //writeEEPROM(a, _2_7SegmentDisplayTheFredWay[a]);    
   
    writeEEPROM(ADDR_OFFSET + a, v);   
  }
  Serial.println("Done");
}


// We will the first 16 byte of the EEPROM
// The byte from 0 to 9 will contains on the first 4 bit:0..9
// The byte from 0 to 9 will contains on bit 5 : 1
// The byte from 10 to 15 will contains on the first 4 bit:0..5
// The byte from 10 to 15 will contains on bit 5 : 0

// The EEPROM bit:0..3 will plugged into a 7 Segment driver 74LS47 to drive 7-segment 1
// The EEPROM bit:5 will be plugged into segment b & c of 7 segment 2. When bit 5 is low
// this will turn on segment b & c and display 1 for ten th

byte ReadOneByte(int addr) {
    
  int val2 = readEEPROM(addr);  
  return val2;
}

void WriteReadOneByte(int addr, byte val) {
  
  Serial.print("Write[");Serial.print(addr); Serial.print("] = ");Serial.println(val);
  writeEEPROM(addr, val);
  delay(1000*1);
  int val2 = readEEPROM(addr);
  Serial.print("Value Read:");Serial.println(val2);
  delay(1000*1);
}
  
void DisplayEEPROMState() {
  
  Serial.println("Read EEPROM");
  //int i = 8;
  //Serial.print(">ReadOneByte:");Serial.println(i);delay(20);
  //int v = ReadOneByte(i);
  //Serial.print(">Value:");Serial.println(v);delay(20);    
    
  for(int i=ADDR_OFFSET; iReadOneByte:");Serial.println(i);delay(20);
     int v = ReadOneByte(i);
     Serial.print(">Value:");Serial.println(v);delay(20);    
     delay(1000*2);
  }
  
  //Serial.println("Output disable - LED ON");  
  //setAddress(0, /*outputEnable*/ false);
  //delay(1000);
  //Serial.println("Output enable - LED OFF");  
  //setAddress(0, /*outputEnable*/ true);
  //delay(1000);
  
  //Serial.println("Reading all EEPROM");
  //printContents();
  //delay(1000*10);
}


void setup() {
  // put your setup code here, to run once:
  digitalWrite(WRITE_EN, HIGH); // Already in INPUT mode, so activate pullup 
  
  pinMode(SHIFT_DATA,   OUTPUT);
  pinMode(SHIFT_CLK,    OUTPUT);
  pinMode(SHIFT_LATCH,  OUTPUT);  
  pinMode(WRITE_EN,     OUTPUT);

  SetDataPinInOutputMode();
  SetDataPinInInputMode();
  
  Serial.begin(115200);
  Serial.println("Waiting 1 second before execution");
  delay(1000*1);
  Serial.println("Running");
    
  //InitEEPROMFor2SevenSegmentDisplayTheFredWay();
  
  //writeEEPROM(8, 24);
  
  //EraseEEPROM(); 
  //Serial.println("Reading all EEPROM");
  //printContents();
  delay(1000);  
  DisplayEEPROMState();  
}

void loop() {


}



Thursday, June 7, 2018

JavaScript For Of Loop

Though the new JavaScript for/of loop construct is a nice syntactic sugar and similar to the foreach loop find in C#. It is worth noting that the behind the scene the implementation is based on an iterator.

eslint has even a rule to not let you use it.

error iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations  no-restricted-syntax

const list = [1,2,3];

for(let i=0; i < list.length; i++) { // Old syntax
  console.log(list[i]);
}

for(let i of list) { // New syntax
  console.log(i);
}
Transpiled with BabelJS.io  [Link]

"use strict";

var list = [1, 2, 3];
for (var i = 0; i < list.length; i++) {
  console.log(list[i]);
}



var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = list[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var _i = _step.value;

    console.log(_i);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator.return) {
      _iterator.return();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}

Thursday, May 17, 2018

How to create and register a React redux middleware?

Overview


This post is just a reminder how to create and register a custom redux middleware.

Redux middleware: definition.

The middleware class

const middleware = function f1(store) { 

    return function f2(next) {
        
        return function f3(action) {
        
            // Do nothing but trace the action 
            console.log(`MIDDLEWARE> action:${JSON.stringify(action)}`)
            return next(action);
        } 
    }
}

export default middleware ;

The store creation and middleware registration

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunkMiddleware  from 'redux-thunk';

import counterReducer   from './counter/counterReducer';

import middleware   from './middleware';

const rootReducer = combineReducers({
    counter : counterReducer,
});

const appliedMiddleware = applyMiddleware(thunkMiddleware, middleware);
const store             = createStore(rootReducer, {}, appliedMiddleware);

store.subscribe(function() {
    //console.log('STORE SUBSCRIBED ');
} );

export default store;

Friday, May 4, 2018

Redux Workflow - Summary

Overview


A blog post to gather my understanding of Redux (redux.js.org/).
Another good explanation can be found at Redux explained.

On button click calling synchronous action

  • The ButtonClickAction() method is called by the onClick attribute of a button and return the action:
    { type :‘BUTTON_CLICK_ACTION’ }
  • Because the action return an object it is automatically dispatched.
  • The dispatching trigger all registered reducers to be called.
  • The dispatching trigger all registered mapStateToProps() to be called. The global state is pass to each mapStateToProps() call. mapStateToProps() return the list of props for its component.
    • If the call return a different result from the previous call, a call to Component.render() will be executed

Summary

  • OnClick
  • Action get dispatched
  • Reducers
  • mapStateToProps
  • Component.render() if needed


On button click calling an asynchronous action

  • Asynchronous action are typically an HTTP call.
  • The Asynchronous action must be a function returning a return function(dispatch) {};
  • The internal function must dispatch 2 synchronous actions, for example requestStartedAction() at first and requestReceivedAction(data) when the data is received, as described below.

//
export function fetchAction(city) {

    return function(dispatch) {

        dispatch(requestStartedAction());

        axios.get(url, config).then(function(data) { 

            dispatch(requestReceivedAction(data));
        });
    }
}

Wednesday, March 7, 2018

Parentheses Expression Parser

Overview 


A simple coding exercise, parsing parentheses expressions
  • Given a string containing the characters '(', ')', '{', '}', '[' and ']' and any other characters in between determine if the parentheses are balanced.
using mocha+chai test framework.

Valid Expressions

 - "()"
 - "(){}[]"
 - "([ok])"
 - "{([ok])}"
 - "function() { return (1+1); }"

Invalid Expressions

 - "(]"
 - "([)]"
 - "([ok]})"
 - "{([ok])})"

Github Repo: ParenthesesParser

Source Code

let Stack = require('./Stack');
let print = console.log;

function ParenthesesParser(expression) {

    this._parentheseDefinition = {
        '(' : ')', 
        '{' : '}', 
        '[' : ']',
    };
    this.parse = function(expression) {

        if(!expression)
            throw new Error(ParenthesesParser.EXPRESSION_CANNOT_BE_NULL_OR_UNDEFINED);

        let stack = new Stack();

        for(let i = 0; i < expression.length; i++) {
            let c = expression.charAt(i);
            if(this.isOpenParenthese(c)) {
                stack.push(c);
            }
            else {
                if(this.isCloseParenthese(c)) {
                    let p = stack.pop();
                    if(c !== this._parentheseDefinition[p]) // Fail
                        return this.createResult(false, `position:${i}, expected:${this._parentheseDefinition[p]}, found:${c}`);
                }
            }
        }
        // if stack is empty the expression is valid else we have an invalid expression
        // with a missing closing parenthese like this '(ok'
        const passed = stack.isEmpty();
        return this.createResult(passed, passed ? `` : `expected:"${this._parentheseDefinition[stack.pop()]}" at end of expression`);
    }
    this.createResult = function (passed, errorMsg) {
        return {
            Expression   : this._expression,
            Passed       : passed,
            ErrorMessage : ParenthesesParser.PARSING_ERROR + " " + errorMsg
        };
    }
    this.isOpenParenthese = function(c) {

        return c in this._parentheseDefinition;
    }
    this.isCloseParenthese = function(c) {
        
        return Object.values(this._parentheseDefinition).indexOf(c) !== -1;
    }
};

ParenthesesParser.EXPRESSION_CANNOT_BE_NULL_OR_UNDEFINED = "expression cannot be null or undefined";
ParenthesesParser.PARSING_ERROR                          = "Parsing Error";

module.exports = ParenthesesParser;


Unit tests


let assert = require('chai').assert;
let expect = require('chai').expect;
let ParenthesesParser = require('../src/ParenthesesParser');
let print = console.log;

describe('ParenthesesParser', () => {

    let subject;

    before(() => {
        subject = new ParenthesesParser();
    });
    
    describe('Valid expressions', () => {

        let validExpressions = [
            "()" ,"(a)", "((((()))))", 
            "((((([[[]]])))))",  "((((([[[{{{}}}]]])))))",  "((((([[[{{{a}}}]]])))))", 
            "function(abs(compute(run(exec(array[list[array[object{object{object[1]}}]]])))))", 
            "(){}[]" , "(()){{}}[[]]" ,
            "([ok])" ,
            "{([ok])}" ,
            "function() { return (1+1); }" ,
            "<(>)", // This one passed because '<' and '>' are not considered Parentheses
            "(\\)", "(\\//)", "(\")",
            "Valid-Expression", "(Valid-Expression)"
        ];
        it(`should return passed on expression '${validExpressions}'`, () => {

            validExpressions.forEach((exp) =>{
                expect(subject.parse(exp).Passed).to.be.true;
            });
        });
    });

    describe('Invalid expressions', () => {
       
        let invalidExpressions = [
            "(invalid-expression", "invalid-expression)",
            "(" , "((((())))", , "(((((])))))", 
            "[", "{",
            "(){[]" ,
            "([ok)" ,
            "{([ok]}" ,
            "function() { return (1+1; }" ,
        ];
        it(`should return failed on expression '${invalidExpressions}'`, () => {

            invalidExpressions.forEach((exp) =>{
                expect(subject.parse(exp).Passed).to.be.false;
            });
        });
        it(`should return specific error message on failure`, () => {

            let exp = "[(a])";
            let expected = "Parsing Error position:3, expected:), found:]";
            let result = subject.parse(exp);
            expect(result.ErrorMessage).to.equal(expected);
            expect(result.ErrorMessage.startsWith(ParenthesesParser.PARSING_ERROR)).to.be.true;
        });
        it(`should return specific error message on failure, complex expression`, () => {

            let exp = "((((([[[{{{a)}}}]]])))))";
            let expected = "Parsing Error position:12, expected:}, found:)";
            let result = subject.parse(exp);
            expect(result.ErrorMessage).to.equal(expected);
        });
    });

    describe('Invalid expressions, missing closing parenthese', () => {

        it(`should return specific error message on failure on '(ok'`, () => {

            let exp = "(ok";
            let expected = 'Parsing Error expected:")" at end of expression';
            let result = subject.parse(exp);
            expect(result.ErrorMessage).to.equal(expected);
        });

        let invalidExpressionsMissingClosingParentheses = [
            "(ok", "{ok", "[ok",
        ];
        it(`should return error message on failure ${invalidExpressionsMissingClosingParentheses}`, () => {

            invalidExpressionsMissingClosingParentheses.forEach((exp) => {

                let result = subject.parse(exp);
                expect(result.ErrorMessage.startsWith(ParenthesesParser.PARSING_ERROR)).to.be.true;
            });
        });
    });

    describe('Invalid parameters', () => {

        it(`should throw exception on empty string`, () => {
            expect(function () { subject.parse("");}).to.throw(ParenthesesParser.EXPRESSION_CANNOT_BE_NULL_OR_UNDEFINED);
        });
        it(`should throw exception on null string`, () => {
            expect(function () { subject.parse(null);}).to.throw(ParenthesesParser.EXPRESSION_CANNOT_BE_NULL_OR_UNDEFINED);
        });
        it(`should throw exception on undefined string`, () => {
            expect(function () { subject.parse(undefined);}).to.throw(ParenthesesParser.EXPRESSION_CANNOT_BE_NULL_OR_UNDEFINED);
        });
    });

    describe('Utility methods', () => {

        it(`isOpenParenthese() positive cases`, () => {
            ['(', "{", "["].forEach((c) => {
                expect(subject.isOpenParenthese(c)).to.be.true;
            });
        });
        it(`isOpenParenthese() negative cases`, () => {
            ['a', "b", "c"].forEach((c) => {
                expect(subject.isOpenParenthese(c)).to.be.false;
            });
        });
        it(`isCloseParenthese() positive cases`, () => {
            [')', "}", "]"].forEach((c) => {
                expect(subject.isCloseParenthese(c)).to.be.true;
            });
        });
        it(`isCloseParenthese() negative cases`, () => {
            ['a', "b", "c"].forEach((c) => {
                expect(subject.isCloseParenthese(c)).to.be.false;
            });
        });
    });
});


Wednesday, February 28, 2018

How to traverse a binary tree in JavaScript?

Overview 

Based on Traverse a Tree - Introduction. How to traverse a binary tree recursively

  • Pre-Order, 
  • In-Order, 
  • Post-Order 
  • Breadth-First
using mocha+chai test framework.

Github Repo: TreeTraversalJS

Source Code


let Queue = require('./Queue');

let print = console.log;

function TreeTraversal() {

    this.PathResult = [];

    this.init = function(first) {

        if(first) this.PathResult = [];
    }
    this.end = function(first) {

        if(first) print(`Path:${JSON.stringify(this.PathResult)}`);
    }
    this.processNode = function(node) {

        this.PathResult.push(node.value);
    }
    this.recursively_PreOrderTraversal = function (t, first) {

        this.init(first);
        this.processNode(t);

        if(t.left) this.recursively_PreOrderTraversal(t.left);
        if(t.right) this.recursively_PreOrderTraversal(t.right);

        this.end(first);
    }
    this.recursively_InOrderTraversal = function (t, first) {

        this.init(first);
        if(t.left) this.recursively_InOrderTraversal(t.left);

        this.processNode(t);

        if(t.right) this.recursively_InOrderTraversal(t.right);

        this.end(first);
    }
    this.recursively_PostOrderTraversal = function(t, first) {

        this.init(first);

        if(t.left) this.recursively_PostOrderTraversal(t.left);
        if(t.right) this.recursively_PostOrderTraversal(t.right);

        this.processNode(t);
        this.end(first);
    }
    this.breadthFirst = function(t) {

        this.init(true);
        let q = new Queue();
        q.enqueue(t);

        while(!q.isEmpty()) {

            let node = q.dequeue();

            this.processNode(node);

            if(node.left) q.enqueue(node.left);
            if(node.right) q.enqueue(node.right);
        }
        this.end(true);
    }
}

module.exports = TreeTraversal;


Unit tests


let assert = require('chai').assert;
let expect = require('chai').expect;
let TreeTraversal = require('../src/TreeTraversal');

/*
                             F
                         /      \
                         B       G
                       /  \       \ 
                       A  D        I
                         / \      /
                        C   E    H

*/
let root = {
    value : "F",
    right : {
        value: "G",
        right:{
            value:"I",
            left : {
               value:"H" 
            }
        }
    },
    left:{
        value:"B",
        left : {
            value:"A"
        },
        right :{
            value:"D",
            left :{
                value:"C"
            },
            right : {
              value:"E"
            }
        }
    }
};

let print = console.log;

function arrayEqual(a0, a1) {
    return a0.join() == a1.join();
}

describe('TreeTraversal', () => {

    let subject;

    before(() => {
        subject = new TreeTraversal();
    });

    describe('recursively_PreOrderTraversal', () => {

        let expected = ["F","B","A","D","C","E","G","I","H"];
        it(`should return ${expected}`, () => {

            subject.recursively_PreOrderTraversal(root, true);
            expect(arrayEqual(expected, subject.PathResult)).to.be.true;
        });
    });
    describe('recursively_InOrderTraversal', () => {

        let expected = ["A","B","C","D","E","F","G","H","I"];
        it(`should return ${expected}`, () => {

            subject.recursively_InOrderTraversal(root, true);
            expect(arrayEqual(expected, subject.PathResult)).to.be.true;
        });
    });
    describe('recursively_PostOrderTraversal', () => {

        let expected = ["A","C","E","D","B","H","I","G","F"];
        it(`should return ${expected}`, () => {

            subject.recursively_PostOrderTraversal(root, true);
            expect(arrayEqual(expected, subject.PathResult)).to.be.true;
        });
    });
    describe('breadthFirst', () => {

        let expected = ["F","B","G","A","D","I","C","E","H"];
        it(`should return ${expected}`, () => {

            subject.breadthFirst(root, true);
            expect(arrayEqual(expected, subject.PathResult)).to.be.true;
        });
    });
});