<?xml version="1.0" encoding="UTF-8" ?>

<rss version="2.0">
    <channel>

        <title>CS50 Blog</title>
        <description>This is CS50.</description>
        <link>https://blog.cs50.net/</link>
        
        <item><description><![CDATA[<div class="paragraph">
<p>Version 1 of <a href="https://x.cs50.net/">CS50x</a> debuted on Monday, 15 October 2012, and concluded (unlike my taxes) on Monday, 15 April 2013.  We&rsquo;ve only just begun to dive into all of the data we collected over those six months, but we thought we&rsquo;d take a moment to share some preliminary datapoints.</p>
</div>
<div class="paragraph">
<p>Day 0 began roughly as follows:</p>
</div>
<div class="paragraph">
<p><strong>2:00pm</strong> CS50x goes live<br><strong>2:02pm</strong> 500 users online<br><strong>3:00pm</strong> 10,000 users online</p>
</div>
<div class="paragraph">
<p>Tommy MacWilliam '13, <span class="line-through">CS50&rsquo;s sole head TF</span> one of CS50&rsquo;s head TFs, shares even <a href="http://blog.tommymacwilliam.com/day/2012/10/25">more detail</a> in his blog.</p>
</div>
<div class="paragraph">
<p>Day <em>n</em>-1, meanwhile, concluded with</p>
</div>
<div class="paragraph">
<p><strong>150,349</strong> students registered<br><strong>100,953</strong> students engaged<br><strong>10,905</strong> <a href="https://x.cs50.net/2012/psets/0/pset0.html">pset0</a> submissions<br><strong>1,482</strong> <a href="https://x.cs50.net/2012/project/project.html">project</a> submissions<br><strong>1,388</strong> certificates awarded</p>
</div>
<div class="paragraph">
<p>whereby students who &ldquo;engaged&rdquo; watched content, asked questions, and used apps, even if they didn&rsquo;t submit work.  Receipt of a certificate required submission of all work with scores of 60% or higher.</p>
</div>
<div class="paragraph">
<p>Submissions of problem sets and quizzes trended as follows:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/submissions.png" alt="submissions"></span></p>
</div>
<div class="paragraph">
<p>Daily engagement in the course, as measured by Google Analytics in terms of unique visitors, trended similarly, peaking early, then reaching some form of equilibrium (that appears to trail off as the course&rsquo;s end loomed!):</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/unique.png" alt="unique visitors"></span></p>
</div>
<div class="paragraph">
<p>If we zoom in on the course&rsquo;s final weeks, unique visits hovered around 2,500 per day:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/zoomed.png" alt="unique visitors"></span></p>
</div>
<div class="paragraph">
<p>Visits by country, meanwhile, were ordered as follows, with Google Analytics reporting at least one visitor from nearly country in the world:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/countries.png" alt="visits"></span></p>
</div>
<div class="paragraph">
<p>As for the students themselves, upon submitting <a href="https://x.cs50.net/2012/psets/0/pset0.html">Problem Set 0</a>, students were asked to submit a <a href="https://blog.cs50.net/pset0.pdf">form</a> that inquired as to their background before CS50x and motivation for taking CS50x.  Based on 10,905 submissions (which may or may not be representative of CS50x&rsquo;s 150,349 registrants), CS50x&rsquo;s demographics were <strong>20% female</strong> and <strong>80% male</strong> (whereas CS50 on campus was <a href="https://manual.cs50.net/Enrollment">36% female in Fall 2012</a>).  The <strong>average age was 29</strong>, with a <strong>median of 27</strong> and a <strong>mode of 21</strong>.  CS50x&rsquo;s <strong>eldest student was 80</strong> and <strong>youngest student was 10</strong>.  Even younger were <a href="http://youtu.be/FBMFsc6e0xw">Louis</a> and <a href="http://youtu.be/1a4m8cEe_ls">his brother</a>, who said &ldquo;hello, world&rdquo; as well.  Though filming those hellos apparently required <a href="http://youtu.be/9XpdABXMDwI">one take too many</a>.</p>
</div>
<div class="paragraph">
<p>Per the below, <strong>56% of CS50x students had no prior background</strong> in CS, versus 75% in CS50 on campus:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/courses.png" alt="prior courses"></span></p>
</div>
<div class="paragraph">
<p>Most students had at least a degree from high school, if not beyond:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/education.png" alt="education"></span></p>
</div>
<div class="paragraph">
<p>Out of the 10,905 students who submitted pset0, 10,137 (93%) intended to do all of the course&rsquo;s work:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/plan.png" alt="plan"></span></p>
</div>
<div class="paragraph">
<p>To be fair, <strong>out of the 150,349 students who registered, only 10,137 (7%) intended to do all of the work</strong> (else they&rsquo;d presumably have submitted at least pset0).</p>
</div>
<div class="paragraph">
<p>Meanwhile, out of the 10,905 students who submitted pset0, 3,381 (31%) took CS50x because of the prospect of a certificate:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/certificate.png" alt="interest in certificates"></span></p>
</div>
<div class="paragraph">
<p>To be fair, <strong>out of the 150,349 students who registered, only 3,381 (2%) took CS50x because of the prospect of a certificate</strong> (else they&rsquo;d presumably have submitted at least pset0).</p>
</div>
<div class="paragraph">
<p>For most students, then, &ldquo;success&rdquo; didn&rsquo;t necessarily mean a certificate.  And &ldquo;completion&rdquo; wasn&rsquo;t necessarily the goal.  Among students' motivations for registering were:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2013/05/01/0/why.png" alt="Why taking CS50x?"></span></p>
</div>
<div class="paragraph">
<p>Ultimately, at least 1,388 students will receive a certificate like John Harvard&rsquo;s here:</p>
</div>
<div class="paragraph">
<p><span class="image"><a class="image" href="https://blog.cs50.net/2013/05/01/0/jharvard.png"><img src="https://blog.cs50.net/2013/05/01/0/jharvard.png" alt="John Harvard&rsquo;s certificate" width="640"></a></span></p>
</div>
<div class="paragraph">
<p>To put all these numbers into perspective:</p>
</div>
<div class="ulist">
<ul><li>
<p>Among 150,349 students, 100,953 engaged (67%).</p>
</li>
<li>
<p>Among those 100,953 students, 10,905 submitted pset0 (11%).</p>
</li>
<li>
<p>Among those 10,905 students,</p>
<div class="ulist">
<ul><li>
<p>3,381 (31%) sought a certificate, and</p>
</li>
<li>
<p>1,388 (41%) will receive a certificate.</p>
</li>
</ul></div>
</li>
</ul></div>
<div class="paragraph">
<p>CS50x&rsquo;s &ldquo;completion rate&rdquo; (whereby completion is defined as submission of all work with scores of at least 60%) was thus:</p>
</div>
<div class="ulist">
<ul><li>
<p>41%, if out of 3,381 who sought a certificate.</p>
</li>
<li>
<p>13%, if out of 10,905 who submitted pset0.</p>
</li>
<li>
<p>1.4%, if out of 100,953 who engaged.</p>
</li>
<li>
<p>0.9%, if out of 150,349 who registered.</p>
</li>
</ul></div>
<div class="paragraph">
<p>By contrast, 703 out of 706 students (99.6%) &ldquo;completed&rdquo; CS50 on campus this past fall.  But, to be fair, for most CS50x students, &ldquo;completion&rdquo; wasn&rsquo;t necessarily the goal.  Indeed, tens of thousands &ldquo;engaged&rdquo; in some form.</p>
</div>
<div class="paragraph">
<p>For the curious, version 2 of CS50x will debut in late 2013 or early 2014 once we&rsquo;ve had a chance to re-tool and improve!  In the meantime, all of version 1&rsquo;s content will remain freely available at <a href="http://cs50.tv/">cs50.tv</a>, at <a href="http://www.youtube.com/cs50">youtube.com/cs50</a>, and at <a href="https://x.cs50.net/">x.cs50.net</a> as well as in <a href="itpc://feeds.feedburner.com/cs50/2012/fall/rss">iTunes</a> and <a href="https://itunes.apple.com/us/course/this-is-cs50-2012./id624655973">iTunes U</a>.  And even before version 2 of CS50x debuts, content from Fall 2013 of CS50 itself will become available at <a href="https://www.cs50.net/">cs50.net</a> in September 2013.</p>
</div>]]></description><guid>http://blog.cs50.net/2013/05/01/0/</guid><link>http://blog.cs50.net/2013/05/01/0/</link><pubDate>Wed, 01 May 2013 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>So it turns out that last screenshot of <a href="http://cs50.tv/">cs50.tv</a>'s guestmap in <a href="https://blog.cs50.net/2010/10/03/0/">10/3</a>'s post should have had a lot more markers on it! Recall that we use <a href="http://docs.google.com/">Google Spreadsheets</a> to store visitors' names and hometowns. It turns out it&rsquo;s not sufficient to write (or talk about) a script that <a href="http://en.wikipedia.org/wiki/Geocoding">geocodes</a> those hometowns, saving the resulting GPS coordinates back in the spreadsheet itself. You actually have to remember to run that script!</p>
</div>
<div class="paragraph">
<p>Truth be told, I thought I&rsquo;d set up a "cronjob" on our server whose purpose in life was to execute that geocoding script nightly. Unfortunately, it seems I never did. (Or, in my defense, I did but set it up under a username that I later deleted. Though that probably still makes it my fault.) But no matter, what a perfect opportunity to talk about <a href="http://en.wikipedia.org/wiki/Cron"><code>cron</code></a>!</p>
</div>
<div class="paragraph">
<p><code>cron</code> is a utility generally found on Linux systems (and others) that allows you to schedule the execution of commands. For instance, if you&rsquo;ve written a command-line program called <code>foo</code>, you can schedule <code>foo</code> to be executed every 5 minutes without having to log into the server yourself every 5 minutes and run it manually. Alternatively, you could schedule foo to be executed once nightly, or once weekly, or, really, anytime you&rsquo;d like, however often you&rsquo;d like. <code>cron</code> is often used by the system itself to, say, rotate logs (i.e., rename them so a file doesn&rsquo;t get too big). In CS50, we use <code>cron</code> (via <code>rsnapshot</code>) in order to back up students' source code every 5 minutes to save them the inevitable anguish of accidentally deleting some file. (The disk space it&rsquo;s cost us to maintain so many backups has been well worth the tears saved!)</p>
</div>
<div class="paragraph">
<p>Anyhow, using <code>cron</code> is wonderfully simple. You essentially run a command like</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>crontab -e</code></pre>
</div>
</div>
<div class="paragraph">
<p>that allows you to edit (per the <code>-e</code>) a text file (otherwise known as a "cron table"), each of whose lines represents a scheduled command. In order to enable daily geocoding of our Google Spreadsheet, I added the one-liner below to the webserver&rsquo;s own crontab, where <code>/path/to/geocode</code> is the full path to the command-line geocoding script I wrote:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>@daily /path/to/geocode</code></pre>
</div>
</div>
<div class="paragraph">
<p>It turns out that my one-liner is actually short-hand notation for</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>0 0 * * *</code></pre>
</div>
</div>
<div class="paragraph">
<p>which means, right to left, that this command (<code>/path/to/geocode</code>) should be executed every day of the week, every month, every day of the month, when the clock strikes 0 hours and 0 seconds, where <code>*</code> denotes "every." In normal-people terms, it means "run this command every night at midnight." To be honest, I almost always forget which number means which, but <a href="http://en.wikipedia.org/w/index.php?title=Cron&amp;oldid=391484981#Examples">Wikipedia</a> has a wonderful cheatsheet. Allow me to excerpt:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>.---------------- minute (0 - 59)
|   .------------- hour (0 - 23)
|   |   .---------- day of month (1 - 31)
|   |   |   .------- month (1 - 12) OR jan,feb,mar,apr ...
|   |   |   |  .----- day of week (0 - 6) (Sunday=0 or 7)  OR sun,mon,tue,wed,thu,fri,sat
|   |   |   |  |
*   *   *   *  *  command to be executed</code></pre>
</div>
</div>
<div class="paragraph">
<p>I should confess that I&rsquo;ve actually told a little white lie in the interests of simplicity. Our cronjob is actually</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code>@daily /bin/nice /path/to/geocode</code></pre>
</div>
</div>
<div class="paragraph">
<p>which means that the command should be "<code>nice</code>'d" before it&rsquo;s run. Like <code>cron</code> itself, <code>nice</code> is a utility found on most Linux systems (and others) that allows you to raise and lower commands' priorities (i.e., how much CPU time they get). By default, <code>nice</code> lowers a command&rsquo;s priority, so our cronjob really tells the system to "run this command every night at midnight but run this command at a lower priority so it doesn&rsquo;t slow other, more important things (like file downloads) down."</p>
</div>
<div class="paragraph">
<p>Odds are, if you have a shell account on a Linux system (that you can access via SSH), you have the ability to schedule cronjobs, at least if the system administrator allows you. If you instead have an account with a third-party Web host that only provides you with a Web-based panel, you may be able to schedule cronjobs via that panel&rsquo;s GUI in a slightly more user-friendly way.</p>
</div>
<div class="paragraph">
<p>Anyhow, thanks to <code>cron</code> (and my actually using it), <a href="http://cs50.tv/">cs50.tv</a>'s guestmap is indeed now updated nightly, and it currently looks like this!</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/10/24/0/guestmap.png" alt="guestmap"></span></p>
</div>]]></description><guid>http://blog.cs50.net/2010/10/24/0/</guid><link>http://blog.cs50.net/2010/10/24/0/</link><pubDate>Sun, 24 Oct 2010 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>For a while, <a href="http://cs50.tv/">cs50.tv</a> (and its sister sites) have had a "guestmap," whereby visitors are invited to record their names and hometowns on a Google Map, if only for the fun of it. To see the map, head to <a href="http://cs50.tv/">http://cs50.tv/</a> and click <strong>Guestmap</strong> in the site&rsquo;s top-left corner. (Feel free to add yourself!) For the impatient, here&rsquo;s a screenshot of what it looks like at the moment:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/10/03/0/guestmap.png" alt="guestmap"></span></p>
</div>
<div class="paragraph">
<p>When we decided (spontaneously one night) to implement this guestmap, we weren&rsquo;t sure if anyone would notice (or care!) if we did, so we didn&rsquo;t want to spend too much time on its implementation. In particular, we didn&rsquo;t want to bother setting up an entire database (e.g., <a href="http://en.wikipedia.org/wiki/MySQL">MySQL</a>) just for this one application. Simpler would have been to store folks' submissions in a local file (e.g., <a href="http://en.wikipedia.org/wiki/Comma-separated_values">CSV</a>, <a href="http://en.wikipedia.org/wiki/SQLite">SQLite</a>, or <a href="http://en.wikipedia.org/wiki/XML">XML</a>). But the plan was to start replicating <a href="http://cs50.tv/">cs50.tv</a>'s content across multiple servers, in which case we&rsquo;d then have to keep copies of that file synchronized across multiple servers. Doable, but somewhat annoying. If we used a local file as our database, we&rsquo;d also have to worry about concurrent writes (as when two visitors happen to submit their names and hometowns simultaneously, the result of which might be to lose one of the visitor&rsquo;s data). Also doable, and not very hard (thanks to functions like <a href="http://php.net/manual/en/function.flock.php"><code>flock</code></a>). But we&rsquo;d still have to implement the form via which we ask for visitors' names and hometowns. (And at this point in the story, it was probably already midnight or so.)</p>
</div>
<div class="paragraph">
<p>If only there were someone to whom we could outsource both the form and the database&hellip;</p>
</div>
<div class="paragraph">
<p>Turns out there was! Odds are you&rsquo;re familiar with <a href="http://docs.google.com/">Google Spreadsheets</a>, which is essentially Google&rsquo;s web-based answer to <a href="http://en.wikipedia.org/wiki/Microsoft_Excel">Microsoft Excel</a>. It&rsquo;s actually wonderfully useful, in large part because it makes it so easy to share (and edit simultaneously) spreadsheets with other people. But with Google Spreadsheets can you also create web-based <a href="http://docs.google.com/support/bin/answer.py?hl=en&amp;answer=87809">forms</a> whose submissions end up in a Google Spreadsheet: each row represents a submission, and each column represents a visitor&rsquo;s answer to a particular question. And you can then share that spreadsheet with yourself (or others) in a variety of formats, among them CSV.</p>
</div>
<div class="paragraph">
<p>Nice! Even without writing any code, I was almost finished implementing the guestmap! (Okay, admittedly, it took a few minutes to create the form and share it with myself as CSV, but way fewer minutes than it&rsquo;d have taken to implement all that myself.)</p>
</div>
<div class="paragraph">
<p>So, at this point in the story, I had two URLs: one for the form and one for the CSV file. My plan was (1) to embed the former in an <a href="http://en.wikipedia.org/wiki/HTML_element#Frames"><code>iframe</code></a> on <a href="http://cs50.tv/">cs50.tv</a> so that folks could add themselves to the guestmap without leaving <a href="http://cs50.tv/">cs50.tv</a> itself and (2) query the latter anytime someone wanted to look at the map. All that remained was to write the code that queries the CSV and generates the map.</p>
</div>
<div class="paragraph">
<p>Thanks to the <a href="http://code.google.com/apis/maps/documentation/javascript/">Google Maps API</a>, this part, too, was amazingly easy. To embed a Google Map in one of your own pages, you first need to include the API in your page&rsquo;s head, as with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/javascript</span><span class="delimiter">"</span></span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://maps.google.com/maps/api/js?sensor=false</span><span class="delimiter">"</span></span><span class="tag">&gt;</span><span class="tag">&lt;/script&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You then need to provide a container for it in your page&rsquo;s <code>body</code>, as with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">map</span><span class="delimiter">"</span></span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">height: 100%; width: 100%;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span><span class="tag">&lt;/div&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Lastly, you need to tell Google to transform that container into a map, as with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript"><span class="keyword">new</span> google.maps.Map(document.getElementById(<span class="string"><span class="delimiter">"</span><span class="content">map</span><span class="delimiter">"</span></span>), {
 <span class="key">center</span>: <span class="keyword">new</span> google.maps.LatLng(<span class="float">42.37411257777324</span>, -<span class="float">71.11905097961426</span>),
 <span class="key">disableDefaultUI</span>: <span class="predefined-constant">true</span>,
 <span class="key">mapTypeControl</span>: <span class="predefined-constant">false</span>,
 <span class="key">mapTypeId</span>: google.maps.MapTypeId.HYBRID,
 <span class="key">navigationControl</span>: <span class="predefined-constant">true</span>,
 <span class="key">zoom</span>: <span class="integer">2</span>
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>Of course, that map won&rsquo;t have any markers on it until you plant them yourself. Planting a marker, though, requires knowing the <a href="http://en.wikipedia.org/wiki/Global_Positioning_System">GPS</a> coordinates of each visitor&rsquo;s hometown. Probably not something most people know. So, how can we geocode (i.e., convert) each visitor&rsquo;s city, state, and country (that he or she inputted via the Guestmap&rsquo;s web-based form) into a latitude and longitude?</p>
</div>
<div class="paragraph">
<p>Well, the Google Maps API does provide a JavaScript <a href="http://code.google.com/apis/maps/documentation/javascript/services.html#Geocoding">geocoding service</a>, but using that service would mean that every visitor&rsquo;s hometown would need to be geocoded every time someone pulls up the guestmap by that someone&rsquo;s own browser. Certainly not the most efficient approach. Plus, Google appears to limit the number of geocoding requests a browser can make in a short period of time, the result of which is that some visitors wouldn&rsquo;t end up marked on the map. (We, um, learned that the hard way.) Better to do the geocoding server-side and remember each visitor&rsquo;s coordinates so that we needn&rsquo;t query for them again.</p>
</div>
<div class="paragraph">
<p>As luck would have it, Google also provides a web service with which you can do exactly that. Via Google&rsquo;s <a href="http://code.google.com/apis/maps/documentation/geocoding/">Geocoding API</a> can you ask for the GPS coordinates of some location (e.g., <a href="http://maps.google.com/maps?q=33+Oxford+Street,+Cambridge,+Massachusetts">33 Oxford Street, Cambridge, Massachusetts</a>) with code like the below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript"><span class="predefined">$o</span> = json_decode(file_get_contents(<span class="string"><span class="delimiter">"</span><span class="content">http://maps.googleapis.com/maps/api/geocode/json?address=33+Oxford+Street,+Cambridge,+Massachusetts+02138&amp;sensor=false</span><span class="delimiter">"</span></span>));</code></pre>
</div>
</div>
<div class="paragraph">
<p>I called <a href="http://php.net/manual/en/function.json-encode.php"><code>json_encode</code></a> in the above because what&rsquo;s returned is a <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> object like the below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript">{
  <span class="key"><span class="delimiter">"</span><span class="content">status</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">OK</span><span class="delimiter">"</span></span>,
  <span class="key"><span class="delimiter">"</span><span class="content">results</span><span class="delimiter">"</span></span>: [ {
    <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">street_address</span><span class="delimiter">"</span></span> ],
    <span class="key"><span class="delimiter">"</span><span class="content">formatted_address</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">33 Oxford St, Cambridge, MA 02138, USA</span><span class="delimiter">"</span></span>,
    <span class="key"><span class="delimiter">"</span><span class="content">address_components</span><span class="delimiter">"</span></span>: [ {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">33</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">33</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">street_number</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Oxford St</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Oxford St</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">route</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Cambridge</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Cambridge</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">locality</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">political</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Cambridge</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Cambridge</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">administrative_area_level_3</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">political</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Middlesex</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Middlesex</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">administrative_area_level_2</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">political</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">Massachusetts</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">MA</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">administrative_area_level_1</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">political</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">United States</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">US</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">country</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">political</span><span class="delimiter">"</span></span> ]
    }, {
      <span class="key"><span class="delimiter">"</span><span class="content">long_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">02138</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">short_name</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">02138</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">types</span><span class="delimiter">"</span></span>: [ <span class="string"><span class="delimiter">"</span><span class="content">postal_code</span><span class="delimiter">"</span></span> ]
    } ],
    <span class="key"><span class="delimiter">"</span><span class="content">geometry</span><span class="delimiter">"</span></span>: {
      <span class="key"><span class="delimiter">"</span><span class="content">location</span><span class="delimiter">"</span></span>: {
        <span class="key"><span class="delimiter">"</span><span class="content">lat</span><span class="delimiter">"</span></span>: <span class="float">42.3787770</span>,
        <span class="key"><span class="delimiter">"</span><span class="content">lng</span><span class="delimiter">"</span></span>: -<span class="float">71.1168170</span>
      },
      <span class="key"><span class="delimiter">"</span><span class="content">location_type</span><span class="delimiter">"</span></span>: <span class="string"><span class="delimiter">"</span><span class="content">ROOFTOP</span><span class="delimiter">"</span></span>,
      <span class="key"><span class="delimiter">"</span><span class="content">viewport</span><span class="delimiter">"</span></span>: {
        <span class="key"><span class="delimiter">"</span><span class="content">southwest</span><span class="delimiter">"</span></span>: {
          <span class="key"><span class="delimiter">"</span><span class="content">lat</span><span class="delimiter">"</span></span>: <span class="float">42.3756294</span>,
          <span class="key"><span class="delimiter">"</span><span class="content">lng</span><span class="delimiter">"</span></span>: -<span class="float">71.1199646</span>
        },
        <span class="key"><span class="delimiter">"</span><span class="content">northeast</span><span class="delimiter">"</span></span>: {
          <span class="key"><span class="delimiter">"</span><span class="content">lat</span><span class="delimiter">"</span></span>: <span class="float">42.3819246</span>,
          <span class="key"><span class="delimiter">"</span><span class="content">lng</span><span class="delimiter">"</span></span>: -<span class="float">71.1136694</span>
        }
      }
    }
  } ]
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>And so we can get at the GPS coordinates we want with code like the below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="javascript language-javascript"><span class="predefined">$lat</span> = <span class="predefined">$o</span>-&gt;results[<span class="integer">0</span>]-&gt;geometry-&gt;location-&gt;lat;
<span class="predefined">$lng</span> = <span class="predefined">$o</span>-&gt;results[<span class="integer">0</span>]-&gt;geometry-&gt;location-&gt;lng;</code></pre>
</div>
</div>
<div class="paragraph">
<p>And we can then store those coordinates somewhere. (We happen to use the <a href="http://code.google.com/apis/gdata/">Google Data Protocol</a> in order to write them back to our own Google Spreadsheet in a column that the web-based form itself doesn&rsquo;t touch.)</p>
</div>
<div class="paragraph">
<p>With all those pieces now assembled, we render the map itself with code like the below, wherein <code>CSV</code> is the URL of our Google Spreadsheet:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="doctype">&lt;!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span>

<span class="tag">&lt;html</span> <span class="attribute-name">xmlns</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://www.w3.org/1999/xhtml</span><span class="delimiter">"</span></span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">height: 100%;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
  <span class="tag">&lt;head&gt;</span>

    <span class="tag">&lt;link</span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">stylesheet</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/css</span><span class="delimiter">"</span></span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://yui.yahooapis.com/2.8.1/build/reset-fonts/reset-fonts.css</span><span class="delimiter">"</span></span> <span class="tag">/&gt;</span>

    <span class="tag">&lt;meta</span> <span class="attribute-name">http-equiv</span>=<span class="string"><span class="delimiter">"</span><span class="content">content-type</span><span class="delimiter">"</span></span> <span class="attribute-name">content</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/html; charset=utf-8</span><span class="delimiter">"</span></span><span class="tag">/&gt;</span>

    <span class="tag">&lt;script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/javascript</span><span class="delimiter">"</span></span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://yui.yahooapis.com/2.8.1/build/yahoo-dom-event/yahoo-dom-event.js</span><span class="delimiter">"</span></span><span class="tag">&gt;</span><span class="tag">&lt;/script&gt;</span>
    <span class="tag">&lt;script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/javascript</span><span class="delimiter">"</span></span> <span class="attribute-name">src</span>=<span class="string"><span class="delimiter">"</span><span class="content">http://maps.google.com/maps/api/js?sensor=false</span><span class="delimiter">"</span></span><span class="tag">&gt;</span><span class="tag">&lt;/script&gt;</span>
    <span class="tag">&lt;script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/javascript</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
<span class="inline">    <span class="comment">// &lt;![CDATA[</span>

        YAHOO.util.Event.onDOMReady(<span class="keyword">function</span>() {

            <span class="keyword">var</span> gmap = <span class="keyword">new</span> google.maps.Map(document.getElementById(<span class="string"><span class="delimiter">"</span><span class="content">map</span><span class="delimiter">"</span></span>), {
             <span class="key">center</span>: <span class="keyword">new</span> google.maps.LatLng(<span class="float">42.37411257777324</span>, -<span class="float">71.11905097961426</span>),
             <span class="key">disableDefaultUI</span>: <span class="predefined-constant">true</span>,
             <span class="key">mapTypeControl</span>: <span class="predefined-constant">false</span>,
             <span class="key">mapTypeId</span>: google.maps.MapTypeId.HYBRID,
             <span class="key">navigationControl</span>: <span class="predefined-constant">true</span>,
             <span class="key">zoom</span>: <span class="integer">2</span>
            });

            <span class="comment">// resize as needed</span>
            YAHOO.util.Event.addListener(window, <span class="string"><span class="delimiter">"</span><span class="content">resize</span><span class="delimiter">"</span></span>, <span class="keyword">function</span>() {
                <span class="keyword">if</span> (<span class="keyword">typeof</span>(gmap) !== <span class="string"><span class="delimiter">"</span><span class="content">undefined</span><span class="delimiter">"</span></span>)
                {
                    google.maps.event.trigger(gmap, <span class="string"><span class="delimiter">"</span><span class="content">resize</span><span class="delimiter">"</span></span>);
                }
            });

            <span class="comment">// prepare infowindow</span>
            <span class="keyword">var</span> infowindow = <span class="keyword">new</span> google.maps.InfoWindow();

            &lt;?

                <span class="comment">// get subscribers</span>
                <span class="predefined">$rows</span> = array();
                <span class="predefined">$handle</span> = fopen(CSV, <span class="string"><span class="delimiter">"</span><span class="content">r</span><span class="delimiter">"</span></span>);
                fgetcsv(<span class="predefined">$handle</span>);
                <span class="keyword">while</span> (<span class="predefined">$row</span> = fgetcsv(<span class="predefined">$handle</span>))
                    <span class="predefined">$rows</span>[] = <span class="predefined">$row</span>;
                fclose(<span class="predefined">$handle</span>);

            ?&gt;

            &lt;? foreach (<span class="predefined">$rows</span> as <span class="predefined">$row</span>): ?&gt;

                &lt;? <span class="keyword">if</span> (!<span class="error">@</span><span class="predefined">$row</span>[<span class="integer">9</span>] || <span class="predefined">$row</span>[<span class="integer">12</span>]) <span class="keyword">continue</span>; ?&gt;

                <span class="keyword">var</span> marker = <span class="keyword">new</span> google.maps.Marker({
                 <span class="key">map</span>: gmap,
                 <span class="key">position</span>: <span class="keyword">new</span> google.maps.LatLng(&lt;?= <span class="predefined">$row</span>[<span class="integer">10</span>] ?&gt;, &lt;?= <span class="predefined">$row</span>[<span class="integer">11</span>] ?&gt;)
                });
                google.maps.event.addListener(marker, <span class="string"><span class="delimiter">"</span><span class="content">click</span><span class="delimiter">"</span></span>, <span class="keyword">function</span>(e) {
                    infowindow.setContent(<span class="string"><span class="delimiter">"</span><span class="content">&lt;div style='width: 240px;'&gt;&lt;b&gt;</span><span class="delimiter">"</span></span> + &lt;?= json_encode(CapitzeLastName(<span class="predefined">$row</span>[<span class="integer">1</span>])) ?&gt; + <span class="string"><span class="delimiter">"</span><span class="content">&lt;/b&gt;</span><span class="delimiter">"</span></span> + <span class="string"><span class="delimiter">"</span><span class="content">&lt;br /&gt;</span><span class="delimiter">"</span></span> + &lt;?= json_encode(<span class="predefined">$row</span>[<span class="integer">9</span>]) ?&gt; + <span class="string"><span class="delimiter">"</span><span class="content">&lt;/div&gt;</span><span class="delimiter">"</span></span>);
                    infowindow.open(gmap, <span class="local-variable">this</span>);
                });

            &lt;? endforeach ?&gt;

        });

    <span class="comment">// ]]&gt;</span></span>
    <span class="tag">&lt;/script&gt;</span>

    <span class="tag">&lt;title&gt;</span>Map<span class="tag">&lt;/title&gt;</span>

  <span class="tag">&lt;/head&gt;</span>

  <span class="tag">&lt;body</span> <span class="attribute-name">class</span>=<span class="string"><span class="delimiter">"</span><span class="content">yui-skin-sam</span><span class="delimiter">"</span></span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">height: 100%;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
    <span class="tag">&lt;div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">map</span><span class="delimiter">"</span></span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">height: 100%; width: 100%;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span><span class="tag">&lt;/div&gt;</span>
  <span class="tag">&lt;/body&gt;</span>
<span class="tag">&lt;/html&gt;</span></code></pre>
</div>
</div>]]></description><guid>http://blog.cs50.net/2010/10/03/0/</guid><link>http://blog.cs50.net/2010/10/03/0/</link><pubDate>Sun, 03 Oct 2010 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>Note to self: get (even) better about actually posting.</p>
</div>
<div class="paragraph">
<p>The irony is that at least once per day I think to myself, "Oh that would make for an interesting post." Clearly my problem is with execution! Unfortunately, sleep usually trumps execution. Suffice it to say I don&rsquo;t have a diary.</p>
</div>
<div class="paragraph">
<p>Those who follow <a href="http://cs50.tv/">cs50.tv</a> (and its sister sites <a href="http://cs75.tv/">cs75.tv</a>, <a href="http://cs259.tv/">cs259.tv</a>, and <a href="http://computerscience1.tv/">computerscience1.tv</a>) may have noticed some downtime over the past several days. The server on which cs50.tv lives (nay, lived) is actually a pretty decent machine: a <a href="http://www.dell.com/us/en/dfb/servers/pedge_2950/pd.aspx?refid=pedge_2950&amp;cs=28&amp;s=dfb">Dell PowerEdge 2950 III</a> with two <a href="http://en.wikipedia.org/wiki/Multi-core_processor">quad-core Xeon</a> CPUs, 32GB of RAM, and 6TB of disk space. Most everything in the machine that can be redundant is redundant: it has two <a href="http://en.wikipedia.org/wiki/Power_supply_unit_(computer)%5C">PSUs</a> so that if one of them (or the electrical circuit to which it&rsquo;s connected) dies, the other can take over the load; 6 HDDs in a <a href="http://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_6">RAID6</a> configuration, which means that as many as 2 <a href="http://en.wikipedia.org/wiki/Hard_drive">HDDs</a> can die without downtime or loss of data, and 2 <a href="http://en.wikipedia.org/wiki/Network_Interface_Controller">NICs</a> so that if one of them (or the switch to which it&rsquo;s connected) dies, we can move the other to our second switch. Because they have moving parts, PSUs and HDDs are among the few components in modern computers that tend to fail these days, so these redundancies help mitigate that risk.</p>
</div>
<div class="paragraph">
<p>Unfortunately, a server tends to have just one <a href="http://en.wikipedia.org/wiki/Backplane">backplane</a>, a circuit board into which important stuff connects (namely, in our case, HDDs). And that&rsquo;s what keeled over this past Wednesday, for no particular reason. The first symptoms were that the <a href="http://en.wikipedia.org/wiki/Virtual_machine">VMs</a> that lived on this box became suddenly unresponsive (i.e., cs50.tv "went down"); on at least one of their consoles was mention of "I/O errors," which generally mean RAM or disk problems. We thus proceeded to look at the server&rsquo;s own console, which boasted, let&rsquo;s call it, a red screen of death. (The server was running <a href="http://en.wikipedia.org/wiki/VMware_ESX">VMware ESX</a> 4.0, whose screen apparently turns red when really bad things happen.) We thus rebooted the whole server and booted into the RAID controller&rsquo;s configuration utility (a tool with which you configure RAID6 and such), which informed us that disks 1 and 2 were "Failed" and, get this, disk 0 was "Missing." I&rsquo;ll admit I wondered for a moment who had stolen my hard drive.</p>
</div>
<div class="paragraph">
<p>Unfortunately, reseating the hard drives achieved nothing, nor did rebooting several times. It was time to call Dell. We were fortunate to reach an excellent technician (thanks, Fadi!) who concurred that the problem was most likely the server&rsquo;s backplane, the "Failed" disks, and/or the RAID controller (in descending order of probability).</p>
</div>
<div class="paragraph">
<p>Within 24 hours, Dell was on site. (Thanks, Tom!) Sure enough, we were in need of a new backplane. Once installed, all 6 of our HDDs started blinking green. (Green is good.) Drives 1 and 2 now appeared as "Foreign," which meant they were functional but unrecognized, presumably the result of the failed backplane. Thanks to RAID6, no matter: we imported the drives back into the array, and we were back in business.</p>
</div>
<div class="paragraph">
<p>Well, almost. Unfortunately, on its way out the door, the backplane had taken some of our data with it: an assortment of files and directories were either corrupted or lost. And so, despite all our redundancies, we had to restore from backups anyway. (Thank you, <a href="http://en.wikipedia.org/wiki/Murphy%E2%80%99s_law">Mr. Murphy</a>.)</p>
</div>
<div class="paragraph">
<p>If there&rsquo;s a silver lining in this particular episode, it&rsquo;s that we used it as an opportunity to upgrade (almost) all of CS50&rsquo;s servers to ESXi 4.1 in one fell swoop (which involved wiping some drives anyway), and we also transitioned <a href="http://cs50.tv/">cs50.tv</a> from <a href="http://en.wikipedia.org/wiki/Lighttpd">lighttpd</a> to <a href="http://en.wikipedia.org/wiki/Nginx">nginx</a>, the latter of which, we&rsquo;re hoping, will perform a bit better under load.</p>
</div>
<div class="paragraph">
<p>This is CS50, behind the scenes.</p>
</div>]]></description><guid>http://blog.cs50.net/2010/08/11/0/</guid><link>http://blog.cs50.net/2010/08/11/0/</link><pubDate>Wed, 11 Aug 2010 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>Our thanks to Motiur Rahman for submitting this bug of the day, a screenshot he took while downloading Chrome on Ubuntu:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/05/03/1/chrome.png" alt="screenshot"></span></p>
</div>
<div class="paragraph">
<p>Spot it yet?</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/05/03/1/</guid><link>http://blog.cs50.net/2010/05/03/1/</link><pubDate>Mon, 03 May 2010 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>Time passes&hellip; An embarrassing number of <span class="line-through">days</span> months has passed since the last post, and it turns out we&rsquo;re not very good at approving comments. (Apologies to those whose comments were in limbo for so long!) Unfortunately, it seems most every time we have an idea for a post, something comes up (including, sometimes, sleep) and out the window goes the idea. But not this time! For those who dabble in JavaScript, read on&hellip;</p>
</div>
<div class="paragraph">
<p><a href="http://cs50.tv/">CS50</a> has a suite of apps that (with a few exceptions) share a common navigation header. Note the common drop-down in the top-right corner of <a href="http://energy.cs50.net/">HarvardEnergy</a>, <a href="http://maps.cs50.net/">HarvardMaps</a>, <a href="http://news.cs50.net/">HarvardNews</a>, <a href="http://tweets.cs50.net/">HarvardTweets</a>, and <a href="http://shuttleboy.cs50.net/">Shuttleboy</a>. (Hm. Which of those names is not like the others?) Up until recently, that common header was the result of a whole lot of copy-paste, which was getting increasingly annoying to maintain, and so I decided to factor it out (like I probably should have from the get-go). Within the <code>body</code> element of each app&rsquo;s <code>index.php</code> now is, essentially, a line like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="php language-php"><span class="predefined">require</span>(<span class="string"><span class="delimiter">"</span><span class="content">/local/path/to/common.php</span><span class="delimiter">"</span></span>);</code></pre>
</div>
</div>
<div class="paragraph">
<p>For purely aesthetic reasons, though, I wanted the links to the left of the drop-down to be crimson (<code>#600</code>) and the links to the right of the drop-down to be black (except for the "50"). All of those stylizations are easy to do with inline CSS, as with the below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;a</span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">color: #600;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>API<span class="tag">&lt;/a&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>However, I also wanted the links to be underlined only when hovered over. Half of that effect is easily achieved with similarly inline CSS:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;a</span> <span class="attribute-name">style</span>=<span class="string"><span class="delimiter">"</span><span class="content">color: #600; text-decoration: none;</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>API<span class="tag">&lt;/a&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Unfortunately, a <a href="http://www.w3.org/TR/CSS2/selector.html#dynamic-pseudo-classes">pseudo-selector</a> like <code>:hover</code> can&rsquo;t be implemented in this same fashion. (See <a href="http://stackoverflow.com/questions/1033156/how-to-write-ahover-in-inline-css">Stack Overflow</a> for a nice discussion.) Of course, it&rsquo;s easy to include in an internal (or external) stylesheet:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;style</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/css</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
<span class="comment">&lt;!--

a:hover
{
    text-decoration: underline;
}

--&gt;</span>
<span class="tag">&lt;/style&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>But, for a page to be considered valid <a href="http://www.w3.org/TR/xhtml1/dtds.html">XHTML</a>, any style elements must be children of a page&rsquo;s <code>head</code>, not <code>body</code>, element. The implication is that I&rsquo;d need to put that CSS inside a second file (e.g., <code>style.php</code>) and then add, within the head element of each app&rsquo;s <code>index.php</code> file, a line like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="php language-php"><span class="predefined">require</span>(<span class="string"><span class="delimiter">"</span><span class="content">/local/path/to/style.php</span><span class="delimiter">"</span></span>);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Or I could let HTTP grab that CSS by adding, within the head element of each app&rsquo;s <code>index.php</code> file, a line like</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;link</span> <span class="attribute-name">href</span>=<span class="string"><span class="delimiter">"</span><span class="content">/remote/path/to/style.css</span><span class="delimiter">"</span></span> <span class="attribute-name">rel</span>=<span class="string"><span class="delimiter">"</span><span class="content">stylesheet</span><span class="delimiter">"</span></span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/css</span><span class="delimiter">"</span></span> <span class="tag">/&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>whereby <code>style.css</code> contains only the CSS, not the XHTML tags. I was annoyed by both solutions, though, since my goal was to simplify, ideally by centralizing all common code in just one file.</p>
</div>
<div class="paragraph">
<p>It occurred to me that a lot of JavaScript libraries support "lazy-loading" of CSS rules, whereby you can incorporate additional CSS rules into a page via JavaScript (cf. <a href="http://developer.yahoo.com/yui/get/">YUI 2&rsquo;s Get Utility</a>), but I didn&rsquo;t want to have to include yet another JavaScript library in every app just to underline some links. Fortunately, you can re-create that same functionality yourself quite easily! Inside of <code>common.php</code>, I essentially included the JavaScript below:</p>
</div>
<div class="listingblock large">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;script</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/javascript</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
<span class="inline"><span class="comment">// &lt;![CDATA[</span>

<span class="comment">// wrap code in an anonymous function so that</span>
<span class="comment">// variable (e) doesn't conflict with variables defined elsewhere</span>
(<span class="keyword">function</span>() {

    <span class="comment">// create style element</span>
    <span class="keyword">var</span> e = document.createElement(<span class="string"><span class="delimiter">"</span><span class="content">style</span><span class="delimiter">"</span></span>);
    <span class="comment">// add attribute to element</span>
    e.type = <span class="string"><span class="delimiter">"</span><span class="content">text/css</span><span class="delimiter">"</span></span>;

    <span class="comment">// define CSS rules in a text node</span>
    <span class="keyword">var</span> t = document.createTextNode(<span class="string"><span class="delimiter">"</span><span class="content">div#apps a { text-decoration: none; } div#apps a:hover { text-decoration: underline; }</span><span class="delimiter">"</span></span>);

    <span class="comment">// make text node a child of element</span>
    e.appendChild(t);

    <span class="comment">// make element a child of page's head element</span>
    document.getElementsByTagName(<span class="string"><span class="delimiter">"</span><span class="content">head</span><span class="delimiter">"</span></span>)[<span class="integer">0</span>].appendChild(e);

})();

<span class="comment">// ]]&gt;</span></span>
<span class="tag">&lt;/script&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>As you may have gathered from its comments, that code instructs a browser to insert this node inside a page&rsquo;s <code>head</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;style</span> <span class="attribute-name">type</span>=<span class="string"><span class="delimiter">"</span><span class="content">text/css</span><span class="delimiter">"</span></span><span class="tag">&gt;</span>
<span class="comment">&lt;!--

div#apps a
{
    text-decoration: none;
}

div#apps a:hover
{
    text-decoration: underline;
}

--&gt;</span>
<span class="tag">&lt;/style&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>(I added some whitespace to the above for clarity.) Incidentally, the XHTML in <code>common.php</code> is wrapped with</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;div</span> <span class="attribute-name">id</span>=<span class="string"><span class="delimiter">"</span><span class="content">apps</span><span class="delimiter">"</span></span><span class="tag">&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>and</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="html language-html"><span class="tag">&lt;/div&gt;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>to ensure that my CSS rules are applied only to the elements within. Ironically, it took me longer to write this post than it did to actually code that solution! But a neat application of JavaScript <a href="http://www.w3schools.com/jsref/">DOM</a> functions that offers some insight into how libraries like YUI achieve their own magic!</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/05/03/0/</guid><link>http://blog.cs50.net/2010/05/03/0/</link><pubDate>Mon, 03 May 2010 04:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>Okay, the bugs in this screenshot (also courtesy of a student) are even more subtle at first glance than <a href="https://blog.cs50.net/../2/">the last</a>. But we think our caption for this one is even wittier.</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/02/17/3/verizon.png" alt="screenshot"></span></p>
</div>
<div class="paragraph">
<p>Perhaps needless to say, someone forgot to <a href="http://en.wikipedia.org/wiki/Variable_interpolation#Interpolation">interpolate</a> his or her variables.</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/02/17/3/</guid><link>http://blog.cs50.net/2010/02/17/3/</link><pubDate>Wed, 17 Feb 2010 05:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>So, after making fun of Domino&rsquo;s in class one night for their <a href="https://blog.cs50.net/../1/"><code>printf</code> fail</a>, a student passed along this screenshot from their online bank account:</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/02/17/2/boa.png" alt="screenshot"></span></p>
</div>
<div class="paragraph">
<p>Sorry, no account number or PIN in the screenshot!</p>
</div>
<div class="paragraph">
<p>See the bug? It&rsquo;s pretty subtle at first. But it boils down to a difference in opinion with <a href="https://www.bankofamerica.com/">BoA</a> as to what "today" means.</p>
</div>
<div class="paragraph">
<p>We&rsquo;ll leave it to the reader (that&rsquo;s you) to noodle over why this bug happened around the time it did.</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/02/17/2/</guid><link>http://blog.cs50.net/2010/02/17/2/</link><pubDate>Wed, 17 Feb 2010 05:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>So this is a screenshot that I took just before checking out from <a href="http://dominos.com/">dominos.com</a> one night. (I used to like their thin-crust pizza, but, frankly, I&rsquo;m not a fan of their <a href="http://www.google.com/search?q=domino%27s+new+recipe">new recipe</a>.) At the time, I was teaching <a href="http://cs75.tv/">Computer Science E-75: Building Dynamic Websites</a> at <a href="http://www.extension.harvard.edu/">Harvard Extension School</a>, and it seemed a fun lecture example of a bug that someone at Domino&rsquo;s really should have caught. (It persisted for months until they replaced this last-second upsell attempt with an ad for "Chocolate Lava Crunch Cakes," whatever those are.)</p>
</div>
<div class="paragraph">
<p><span class="image"><img src="https://blog.cs50.net/2010/02/17/1/chicken.png" alt="screenshot"></span></p>
</div>
<div class="paragraph">
<p>See it yet?</p>
</div>
<div class="paragraph">
<p>Indeed, the price of those Kickers should probably be $6.30, not $6.3. Of course, as <a href="http://en.wikipedia.org/wiki/Floating_point">floating-point values</a>, the two are arguably the same (imprecision of floats aside), but most people prefer their money formatted to two decimal places.</p>
</div>
<div class="paragraph">
<p>How to fix? Without seeing the code, it&rsquo;s hard to say for sure, but odds are the bug relates to improper use of a function like <a href="http://en.wikipedia.org/wiki/Printf"><code>printf</code></a>, which allows programmers to format numbers as they see fit. For instance, if f is a float, it can be formatted like money with <code>printf</code> in C as follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="c language-c">printf(<span class="string"><span class="delimiter">"</span><span class="content">%.2f</span><span class="delimiter">"</span></span>, f);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The <code>.2</code> tells <code>printf</code> to format the float to two decimal places. Of course, the site&rsquo;s probably not implemented in C (in which much of <a href="http://cs50.tv/">CS50</a> is taught). In fact, a quick glance at the site&rsquo;s URLs suggests it&rsquo;s implemented in Java (c.f. <a href="http://www.dominos.com/home/index.jsp)">http://www.dominos.com/home/index.jsp)</a> using <a href="http://en.wikipedia.org/wiki/JavaServer_Pages">JSP</a>, which has its <a href="http://java.sun.com/javase/6/docs/api/java/io/Console.html#printf(java.lang.String,%20java.lang.Object%E2%80%A6)">own version of <code>printf</code></a> as well as alternatives, including <a href="http://java.sun.com/javase/6/docs/api/java/text/NumberFormat.html"><code>java.text.NumberFormat</code></a>, <a href="http://java.sun.com/javase/6/docs/api/java/text/DecimalFormat.html"><code>java.text.DecimalFormat</code></a>, and <a href="http://java.sun.com/javase/6/docs/api/java/util/Currency.html"><code>java.util.Currency</code></a>. Of course, because of the imprecision of floats, they really shouldn&rsquo;t be used for money at all, unless Domino&rsquo;s is willing to lose (lots of) pennies over time. Better to store prices as cents or use some <a href="http://en.wikipedia.org/wiki/Fixed-point_arithmetic">fixed-point</a> data type (e.g., MySQL offers a <a href="http://dev.mysql.com/doc/refman/5.1/en/numeric-types.html"><code>DECIMAL</code></a> data type that might help in this case).</p>
</div>
<div class="paragraph">
<p>Whatever the language or database in use, though, someone screwed up. And it shouldn&rsquo;t have been hard to fix! I suppose replacing Kickers with Chocolate Lava Crunch Cakes was, as they say, a workaround.</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/02/17/1/</guid><link>http://blog.cs50.net/2010/02/17/1/</link><pubDate>Wed, 17 Feb 2010 05:00:00 +0000</pubDate></item><item><description><![CDATA[<div class="paragraph">
<p>So this first post has been on the to-do list for quite some time, as we felt the inaugural post should really be something special.  Unfortunately, we keep coming up with some really good ideas for second and third posts but, having not yet posted the first, we haven&rsquo;t quite gotten around to posting those ideas either.
So we&rsquo;re not very good at this blogging thing it seems.</p>
</div>
<div class="paragraph">
<p>But that ends tonight.  We&rsquo;re finally crossing the first post off the to-do list.  Rather than aspire to be special, though, why don&rsquo;t we aim a bit lower and just explain the intent of this blog.</p>
</div>
<div class="paragraph">
<p>I happen to teach a course (<a href="http://cs50.tv/">CS50</a>) at <a href="http://www.college.harvard.edu/">Harvard College</a> as well as, like Dan, a few at <a href="http://www.extension.harvard.edu/">Harvard Extension School</a>, and, most every day of the week, it seems, some interesting technical question or topic comes up that&rsquo;s fun (for us, at least) to discuss.  Given that both of us learned most everything we know from others, we thought it&rsquo;d be fun (and only fair) to have some of those discussions a bit more publicly in hopes that others might enjoy learning what we&rsquo;ve learned from others.</p>
</div>
<div class="paragraph">
<p>Officially, this blog is "on the intellectual enterprises of computer science and the art of programming."  In the interests of full disclosure, we stole that from CS50&rsquo;s own <a href="http://www.registrar.fas.harvard.edu/computer-science">catalogue description</a>.  But it would appear to give us license to discuss most anything of technical interest, so that&rsquo;s why we chose it.  Admittedly, it&rsquo;s probably misleadingly lofty, since I wouldn&rsquo;t be surprised if our next post has a <a href="http://en.wikipedia.org/wiki/Lolcat">lolcat</a>.  But, overall, we do aspire for this blog to be educational (though occasionally silly) in nature.  So rather than just comment on technical things, we&rsquo;ll try to explain them along the way.</p>
</div>
<div class="paragraph">
<p>In the interests of self-defense, I should say that both Dan and I used to cringe anytime we heard the word "blog," since we didn&rsquo;t quite understand why we&rsquo;d want to read on a website what other people, oh, had for breakfast.  But these things seem here to stay, and a blog seems an apt forum to share technical thoughts publicly.  So thus was born the CS50 Blog.</p>
</div>
<div class="paragraph">
<p>Actually, originally, this blog was to be about funny bugs we&rsquo;ve encountered in real life, so the next few posts may be a bit screenshot-heavy as we clear out our backlog.  Rest assured they have nothing to do with the intellectual enterprises of computer science.</p>
</div>
<div class="paragraph">
<p>Anyhow, Dan tells me he had yogurt + granola + nuts + berries for breakfast.  I had lunch.</p>
</div>
<div class="paragraph">
<p>This is the CS50 Blog.</p>
</div>
<div class="paragraph">
<p>djm</p>
</div>]]></description><guid>http://blog.cs50.net/2010/02/17/0/</guid><link>http://blog.cs50.net/2010/02/17/0/</link><pubDate>Wed, 17 Feb 2010 05:00:00 +0000</pubDate></item>
    </channel>
</rss>
