wealth_calc.html 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <html>
  2. <head>
  3. <meta charset="utf8">
  4. <link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.4.0/metricsgraphics.min.css" />
  5. <link rel="stylesheet" type="text/css" href="./bada55.css" />
  6. <style>
  7. body {
  8. padding: 10px;
  9. margin: 0px;
  10. font-family: 'Sans Serif';
  11. width: 100% !important;
  12. }
  13. li {
  14. padding-bottom: 2px;
  15. }
  16. input[type=edit] {
  17. background-color: #444444;
  18. color: #dddddd;
  19. }
  20. .left {
  21. float: left;
  22. display: block;
  23. }
  24. /* chart */
  25. .mg-x-axis text, .mg-y-axis text, .mg-histogram .axis text {
  26. fill: #fff;
  27. }
  28. .mg-markers text {
  29. fill: #fff;
  30. }
  31. text {
  32. fill: #fff;
  33. }
  34. .mg-active-datapoint {
  35. fill: #fff;
  36. }
  37. .mg-line6-color, .mg-line6-legend-color, .mg-hover-line6-color, .mg-area6-color {
  38. stroke: purple;
  39. fill: purple;
  40. }
  41. .mg-line7-color, .mg-line7-legend-color, .mg-hover-line7-color, .mg-area7-color {
  42. stroke: cyan;
  43. fill: cyan;
  44. }
  45. .mg-line8-color, .mg-line8-legend-color, .mg-hover-line8-color, .mg-area8-color {
  46. stroke: pink;
  47. fill: pink;
  48. }
  49. .mg-line9-color, .mg-line9-legend-color, .mg-hover-line9-color, .mg-area9-color {
  50. stroke: lime;
  51. fill: lime;
  52. }
  53. </style>
  54. <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
  55. <script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.1.0/moment.min.js"></script>
  56. <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
  57. <script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js"></script>
  58. <script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  59. <script src="http://cdnjs.cloudflare.com/ajax/libs/metrics-graphics/2.4.0/metricsgraphics.js"></script>
  60. <script type="text/javascript">
  61. function min(a,b) { return a < b ? a : b };
  62. function max(a,b) { return a >= b ? a : b };
  63. var nextID = 1;
  64. var HB = {};
  65. Handlebars.registerHelper('df', function(d, fmtStr) {
  66. if(typeof d == 'undefined' || d == null) {
  67. return 'Unknown';
  68. }
  69. var fs = typeof fmtStr == "string" ? fmtStr : "YYYY-MM-DD";
  70. return moment(d).format(fs);
  71. });
  72. Handlebars.registerHelper('$', function(n) {
  73. return parseFloat(n).toMoney()
  74. });
  75. $(function() {
  76. $('script[type="x-application/handlebars"]').each(function() {
  77. HB[$(this).attr('name')] = Handlebars.compile($(this).html());
  78. });
  79. });
  80. $(function() {
  81. $('.editor [json]').val(''); // fuck you firefox
  82. $('.editor [json="date"]').val(moment().format('YYYY-MM-DD'));
  83. })
  84. Number.prototype.toMoney = function() {
  85. // return this;
  86. var str = (this.toFixed(2)+'').toString();
  87. var x = 3 - str.length;
  88. if(x > 0) {
  89. str = (x > 1 ? '00' : '0') + str ;
  90. }
  91. return str.replace(/\B(\d\d)$/, '.$1').replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  92. };
  93. </script>
  94. <script>
  95. //var mon_interest = ARP / 12; // only works for /APR/, not "annual interest rate"
  96. /*
  97. A = periodic payment amount
  98. P = net principle
  99. i = periodic interest rate (mon_interest above)
  100. n = total number of payments (first payment is 30 days after the loan originates)
  101. p(t) = principle remaining at time t
  102. A = P(((i(1_i)^n) / ((1+i)^n - 1))
  103. = (Pi) / (1- (1+i)^-n)
  104. = P(i + ( i / ((1+i)^n - 1) )
  105. p(t) / P = 1 - ( ((1+it^t - 1)) / ((1+i)^n - 1) )
  106. calculation of mortgage stuff:
  107. 1) calculate the monthly payment amount using loan amount and interest
  108. 2) for each pay period,
  109. calc interest i_p = (remaining balance * monthly interest)
  110. calc payment principle = payment - i_p
  111. calc new blance = first balance - payment principle
  112. */
  113. function makeRow() {
  114. return $('<tr>' + Array.prototype.slice.apply(arguments).map(function(x) { return '<td>' + (x.toMoney && x.toMoney() || x) + '</td>' }).join(' ') + '</tr>');
  115. }
  116. function monthlyPayment(loan_amount, monthly_interest, num_payments) {
  117. return (loan_amount * monthly_interest) / (1 - Math.pow(1 + monthly_interest, -num_payments) );
  118. }
  119. function piRatios(balance, monthly_interest, payment_amount) {
  120. var i_p = balance * monthly_interest;
  121. return {
  122. principle: payment_amount - i_p,
  123. interest: i_p,
  124. new_balance: balance - payment_amount + i_p,
  125. };
  126. }
  127. function annualToMonthlyInterest(annual) {
  128. return Math.pow(1 + annual, 1/12) - 1;
  129. }
  130. function prettyPeriod(n) {
  131. var y = Math.floor(n / 12);
  132. var mons = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  133. var m = mons[n % 12];
  134. return m + ', ' + y;
  135. }
  136. function whyIsntThisBuiltIn(qstr) {
  137. return _.object(qstr.split('&').map(function(c) {
  138. return c.split('=').map(decodeURIComponent);
  139. }));
  140. }
  141. // initialize any values given in the url
  142. $(function(){
  143. var d = _.mapObject(whyIsntThisBuiltIn(window.location.hash.replace(/^#/, '')), parseFloat);
  144. for(var k in d) {
  145. $('[data-json="'+k+'"]').val(d[k]);
  146. }
  147. });
  148. function grabData(parent) {
  149. var data = {};
  150. var formatters = {
  151. string: function(x) { return x },
  152. pct: function(x) { return parseFloat(x) * .01 },
  153. amort: function(x) { return parseFloat(x) / 12 },
  154. num: parseFloat,
  155. };
  156. parent.find('[data-json]').each(function() {
  157. var n = $(this);
  158. var name = n.attr('data-json');
  159. var val = n.val();
  160. var fmt = n.attr('fmt') || 'num';
  161. console.log(name, val);
  162. data[name] = formatters[fmt](val);
  163. });
  164. return data;
  165. };
  166. function mkChart(sel, title, data) {
  167. var legend = [];
  168. var d2 = data.map(function(ds) {
  169. legend.push(ds[0]);
  170. return ds[1].map(function(x, k) {
  171. return {
  172. period: k,
  173. value: x,
  174. }
  175. });
  176. });
  177. console.log(d2);
  178. // d2 = MG.convert.date(d2, 'date');
  179. MG.data_graphic({
  180. title: title,
  181. data: d2,
  182. width: 1100,
  183. height: 500,
  184. area: false,
  185. right: 100,
  186. left: 70,
  187. target: sel,
  188. x_accessor: 'period',
  189. y_accessor: 'value',
  190. yax_units: '$',
  191. xax_count: 12,
  192. decimals: 0,
  193. legend: legend,
  194. });
  195. // var ctx = $(sel)[0].getContext("2d");
  196. // var chart = new Chart(ctx).Line(chartData, opts);
  197. //
  198. //
  199. }
  200. $(function() {
  201. $('.go').click(function(e) {
  202. e.preventDefault();
  203. calcTable('mortgage');
  204. // calcAppartment('appartment');
  205. });
  206. });
  207. function initSlider(parent) {
  208. parent = $(parent).addClass('slider');
  209. var value = $('<div></div>').addClass('value');
  210. var slide = $('<div></div>').addClass('slide');
  211. var bar = $('<div></div>').addClass('bar');
  212. var handle = $('<div></div>').addClass('handle');
  213. var value = $('<div></div>').addClass('value');
  214. slide.append([bar, handle, value]);
  215. parent.append([slide, value]);
  216. var clickOffset = 0;
  217. var range = bar.width() - handle.width();
  218. function onDragMove(e) {
  219. var x = e.pageX - slide.offset().left - clickOffset;
  220. x = min(max(x, 0), range);
  221. handle.css({left: x + 'px'});
  222. value.html((x*100/range).toFixed() + '%');
  223. parent.attr('percent', x/range);
  224. parent.trigger('change', x/range, value);
  225. }
  226. function startDrag(e) {
  227. clickOffset = e.pageX - handle.offset().left;
  228. $(document).on('mousemove', onDragMove);
  229. $(document).on('mouseup', stopDrag);
  230. }
  231. function stopDrag() {
  232. $(document).off('mousemove', onDragMove);
  233. }
  234. handle.on('mousedown', startDrag);
  235. };
  236. function rangeSlider(elem, minV, maxV) {
  237. elem = $(elem);
  238. initSlider(elem);
  239. elem.on('change', function(e, pct, disp) {
  240. var x = (minV + (pct * (maxV - minV))).round();
  241. elem.attr('value', x);
  242. disp(x);
  243. elem.trigger('updated');
  244. });
  245. }
  246. var fc = $('.figures');
  247. var figures = {};
  248. function figure(name) {
  249. // find or create the dom node
  250. var n = fc.find('[fig="'+name+'"]');
  251. if(!n) {
  252. n = $('<div fig="'+name'"></div>');
  253. n.appendTo(fc);
  254. }
  255. // find or create the value
  256. if(figures[name] == undefined) figures[name] = 0;
  257. rangeSlider(n, 1000, 50000);
  258. return {
  259. change: function(fn) {
  260. n.on('changed', function() {
  261. fn(parseFloat(n.attr('value')));
  262. });
  263. }
  264. }
  265. };
  266. function sum(name, a, b) {
  267. var av = 0, bv = 0, cf;
  268. function changed() {
  269. figures[name] = av + bv;
  270. cf(av + bv);
  271. }
  272. a.change(function(aval) { av = aval; changed() });
  273. b.change(function(bval) { bv = aval; changed() });
  274. return {
  275. changed: function(fn) {
  276. cf = fn;
  277. }
  278. }
  279. }
  280. $(function() {
  281. // rangeSlider('.land', 5000, 50000);
  282. });
  283. </script>
  284. <style>
  285. .slider {
  286. width: 300px;
  287. padding: 5px;
  288. padding-right: 80px;
  289. background-color: purple;
  290. position: relative;
  291. }
  292. .slider .slide {
  293. position: relative;
  294. width: 100%;
  295. height: 20px;
  296. }
  297. .slider .slide .bar {
  298. background-color: black;
  299. position: absolute;
  300. top: 5px;
  301. left: 0px;
  302. width: 100%;
  303. height: 10px;
  304. }
  305. .slider .slide .handle {
  306. width: 20px;
  307. height: 20px;
  308. background-color: green;
  309. position: absolute;
  310. left: 0px;
  311. top: 0px;
  312. }
  313. .slider .value {
  314. width: 65px;
  315. height: 20px;
  316. background-color: green;
  317. font-size: 16px;
  318. font-family: 'Sans Serif';
  319. text-align: right;
  320. position: absolute;
  321. right: 0px;
  322. top: 0px;
  323. color: white;
  324. padding: 5px 5px;
  325. }
  326. </style>
  327. </head>
  328. <body>
  329. <!--div class="slider"></div-->
  330. <div class="figures"><div>
  331. </body>
  332. </html>