PageRenderTime 54ms CodeModel.GetById 28ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/public/javascripts/afford_content.js

https://bitbucket.org/keithelliott/simplecalc_web
JavaScript | 660 lines | 551 code | 105 blank | 4 comment | 2 complexity | 626cf636d1ffcad5f78a66c85f0db32f MD5 | raw file
  1define([
  2	'jquery',     
  3  'underscore', 
  4  'backbone',
  5	'backbone.marionette',
  6	'accounting',
  7	'afford',
  8	'text!templates/afford_income_input.html',
  9	'text!templates/afford_expense.html',
 10	'text!templates/afford_ratios.html',
 11	'text!templates/afford_loan.html'
 12	], function($,_,Backbone, Marionette, accounting, AffordCalc, income_input, afford_expense, afford_ratio, loan_tmpl){
 13		
 14		var AffordLayout = {};
 15		
 16		var ModalRegion = Backbone.Marionette.Region.extend({
 17			el: "#modal",
 18			
 19			constructor: function(){
 20			    _.bindAll(this);
 21			    Backbone.Marionette.Region.prototype.constructor.apply(this, arguments);
 22			    this.on("view:show", this.showModal, this);
 23			  },
 24
 25			  getEl: function(selector){
 26			    var $el = $(selector);
 27			    $el.on("hidden", this.close);
 28			    return $el;
 29			  },
 30
 31			  showModal: function(view){
 32			    view.on("close", this.hideModal, this);
 33			    this.$el.modal('show');
 34			  },
 35
 36			  hideModal: function(){
 37			    this.$el.modal('hide');
 38			  }
 39		});
 40		
 41		var Afford_Layout = Backbone.Marionette.Layout.extend({
 42			template: '#afford_template',
 43			
 44			regions: {
 45				'sidebar' : '#demo_sidebar',
 46				'income_input' : '#afford_income_input',
 47				'income_table' : '#income_table',
 48				'income_subtotal' : '#income_total',
 49				'expense_input' : '#afford_expense',
 50				'expense_table' : '#expense_table',
 51				'expense_subtotal' : '#expense_total',
 52				'debt_view' : '#afford_credit',
 53				'loan_view' : '#afford_loan',
 54				'outputs' : '#afford_outputs'
 55			},
 56			
 57			display: function(){
 58				
 59				var sidebar = new AffordLayout.Sidebar();
 60				var income_list = new AffordLayout.IncomeList();
 61				var income_input = new AffordLayout.income_input({collection: income_list});
 62				var income_table = new AffordLayout.income_table({collection: income_list});
 63				var income_subtotal = new AffordLayout.income_subtotal({collection:income_list});
 64
 65				var expense_list = new AffordLayout.ExpenseList();
 66				var expense_input = new AffordLayout.expense_input({collection:expense_list});
 67				var expense_table = new AffordLayout.expense_table({collection: expense_list});
 68				var expense_subtotal = new AffordLayout.expense_subtotal({collection:expense_list});
 69
 70				var debt = new AffordLayout.DebtRatios();
 71				var debtView = new AffordLayout.debt_view({model:debt});
 72
 73				var loan = new AffordLayout.LoanAssumptions();
 74				var loan_view = new AffordLayout.LoanView({model:loan});
 75				var outputs = new AffordLayout.OutputsModel();
 76				var outputs_view = new AffordLayout.OutputsView({income_list: income_list, expense_list: expense_list,
 77					debt: debt, loan: loan, model:outputs});
 78					
 79				var display_outputs = function(){
 80					console.log('clearing income list');
 81					var affordCalc = new AffordCalc();
 82					var totalMonthlyIncome = 0;
 83					income_list.models.forEach(function(income){
 84						console.log('income:' + income.get('amount'));
 85						var moAmount = affordCalc.convertMonthly(income.get('amount'));
 86						console.log('adding ' + income.get('name') + ': ' + moAmount + ' to list');
 87						totalMonthlyIncome = totalMonthlyIncome + moAmount;
 88					});
 89					
 90					var totalMonthlyDebt = 0;
 91					expense_list.models.forEach(function(expense){
 92						var moAmount = expense.get('amount');
 93						console.log('adding ' + expense.get('name') + ': ' + moAmount + ' to list');
 94						totalMonthlyDebt = totalMonthlyDebt + accounting.unformat(moAmount);
 95					});
 96					
 97					var monthlyPaymentAggressive = affordCalc.calculateMonthlyPayment({
 98						totalMonthlyIncome : totalMonthlyIncome,
 99						totalMonthlyDebt : totalMonthlyDebt,
100						debtToIncomeRatio : debt.get('DebtToIncomeAggressive') / 100,
101						propertyTaxes : accounting.unformat(loan.get('tax_rate')),
102						homeOwnersFeesYrly : accounting.unformat(loan.get('homeowners_amount'))
103					});
104					
105					var monthlyPaymentConservative = affordCalc.calculateMonthlyPayment({
106						totalMonthlyIncome : totalMonthlyIncome,
107						totalMonthlyDebt : totalMonthlyDebt,
108						debtToIncomeRatio : debt.get('DebtToIncomeConservative') / 100,
109						propertyTaxes : accounting.unformat(loan.get('tax_rate')),
110						homeOwnersFeesYrly : accounting.unformat(loan.get('homeowners_amount'))
111					});
112					
113					var maxLoanAmount = affordCalc.calculateLoanAmount({
114						payment: monthlyPaymentAggressive,
115						interestRate: loan.get('interest_rate')/100,
116						term: accounting.unformat(loan.get('term'))
117					});
118					
119					var minLoanAmount = affordCalc.calculateLoanAmount({
120						payment: monthlyPaymentConservative,
121						interestRate: loan.get('interest_rate')/100,
122						term: accounting.unformat(loan.get('term'))
123					});
124					
125					outputs.set({'loan_aggressive' : accounting.formatMoney(maxLoanAmount),
126						'loan_conservative': accounting.formatMoney(minLoanAmount),
127						'payment_aggressive': accounting.formatMoney(monthlyPaymentAggressive),
128						'payment_conservative': accounting.formatMoney(monthlyPaymentConservative),
129						'taxes': accounting.formatMoney(loan.get('tax_rate') / 12),
130						'total_aggressive': accounting.formatMoney(monthlyPaymentAggressive + accounting.unformat(loan.get('tax_rate'))/12),
131						'total_conservative' : accounting.formatMoney(monthlyPaymentConservative + accounting.unformat(loan.get('tax_rate'))/12)});
132					
133				};
134				
135				income_list.on('change', display_outputs);
136				income_list.on('add', display_outputs);
137				income_list.on('remove', display_outputs);
138				expense_list.on('change',display_outputs);
139				expense_list.on('add',display_outputs);
140				expense_list.on('remove',display_outputs);
141				debt.on('change',display_outputs);
142				loan.on('change', display_outputs);
143				
144				income_input.on('income:focus', function(){
145					sidebar.focus_income();
146				});
147
148				debtView.on('ratio:focus', function(expense){
149					sidebar.focus_ratio();
150				});
151
152				loan_view.on('loan:focus', function(expense){
153					sidebar.focus_loan();
154				});
155
156				expense_input.on('expense:focus', function(expense){
157					sidebar.focus_expense();
158				});
159		
160				this.sidebar.show(sidebar);
161				this.income_input.show(income_input);
162				this.income_table.show(income_table);
163				this.income_subtotal.show(income_subtotal);
164				this.expense_input.show(expense_input);
165				this.expense_table.show(expense_table);
166				this.expense_subtotal.show(expense_subtotal);
167				this.debt_view.show(debtView);
168				this.loan_view.show(loan_view);
169				this.outputs.show(outputs_view);
170			}
171
172		});
173		
174		AffordLayout.Afford = Afford_Layout;
175		
176		var sidebar = Backbone.Marionette.ItemView.extend({
177			template: '#affordMenu',
178			
179			events: {
180				'click #income_menu a' : 'focus_income',
181				'click #expense_menu a': 'focus_expense',
182				'click #ratio_menu a': 'focus_ratio',
183				'click #loan_menu a': 'focus_loan',
184				'click #outputs_menu a': 'focus_outputs'
185			},
186			
187			ui:{
188				income_menu : '#income_menu',
189				expense_menu : '#expense_menu',
190				ratio_menu : '#ratio_menu',
191				loan_menu : '#loan_menu',
192				outputs_menu : '#outputs_menu'
193			},
194			
195			focus_income: function(e){			
196				this.ui.income_menu.siblings().removeClass('active');
197				this.ui.income_menu.addClass('active');
198				return false;
199			},
200			
201			focus_expense: function(e){
202				this.ui.expense_menu.siblings().removeClass('active');
203				this.ui.expense_menu.addClass('active');
204				return false;
205			},
206			focus_ratio: function(e){
207				this.ui.ratio_menu.siblings().removeClass('active');
208				this.ui.ratio_menu.addClass('active');
209				return false;
210			},
211			focus_loan: function(e){
212				this.ui.loan_menu.siblings().removeClass('active');
213				this.ui.loan_menu.addClass('active');
214				return false;
215			},
216			focus_outputs: function(e){
217				this.ui.outputs_menu.siblings().removeClass('active');
218				this.ui.outputs_menu.addClass('active');
219				return false;
220			}
221		});		
222		
223		var income_input = Backbone.Marionette.ItemView.extend({
224			template: income_input,
225			
226			events:{
227				'click #income_submit' : 'addRow',
228				'change #income_name' : 'nameAdded',
229				'change #income_amount' : 'amountAdded',
230				'change #income_frequency': 'freq_changed'
231			},
232			
233			triggers: {
234				'mouseover #income_div': 'income:focus'
235			},
236			
237			ui: {
238				income_amount: '#income_amount',
239				income_frequency: 'select#income_frequency',
240				income_name : '#income_name',
241				income_submit : '#income_submit'
242			},
243			
244			freq_changed: function(e){
245				this.ui.income_amount.attr('placeholder',this.ui.income_frequency.val() + ' Amount');
246			},
247			
248			nameAdded: function(e){
249				console.log(this.ui.income_name.val() + ' added');
250			},
251			
252			amountAdded: function(e){
253				console.log(this.ui.income_amount.val() + ' added');
254			},
255			
256			addRow: function(e){
257				e.preventDefault();
258				console.log('new income added');
259				var amount = parseFloat(this.ui.income_amount.val());
260				if (this.ui.income_frequency.val() === 'Monthly'){
261					amount = amount * 12;
262				} 
263				var income = new Income({'name': this.ui.income_name.val(),
264				'amount': accounting.formatMoney(amount)});
265				
266				this.ui.income_name.attr('value','');
267				this.ui.income_amount.attr('value', '');
268				this.collection.add(income);
269			}
270		});
271		
272		var Income = Backbone.Model.extend();
273		
274		var IncomeList = Backbone.Collection.extend({
275			model: Income
276		});
277		
278		var IncomeView = Backbone.Marionette.ItemView.extend({
279			template: '#income_item_view',
280			tagName: 'tr',
281			events: {
282				'click tr:hover button.income_edit' : 'edit',
283				'click tr:hover button.income_edit_done': 'done'
284			},
285			
286			ui:{
287				remove_btn: 'tr:hover button.income_remove',
288				edit_btn: 'tr:hover button.income_edit',
289				done_btn: 'tr:hover button.income_edit_done',
290				text_input: 'tr:hover span.txt_input',
291				text_cell: 'tr:hover span.txt_cell',
292				name_cell: 'tr:hover .name_cell',
293				amount_cell: 'tr:hover .amount_cell'
294			},
295			
296			triggers:{
297				'click tr:hover button.income_remove' : 'remove:model'
298			},
299			
300			edit: function(e){
301				e.preventDefault();
302				console.log('edit called');
303				this.ui.text_input.removeClass('hide');
304				this.ui.text_cell.addClass('hide');
305				this.ui.edit_btn.show();
306				this.ui.done_btn.hide();
307				
308				return false;
309			},
310			
311			done: function(e){
312				e.preventDefault();
313				console.log('done called');
314				this.ui.text_input.addClass('hide');
315				this.ui.text_cell.removeClass('hide');
316				this.ui.edit_btn.hide();
317				this.ui.done_btn.show();
318				this.update(e);
319				return false;
320			},
321			
322			update: function(e){
323				e.preventDefault();
324				console.log('update called');
325				this.model.set({'name': this.ui.name_cell.val(),
326				'amount': accounting.formatMoney(this.ui.amount_cell.val()) });
327				this.render();
328			}
329		});
330		
331		var IncomeCollectionView = Backbone.Marionette.CompositeView.extend({
332			template: '#income_list_view',
333			id:'incomelist',
334			tagName: 'table',
335			className: 'table table-striped table-condensed table-hover table-bordered',
336			itemView: IncomeView,	
337			initialize: function(){
338				this.bindTo(this, 'itemview:remove:model', this.modelDeleted);
339			},
340			
341			modelDeleted: function(view){
342				console.log('removed view');
343				this.collection.remove(view.model);
344				this.display();
345			},
346			
347			display: function(model){
348				console.log('displaying income');
349				this.render();
350			},
351			
352			appendHtml: function(collectionView, itemView){
353				  console.log('collection has ' + collectionView.collection.length + ' items');
354			    collectionView.$("tbody").append(itemView.el);
355			}
356		});
357	
358		var income_subtotal = Backbone.Marionette.ItemView.extend({
359			template: '#income_total',
360			initialize: function(){
361				this.bindTo(this.collection, "add", this.collectionChanged);
362				this.bindTo(this.collection, "remove", this.collectionChanged);
363				this.bindTo(this.collection, "change", this.collectionChanged);
364			},
365			
366			collectionChanged: function(model){
367				console.log('collection changed');
368				var total = 0;
369				this.collection.models.forEach(function(val){
370					total = total + accounting.unformat(val.get('amount'));
371				});
372	
373				console.log('total: ' + total);
374				$(this.el).html(accounting.formatMoney(total));
375			}
376		});
377				
378		//expenses
379			var expense_input = Backbone.Marionette.ItemView.extend({
380				template: afford_expense,
381
382				events:{
383					'click #expense_submit' : 'addRow',
384					'change #expense_name' : 'nameAdded',
385					'change #expense_amount' : 'amountAdded',
386					'change #expense_frequency': 'freq_changed'
387				},
388				
389				ui:{
390					expense_amount: '#expense_amount',
391					expense_frequency: 'select#expense_frequency',
392					expense_name: '#expense_name',
393					expense_amount: '#expense_amount'
394				},
395				
396				triggers:{
397					'mouseover #expense_div' : 'expense:focus'
398				},
399				
400				freq_changed: function(e){
401					this.ui.expense_amount.attr('placeholder',this.ui.expense_frequency.val() + ' Amount');
402				},
403				
404				nameAdded: function(e){
405					console.log(this.ui.expense_name.val() + ' added');
406				},
407
408				amountAdded: function(e){
409					console.log(this.ui.expense_amount.val() + ' added');
410				},
411
412				addRow: function(e){
413					e.preventDefault();
414					console.log('new expense added');
415					var amount = parseFloat(this.ui.expense_amount.val());
416					if (this.ui.expense_frequency.val() === 'Yearly'){
417						amount = amount / 12;
418					} 
419					var expense = new Expense({'name': this.ui.expense_name.val(),
420					'amount': accounting.formatMoney(amount)});
421
422					this.ui.expense_name.attr('value','');
423					this.ui.expense_amount.attr('value', '');
424					this.collection.add(expense);
425				}
426			});
427
428			var Expense = Backbone.Model.extend();
429
430			var ExpenseList = Backbone.Collection.extend({
431				model: Expense
432			});
433
434			var ExpenseView = Backbone.Marionette.ItemView.extend({
435				template: '#expense_item_view',
436				tagName: 'tr',
437				events: {
438					'click tr:hover button.expense_edit' : 'edit',
439					'click tr:hover button.expense_edit_done': 'done'
440				},
441				
442				ui: {
443						remove_btn: 'tr:hover button.expense_remove',
444						edit_btn: 'tr:hover button.expense_edit',
445						done_btn: 'tr:hover button.expense_edit_done',
446						text_input: 'tr:hover span.txt_input',
447						text_cell: 'tr:hover span.txt_cell',
448						name_cell: 'tr:hover .name_cell',
449						amount_cell: 'tr:hover .amount_cell'
450				},
451				
452				triggers:{
453					'click tr:hover button.expense_remove' : 'remove:model'
454				},
455
456				edit: function(e){
457					e.preventDefault();
458					console.log('edit called');
459					this.ui.text_input.removeClass('hide');
460					this.ui.text_cell.addClass('hide');
461					this.ui.done_btn.show();
462					this.ui.edit_btn.hide();
463					return false;
464				},
465				
466				done: function(e){
467					e.preventDefault();
468					console.log('edit called');
469					this.ui.text_input.addClass('hide');
470					this.ui.text_cell.removeClass('hide');
471					this.ui.done_btn.hide();
472					this.ui.edit_btn.show();
473				},
474				
475				update: function(e){
476					e.preventDefault();
477					console.log('update called');
478					this.model.set({'name': this.ui.name_cell.val(),
479					'amount': accounting.formatMoney(this.ui.amount_cell.val()) });
480					this.render();
481				}
482			});
483
484			var ExpenseCollectionView = Backbone.Marionette.CompositeView.extend({
485				template: '#expense_list_view',
486				id:'expenselist',
487				tagName: 'table',
488				className: 'table table-striped table-condensed table-hover table-bordered',
489				itemView: ExpenseView,
490
491				initialize: function(){
492					this.bindTo(this, 'itemview:remove:model', this.modelDeleted);
493				},
494				
495				modelDeleted: function(view){
496					console.log('removed view');
497					this.collection.remove(view.model);
498					this.display();
499				},
500				
501				display: function(model){
502					console.log('displaying income');
503					this.render();
504				},
505				
506				appendHtml: function(collectionView, itemView){
507				    collectionView.$("tbody").append(itemView.el);
508				}
509			});
510			
511			var expense_subtotal = Backbone.Marionette.ItemView.extend({
512				template: '#expense_total',
513				initialize: function(){
514					this.bindTo(this.collection, "add", this.collectionChanged);
515					this.bindTo(this.collection, "remove", this.collectionChanged);
516					this.bindTo(this.collection, "change", this.collectionChanged);
517				},
518
519				collectionChanged: function(model){
520					console.log('collection changed');
521					var total = 0;
522					this.collection.models.forEach(function(val){
523						total = total + accounting.unformat(val.get('amount'));
524					});
525
526					console.log('total: ' + total);
527					$(this.el).html(accounting.formatMoney(total));
528				}
529			});
530		
531		// ratios
532			var DebtRatios = Backbone.Model.extend();
533			var DebtView = Backbone.Marionette.ItemView.extend({
534				template: afford_ratio,
535				
536				events:{
537					'change #d-to-i-conservative' : 'updateDtoIConservative',
538					'change #d-to-i-aggressive' : 'updateDtoIAggressive'
539				},
540				
541				triggers:{
542					'mouseover #ratio_div' : 'ratio:focus'
543				},
544				
545				ui:{
546					d_to_i_conservative: '#d-to-i-conservative',
547					d_to_i_aggressive: '#d-to-i-aggressive'
548				},
549				
550				updateDtoIConservative: function(e){
551					this.model.set({'DebtToIncomeConservative' : this.ui.d_to_i_conservative.val()});
552					console.log('conservative ratio updated to: ' + this.model.get('DebtToIncomeConservative'));
553				},
554				
555				updateDtoIAggressive: function(e){
556					this.model.set({'DebtToIncomeAggressive' : this.ui.d_to_i_aggressive.val()});
557					console.log('aggressive ratio updated to:' + this.model.get('DebtToIncomeAggressive'));
558				}
559			});
560			
561		// loan assumptions
562		var LoanAssumptions = Backbone.Model.extend();
563		var LoanView = Backbone.Marionette.ItemView.extend({
564			template: loan_tmpl,
565			
566			events:{
567				'change input#interest_rate': 'update_rate',
568				'change input#tax_rate': 'update_tax',
569				'change input#homeowners_amount' : 'update_homeowners',
570				'change input#loan_term' : 'update_term'
571			},
572			
573			triggers:{
574				'mouseover #loan_div': 'loan:focus'
575			},
576			
577			ui: {
578				interest_rate: '#interest_rate',
579				loan_term: '#loan_term',
580				tax_rate: '#tax_rate',
581				homeowners: '#homeowners_amount'
582			},
583			
584			update_rate: function(){
585				this.model.set({'interest_rate': this.ui.interest_rate.val()});
586				console.log('interest rate updated');
587			},
588			
589			update_term: function(){
590				this.model.set({'term': this.ui.loan_term.val()});
591				console.log('term updated');
592			},
593			
594			update_tax: function(){
595				this.model.set({'tax_rate': this.ui.tax_rate.val()});
596				console.log('tax rate updated');
597			},
598			
599			update_homeowners : function(){
600				this.model.set({'homeowners_amount': this.ui.homeowners.val()});
601				console.log('homeowners updated');
602			}
603		});
604		
605		//Outputs
606		var OutputsModel = Backbone.Model.extend();
607		var OutputsView = Backbone.Marionette.ItemView.extend({
608			template: '#afford_output_tmpl',
609			
610			initialize: function(options){					
611					this.bindTo(this.model, 'change', this.display);
612			},
613			
614			ui:{
615				loan_aggressive: '#afford_aggressive_loan',
616				loan_conservative: '#afford_conservative_loan',
617				aggressive_pmt: 'td#afford_aggressive_pmt',
618				conservative_pmt: '#afford_conservative_pmt',
619				aggressive_taxes: '#afford_aggressive_taxes',
620				conservative_taxes: '#afford_conservative_taxes',
621				aggressive_total: '#afford_aggressive_total',
622				conservative_total: '#afford_conservative_total'
623			},
624			
625			display: function(){
626				console.log('outputs displayed');
627
628			 	this.ui.loan_aggressive.html(this.model.get('loan_aggressive'));
629				this.ui.loan_conservative.html(this.model.get('loan_conservative'));
630			  this.ui.aggressive_pmt.html(this.model.get('payment_aggressive'));
631				this.ui.conservative_pmt.html(this.model.get('payment_conservative'));
632				this.ui.aggressive_taxes.html(this.model.get('taxes'));
633				this.ui.conservative_taxes.html(this.model.get('taxes'));
634				this.ui.aggressive_total.html(this.model.get('total_aggressive'));
635				this.ui.conservative_total.html(this.model.get('total_conservative'));
636
637			}	
638		});
639			
640		AffordLayout.Sidebar = sidebar;
641		AffordLayout.income_input = income_input;
642		AffordLayout.Income = Income;
643		AffordLayout.IncomeList = IncomeList;
644		AffordLayout.IncomeView = IncomeView;
645		AffordLayout.income_table = IncomeCollectionView;
646		AffordLayout.income_subtotal = income_subtotal;
647		AffordLayout.expense_input = expense_input;
648		AffordLayout.Expense = Expense;
649		AffordLayout.ExpenseList = ExpenseList;
650		AffordLayout.ExpenseView = ExpenseView;
651		AffordLayout.expense_table = ExpenseCollectionView;
652		AffordLayout.expense_subtotal = expense_subtotal;
653		AffordLayout.DebtRatios = DebtRatios;
654		AffordLayout.debt_view = DebtView;
655		AffordLayout.LoanAssumptions = LoanAssumptions;
656		AffordLayout.LoanView = LoanView;
657		AffordLayout.OutputsModel = OutputsModel;
658		AffordLayout.OutputsView = OutputsView;
659		return AffordLayout;
660	});