<commit_before>var Playlist = require('../models/playlist').model;
var User = require('../models/user').model;
var Track = require('../models/track').model;
var _ = require('underscore');

exports.createPlaylist = function (req, res) {
    User.findById(req.user.id, function (err, user) {
        if (!err) {
            var playlist = new Playlist({
                name: req.body.name,
                owner: user.toJSON()
            });
            playlist.save(function (err) {
                if (!err) {
                    res.status(201).send(playlist);
                } else {
                    console.error(err);
                }
            });
        } else {
            console.error(err);
        }
    });
};

// Unsecure (Will be removed after release 2)
exports.createPlaylistUnsecure = function (req, res) {
    var playlist = new Playlist({
        name: req.body.name,
        owner: req.body.owner
    });
    playlist.save(function (err) {
        if (!err) {
            res.status(201).send(playlist);
        } else {
            console.error(err);
        }
    });
};

exports.addTrackToPlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                if (req.body) {
                    var track = new Track(req.body);
                    playlist.tracks.push(track.toJSON());
                    playlist.save();
                    res.status(200).send(playlist);
                } else {
                    res.status(412).send({
                        errorCode: 'REQUEST_BODY_REQUIRED',
                        message: 'Request body is missing'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.removeTrackFromPlaylist = function (req, res) {
    console.log(req.params.trackId);
    Playlist.findById(req.params.playlistId, function (err, playlist) {
        if (!err) {
            if (playlist) {
                console.log(playlist.tracks);
                var trackToRemove = playlist.tracks.id(req.params.trackId);
                if (trackToRemove) {
                    trackToRemove.remove();
                    playlist.save();
                    res.status(200).send();
                } else {
                    res.status(404).send({
                        errorCode: 'TRACK_NOT_FOUND',
                        message: 'Track ' + req.params.id + ' was not found'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.updatePlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                playlist.name = req.body.name;
                playlist.tracks = req.body.tracks;
                playlist.save();
                res.status(200).send(playlist);
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.removePlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                if(playlist.owner.id == req.user.id) {
                    playlist.remove();
                    res.status(200).send({
                        message: 'Playlist ' + req.params.id + ' deleted successfully'
                    });
                }
                else {
                    res.status(412).send({
                        errorCode: 'NOT_PLAYLIST_OWNER',
                        message: 'Playlist can only be deleted by their owner'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

// Unsecure (Will be removed after release 2)
exports.removePlaylistUnsecure = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                playlist.remove();
                res.status(200).send({
                    message: 'Playlist ' + req.params.id + ' deleted successfully'
                });
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.getPlaylists = function (req, res) {
    Playlist.find({}, function (err, playlists) {
        if (!err) {
            res.status(200).send(playlists || []);
        } else {
            console.log(err);
            res.status(500).send(err);
        }
    });
};

exports.findPlaylistById = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                res.status(200).send(playlist);
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};
<commit_msg>! Fix delete track<commit_after>var Playlist = require('../models/playlist').model;
var User = require('../models/user').model;
var Track = require('../models/track').model;
var _ = require('underscore');

exports.createPlaylist = function (req, res) {
    User.findById(req.user.id, function (err, user) {
        if (!err) {
            var playlist = new Playlist({
                name: req.body.name,
                owner: user.toJSON()
            });
            playlist.save(function (err) {
                if (!err) {
                    res.status(201).send(playlist);
                } else {
                    console.error(err);
                }
            });
        } else {
            console.error(err);
        }
    });
};

// Unsecure (Will be removed after release 2)
exports.createPlaylistUnsecure = function (req, res) {
    var playlist = new Playlist({
        name: req.body.name,
        owner: req.body.owner
    });
    playlist.save(function (err) {
        if (!err) {
            res.status(201).send(playlist);
        } else {
            console.error(err);
        }
    });
};

exports.addTrackToPlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                if (req.body) {
                    var track = new Track(req.body);
                    playlist.tracks.push(track.toJSON());
                    playlist.save();
                    res.status(200).send(playlist);
                } else {
                    res.status(412).send({
                        errorCode: 'REQUEST_BODY_REQUIRED',
                        message: 'Request body is missing'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.removeTrackFromPlaylist = function (req, res) {
    console.log(req.params.trackId);
    Playlist.findById(req.params.playlistId, function (err, playlist) {
        if (!err) {
            if (playlist) {
                console.log(playlist.tracks);
                var trackToRemove = _.findWhere(playlist.tracks, {
                    trackId: req.params.trackId
                });
                console.log(trackToRemove);
                if (trackToRemove) {
                    trackToRemove.remove();
                    playlist.save();
                    res.status(200).send();
                } else {
                    res.status(404).send({
                        errorCode: 'TRACK_NOT_FOUND',
                        message: 'Track ' + req.params.trackId + ' was not found'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.trackId + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.playlistId + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.updatePlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                playlist.name = req.body.name;
                playlist.tracks = req.body.tracks;
                playlist.save();
                res.status(200).send(playlist);
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.removePlaylist = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                if (playlist.owner.id == req.user.id) {
                    playlist.remove();
                    res.status(200).send({
                        message: 'Playlist ' + req.params.id + ' deleted successfully'
                    });
                }
                else {
                    res.status(412).send({
                        errorCode: 'NOT_PLAYLIST_OWNER',
                        message: 'Playlist can only be deleted by their owner'
                    });
                }
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

// Unsecure (Will be removed after release 2)
exports.removePlaylistUnsecure = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                playlist.remove();
                res.status(200).send({
                    message: 'Playlist ' + req.params.id + ' deleted successfully'
                });
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};

exports.getPlaylists = function (req, res) {
    Playlist.find({}, function (err, playlists) {
        if (!err) {
            res.status(200).send(playlists || []);
        } else {
            console.log(err);
            res.status(500).send(err);
        }
    });
};

exports.findPlaylistById = function (req, res) {
    Playlist.findById(req.params.id, function (err, playlist) {
        if (!err) {
            if (playlist) {
                res.status(200).send(playlist);
            } else {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            }
        } else {
            console.error(err);
            if (err.name === 'CastError') {
                res.status(404).send({
                    errorCode: 'PLAYLIST_NOT_FOUND',
                    message: 'Playlist ' + req.params.id + ' was not found'
                });
            } else {
                res.status(500).send(err);
            }
        }
    });
};
<|endoftext|>


<commit_before>/* jshint esversion: 6 */
/* jshint devel: true */
/* jshint node: true */
/* jshint browser: true */
'use strict';

const boom  = require('boom');
const express = require('express');
const bcrypt = require('bcrypt-as-promised');
var knex = require('../db/knex');
var yahooFinance = require('yahoo-finance');
var _ = require('lodash');
var Trx = require('../models/trx.js').Trx;
const { camelizeKeys, decamelizeKeys } = require('humps');
// eslint-disable-next-line new-cap
const router = express.Router();

// =============================================================================
// Build open positions prices
/**
FIELD codes
n: name
l1: lastTradePriceOnly
c1: change
p2: changeInPercent
y: dividendYield
r: peRatio
*/
let FIELDS = ['n', 'l1', 'c1', 'p2', 'y', 'r']
let openPositions = [];
let closedPositions = [];
let symbols = [];
let closedSymbols = [];
let quoteGSCP = "";
console.log('OPEN POSITIONS!!');

// Datetime to simple date converter
function simpleDate(date) {
    var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;

    return [year, month, day].join('-');
}

// =============================================================================
// Define Position class
var Position = function(userName, ticker, sharePrice, tradeDate, numShares) {
  this.userName = userName,
  this.ticker = ticker,
  this.sharePrice = sharePrice,
  this.tradeDate = new Date(tradeDate),
  this.numShares = numShares
}

// =============================================================================
// show all open positions for current user
router.get('/open', function(req, res) {
  openPositions = [];
  if (!req.cookies['/token']) {
    res.redirect('login');
  }
  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('transactions.id', 'users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('closed_flag', false)
  .where('users.id', userId)
  .then((rows) => {
    if (req.cookies['/token']) {
    //   console.log("Robs Rows", rows);
      // Build symbol (to get quotes) and open position arrays
      _.each(rows, function(stock) {
          let newPos = new Position(stock.user_name, stock.ticker, stock.share_price, stock.trade_date, stock.num_shares)

          openPositions.push(newPos);
          symbols.push(newPos.ticker);
      })

      // Get realtime quotes
      yahooFinance.snapshot({
        fields: FIELDS,
        symbols: symbols
      }).then(function (result) {
        _.each(result, function (snapshot, symbolIndex) {
          let currPos = openPositions[symbolIndex];
          currPos.name = snapshot.name;
          currPos.lastTradePriceOnly = snapshot.lastTradePriceOnly;
          currPos.change = snapshot.change;
          currPos.changeInPercent = (snapshot.changeInPercent * 100).toFixed(2);
          currPos.glDollar = (Number(snapshot.lastTradePriceOnly * currPos.numShares) - Number(currPos.sharePrice * currPos.numShares)).toFixed(2);
          currPos.glInPercent = (currPos.glDollar/(Number(snapshot.lastTradePriceOnly * currPos.numShares)) * 100).toFixed(2);
          currPos.dividendYield = snapshot.dividendYield;
          currPos.peRatio = snapshot.peRatio;
        });
        console.log("openPositions: ", openPositions)
        res.render('showall', {openPositions: openPositions})
      });
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }
  }).catch((err) => {
    res.status(401).send(err);
  });
});

// =============================================================================
// show all closed positions for current user

router.get('/closed', function(req, res) {

  if (!req.cookies['/token']) {
    res.redirect('login');
  }

  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('transactions.id', 'users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('closed_flag', true)
  .where('users.id', userId)
  .orderBy('ticker') // Added to sort by ticker
  .then((rows) => {

    var closedTrx = [];

    for (var i = 0; i < rows.length; i++) {
      var newTrx = new Trx(rows[i].id, rows[i].user_name, rows[i].ticker,
        rows[i].share_price, rows[i].trade_date, rows[i].num_shares);

        closedTrx.push(newTrx);
    }

    if (req.cookies['/token']) {
    //   console.log(closedTrx);
      console.log("closedTrx.length", closedTrx.length);

      //<-- BEGIN Code to calc closed table-->
      let theUser = closedTrx[0].userId;
      console.log('theUser', theUser)

      // Assemble buy vs sell object
      let buyVsSellObj = {};
      _.each(closedTrx, function(record, idx) {
        // console.log(record.ticker)
        if (!(record.ticker in buyVsSellObj)) {
            buyVsSellObj[record.ticker] = {buys: [], sells: []};
        }
        if (record.numShares > 0) {
            buyVsSellObj[record.ticker].buys.push({buyDate: record.buyDate,
                                               buySimpleDate: simpleDate(record.buyDate),
                                               buyShares: Number(record.numShares),
                                               buyPrice: Number(record.buyPrice),
                                               buyAmount: record.buyPrice * record.numShares})
        } else if (record.numShares < 0) {
            buyVsSellObj[record.ticker].sells.push({sellDate: record.sellDate,
                                               sellSimpleDate: simpleDate(record.sellDate),
                                               sellShares: Number(record.numShares),
                                               sellPrice: Number(record.sellPrice),
                                               sellAmount: record.sellPrice * record.numShares})
        }
        // console.log("index = ", idx);
      })
      //   console.log("buyVsSellObj", buyVsSellObj);

      // Calculate balances
    //   let calcObj = {};
      if (theUser === 1) {
          var hypoFolioAmount = 100000;
          var origHypoFolioAmount = 100000;
          var percentAcct = 0.10
      } else if (theUser === 2) {
          var hypoFolioAmount = 10000;
          var origHypoFolioAmount = 10000;
          var percentAcct = 0.10
      } else {
          var hypoFolioAmount = 100000;
          var origHypoFolioAmount = 100000;
          var percentAcct = 0.10
      }
      let calcArr = [];
      _.each(buyVsSellObj, function(item, key) {
        //   console.log("each item", item);
          console.log("each item.buys", item.buys);
        //   console.log("each item.buys.buyAmount", item.buys.buyAmount);
        //   calcObj.ticker = buyVsSellObj.ticker;
          let calcObj = {};
          calcObj.ticker = key;
          calcObj.buyAmount = item.buys.reduce(function(a, b){
              return a + b.buyAmount}, 0).toFixed(2)
          calcObj.sellAmount = item.sells.reduce(function(a, b){
                  return a + b.sellAmount}, 0).toFixed(2)
          calcObj.glAmount = ((Number(calcObj.sellAmount) + Number(calcObj.buyAmount)) * -1).toFixed(2);
          calcObj.glInPercent = (calcObj.glAmount / calcObj.buyAmount * 100).toFixed(2);
          if (item.buys.length > 1) {
              calcObj.buyDate = 'various'
          } else {
              calcObj.buyDate = item.buys[0].buySimpleDate;
          }
          if (item.sells.length > 1) {
              calcObj.sellDate = 'various'
          } else {
              calcObj.sellDate = item.sells[0].sellSimpleDate;
          }

        //   hypoFolioAmount = hypoFolioAmount + (Number(calcObj.glInPercent) / 100 * hypoFolioAmount * percentAcct) // Use to calc hypothetical total amount

          hypoFolioAmount += Number(calcObj.glAmount) // Use for real total amount

          calcObj.hypoFolioAmount = hypoFolioAmount.toFixed(2)
          calcObj.hypoFolioAmountPercent = (((hypoFolioAmount - origHypoFolioAmount) / origHypoFolioAmount)*100).toFixed(2);

          calcArr.push(calcObj)
      });
      //<-- END Code to calc closed table-->

    //   res.send(closedTrx);
    //   res.send(buyVsSellObj);
    //   res.send(calcObj);
      res.send(calcArr);
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }

  }).catch((err) => {

    res.status(401).send(err);
  });
});

// =============================================================================
// show specified open position for current user
router.get('/:id', function(req, res) {
  var trxId = Number(req.params.id);
  if (!req.cookies['/token']) {
    res.redirect('login');
  }

  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('transactions.id', trxId)
  .where('users.id', userId)
  .then((rows) => {

    if (req.cookies['/token']) {
      console.log('SHOW ROW: ', rows);
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }

  }).catch((err) => {

    res.status(401).send(err);
  });
});

module.exports = router;
<commit_msg>Got a choice for hypothetical or real amount for tracking - set for real now<commit_after>/* jshint esversion: 6 */
/* jshint devel: true */
/* jshint node: true */
/* jshint browser: true */
'use strict';

const boom  = require('boom');
const express = require('express');
const bcrypt = require('bcrypt-as-promised');
var knex = require('../db/knex');
var yahooFinance = require('yahoo-finance');
var _ = require('lodash');
var Trx = require('../models/trx.js').Trx;
const { camelizeKeys, decamelizeKeys } = require('humps');
// eslint-disable-next-line new-cap
const router = express.Router();

// =============================================================================
// Build open positions prices
/**
FIELD codes
n: name
l1: lastTradePriceOnly
c1: change
p2: changeInPercent
y: dividendYield
r: peRatio
*/
let FIELDS = ['n', 'l1', 'c1', 'p2', 'y', 'r']
let openPositions = [];
let closedPositions = [];
let symbols = [];
let closedSymbols = [];
let quoteGSCP = "";
console.log('OPEN POSITIONS!!');

// Datetime to simple date converter
function simpleDate(date) {
    var d = new Date(date),
        month = '' + (d.getMonth() + 1),
        day = '' + d.getDate(),
        year = d.getFullYear();

    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;

    return [year, month, day].join('-');
}

// =============================================================================
// Define Position class
var Position = function(userName, ticker, sharePrice, tradeDate, numShares) {
  this.userName = userName,
  this.ticker = ticker,
  this.sharePrice = sharePrice,
  this.tradeDate = new Date(tradeDate),
  this.numShares = numShares
}

// =============================================================================
// show all open positions for current user
router.get('/open', function(req, res) {
  openPositions = [];
  if (!req.cookies['/token']) {
    res.redirect('login');
  }
  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('transactions.id', 'users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('closed_flag', false)
  .where('users.id', userId)
  .then((rows) => {
    if (req.cookies['/token']) {
    //   console.log("Robs Rows", rows);
      // Build symbol (to get quotes) and open position arrays
      _.each(rows, function(stock) {
          let newPos = new Position(stock.user_name, stock.ticker, stock.share_price, stock.trade_date, stock.num_shares)

          openPositions.push(newPos);
          symbols.push(newPos.ticker);
      })

      // Get realtime quotes
      yahooFinance.snapshot({
        fields: FIELDS,
        symbols: symbols
      }).then(function (result) {
        _.each(result, function (snapshot, symbolIndex) {
          let currPos = openPositions[symbolIndex];
          currPos.name = snapshot.name;
          currPos.lastTradePriceOnly = snapshot.lastTradePriceOnly;
          currPos.change = snapshot.change;
          currPos.changeInPercent = (snapshot.changeInPercent * 100).toFixed(2);
          currPos.glDollar = (Number(snapshot.lastTradePriceOnly * currPos.numShares) - Number(currPos.sharePrice * currPos.numShares)).toFixed(2);
          currPos.glInPercent = (currPos.glDollar/(Number(snapshot.lastTradePriceOnly * currPos.numShares)) * 100).toFixed(2);
          currPos.dividendYield = snapshot.dividendYield;
          currPos.peRatio = snapshot.peRatio;
        });
        console.log("openPositions: ", openPositions)
        res.render('showall', {openPositions: openPositions})
      });
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }
  }).catch((err) => {
    res.status(401).send(err);
  });
});

// =============================================================================
// show all closed positions for current user

router.get('/closed', function(req, res) {

  if (!req.cookies['/token']) {
    res.redirect('login');
  }

  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('transactions.id', 'users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('closed_flag', true)
  .where('users.id', userId)
  .orderBy('ticker') // Added to sort by ticker
  .then((rows) => {

    var closedTrx = [];

    for (var i = 0; i < rows.length; i++) {
      var newTrx = new Trx(rows[i].id, rows[i].user_name, rows[i].ticker,
        rows[i].share_price, rows[i].trade_date, rows[i].num_shares);

        closedTrx.push(newTrx);
    }

    if (req.cookies['/token']) {
    //   console.log(closedTrx);
      console.log("closedTrx.length", closedTrx.length);

      //<-- BEGIN Code to calc closed table-->
      let theUser = closedTrx[0].userId;
      console.log('theUser', theUser)

      // Assemble buy vs sell object
      let buyVsSellObj = {};
      _.each(closedTrx, function(record, idx) {
        // console.log(record.ticker)
        if (!(record.ticker in buyVsSellObj)) {
            buyVsSellObj[record.ticker] = {buys: [], sells: []};
        }
        if (record.numShares > 0) {
            buyVsSellObj[record.ticker].buys.push({buyDate: record.buyDate,
                                               buySimpleDate: simpleDate(record.buyDate),
                                               buyShares: Number(record.numShares),
                                               buyPrice: Number(record.buyPrice),
                                               buyAmount: record.buyPrice * record.numShares})
        } else if (record.numShares < 0) {
            buyVsSellObj[record.ticker].sells.push({sellDate: record.sellDate,
                                               sellSimpleDate: simpleDate(record.sellDate),
                                               sellShares: Number(record.numShares),
                                               sellPrice: Number(record.sellPrice),
                                               sellAmount: record.sellPrice * record.numShares})
        }
        // console.log("index = ", idx);
      })
      //   console.log("buyVsSellObj", buyVsSellObj);

      // Calculate balances
    //   let calcObj = {};
      if (theUser === 1) {
          var hypoFolioAmount = 100000;
          var origHypoFolioAmount = 100000;
          var percentAcct = 0.10
      } else if (theUser === 2) {
          var hypoFolioAmount = 10000;
          var origHypoFolioAmount = 10000;
          var percentAcct = 0.10
      } else {
          var hypoFolioAmount = 100000;
          var origHypoFolioAmount = 100000;
          var percentAcct = 0.10
      }
      let calcArr = [];
      _.each(buyVsSellObj, function(item, key) {
        //   console.log("each item", item);
          console.log("each item.buys", item.buys);
        //   console.log("each item.buys.buyAmount", item.buys.buyAmount);
        //   calcObj.ticker = buyVsSellObj.ticker;
          let calcObj = {};
          calcObj.ticker = key;
          calcObj.buyAmount = item.buys.reduce(function(a, b){
              return a + b.buyAmount}, 0).toFixed(2)
          calcObj.sellAmount = item.sells.reduce(function(a, b){
                  return a + b.sellAmount}, 0).toFixed(2)
          calcObj.glAmount = ((Number(calcObj.sellAmount) + Number(calcObj.buyAmount)) * -1).toFixed(2);
          calcObj.glInPercent = (calcObj.glAmount / calcObj.buyAmount * 100).toFixed(2);
          if (item.buys.length > 1) {
              calcObj.buyDate = 'various'
          } else {
              calcObj.buyDate = item.buys[0].buySimpleDate;
          }
          if (item.sells.length > 1) {
              calcObj.sellDate = 'various'
          } else {
              calcObj.sellDate = item.sells[0].sellSimpleDate;
          }

        //   hypoFolioAmount = hypoFolioAmount + (Number(calcObj.glInPercent) / 100 * hypoFolioAmount * percentAcct) // Use to calc hypothetical total amount

          hypoFolioAmount += Number(calcObj.glAmount) // Use for real total amount

          calcObj.hypoFolioAmount = hypoFolioAmount.toFixed(2)
          calcObj.hypoFolioAmountPercent = (((hypoFolioAmount - origHypoFolioAmount) / origHypoFolioAmount)*100).toFixed(2);

          calcArr.push(calcObj)
      });
      //<-- END Code to calc closed table-->

    //   res.send(closedTrx);
    //   res.send(buyVsSellObj);
    //   res.send(calcObj);
      res.send(calcArr);
    //   res.render('closedall')
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }

  }).catch((err) => {

    res.status(401).send(err);
  });
});

// =============================================================================
// show specified open position for current user
router.get('/:id', function(req, res) {
  var trxId = Number(req.params.id);
  if (!req.cookies['/token']) {
    res.redirect('login');
  }

  let userId = Number(req.cookies['/token'].split('.')[0]);

  knex.select('users.id','user_name', 'ticker', 'share_price', 'trade_date', 'num_shares')
  .from('transactions')
  .join('users', 'transactions.user_id', 'users.id')
  .join('stocks', 'transactions.stock_id', 'stocks.id')
  .where('transactions.id', trxId)
  .where('users.id', userId)
  .then((rows) => {

    if (req.cookies['/token']) {
      console.log('SHOW ROW: ', rows);
    } else {
      res.status(401);
      res.set('Content-Type', 'text/plain');
      res.send('Unauthorized');
    }

  }).catch((err) => {

    res.status(401).send(err);
  });
});

module.exports = router;
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

router.post('/update', function(req, res, next) {

	var db = req.db;

	// boards
	var boardName = req.body.boardName;
	var boardId = req.body.boardId;
	var columnsTable = db.get('columns');

	// column
	columnsTable.find({ "boardId" : boardId }, { "sort" : "sortOrder" }, function(err, columns) {

		var columnName = req.body.columnName;

		if (req.body.columnId && req.body.columnId.length > 1 && req.body.columnName && req.body.columnName.length > 0) {
			var columnId = req.body.columnId;
			res.redirect('/w/boards');
		}
		else {
			columnsTable.insert({ "boardId" : boardId, "name" : columnName, "sortOrder" : columns.length }, function(err, column) {
				if (err) {
					res.send("*** ERROR: there was a problem adding that column to the database.");
					res.redirect('/w/boards');
				}
				else {
					var boards = db.get('boards');
					boards.findById(boardId, {}, function(err, board) {
						if (err) {
							console.log("*** ERROR: could not find board (id=" + boardId + ") for new column.");
							res.redirect('/w/boards');
						}
						else if (board) {
							columnsTable = db.get('columns');
							columnsTable.find({ "boardId" : boardId }, { "sort" : "sortOrder" }, function(e, columns) {
								res.render('boards/edit', { "board" : board, "columns" : columns });
							});
						}
					});
				}
			});
		}
	});
});

module.exports = router;
<commit_msg>A bit of refactoring.<commit_after>var express = require('express');
var router = express.Router();

router.post('/update', function(req, res, next) {

	var db = req.db;

	// boards
	var boardName = req.body.boardName;
	var boardId = req.body.boardId;
	var columnsTable = db.get('columns');

	// column
	columnsTable.find({ "boardId" : boardId }, { "sort" : "sortOrder" }, function(err, columns) {

		var columnName = req.body.columnName;

		if (req.body.columnId && req.body.columnId.length > 1 && req.body.columnName && req.body.columnName.length > 0) {

			var columnId = req.body.columnId;

			columnsTable.findAndModify({
				"query" : { "_id" : columnId },
				"update" : { "name" : columnName },
				"new" : true,		// no workie?
				"upsert" : false	// no workie?
			}, function(err, oldColumn) {
				if (err) {
					res.send("*** ERROR: there was a problem modifying that column in the database.");
					res.redirect('/w/boards');
				}
				else {
					refresh(boardId);
				}
			});
		}
		else {
			columnsTable.insert({ "boardId" : boardId, "name" : columnName, "sortOrder" : columns.length }, function(err, column) {
				if (err) {
					res.send("*** ERROR: there was a problem adding that column to the database.");
					res.redirect('/w/boards');
				}
				else {
					refresh(boardId);
				}
			});
		}
	});
});

function refresh(boardId) {
	var boards = db.get('boards');
	boards.findById(boardId, {}, function(err, board) {
		if (err) {
			console.log("*** ERROR: could not find board (id=" + boardId + ") for new column.");
			res.redirect('/w/boards');
		}
		else if (board) {
			columnsTable = db.get('columns');
			columnsTable.find({ "boardId" : boardId }, { "sort" : "sortOrder" }, function(e, columns) {
				res.render('boards/edit', { "board" : board, "columns" : columns });
			});
		}
	});
}

module.exports = router;
<|endoftext|>


<commit_before>/* eslint-disable no-console */
'use strict';

const spawnSync = require('child_process').spawnSync;
const fs = require('fs');
const path = require('path');

const conf = {};
const confUiFile = './patternlab-config.json';

let sourceDir;

if (fs.existsSync(confUiFile)) {
  const confUiStr = fs.readFileSync(confUiFile, 'utf8');

  try {
    conf.ui = JSON.parse(confUiStr);
  }
  catch (err) {
    throw err;
  }

  if (conf.ui) {
    sourceDir = conf.ui.paths.source.root;
  }
}

// Conf file may not exists at this point of installation. Hard-code in that case.
else {
  sourceDir = 'source';
}

// First, create empty source dir so the postinstall script doesn't write the main profile there.
if (!fs.existsSync(sourceDir)) {
  fs.mkdirSync(sourceDir);
}

// Return if node_modules is already installed. (Avoid infinite loops!)
if (fs.existsSync('node_modules')) {
  return;
}

// Else, run npm install.
else {
  spawnSync('npm', ['install'], {stdio: 'inherit'});
}

// Check if source dir is already populated.
const sourceDirContent = fs.readdirSync(sourceDir);

// Return if already populated.
if (sourceDirContent.length) {
  console.warn(`${sourceDir} already has content! Aborting base install!`);

  return;
}

// Delete the empty source dir so a new one can be copied over.
fs.rmdirSync(sourceDir);

// Copy over the base profile source dir.
const binGulp = path.resolve('node_modules', '.bin', 'gulp');
const spawnedObj =
  spawnSync(binGulp, ['--gulpfile', 'node_modules/fepper/tasker.js', 'install:copy-base'], {stdio: 'inherit'});

// Output to install.log.
const installLog = 'install.log';

fs.writeFileSync(installLog, '');

if (spawnedObj.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj.stderr}\n`);
}

fs.appendFileSync(installLog, `Process exited with status ${spawnedObj.status}.\n`);

// Compile UI.
spawnSync('node', ['node_modules/fepper/index.js', 'ui:compile'], {stdio: 'inherit'});
<commit_msg>install script messaging updates<commit_after>/* eslint-disable no-console */
'use strict';

const spawnSync = require('child_process').spawnSync;
const fs = require('fs');
const path = require('path');

const conf = {};
const confUiFile = './patternlab-config.json';

let sourceDir;

if (fs.existsSync(confUiFile)) {
  const confUiStr = fs.readFileSync(confUiFile, 'utf8');

  try {
    conf.ui = JSON.parse(confUiStr);
  }
  catch (err) {
    throw err;
  }

  if (conf.ui) {
    sourceDir = conf.ui.paths.source.root;
  }
}

// Conf file may not exists at this point of installation. Hard-code in that case.
else {
  sourceDir = 'source';
}

// First, create empty source dir so the postinstall script doesn't write the main profile there.
if (!fs.existsSync(sourceDir)) {
  fs.mkdirSync(sourceDir);
}

// Return if node_modules is already installed. (Avoid infinite loops!)
if (fs.existsSync('node_modules')) {
  console.warn(`Fepper Base already installed! Aborting!`);

  return;
}

// Else, run npm install.
else {
  spawnSync('npm', ['install'], {stdio: 'inherit'});
}

// Check if source dir is already populated.
const sourceDirContent = fs.readdirSync(sourceDir);

// Return if already populated.
if (sourceDirContent.length) {
  console.warn(`${sourceDir} dir already has content! Aborting base install!`);

  return;
}

// Delete the empty source dir so a new one can be copied over.
fs.rmdirSync(sourceDir);

// Copy over the base profile source dir.
const binGulp = path.resolve('node_modules', '.bin', 'gulp');
const spawnedObj =
  spawnSync(binGulp, ['--gulpfile', 'node_modules/fepper/tasker.js', 'install:copy-base'], {stdio: 'inherit'});

// Output to install.log.
const installLog = 'install.log';

fs.writeFileSync(installLog, '');

if (spawnedObj.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj.stderr}\n`);
}

fs.appendFileSync(installLog, `Process exited with status ${spawnedObj.status}.\n`);

// Compile UI.
spawnSync('node', ['node_modules/fepper/index.js', 'ui:compile'], {stdio: 'inherit'});
<|endoftext|>


<commit_before>'use strict';

const spawnSync = require('child_process').spawnSync;
const fs = require('fs');
const path = require('path');

const conf = {};
const confFile = './conf.yml';
const confUiFile = './patternlab-config.json';
const enc = 'utf8';

let extendDir;
let sourceDir;

if (fs.existsSync(confFile)) {
  const confStr = fs.readFileSync(confFile, enc);
  const confLines = confStr.split('\n');

  for (let confLine of confLines) {
    const keyVal = confLine.split(':');

    if (keyVal[0].trim() === 'extend_dir' && keyVal[1]) {
      extendDir = keyVal[1].trim();
    }
  }
}
// confFile may not exists at this point of installation. Hard-code in that case.
else {
  extendDir = 'extend';
}

if (fs.existsSync(confUiFile)) {
  const confUiStr = fs.readFileSync(confUiFile, enc);

  try {
    conf.ui = JSON.parse(confUiStr);
  }
  catch (err) {
    throw err;
  }

  if (conf.ui) {
    sourceDir = conf.ui.paths.source.root;
  }
}
// confUiFile may not exists at this point of installation. Hard-code in that case.
else {
  sourceDir = 'source';
}

// Create empty extend and source dirs so the postinstall script doesn't write the main profile there.
if (!fs.existsSync(extendDir)) {
  fs.mkdirSync(extendDir);
}

if (!fs.existsSync(sourceDir)) {
  fs.mkdirSync(sourceDir);
  fs.mkdirSync(`${sourceDir}/_data`);
  fs.mkdirSync(`${sourceDir}/_patterns`);
  fs.mkdirSync(`${sourceDir}/_styles`);
  fs.writeFileSync(`${sourceDir}/_data/data.json`, '{}');
  fs.writeFileSync(`${sourceDir}/_data/listitems.json`, '{}');
}

// Output to install.log.
const installLog = 'install.log';
let spawnedObj;

fs.writeFileSync(installLog, '');

// Only run npm install if not already installed.
if (!fs.existsSync('node_modules')) {
  let binNpm = 'npm';

  // Spawn npm.cmd if Windows and not BASH.
  if (process.env.ComSpec && process.env.ComSpec.toLowerCase() === 'c:\\windows\\system32\\cmd.exe') {
    binNpm = 'npm.cmd';
  }

  spawnedObj = spawnSync(binNpm, ['install', '--ignore-scripts'], {stdio: 'inherit'});
}

if (spawnedObj && spawnedObj.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj.stderr}\n`);
}

// Check if patterns dir is already populated.
const patternsDirContent = fs.readdirSync(`${sourceDir}/_patterns`);

// Return if already populated.
if (patternsDirContent.length) {
  const warning = `The ${sourceDir} directory already has content! Aborting base install!`;

  // eslint-disable-next-line no-console
  console.warn(warning);
  fs.appendFileSync(installLog, `${warning}\n`);

  return;
}

// Delete the source and extend dirs so new ones can be copied over.
fs.unlinkSync(`${sourceDir}/_data/listitems.json`);
fs.unlinkSync(`${sourceDir}/_data/data.json`);
fs.rmdirSync(`${sourceDir}/_styles`);
fs.rmdirSync(`${sourceDir}/_patterns`);
fs.rmdirSync(`${sourceDir}/_data`);
fs.rmdirSync(sourceDir);
fs.rmdirSync(extendDir);

const binPath = path.resolve('node_modules', '.bin');
const fepperPath = path.resolve('node_modules', 'fepper');
const winGulp = path.resolve(binPath, 'gulp.cmd');

let binGulp = path.resolve(binPath, 'gulp');

// Spawn gulp.cmd if Windows and not BASH.
if (process.env.ComSpec && process.env.ComSpec.toLowerCase() === 'c:\\windows\\system32\\cmd.exe') {
  binGulp = winGulp;
}

// Copy over the base profile source dir.
const spawnedObj1 =
  spawnSync(binGulp, ['--gulpfile', path.resolve(fepperPath, 'tasker.js'), 'install:copy-base'], {stdio: 'inherit'});

if (spawnedObj1.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj1.stderr}\n`);
}

// Complete installation.
const spawnedObj2 = spawnSync('node', [path.resolve(fepperPath, 'run', 'install.js')], {stdio: 'inherit'});

if (spawnedObj2.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj2.stderr}\n`);
}

fs.appendFileSync(installLog, `Process exited with status ${spawnedObj2.status}.\n`);
<commit_msg>fixing install-base script<commit_after>'use strict';

const spawnSync = require('child_process').spawnSync;
const fs = require('fs');
const path = require('path');

const conf = {};
const confFile = './conf.yml';
const confUiFile = './patternlab-config.json';
const enc = 'utf8';
const fepperPath = path.resolve('node_modules', 'fepper');
const prefFile = './pref.yml';

let extendDir;
let sourceDir;

if (fs.existsSync(confFile)) {
  const confStr = fs.readFileSync(confFile, enc);
  const confLines = confStr.split('\n');

  for (let confLine of confLines) {
    const keyVal = confLine.split(':');

    if (keyVal[0].trim() === 'extend_dir' && keyVal[1]) {
      extendDir = keyVal[1].trim();
    }
  }
}
// confFile may not exists at this point of installation. Hard-code in that case.
else {
  extendDir = 'extend';
}

if (fs.existsSync(confUiFile)) {
  const confUiStr = fs.readFileSync(confUiFile, enc);

  try {
    conf.ui = JSON.parse(confUiStr);
  }
  catch (err) {
    throw err;
  }

  if (conf.ui) {
    sourceDir = conf.ui.paths.source.root;
  }
}
// confUiFile may not exists at this point of installation. Hard-code in that case.
else {
  sourceDir = 'source';
}

// Create empty extend and source dirs so the postinstall script doesn't write the main profile there.
if (!fs.existsSync(extendDir)) {
  fs.mkdirSync(extendDir);
}

if (!fs.existsSync(sourceDir)) {
  fs.mkdirSync(sourceDir);
  fs.mkdirSync(`${sourceDir}/_data`);
  fs.mkdirSync(`${sourceDir}/_patterns`);
  fs.mkdirSync(`${sourceDir}/_styles`);
  fs.writeFileSync(`${sourceDir}/_data/data.json`, '{}');
  fs.writeFileSync(`${sourceDir}/_data/listitems.json`, '{}');
}

// Output to install.log.
const installLog = 'install.log';
let spawnedObj;

fs.writeFileSync(installLog, '');

// Only run npm install if not already installed.
if (!fs.existsSync('node_modules')) {
  let binNpm = 'npm';

  // Spawn npm.cmd if Windows and not BASH.
  if (process.env.ComSpec && process.env.ComSpec.toLowerCase() === 'c:\\windows\\system32\\cmd.exe') {
    binNpm = 'npm.cmd';
  }

  spawnedObj = spawnSync(binNpm, ['install', '--ignore-scripts'], {stdio: 'inherit'});
}

if (spawnedObj && spawnedObj.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj.stderr}\n`);
}

if (!fs.existsSync(confFile)) {
  fs.copyFileSync(path.resolve(fepperPath, 'excludes', 'conf.yml'), confFile);
}

if (!fs.existsSync(confUiFile)) {
  fs.copyFileSync(path.resolve(fepperPath, 'excludes', 'patternlab-config.json'), confUiFile);
}

if (!fs.existsSync(prefFile)) {
  fs.copyFileSync(path.resolve(fepperPath, 'excludes', 'pref.yml'), prefFile);
}

// Delete the source and extend dirs so new ones can be copied over.
// fs.rmdirSync will throw an error if directory is already populated.
fs.unlinkSync(`${sourceDir}/_data/listitems.json`);
fs.unlinkSync(`${sourceDir}/_data/data.json`);
fs.rmdirSync(`${sourceDir}/_styles`);
fs.rmdirSync(`${sourceDir}/_patterns`);
fs.rmdirSync(`${sourceDir}/_data`);
fs.rmdirSync(sourceDir);
fs.rmdirSync(extendDir);

const binPath = path.resolve('node_modules', '.bin');
const winGulp = path.resolve(binPath, 'gulp.cmd');

let binGulp = path.resolve(binPath, 'gulp');

// Spawn gulp.cmd if Windows and not BASH.
if (process.env.ComSpec && process.env.ComSpec.toLowerCase() === 'c:\\windows\\system32\\cmd.exe') {
  binGulp = winGulp;
}

// Copy over the base profile source dir.
const spawnedObj1 =
  spawnSync(binGulp, ['--gulpfile', path.resolve(fepperPath, 'tasker.js'), 'install:copy-base'], {stdio: 'inherit'});

if (spawnedObj1.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj1.stderr}\n`);
}

// Complete installation.
const spawnedObj2 = spawnSync('node', [path.resolve('run', 'install.js')], {stdio: 'inherit'});

if (spawnedObj2.stderr) {
  fs.appendFileSync(installLog, `${spawnedObj2.stderr}\n`);
}

fs.appendFileSync(installLog, `Process exited with status ${spawnedObj2.status}.\n`);
<|endoftext|>


<commit_before>(function() {
'use strict';

if (!window.location.origin) {
  window.location.origin =
      window.location.protocol + "//" +
      window.location.hostname +
      (window.location.port ? (':' + window.location.port) : '');
}

Elm.Debugger = null;
Elm.debuggerAttach = function(module, hotSwapState /* =undefined */) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime, hotSwapState);
      dispatchElmDebuggerInit();
      return wrappedModule.debuggedModule;
    }
  };
};

var EVENTS_PER_SAVE = 100;
var eventCounter = 0;

function dispatchElmDebuggerInit() {
  if (parent.window) {
    var dispatch = function() {
      parent.window.postMessage("elmDebuggerInit", window.location.origin);
    };
    if (parent.window.document.readyState === "complete") {
      dispatch();
    }
    else {
      parent.window.addEventListener("load", dispatch);
    }
  }
}

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var asyncCallbacks = [];
  var snapshots = [];
  var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
  var pauseTime = 0;
  var eventsUntilSnapshot = EVENTS_PER_SAVE;

  // runtime is the prototype of wrappedRuntime
  // so we can access all runtime properties too
  var wrappedRuntime = Object.create(runtime);
  wrappedRuntime.notify = wrapNotify;
  wrappedRuntime.runDelayed = wrapRunDelayed;

  // make a copy of the wrappedRuntime
  var assignedPropTracker = Object.create(wrappedRuntime);
  var debuggedModule = module.make(assignedPropTracker);

  // make sure the signal graph is actually a signal & extract the visual model
  if ( !('recv' in debuggedModule.main) ) {
    debuggedModule.main = Elm.Signal.make(runtime).constant(debuggedModule.main);
  }

  // The main module stores imported modules onto the runtime.
  // To ensure only one instance of each module is created,
  // we assign them back on the original runtime object.
  Object.keys(assignedPropTracker).forEach(function(key) {
    runtime[key] = assignedPropTracker[key];
  });

  var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
  var tracePath = tracePathInit(runtime, debuggedModule.main);

  snapshots.push(snapshotSignalGraph(signalGraphNodes));

  function wrapNotify(id, v) {
    var timestep = runtime.timer.now();

    if (programPaused) {
      // ignore async events generated while playing back
      // or user events while program is paused
      return false;
    }
    else {
      recordEvent(id, v, timestep);
      var changed = runtime.notify(id, v, timestep);
      snapshotOnCheckpoint();
      if (parent.window) {
        parent.window.postMessage("elmNotify", window.location.origin);
      }
      return changed;
    }
  };

  function wrapRunDelayed(func, delayMs) {
    if (programPaused) {
      // Don't push timers and such to the callback stack while we're paused.
      // It causes too many callbacks to be fired during unpausing.
      return 0;
    }
    var cbObj = { func:func, delayMs:delayMs, timerId:0, executed:false };
    var timerId = setTimeout(function() {
        cbObj.executed = true;
        func();
      }, delayMs);
    cbObj.timerId = timerId;
    asyncCallbacks.push(cbObj);
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    watchTracker.pushFrame();
    recordedEvents.push({ id:id, value:v, timestep:timestep });
    eventCounter += 1;
  }

  function clearAsyncCallbacks() {
    asyncCallbacks.forEach(function(timer) {
      if (!timer.executed) {
        clearTimeout(timer.timerId);
      }
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
    eventCounter = 0;
  }

  function getRecordedEventsLength() {
    return recordedEvents.length;
  }

  function getRecordedEventAt(i) {
    return recordedEvents[i];
  }

  function copyRecordedEvents() {
    return recordedEvents.slice();
  }

  function loadRecordedEvents(events) {
    recordedEvents = events.slice();
  }

  function clearSnapshots() {
    snapshots = [snapshotSignalGraph(signalGraphNodes)];
  }

  function getSnapshotAt(i) {
    var snapshotEvent = Math.floor(i / EVENTS_PER_SAVE);
    assert(snapshotEvent < snapshots.length && snapshotEvent >= 0,
           "Out of bounds index: " + snapshotEvent);
    return snapshots[snapshotEvent];
  }

  function snapshotOnCheckpoint() {
    if (eventsUntilSnapshot === 1) {
      snapshots.push(snapshotSignalGraph(signalGraphNodes));
      eventsUntilSnapshot = EVENTS_PER_SAVE;
    } else {
      eventsUntilSnapshot -= 1;
    }
  }

  function setPaused() {
    programPaused = true;
    clearAsyncCallbacks();
    tracePath.stopRecording();
    pauseTime = Date.now();
  }

  function setContinue(position) {
    programPaused = false;
    if (position > 0) {
      // If we're not unpausing at the head, then we need to dump the
      // events that are ahead of where we're continuing.
      var lastSnapshotPosition = Math.floor(position / EVENTS_PER_SAVE);
      eventsUntilSnapshot = EVENTS_PER_SAVE - (position % EVENTS_PER_SAVE);
      snapshots = snapshots.slice(0, lastSnapshotPosition + 1);

      var lastEventTime = recordedEvents[position-1].timestep;
      var scrubTime = runtime.timer.now() - lastEventTime;
      runtime.timer.addDelay(scrubTime);

      recordedEvents = recordedEvents.slice(0, position);
      tracePath.clearTracesAfter(position);
      eventCounter = position;
      executeCallbacks(asyncCallbacks, false);
    }
    tracePath.startRecording();
  }

  function getPaused() {
    return programPaused;
  }

  return {
    debuggedModule: debuggedModule,
    signalGraphNodes: signalGraphNodes,
    initialSnapshot: snapshotSignalGraph(signalGraphNodes),
    initialAsyncCallbacks: asyncCallbacks.slice(),
    // API functions
    clearAsyncCallbacks: clearAsyncCallbacks,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEventsLength: getRecordedEventsLength,
    getRecordedEventAt: getRecordedEventAt,
    copyRecordedEvents: copyRecordedEvents,
    loadRecordedEvents: loadRecordedEvents,
    clearSnapshots: clearSnapshots,
    getSnapshotAt: getSnapshotAt,
    snapshotOnCheckpoint: snapshotOnCheckpoint,
    getPaused: getPaused,
    setPaused: setPaused,
    setContinue: setContinue,
    tracePath: tracePath,
    watchTracker: watchTracker
  };
}

function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
  var currentEventIndex = 0;

  function resetProgram(position) {
    var closestSnapshot = debugModule.getSnapshotAt(position);
    debugModule.clearAsyncCallbacks();
    restoreSnapshot(debugModule.signalGraphNodes, closestSnapshot);
    redrawGraphics();
  }

  function restartProgram() {
    pauseProgram();
    resetProgram(0);
    debugModule.watchTracker.clear();
    debugModule.tracePath.clearTraces();
    debugModule.clearRecordedEvents();
    debugModule.clearSnapshots();
    debugModule.setContinue(0);
    executeCallbacks(debugModule.initialAsyncCallbacks, true);
  }

  function pauseProgram() {
    debugModule.setPaused();
    currentEventIndex = debugModule.getRecordedEventsLength();
  }

  function continueProgram() {
    if (debugModule.getPaused())
    {
      if (currentEventIndex === 0) {
        restartProgram();
        return;
      }
      var closestSnapshotIndex =
          Math.floor(currentEventIndex / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
      resetProgram(currentEventIndex);
      var continueIndex = currentEventIndex;
      currentEventIndex = closestSnapshotIndex;
      stepTo(continueIndex);
      debugModule.setContinue(currentEventIndex);
    }
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused();
      resetProgram();
    }

    if (index < 0 || index > getMaxSteps()) {
      throw "Index out of bounds: " + index;
    }

    if (index < currentEventIndex) {
      var closestSnapshotIndex = Math.floor(index / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
      resetProgram(index);
      currentEventIndex = closestSnapshotIndex;
    }

    while (currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      currentEventIndex += 1;
    }
  }

  function getMaxSteps() {
    return debugModule.getRecordedEventsLength();
  }

  function redrawGraphics() {
    var main = debugModule.debuggedModule.main
    for (var i = main.kids.length ; i-- ; ) {
      main.kids[i].recv(runtime.timer.now(), true, main.id);
    }
  }

  function getHotSwapState() {
    var continueIndex = currentEventIndex;
    if (!debugModule.getPaused()) {
      continueIndex = getMaxSteps();
    }
    return {
      recordedEvents: debugModule.copyRecordedEvents(),
      currentEventIndex: continueIndex
    };
  }

  function dispose() {
    var parentNode = runtime.node.parentNode;
    parentNode.removeChild(debugModule.tracePath.canvas);
    parentNode.removeChild(runtime.node);
  }

  if (hotSwapState) {
    // The problem is that we want to previous paused state. But
    // by the time JS reaches here, the old code has been swapped out
    // and the new modules are being generated. So we can ask the
    // debugging console what it thinks the pause state is and go
    // from there.
    var paused = top.debug.paused;
    debugModule.setPaused();
    debugModule.loadRecordedEvents(hotSwapState.recordedEvents);
    var index = getMaxSteps();
    eventCounter = 0;
    debugModule.tracePath.clearTraces();

    // draw new trace path
    debugModule.tracePath.startRecording();
    while(currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      eventCounter += 1;
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
      debugModule.snapshotOnCheckpoint();
      currentEventIndex += 1;
    }
    debugModule.tracePath.stopRecording();

    stepTo(hotSwapState.currentEventIndex);
    if (!paused) {
      debugModule.setContinue(hotSwapState.currentEventIndex);
    }
  }

  runtime.node.parentNode.appendChild(debugModule.tracePath.canvas);

  var elmDebugger = {
      restart: restartProgram,
      pause: pauseProgram,
      kontinue: continueProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo,
      getPaused: debugModule.getPaused,
      getHotSwapState: getHotSwapState,
      dispose: dispose,
      allNodes: debugModule.signalGraphNodes,
      watchTracker: debugModule.watchTracker
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime, signalGraphMain) {
  var List = Elm.List.make(runtime);
  var Signal = Elm.Signal.make(runtime);
  var tracePathNode = A2(Signal.lift, graphicsUpdate, signalGraphMain);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};
  var recordingTraces = true;

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      if (elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        tracePositions[id].push(pos);
      }
      else {
        tracePositions[id] = [pos];
      }
      if (tracePositions[id].length < eventCounter) {
        var padCount = eventCounter - tracePositions[id].length;
        var lastTracePosition = tracePositions[id][tracePositions[id].length - 1];
        for (var i = padCount; i--;) {
          tracePositions[id].push(lastTracePosition)
        }
      }
      assert(tracePositions[id].length === eventCounter,
             "We don't have a 1-1 mapping of trace positions to events");
    }
  }

  function graphicsUpdate(currentScene) {
    if (!recordingTraces) {
      return;
    }

    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
      ctx.lineWidth = 1;
      ctx.strokeStyle = "rgba(50, 50, 50, 0.4)";
      ctx.stroke();
    }

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  function stopRecording() {
    recordingTraces = false;
  }

  function startRecording() {
    recordingTraces = true;
  }

  function clearTracesAfter(position) {
    var newTraces = {};
    for (var id in tracePositions) {
      newTraces[id] = tracePositions[id].slice(0,position);
    }
    tracePositions = newTraces;
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces,
    clearTracesAfter: clearTracesAfter,
    stopRecording: stopRecording,
    startRecording: startRecording
  }
}

function executeCallbacks(callbacks, reexecute) {
  callbacks.forEach(function(timer) {
    if (reexecute || !timer.executed) {
      var func = timer.func;
      timer.executed = true;
      func();
    }
  });
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.pointerEvents = "none";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function snapshotSignalGraph(signalGraphNodes) {
  var nodeValues = [];

  signalGraphNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreSnapshot(signalGraphNodes, snapshot) {
  assert(signalGraphNodes.length == snapshot.length,
         "saved program state has wrong length");
  for (var i=0; i < signalGraphNodes.length; i++) {
    var node = signalGraphNodes[i];
    var state = snapshot[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenSignalGraph(nodes) {
  var nodesById = {};

  function addAllToDict(node) {
    nodesById[node.id] = node;
    node.kids.forEach(addAllToDict);
  }
  nodes.forEach(addAllToDict);

  var allNodes = Object.keys(nodesById).sort().map(function(key) {
    return nodesById[key];
  });
  return allNodes;
};

}());
<commit_msg>pauseTime is not needed<commit_after>(function() {
'use strict';

if (!window.location.origin) {
  window.location.origin =
      window.location.protocol + "//" +
      window.location.hostname +
      (window.location.port ? (':' + window.location.port) : '');
}

Elm.Debugger = null;
Elm.debuggerAttach = function(module, hotSwapState /* =undefined */) {
  return {
    make: function(runtime) {
      var wrappedModule = debugModule(module, runtime);
      Elm.Debugger = debuggerInit(wrappedModule, runtime, hotSwapState);
      dispatchElmDebuggerInit();
      return wrappedModule.debuggedModule;
    }
  };
};

var EVENTS_PER_SAVE = 100;
var eventCounter = 0;

function dispatchElmDebuggerInit() {
  if (parent.window) {
    var dispatch = function() {
      parent.window.postMessage("elmDebuggerInit", window.location.origin);
    };
    if (parent.window.document.readyState === "complete") {
      dispatch();
    }
    else {
      parent.window.addEventListener("load", dispatch);
    }
  }
}

function debugModule(module, runtime) {
  var programPaused = false;
  var recordedEvents = [];
  var asyncCallbacks = [];
  var snapshots = [];
  var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
  var eventsUntilSnapshot = EVENTS_PER_SAVE;

  // runtime is the prototype of wrappedRuntime
  // so we can access all runtime properties too
  var wrappedRuntime = Object.create(runtime);
  wrappedRuntime.notify = wrapNotify;
  wrappedRuntime.runDelayed = wrapRunDelayed;

  // make a copy of the wrappedRuntime
  var assignedPropTracker = Object.create(wrappedRuntime);
  var debuggedModule = module.make(assignedPropTracker);

  // make sure the signal graph is actually a signal & extract the visual model
  if ( !('recv' in debuggedModule.main) ) {
    debuggedModule.main = Elm.Signal.make(runtime).constant(debuggedModule.main);
  }

  // The main module stores imported modules onto the runtime.
  // To ensure only one instance of each module is created,
  // we assign them back on the original runtime object.
  Object.keys(assignedPropTracker).forEach(function(key) {
    runtime[key] = assignedPropTracker[key];
  });

  var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
  var tracePath = tracePathInit(runtime, debuggedModule.main);

  snapshots.push(snapshotSignalGraph(signalGraphNodes));

  function wrapNotify(id, v) {
    var timestep = runtime.timer.now();

    if (programPaused) {
      // ignore async events generated while playing back
      // or user events while program is paused
      return false;
    }
    else {
      recordEvent(id, v, timestep);
      var changed = runtime.notify(id, v, timestep);
      snapshotOnCheckpoint();
      if (parent.window) {
        parent.window.postMessage("elmNotify", window.location.origin);
      }
      return changed;
    }
  };

  function wrapRunDelayed(func, delayMs) {
    if (programPaused) {
      // Don't push timers and such to the callback stack while we're paused.
      // It causes too many callbacks to be fired during unpausing.
      return 0;
    }
    var cbObj = { func:func, delayMs:delayMs, timerId:0, executed:false };
    var timerId = setTimeout(function() {
        cbObj.executed = true;
        func();
      }, delayMs);
    cbObj.timerId = timerId;
    asyncCallbacks.push(cbObj);
    return timerId;
  }

  function recordEvent(id, v, timestep) {
    watchTracker.pushFrame();
    recordedEvents.push({ id:id, value:v, timestep:timestep });
    eventCounter += 1;
  }

  function clearAsyncCallbacks() {
    asyncCallbacks.forEach(function(timer) {
      if (!timer.executed) {
        clearTimeout(timer.timerId);
      }
    });
  }

  function clearRecordedEvents() {
    recordedEvents = [];
    eventCounter = 0;
  }

  function getRecordedEventsLength() {
    return recordedEvents.length;
  }

  function getRecordedEventAt(i) {
    return recordedEvents[i];
  }

  function copyRecordedEvents() {
    return recordedEvents.slice();
  }

  function loadRecordedEvents(events) {
    recordedEvents = events.slice();
  }

  function clearSnapshots() {
    snapshots = [snapshotSignalGraph(signalGraphNodes)];
  }

  function getSnapshotAt(i) {
    var snapshotEvent = Math.floor(i / EVENTS_PER_SAVE);
    assert(snapshotEvent < snapshots.length && snapshotEvent >= 0,
           "Out of bounds index: " + snapshotEvent);
    return snapshots[snapshotEvent];
  }

  function snapshotOnCheckpoint() {
    if (eventsUntilSnapshot === 1) {
      snapshots.push(snapshotSignalGraph(signalGraphNodes));
      eventsUntilSnapshot = EVENTS_PER_SAVE;
    } else {
      eventsUntilSnapshot -= 1;
    }
  }

  function setPaused() {
    programPaused = true;
    clearAsyncCallbacks();
    tracePath.stopRecording();
  }

  function setContinue(position) {
    programPaused = false;
    if (position > 0) {
      // If we're not unpausing at the head, then we need to dump the
      // events that are ahead of where we're continuing.
      var lastSnapshotPosition = Math.floor(position / EVENTS_PER_SAVE);
      eventsUntilSnapshot = EVENTS_PER_SAVE - (position % EVENTS_PER_SAVE);
      snapshots = snapshots.slice(0, lastSnapshotPosition + 1);

      var lastEventTime = recordedEvents[position-1].timestep;
      var scrubTime = runtime.timer.now() - lastEventTime;
      runtime.timer.addDelay(scrubTime);

      recordedEvents = recordedEvents.slice(0, position);
      tracePath.clearTracesAfter(position);
      eventCounter = position;
      executeCallbacks(asyncCallbacks, false);
    }
    tracePath.startRecording();
  }

  function getPaused() {
    return programPaused;
  }

  return {
    debuggedModule: debuggedModule,
    signalGraphNodes: signalGraphNodes,
    initialSnapshot: snapshotSignalGraph(signalGraphNodes),
    initialAsyncCallbacks: asyncCallbacks.slice(),
    // API functions
    clearAsyncCallbacks: clearAsyncCallbacks,
    clearRecordedEvents: clearRecordedEvents,
    getRecordedEventsLength: getRecordedEventsLength,
    getRecordedEventAt: getRecordedEventAt,
    copyRecordedEvents: copyRecordedEvents,
    loadRecordedEvents: loadRecordedEvents,
    clearSnapshots: clearSnapshots,
    getSnapshotAt: getSnapshotAt,
    snapshotOnCheckpoint: snapshotOnCheckpoint,
    getPaused: getPaused,
    setPaused: setPaused,
    setContinue: setContinue,
    tracePath: tracePath,
    watchTracker: watchTracker
  };
}

function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
  var currentEventIndex = 0;

  function resetProgram(position) {
    var closestSnapshot = debugModule.getSnapshotAt(position);
    debugModule.clearAsyncCallbacks();
    restoreSnapshot(debugModule.signalGraphNodes, closestSnapshot);
    redrawGraphics();
  }

  function restartProgram() {
    pauseProgram();
    resetProgram(0);
    debugModule.watchTracker.clear();
    debugModule.tracePath.clearTraces();
    debugModule.clearRecordedEvents();
    debugModule.clearSnapshots();
    debugModule.setContinue(0);
    executeCallbacks(debugModule.initialAsyncCallbacks, true);
  }

  function pauseProgram() {
    debugModule.setPaused();
    currentEventIndex = debugModule.getRecordedEventsLength();
  }

  function continueProgram() {
    if (debugModule.getPaused())
    {
      if (currentEventIndex === 0) {
        restartProgram();
        return;
      }
      var closestSnapshotIndex =
          Math.floor(currentEventIndex / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
      resetProgram(currentEventIndex);
      var continueIndex = currentEventIndex;
      currentEventIndex = closestSnapshotIndex;
      stepTo(continueIndex);
      debugModule.setContinue(currentEventIndex);
    }
  }

  function stepTo(index) {
    if (!debugModule.getPaused()) {
      debugModule.setPaused();
      resetProgram();
    }

    if (index < 0 || index > getMaxSteps()) {
      throw "Index out of bounds: " + index;
    }

    if (index < currentEventIndex) {
      var closestSnapshotIndex = Math.floor(index / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
      resetProgram(index);
      currentEventIndex = closestSnapshotIndex;
    }

    while (currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);

      currentEventIndex += 1;
    }
  }

  function getMaxSteps() {
    return debugModule.getRecordedEventsLength();
  }

  function redrawGraphics() {
    var main = debugModule.debuggedModule.main
    for (var i = main.kids.length ; i-- ; ) {
      main.kids[i].recv(runtime.timer.now(), true, main.id);
    }
  }

  function getHotSwapState() {
    var continueIndex = currentEventIndex;
    if (!debugModule.getPaused()) {
      continueIndex = getMaxSteps();
    }
    return {
      recordedEvents: debugModule.copyRecordedEvents(),
      currentEventIndex: continueIndex
    };
  }

  function dispose() {
    var parentNode = runtime.node.parentNode;
    parentNode.removeChild(debugModule.tracePath.canvas);
    parentNode.removeChild(runtime.node);
  }

  if (hotSwapState) {
    // The problem is that we want to previous paused state. But
    // by the time JS reaches here, the old code has been swapped out
    // and the new modules are being generated. So we can ask the
    // debugging console what it thinks the pause state is and go
    // from there.
    var paused = top.debug.paused;
    debugModule.setPaused();
    debugModule.loadRecordedEvents(hotSwapState.recordedEvents);
    var index = getMaxSteps();
    eventCounter = 0;
    debugModule.tracePath.clearTraces();

    // draw new trace path
    debugModule.tracePath.startRecording();
    while(currentEventIndex < index) {
      var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
      eventCounter += 1;
      runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
      debugModule.snapshotOnCheckpoint();
      currentEventIndex += 1;
    }
    debugModule.tracePath.stopRecording();

    stepTo(hotSwapState.currentEventIndex);
    if (!paused) {
      debugModule.setContinue(hotSwapState.currentEventIndex);
    }
  }

  runtime.node.parentNode.appendChild(debugModule.tracePath.canvas);

  var elmDebugger = {
      restart: restartProgram,
      pause: pauseProgram,
      kontinue: continueProgram,
      getMaxSteps: getMaxSteps,
      stepTo: stepTo,
      getPaused: debugModule.getPaused,
      getHotSwapState: getHotSwapState,
      dispose: dispose,
      allNodes: debugModule.signalGraphNodes,
      watchTracker: debugModule.watchTracker
  };

  return elmDebugger;
}

function Point(x, y) {
  this.x = x;
  this.y = y;

  this.translate = function(x, y) {
    return new Point(this.x + x, this.y + y);
  }

  this.equals = function(p) {
    return this.x == p.x && this.y == p.y;
  }
}

function tracePathInit(runtime, signalGraphMain) {
  var List = Elm.List.make(runtime);
  var Signal = Elm.Signal.make(runtime);
  var tracePathNode = A2(Signal.lift, graphicsUpdate, signalGraphMain);
  var tracePathCanvas = createCanvas();
  var tracePositions = {};
  var recordingTraces = true;

  function findPositions(currentScene) {
    var positions = {};
    function processElement(elem, offset) {
      if (elem.element.ctor == "Custom" && elem.element.type == "Collage")
      {
        List.map(F2(processForm)(offset))(elem.element.model.forms);
      }
      if (elem.props.debugTracePathId)
      {
        positions[elem.props.debugTracePathId] = new Point(offset.x, offset.y);
      }
    }

    function processForm(offset, form) {
      if (form.form.ctor == "FElement")
      {
        processElement(form.form._0, offset.translate(form.x, -form.y));
      }
    }

    processElement(currentScene, new Point(0, 0));
    return positions;
  }

  function appendPositions(positions) {
    for (var id in positions) {
      var pos = positions[id];
      if (tracePositions.hasOwnProperty(id)) {
        tracePositions[id].push(pos);
      }
      else {
        tracePositions[id] = [pos];
      }
      if (tracePositions[id].length < eventCounter) {
        var padCount = eventCounter - tracePositions[id].length;
        var lastTracePosition = tracePositions[id][tracePositions[id].length - 1];
        for (var i = padCount; i--;) {
          tracePositions[id].push(lastTracePosition)
        }
      }
      assert(tracePositions[id].length === eventCounter,
             "We don't have a 1-1 mapping of trace positions to events");
    }
  }

  function graphicsUpdate(currentScene) {
    if (!recordingTraces) {
      return;
    }

    var ctx = tracePathCanvas.getContext('2d');
    ctx.clearRect(0, 0, tracePathCanvas.width, tracePathCanvas.height);

    ctx.save();
    ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
    appendPositions(findPositions(currentScene));
    for (var id in tracePositions)
    {
      ctx.beginPath();
      var points = tracePositions[id];
      for (var i=0; i < points.length; i++)
      {
        var p = points[i];
        if (i == 0) {
          ctx.moveTo(p.x, p.y);
        }
        else {
          ctx.lineTo(p.x, p.y);
        }
      }
      ctx.lineWidth = 1;
      ctx.strokeStyle = "rgba(50, 50, 50, 0.4)";
      ctx.stroke();
    }

    ctx.restore();
  }

  function clearTraces() {
    tracePositions = {};
  }

  function stopRecording() {
    recordingTraces = false;
  }

  function startRecording() {
    recordingTraces = true;
  }

  function clearTracesAfter(position) {
    var newTraces = {};
    for (var id in tracePositions) {
      newTraces[id] = tracePositions[id].slice(0,position);
    }
    tracePositions = newTraces;
  }

  return {
    graphicsUpdate: graphicsUpdate,
    canvas: tracePathCanvas,
    clearTraces: clearTraces,
    clearTracesAfter: clearTracesAfter,
    stopRecording: stopRecording,
    startRecording: startRecording
  }
}

function executeCallbacks(callbacks, reexecute) {
  callbacks.forEach(function(timer) {
    if (reexecute || !timer.executed) {
      var func = timer.func;
      timer.executed = true;
      func();
    }
  });
}

function createCanvas() {
  var c = document.createElement('canvas');
  c.width = window.innerWidth;
  c.height = window.innerHeight;
  c.style.position = "absolute";
  c.style.top = "0";
  c.style.left = "0";
  c.style.pointerEvents = "none";
  return c;
}

function assert(bool, msg) {
  if (!bool) {
    throw "Assertion error: " + msg;
  }
}

function snapshotSignalGraph(signalGraphNodes) {
  var nodeValues = [];

  signalGraphNodes.forEach(function(node) {
    nodeValues.push({ value: node.value, id: node.id });
  });

  return nodeValues;
};

function restoreSnapshot(signalGraphNodes, snapshot) {
  assert(signalGraphNodes.length == snapshot.length,
         "saved program state has wrong length");
  for (var i=0; i < signalGraphNodes.length; i++) {
    var node = signalGraphNodes[i];
    var state = snapshot[i];
    assert(node.id == state.id, "the nodes moved position");

    node.value = state.value;
  }
}

function flattenSignalGraph(nodes) {
  var nodesById = {};

  function addAllToDict(node) {
    nodesById[node.id] = node;
    node.kids.forEach(addAllToDict);
  }
  nodes.forEach(addAllToDict);

  var allNodes = Object.keys(nodesById).sort().map(function(key) {
    return nodesById[key];
  });
  return allNodes;
};

}());
<|endoftext|>


<commit_before>/**
 * Created by cmiles on 8/9/2017.
 */

var loader_html = '
 \

 \

 \

 \

 \

 \

 \

 \

 \

';

$( document ).ready(function() {
	remove_init_form();
});

/*
 * When a sidebar item is clicked in mobile - let's make sure we push the sidebar back in
 */

$(document).on('on_pjax_complete',function(){
	if($('.ls-closed').length > 0){
		$('body').removeClass('overlay-open');
		$('.overlay').css("display", "none");
	}

	/*
	 * Form input focus event
	 */
    $('.form-control').focus(function () {
        $(this).parent().addClass('focused');
    });

    //On focusout event
    $('.form-control').focusout(function () {
        var $this = $(this);
        if ($this.parents('.form-group').hasClass('form-float')) {
            if ($this.val() == '') { $this.parents('.form-line').removeClass('focused'); }
        }
        else {
            $this.parents('.form-line').removeClass('focused');
        }
    });

    //On label click
    $('body').on('click', '.form-float .form-line .form-label', function () {
        $(this).parent().find('input').focus();
    });

    //Not blank form
    $('.form-control').each(function () {
        if ($(this).val() !== '') {
            $(this).parents('.form-line').addClass('focused');
        }
    });

    remove_init_form();

    remove_init_form();
});

function remove_init_form(){
    setTimeout(function(){
        $('.form-line').removeClass("focused");
    }, 10);
}

function modal (title, content, buttons) {
	$('#modal-buttons').html('');
	$('#modal-title').html(title);
	$('#modal-body').html(content);

	// <button type="button" class="btn btn-link waves-effect">SAVE CHANGES</button>
	if(buttons != '') {
		$('#modal-buttons').html(buttons);
	}
	$('#mdModal').modal('show');
}

function get_form_query_string(form_id){
	query_string = "";
	$('#' + form_id).find('input, select, textarea').each(function (key) {
		val = $(this).val();
		if (val == 'undefined') {
			val = '';
		}
		if($(this).attr('type') == "checkbox"){
			if (!$(this).is(':checked')) {
				val = 0;
			}
		}
		query_string = query_string + "&" + $(this).attr('id') + "=" + encodeURIComponent(val);
	});
	return query_string;
}

function save_config() {
	glass_settings = get_form_query_string("glass-settings-form");

	$.post( "/glass_settings_save", glass_settings, function( data ) {
		$( "#glass_settings_result" ).html( data );
	});
}

function notification(text){
	colorName = 'bg-black';
	animateEnter = 'animated fadeInDown';
	animateExit = 'animated fadeOutUp';
	var allowDismiss = true;

	$.notify({
			message: text
		},
		{
		type: colorName,
		allow_dismiss: allowDismiss,
		newest_on_top: true,
		timer: 1000,
		animate: {
			enter: animateEnter,
			exit: animateExit
		},
		template: '
' +
		'<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +
		' ' +
		'{1} ' +
		'{2}' +
		'
' +
		'

' +
		'
' +
		'' +
		'
'
	});
}

function change_favicon(img) {
	var favicon = document.querySelector('link[rel="shortcut icon"]');

	if (!favicon) {
		favicon = document.createElement('link');
		favicon.setAttribute('rel', 'shortcut icon');
		var head = document.querySelector('head');
		head.appendChild(favicon);
	}

	favicon.setAttribute('type', 'image/png');
	favicon.setAttribute('href', img);
}

$(document).on("click",".option_data",function() {
    var lease = $(this).attr("lease");
    if ( $("#" + lease).is(":visible") ) {
        $("#" + lease).hide();
        $(this).text('Show');
    } else if ( $("#" + lease).is(":hidden") ) {
        $("#" + lease).show();
        $(this).text('Hide');
    }
});

$(document).on("keypress","#lease_search_criteria", function(e) {
    if (e.which == 13) {
        $('#search_result').html(loader_html);
        $.post("/dhcp_lease_search", { search: $("#lease_search_criteria").val() }, function(result) {
            $("#search_result").html(result);

            if(typeof display_leases !== "undefined")
            	display_leases.destroy();

            display_leases = $('#display-leases').DataTable({
                dom: 'tip',
                responsive: true,
                "pageLength": 100,
                "aaSorting": []
            });
        });
    }
});<commit_msg>Fix indents<commit_after>/**
 * Created by cmiles on 8/9/2017.
 */

var loader_html = '
 \

 \

 \

 \

 \

 \

 \

 \

 \

';

$(document).ready(function () {
    remove_init_form();
});

/*
 * When a sidebar item is clicked in mobile - let's make sure we push the sidebar back in
 */

$(document).on('on_pjax_complete', function () {
    if ($('.ls-closed').length > 0) {
        $('body').removeClass('overlay-open');
        $('.overlay').css("display", "none");
    }

    /*
     * Form input focus event
     */
    $('.form-control').focus(function () {
        $(this).parent().addClass('focused');
    });

    //On focusout event
    $('.form-control').focusout(function () {
        var $this = $(this);
        if ($this.parents('.form-group').hasClass('form-float')) {
            if ($this.val() == '') {
                $this.parents('.form-line').removeClass('focused');
            }
        }
        else {
            $this.parents('.form-line').removeClass('focused');
        }
    });

    //On label click
    $('body').on('click', '.form-float .form-line .form-label', function () {
        $(this).parent().find('input').focus();
    });

    //Not blank form
    $('.form-control').each(function () {
        if ($(this).val() !== '') {
            $(this).parents('.form-line').addClass('focused');
        }
    });

    remove_init_form();

    remove_init_form();
});

function remove_init_form() {
    setTimeout(function () {
        $('.form-line').removeClass("focused");
    }, 10);
}

function modal(title, content, buttons) {
    $('#modal-buttons').html('');
    $('#modal-title').html(title);
    $('#modal-body').html(content);

    // <button type="button" class="btn btn-link waves-effect">SAVE CHANGES</button>
    if (buttons != '') {
        $('#modal-buttons').html(buttons);
    }
    $('#mdModal').modal('show');
}

function get_form_query_string(form_id) {
    query_string = "";
    $('#' + form_id).find('input, select, textarea').each(function (key) {
        val = $(this).val();
        if (val == 'undefined') {
            val = '';
        }
        if ($(this).attr('type') == "checkbox") {
            if (!$(this).is(':checked')) {
                val = 0;
            }
        }
        query_string = query_string + "&" + $(this).attr('id') + "=" + encodeURIComponent(val);
    });
    return query_string;
}

function save_config() {
    glass_settings = get_form_query_string("glass-settings-form");

    $.post("/glass_settings_save", glass_settings, function (data) {
        $("#glass_settings_result").html(data);
    });
}

function notification(text) {
    colorName = 'bg-black';
    animateEnter = 'animated fadeInDown';
    animateExit = 'animated fadeOutUp';
    var allowDismiss = true;

    $.notify({
            message: text
        },
        {
            type: colorName,
            allow_dismiss: allowDismiss,
            newest_on_top: true,
            timer: 1000,
            animate: {
                enter: animateEnter,
                exit: animateExit
            },
            template: '
' +
            '<button type="button" aria-hidden="true" class="close" data-notify="dismiss">×</button>' +
            ' ' +
            '{1} ' +
            '{2}' +
            '
' +
            '

' +
            '
' +
            '' +
            '
'
        });
}

function change_favicon(img) {
    var favicon = document.querySelector('link[rel="shortcut icon"]');

    if (!favicon) {
        favicon = document.createElement('link');
        favicon.setAttribute('rel', 'shortcut icon');
        var head = document.querySelector('head');
        head.appendChild(favicon);
    }

    favicon.setAttribute('type', 'image/png');
    favicon.setAttribute('href', img);
}

$(document).on("click", ".option_data", function () {
    var lease = $(this).attr("lease");
    if ($("#" + lease).is(":visible")) {
        $("#" + lease).hide();
        $(this).text('Show');
    } else if ($("#" + lease).is(":hidden")) {
        $("#" + lease).show();
        $(this).text('Hide');
    }
});

$(document).on("keypress", "#lease_search_criteria", function (e) {
    if (e.which == 13) {
        $('#search_result').html(loader_html);
        $.post("/dhcp_lease_search", {search: $("#lease_search_criteria").val()}, function (result) {
            $("#search_result").html(result);

            if (typeof display_leases !== "undefined")
                display_leases.destroy();

            display_leases = $('#display-leases').DataTable({
                dom: 'tip',
                responsive: true,
                "pageLength": 100,
                "aaSorting": []
            });
        });
    }
});<|endoftext|>


<commit_before>/*
 * When testing with webpack and ES6, we have to do some extra
 * things get testing to work right. Because we are gonna write test
 * in ES6 to, we have to compile those as well. That's handled in
 * karma.conf.js with the karma-webpack plugin. This is the entry
 * file for webpack test. Just like webpack will create a bundle.js
 * file for our client, when we run test, it well compile and bundle them
 * all here! Crazy huh. So we need to do some setup
 */
Error.stackTraceLimit = Infinity;
require('phantomjs-polyfill');
require('es6-promise');
require('es6-shim');
require('es7-reflect-metadata/dist/browser');

require('zone.js/dist/zone.js');
require('zone.js/dist/long-stack-trace-zone.js');
require('zone.js/dist/jasmine-patch.js');

// RxJS
require('rxjs/Rx');

const testing = require('angular2/testing');
const browser = require('angular2/platform/testing/browser');

testing.setBaseTestProviders(
    browser.TEST_BROWSER_PLATFORM_PROVIDERS,
    browser.TEST_BROWSER_APPLICATION_PROVIDERS
);

Object.assign(global, testing);

/*
 Ok, this is kinda crazy. We can use the the context method on
 require that webpack created in order to tell webpack
 what files we actually want to require or import.
 Below, context will be an function/object with file names as keys.
 using that regex we are saying look in ./src/app and ./test then find
 any file that ends with spec.js and get its path. By passing in true
 we say do this recursively
 */
const testContext = require.context('../src', true, /\.spec\.js/);

// get all the files, for each file, call the context function
// that will require the file and load it up here. Context will
// loop and require those spec files here
testContext.keys().map(requireContext);
<commit_msg>chore(build): Fixing spec bundle for Karma<commit_after>/*
 * When testing with webpack and ES6, we have to do some extra
 * things get testing to work right. Because we are gonna write test
 * in ES6 to, we have to compile those as well. That's handled in
 * karma.conf.js with the karma-webpack plugin. This is the entry
 * file for webpack test. Just like webpack will create a bundle.js
 * file for our client, when we run test, it well compile and bundle them
 * all here! Crazy huh. So we need to do some setup
 */
Error.stackTraceLimit = Infinity;
require('phantomjs-polyfill');
require('es6-promise');
require('es6-shim');
require('es7-reflect-metadata/dist/browser');

require('zone.js/dist/zone.js');
require('zone.js/dist/long-stack-trace-zone.js');
require('zone.js/dist/jasmine-patch.js');

// RxJS
require('rxjs/Rx');

const testing = require('angular2/testing');
const browser = require('angular2/platform/testing/browser');

testing.setBaseTestProviders(
    browser.TEST_BROWSER_PLATFORM_PROVIDERS,
    browser.TEST_BROWSER_APPLICATION_PROVIDERS
);

Object.assign(global, testing);

/*
 Ok, this is kinda crazy. We can use the the context method on
 require that webpack created in order to tell webpack
 what files we actually want to require or import.
 Below, context will be an function/object with file names as keys.
 using that regex we are saying look in ./src/app and ./test then find
 any file that ends with spec.js and get its path. By passing in true
 we say do this recursively
 */
var testContext = require.context('../src', true, /\.spec\.js/);

// get all the files, for each file, call the context function
// that will require the file and load it up here. Context will
// loop and require those spec files here
function requireAll(requireContext) {
    return requireContext.keys().map(requireContext);
}

const modules = requireAll(testContext);
// requires and returns all modules that match
<|endoftext|>