Skip to content

Commit 07af106

Browse files
committed
feat: history
1 parent f15d9d0 commit 07af106

File tree

143 files changed

+6448
-99
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+6448
-99
lines changed

js/js6_code/01.01.loancalc.html

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>JavaScript Loan Calculator</title>
5+
<style> /* This is a CSS style sheet: it adds style to the program output */
6+
.output { font-weight: bold; } /* Calculated values in bold */
7+
#payment { text-decoration: underline; } /* For element with id="payment" */
8+
#graph { border: solid black 1px; } /* Chart has a simple border */
9+
th, td { vertical-align: top; } /* Don't center table cells */
10+
</style>
11+
</head>
12+
<body>
13+
<!--
14+
This is an HTML table with <input> elements that allow the user to enter data
15+
and <span> elements in which the program can display its results.
16+
These elements have ids like "interest" and "years". These ids are used
17+
in the JavaScript code that follows the table. Note that some of the input
18+
elements define "onchange" or "onclick" event handlers. These specify strings
19+
of JavaScript code to be executed when the user enters data or clicks.
20+
-->
21+
<table>
22+
<tr><th>Enter Loan Data:</th>
23+
<td></td>
24+
<th>Loan Balance, Cumulative Equity, and Interest Payments</th></tr>
25+
<tr><td>Amount of the loan ($):</td>
26+
<td><input id="amount" onchange="calculate();"></td>
27+
<td rowspan=8>
28+
<canvas id="graph" width="400" height="250"></canvas></td></tr>
29+
<tr><td>Annual interest (%):</td>
30+
<td><input id="apr" onchange="calculate();"></td></tr>
31+
<tr><td>Repayment period (years):</td>
32+
<td><input id="years" onchange="calculate();"></td>
33+
<tr><td>Zipcode (to find lenders):</td>
34+
<td><input id="zipcode" onchange="calculate();"></td>
35+
<tr><th>Approximate Payments:</th>
36+
<td><button onclick="calculate();">Calculate</button></td></tr>
37+
<tr><td>Monthly payment:</td>
38+
<td>$<span class="output" id="payment"></span></td></tr>
39+
<tr><td>Total payment:</td>
40+
<td>$<span class="output" id="total"></span></td></tr>
41+
<tr><td>Total interest:</td>
42+
<td>$<span class="output" id="totalinterest"></span></td></tr>
43+
<tr><th>Sponsors:</th><td colspan=2>
44+
Apply for your loan with one of these fine lenders:
45+
<div id="lenders"></div></td></tr>
46+
</table>
47+
48+
<!-- The rest of this example is JavaScript code in the <script> tag below -->
49+
<!-- Normally, this script would go in the document <head> above but it -->
50+
<!-- is easier to understand here, after you've seen its HTML context. -->
51+
<script>
52+
"use strict"; // Use ECMAScript 5 strict mode in browsers that support it
53+
54+
/*
55+
* This script defines the calculate() function called by the event handlers
56+
* in HTML above. The function reads values from <input> elements, calculates
57+
* loan payment information, displays the results in <span> elements. It also
58+
* saves the user's data, displays links to lenders, and draws a chart.
59+
*/
60+
function calculate() {
61+
// Look up the input and output elements in the document
62+
var amount = document.getElementById("amount");
63+
var apr = document.getElementById("apr");
64+
var years = document.getElementById("years");
65+
var zipcode = document.getElementById("zipcode");
66+
var payment = document.getElementById("payment");
67+
var total = document.getElementById("total");
68+
var totalinterest = document.getElementById("totalinterest");
69+
70+
// Get the user's input from the input elements. Assume it is all valid.
71+
// Convert interest from a percentage to a decimal, and convert from
72+
// an annual rate to a monthly rate. Convert payment period in years
73+
// to the number of monthly payments.
74+
var principal = parseFloat(amount.value);
75+
var interest = parseFloat(apr.value) / 100 / 12;
76+
var payments = parseFloat(years.value) * 12;
77+
78+
// Now compute the monthly payment figure.
79+
var x = Math.pow(1 + interest, payments); // Math.pow() computes powers
80+
var monthly = (principal*x*interest)/(x-1);
81+
82+
// If the result is a finite number, the user's input was good and
83+
// we have meaningful results to display
84+
if (isFinite(monthly)) {
85+
// Fill in the output fields, rounding to 2 decimal places
86+
payment.innerHTML = monthly.toFixed(2);
87+
total.innerHTML = (monthly * payments).toFixed(2);
88+
totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);
89+
90+
// Save the user's input so we can restore it the next time they visit
91+
save(amount.value, apr.value, years.value, zipcode.value);
92+
93+
// Advertise: find and display local lenders, but ignore network errors
94+
try { // Catch any errors that occur within these curly braces
95+
getLenders(amount.value, apr.value, years.value, zipcode.value);
96+
}
97+
catch(e) { /* And ignore those errors */ }
98+
99+
// Finally, chart loan balance, and interest and equity payments
100+
chart(principal, interest, monthly, payments);
101+
}
102+
else {
103+
// Result was Not-a-Number or infinite, which means the input was
104+
// incomplete or invalid. Clear any previously displayed output.
105+
payment.innerHTML = ""; // Erase the content of these elements
106+
total.innerHTML = ""
107+
totalinterest.innerHTML = "";
108+
chart(); // With no arguments, clears the chart
109+
}
110+
}
111+
112+
// Save the user's input as properties of the localStorage object. Those
113+
// properties will still be there when the user visits in the future
114+
// This storage feature will not work in some browsers (Firefox, e.g.) if you
115+
// run the example from a local file:// URL. It does work over HTTP, however.
116+
function save(amount, apr, years, zipcode) {
117+
if (window.localStorage) { // Only do this if the browser supports it
118+
localStorage.loan_amount = amount;
119+
localStorage.loan_apr = apr;
120+
localStorage.loan_years = years;
121+
localStorage.loan_zipcode = zipcode;
122+
}
123+
}
124+
125+
// Automatically attempt to restore input fields when the document first loads.
126+
window.onload = function() {
127+
// If the browser supports localStorage and we have some stored data
128+
if (window.localStorage && localStorage.loan_amount) {
129+
document.getElementById("amount").value = localStorage.loan_amount;
130+
document.getElementById("apr").value = localStorage.loan_apr;
131+
document.getElementById("years").value = localStorage.loan_years;
132+
document.getElementById("zipcode").value = localStorage.loan_zipcode;
133+
}
134+
};
135+
136+
// Pass the user's input to a server-side script which can (in theory) return
137+
// a list of links to local lenders interested in making loans. This example
138+
// does not actually include a working implementation of such a lender-finding
139+
// service. But if the service existed, this function would work with it.
140+
function getLenders(amount, apr, years, zipcode) {
141+
// If the browser does not support the XMLHttpRequest object, do nothing
142+
if (!window.XMLHttpRequest) return;
143+
144+
// Find the element to display the list of lenders in
145+
var ad = document.getElementById("lenders");
146+
if (!ad) return; // Quit if no spot for output
147+
148+
// Encode the user's input as query parameters in a URL
149+
var url = "getLenders.php" + // Service url plus
150+
"?amt=" + encodeURIComponent(amount) + // user data in query string
151+
"&apr=" + encodeURIComponent(apr) +
152+
"&yrs=" + encodeURIComponent(years) +
153+
"&zip=" + encodeURIComponent(zipcode);
154+
155+
// Fetch the contents of that URL using the XMLHttpRequest object
156+
var req = new XMLHttpRequest(); // Begin a new request
157+
req.open("GET", url); // An HTTP GET request for the url
158+
req.send(null); // Send the request with no body
159+
160+
// Before returning, register an event handler function that will be called
161+
// at some later time when the HTTP server's response arrives. This kind of
162+
// asynchronous programming is very common in client-side JavaScript.
163+
req.onreadystatechange = function() {
164+
if (req.readyState == 4 && req.status == 200) {
165+
// If we get here, we got a complete valid HTTP response
166+
var response = req.responseText; // HTTP response as a string
167+
var lenders = JSON.parse(response); // Parse it to a JS array
168+
169+
// Convert the array of lender objects to a string of HTML
170+
var list = "";
171+
for(var i = 0; i < lenders.length; i++) {
172+
list += "<li><a href='" + lenders[i].url + "'>" +
173+
lenders[i].name + "</a>";
174+
}
175+
176+
// Display the HTML in the element from above.
177+
ad.innerHTML = "<ul>" + list + "</ul>";
178+
}
179+
}
180+
}
181+
182+
// Chart monthly loan balance, interest and equity in an HTML <canvas> element.
183+
// If called with no arguments then just erase any previously drawn chart.
184+
function chart(principal, interest, monthly, payments) {
185+
var graph = document.getElementById("graph"); // Get the <canvas> tag
186+
graph.width = graph.width; // Magic to clear and reset the canvas element
187+
188+
// If we're called with no arguments, or if this browser does not support
189+
// graphics in a <canvas> element, then just return now.
190+
if (arguments.length == 0 || !graph.getContext) return;
191+
192+
// Get the "context" object for the <canvas> that defines the drawing API
193+
var g = graph.getContext("2d"); // All drawing is done with this object
194+
var width = graph.width, height = graph.height; // Get canvas size
195+
196+
// These functions convert payment numbers and dollar amounts to pixels
197+
function paymentToX(n) { return n * width/payments; }
198+
function amountToY(a) { return height-(a * height/(monthly*payments*1.05));}
199+
200+
// Payments are a straight line from (0,0) to (payments, monthly*payments)
201+
g.moveTo(paymentToX(0), amountToY(0)); // Start at lower left
202+
g.lineTo(paymentToX(payments), // Draw to upper right
203+
amountToY(monthly*payments));
204+
g.lineTo(paymentToX(payments), amountToY(0)); // Down to lower right
205+
g.closePath(); // And back to start
206+
g.fillStyle = "#f88"; // Light red
207+
g.fill(); // Fill the triangle
208+
g.font = "bold 12px sans-serif"; // Define a font
209+
g.fillText("Total Interest Payments", 20,20); // Draw text in legend
210+
211+
// Cumulative equity is non-linear and trickier to chart
212+
var equity = 0;
213+
g.beginPath(); // Begin a new shape
214+
g.moveTo(paymentToX(0), amountToY(0)); // starting at lower-left
215+
for(var p = 1; p <= payments; p++) {
216+
// For each payment, figure out how much is interest
217+
var thisMonthsInterest = (principal-equity)*interest;
218+
equity += (monthly - thisMonthsInterest); // The rest goes to equity
219+
g.lineTo(paymentToX(p),amountToY(equity)); // Line to this point
220+
}
221+
g.lineTo(paymentToX(payments), amountToY(0)); // Line back to X axis
222+
g.closePath(); // And back to start point
223+
g.fillStyle = "green"; // Now use green paint
224+
g.fill(); // And fill area under curve
225+
g.fillText("Total Equity", 20,35); // Label it in green
226+
227+
// Loop again, as above, but chart loan balance as a thick black line
228+
var bal = principal;
229+
g.beginPath();
230+
g.moveTo(paymentToX(0),amountToY(bal));
231+
for(var p = 1; p <= payments; p++) {
232+
var thisMonthsInterest = bal*interest;
233+
bal -= (monthly - thisMonthsInterest); // The rest goes to equity
234+
g.lineTo(paymentToX(p),amountToY(bal)); // Draw line to this point
235+
}
236+
g.lineWidth = 3; // Use a thick line
237+
g.stroke(); // Draw the balance curve
238+
g.fillStyle = "black"; // Switch to black text
239+
g.fillText("Loan Balance", 20,50); // Legend entry
240+
241+
// Now make yearly tick marks and year numbers on X axis
242+
g.textAlign="center"; // Center text over ticks
243+
var y = amountToY(0); // Y coordinate of X axis
244+
for(var year=1; year*12 <= payments; year++) { // For each year
245+
var x = paymentToX(year*12); // Compute tick position
246+
g.fillRect(x-0.5,y-3,1,3); // Draw the tick
247+
if (year == 1) g.fillText("Year", x, y-5); // Label the axis
248+
if (year % 5 == 0 && year*12 !== payments) // Number every 5 years
249+
g.fillText(String(year), x, y-5);
250+
}
251+
252+
// Mark payment amounts along the right edge
253+
g.textAlign = "right"; // Right-justify text
254+
g.textBaseline = "middle"; // Center it vertically
255+
var ticks = [monthly*payments, principal]; // The two points we'll mark
256+
var rightEdge = paymentToX(payments); // X coordinate of Y axis
257+
for(var i = 0; i < ticks.length; i++) { // For each of the 2 points
258+
var y = amountToY(ticks[i]); // Compute Y position of tick
259+
g.fillRect(rightEdge-3, y-0.5, 3,1); // Draw the tick mark
260+
g.fillText(String(ticks[i].toFixed(0)), // And label it.
261+
rightEdge-5, y);
262+
}
263+
}
264+
</script>
265+
</body>
266+
</html>

js/js6_code/06.01.inherit.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// inherit() returns a newly created object that inherits properties from the
2+
// prototype object p. It uses the ECMAScript 5 function Object.create() if
3+
// it is defined, and otherwise falls back to an older technique.
4+
function inherit(p) {
5+
if (p == null) throw TypeError(); // p must be a non-null object
6+
if (Object.create) // If Object.create() is defined...
7+
return Object.create(p); // then just use it.
8+
var t = typeof p; // Otherwise do some more type checking
9+
if (t !== "object" && t !== "function") throw TypeError();
10+
function f() {}; // Define a dummy constructor function.
11+
f.prototype = p; // Set its prototype property to p.
12+
return new f(); // Use f() to create an "heir" of p.
13+
}

js/js6_code/06.02.extend.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copy the enumerable properties of p to o, and return o.
3+
* If o and p have a property by the same name, o's property is overwritten.
4+
* This function does not handle getters and setters or copy attributes.
5+
*/
6+
function extend(o, p) {
7+
for(prop in p) { // For all props in p.
8+
o[prop] = p[prop]; // Add the property to o.
9+
}
10+
return o;
11+
}
12+
13+
/*
14+
* Copy the enumerable properties of p to o, and return o.
15+
* If o and p have a property by the same name, o's property is left alone.
16+
* This function does not handle getters and setters or copy attributes.
17+
*/
18+
function merge(o, p) {
19+
for(prop in p) { // For all props in p.
20+
if (o.hasOwnProperty[prop]) continue; // Except those already in o.
21+
o[prop] = p[prop]; // Add the property to o.
22+
}
23+
return o;
24+
}
25+
26+
/*
27+
* Remove properties from o if there is not a property with the same name in p.
28+
* Return o.
29+
*/
30+
function restrict(o, p) {
31+
for(prop in o) { // For all props in o
32+
if (!(prop in p)) delete o[prop]; // Delete if not in p
33+
}
34+
return o;
35+
}
36+
37+
/*
38+
* For each property of p, delete the property with the same name from o.
39+
* Return o.
40+
*/
41+
function subtract(o, p) {
42+
for(prop in p) { // For all props in p
43+
delete o[prop]; // Delete from o (deleting a
44+
// nonexistent prop is harmless)
45+
}
46+
return o;
47+
}
48+
49+
/*
50+
* Return a new object that holds the properties of both o and p.
51+
* If o and p have properties by the same name, the values from o are used.
52+
*/
53+
function union(o,p) { return extend(extend({},o), p); }
54+
55+
/*
56+
* Return a new object that holds only the properties of o that also appear
57+
* in p. This is something like the intersection of o and p, but the values of
58+
* the properties in p are discarded
59+
*/
60+
function intersection(o,p) { return restrict(extend({}, o), p); }
61+
62+
/*
63+
* Return an array that holds the names of the enumerable own properties of o.
64+
*/
65+
function keys(o) {
66+
if (typeof o !== "object") throw TypeError(); // Object argument required
67+
var result = []; // The array we will return
68+
for(var prop in o) { // For all enumerable properties
69+
if (o.hasOwnProperty(prop)) // If it is an own property
70+
result.push(prop); // add it to the array.
71+
}
72+
return result; // Return the array.
73+
}

js/js6_code/06.03.extend2.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Add a nonenumerable extend() method to Object.prototype.
3+
* This method extends the object on which it is called by copying properties
4+
* from the object passed as its argument. All property attributes are
5+
* copied, not just the property value. All own properties (even non-
6+
* enumerable ones) of the argument object are copied unless a property
7+
* with the same name already exists in the target object.
8+
*/
9+
Object.defineProperty(Object.prototype,
10+
"extend", // Define Object.prototype.extend
11+
{
12+
writable: true,
13+
enumerable: false, // Make it nonenumerable
14+
configurable: true,
15+
value: function(o) { // Its value is this function
16+
// Get all own props, even nonenumerable ones
17+
var names = Object.getOwnPropertyNames(o);
18+
// Loop through them
19+
for(var i = 0; i < names.length; i++) {
20+
// Skip props already in this object
21+
if (names[i] in this) continue;
22+
// Get property description from o
23+
var desc = Object.getOwnPropertyDescriptor(o,names[i]);
24+
// Use it to create property on this
25+
Object.defineProperty(this, names[i], desc);
26+
}
27+
}
28+
});

js/js6_code/06.04.classof

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function classof(o) {
2+
if (o === null) return "Null";
3+
if (o === undefined) return "Undefined";
4+
return Object.prototype.toString.call(o).slice(8,-1);
5+
}

0 commit comments

Comments
 (0)