<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>dougv.com « Doug Vanderweide &#187; Programming</title>
	<atom:link href="http://www.dougv.com/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dougv.com</link>
	<description>ASP.NET, PHP, XML, JavaScript, Web geekery, Entrepreneurship</description>
	<lastBuildDate>Thu, 17 May 2012 22:33:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>The Basics Of Avoiding MySQL Injection Attacks In ASP.NET Web Forms</title>
		<link>https://www.dougv.com/2012/05/08/the-basics-of-avoiding-sql-injection-attacks-in-asp-net-web-forms/</link>
		<comments>https://www.dougv.com/2012/05/08/the-basics-of-avoiding-sql-injection-attacks-in-asp-net-web-forms/#comments</comments>
		<pubDate>Tue, 08 May 2012 22:16:45 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Stored Procedures]]></category>
		<category><![CDATA[Web Forms]]></category>
		<category><![CDATA[coding standards]]></category>
		<category><![CDATA[data types]]></category>
		<category><![CDATA[elegance]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[regular expression]]></category>
		<category><![CDATA[Windows Server]]></category>

		<guid isPermaLink="false">https://www.dougv.com/?p=4805</guid>
		<description><![CDATA[Received in my email today: Hi say your blog and thought you might help. strsql = &#8220;SELECT StaffID, DesignationID, StaffName, Password, ShopID from staffT where StaffName =&#8221; &#038; UserName.Text &#038; &#8221; AND Password =&#8221; &#038; Password.Text &#038; &#8220;&#8221; from the string, the username.text and password.text are form controls. what is happening is there are passing [...]<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (25.1)</li>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24.2)</li>
				<li><a href="https://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/" rel="bookmark">Displaying Selected YouTube Data API Thumbnails On A Web Page Via ASP.NET Web Forms</a> (24.2)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>Received in my email today:</p>
<blockquote><p>
Hi</p>
<p>say your blog and thought you might help. </p>
<p>strsql = &#8220;SELECT StaffID, DesignationID, StaffName, Password, ShopID from staffT where StaffName =&#8221; &#038; UserName.Text &#038; &#8221; AND Password =&#8221; &#038; Password.Text &#038; &#8220;&#8221;</p>
<p>from the string, the username.text and password.text are form controls. what is happening is there are passing null values regardless of what you input in the text boxes resulting in a system error.</p>
<p>&#8220;System Error Object reference not set to an instance of an object&#8221;</p>
<p>Am using Mysql as the database.
</p></blockquote>
<p>I&#8217;m always glad to answer such questions, especially when the questioner is flirting with disaster, as much as this questioner is.</p>
<p>A trained eye can immediately spot the problem with the SQL statement above, aside from the problem of NULL values tossing errors. Namely, it&#8217;s wide-open to SQL injection. (And an even keener eye will note that the values for user name and password aren&#8217;t delimited with single-quotes.)</p>
<p>So here&#8217;s my reply email to the questioner:<br />
<span id="more-4805"></span><br />
Your SQL statement has three problems.</p>
<ol>
<li>It is wide open to injection attack. See <a href="http://www.unixwiz.net/techtips/sql-injection.html" target="_blank">http://www.unixwiz.net/techtips/sql-injection.html</a> for examples.</li>
<li>As you noted, when nulls are passed in, the expression fails.</li>
<li>It appears you have not delimited your text inputs with single quotes.</li>
</ol>
<p>Assuming you are using ASP.NET, and that your user names and passwords are only alphanumeric, the direct fix to your problem is this:</p>
<pre class="brush: vb; title: ; notranslate">
Dim strUser As String
If String.IsNullOrEmpty(UserName.Text) Then
	strUser = String.Empty
Else
	strUser = UserName.Text.Replace(&quot;'&quot;, &quot;''&quot;)
End If

Dim strPass As String
If String.IsNullOrEmpty(Password.Text) Then
	strPass = String.Empty
Else
	strPass = Password.Text.Replace(&quot;'&quot;, &quot;''&quot;)
End If

strsql = &quot;SELECT StaffID, DesignationID, StaffName, Password, ShopID from staffT where StaffName = '&quot; &amp; strUser &amp; &quot;' AND Password = '&quot; &amp; strPass &amp; &quot;'&quot;
</pre>
<p>That should get you going, but you should employ the following best practices fixes:</p>
<ol>
<li>Use <a href="http://dev.mysql.com/doc/refman/5.0/en/stored-routines.html" target="_blank">stored procedures</a> with paramerterized input values. This serves to automatically sanitize your queries for data type.</li>
<li>Use <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.requiredfieldvalidator.aspx" target="_blank">RequiredFieldValidators</a> and <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.regularexpressionvalidator.aspx" target="_blank">RegularExpressionValidators</a> to ensure that you receive some sort of input for each field, and that each does not contain an effort to inject your code with SQL.</li>
<li>Impose some sort of control that limits multiple login attempts.</li>
<li>Better yet, use the built-in <a href="http://msdn.microsoft.com/en-us/library/yh26yfzy.aspx" target="_blank">membership provider in ASP.NET</a>, which can be <a href="http://dev.mysql.com/doc/refman/5.1/en/connector-net-tutorials-asp-roles.html" target="_blank">used with MySQL</a>, and has already resolved many potential attack vectors.</li>
</ol>
<p>Hope this helps.</p>
<h3>Additional Thoughts And Clarification</h3>
<p>The guidance I gave the emailer, instructing him to escape single quotes, is a bare-minimum escaping sequence. As a practical matter, he should sanitize his inputs against additional MySQL sequences, such as double-dashes and semicolons, as well as reserved SQL statement words, such as DROP, ALTER, DELETE, etc. There&#8217;s an <a href="http://forums.asp.net/t/1254125.aspx" target="_blank">HTTPModule example over at the ASP.NET forums</a> that does this automatically for an application.</p>
<p>Using parameterized queries / stored procedures to combat SQL injection is a primary recommendation from both <a href="http://msdn.microsoft.com/en-us/library/ff648339.aspx" target="_blank">Microsoft</a> and <a href="http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf" target="_blank">MySQL</a> (pdf). </p>
<p>In addition to securing data type, query parameters limit the ability of an attacker to inject SQL by fixing the form of the query implicitly. In other words, it&#8217;s harder for him to mangle a parameter than it is to mangle a string.</p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/the-basics-of-avoiding-sql-injection-attacks-in-asp-net-web-forms" target="_blank">http://delicious.com/dougvdotcom/the-basics-of-avoiding-sql-injection-attacks-in-asp-net-web-forms</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (25.1)</li>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24.2)</li>
				<li><a href="https://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/" rel="bookmark">Displaying Selected YouTube Data API Thumbnails On A Web Page Via ASP.NET Web Forms</a> (24.2)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/coding-standards/" title="coding standards" rel="tag">coding standards</a>, <a href="https://www.dougv.com/tag/data-types/" title="data types" rel="tag">data types</a>, <a href="https://www.dougv.com/tag/elegance/" title="elegance" rel="tag">elegance</a>, <a href="https://www.dougv.com/tag/hacking/" title="hacking" rel="tag">hacking</a>, <a href="https://www.dougv.com/tag/regular-expression/" title="regular expression" rel="tag">regular expression</a>, <a href="https://www.dougv.com/tag/windows-server/" title="Windows Server" rel="tag">Windows Server</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/05/08/the-basics-of-avoiding-sql-injection-attacks-in-asp-net-web-forms/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TEA Time: New England GiveCamp 2012 Recap</title>
		<link>https://www.dougv.com/2012/05/07/tea-time-new-england-givecamp-2012-recap/</link>
		<comments>https://www.dougv.com/2012/05/07/tea-time-new-england-givecamp-2012-recap/#comments</comments>
		<pubDate>Tue, 08 May 2012 02:38:47 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Plugins]]></category>
		<category><![CDATA[WordPress Themes]]></category>
		<category><![CDATA[coding standards]]></category>
		<category><![CDATA[crowdsourcing]]></category>
		<category><![CDATA[ethics]]></category>
		<category><![CDATA[GiveCamp]]></category>
		<category><![CDATA[graphic design]]></category>
		<category><![CDATA[Jim O'Neil]]></category>
		<category><![CDATA[marketing]]></category>
		<category><![CDATA[Microsoft]]></category>

		<guid isPermaLink="false">https://www.dougv.com/?p=4756</guid>
		<description><![CDATA[Last weekend I was in Cambridge, Mass. for New England GiveCamp 2012, the third of annual meet-ups that match technical and design people with nonprofit organizations that need their help. My cause was The Esplanade Association, an organization that cares for the Charles River Esplanade Park. The Charles River Esplanade Park is the Boston-side green [...]<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2010/05/13/designers-and-developers-donate-your-time-talent-at-new-england-give-camp-june-11-13-2010/" rel="bookmark">Designers And Developers: Donate Your Time, Talent At New England GiveCamp, June 11-13, 2010</a> (30.2)</li>
				<li><a href="https://www.dougv.com/2011/05/03/new-england-givecamp-2011-what-a-weekend/" rel="bookmark">New England GiveCamp 2011: What A Weekend!</a> (24.2)</li>
				<li><a href="https://www.dougv.com/2010/06/10/at-new-england-givecamp-this-weekend/" rel="bookmark">At New England GiveCamp This Weekend</a> (23.4)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>Last weekend I was in Cambridge, Mass. for <a href="http://www.newenglandgivecamp.org" target="_blank">New England GiveCamp</a> 2012, the third of annual meet-ups that match technical and design people with nonprofit organizations that need their help.</p>
<div id="attachment_4757" class="wp-caption alignleft" style="width: 360px"><a href="https://www.dougv.com/wp-content/uploads/2012/05/Vanderwarker_IMG_1542_big.jpg"><img class="size-medium wp-image-4757 " title="Charles River Esplanade" src="https://www.dougv.com/wp-content/uploads/2012/05/Vanderwarker_IMG_1542_big-350x233.jpg" alt="Charles River Esplanade" width="350" height="233" /></a><p class="wp-caption-text">The Charles River Esplanade is on the left. Hatch Memorial Shell and Teddy Ebersol&#39;s Red Sox Fields are in the foreground.</p></div>
<p>My cause was <a href="http://www.esplanadeassociation.org/">The Esplanade Association</a>, an organization that cares for the Charles River Esplanade Park.</p>
<p>The <a href="http://g.co/maps/cxt83" target="_blank">Charles River Esplanade Park</a> is the Boston-side green space along the river, from the <a href="http://www.mos.org/" target="_blank">Museum of Science</a> to the Boston University Bridge. While it&#8217;s owned and managed by the state of Massachusetts, TEA (which has to be the coolest acronym possible for a Boston-based group) exists to organize people to help protect and care for the park.</p>
<p>Much of their work involves organizing volunteers to clean up the park several times each year. TEA also holds a number of programs in the park &#8212; yoga, Zumba, dances and the like &#8212; and runs several fund raising projects.</p>
<p>They came to GiveCamp, initially, looking for a way to better coordinate singing up groups and individuals for cleanup days.<br />
<span id="more-4756"></span></p>
<h3>Ticketing Turns To Overhaul</h3>
<p>Currently, the way <strong>Jessica B. Pederson</strong>, TEA&#8217;s project manager, handles such reservations is by posting a schedule of cleanup days on her Web site, and asking people to email her if they are interested in participating. Then, via email exchanges back and forth, the details get worked out.</p>
<p>Even though the majority of these events are done through groups &#8212; so Jessica is in contact with a few group representatives, rather than scores of individuals &#8212; this proves an understandably arduous process that consumes a great deal of Jessica&#8217;s time, and it was something that she knew just screamed out to be automated.</p>
<p>You can&#8217;t swing a cat without hitting sample booking / ticketing system code on the Web, and there&#8217;s little practical difference between booking, say, a hotel room or buying a ticket to a play, and reserving a volunteer opportunity slot. Just provide the date, the number and types of tickets available, and a way for people to provide their contact information, and you&#8217;re done.</p>
<p>So I started by looking for ASP.NET-based ticketing solutions on the Web, since most of the Web developers who participate in GiveCamp are .NET folks. (Actually, I was side-hoping I could request <a href="https://twitter.com/#!/saush11" target="_blank">Saurabh Moondhra</a> and <a href="http://dirigosoftwaresolutions.com/Dirigo-Software-Solutions.aspx" target="_blank">William Wade</a>, the two ASP.NET developers I worked with during GiveCamp 2011, whom I knew would shine on such a project.)</p>
<p>And I was also thinking, in the back of my mind, it would be nice to address TEA&#8217;s Web site.</p>
<p>Don&#8217;t get me wrong; the site they had worked fine and contains a lot of good information presented in a sensible way. But it&#8217;s also stuck in 2005, and could use a cosmetic and functional overhaul.</p>
<p>And coincidentally, I am working on a WordPress site for a client right now that included the need for a basic calendaring and ticketing system. I had identified a plugin, <a href="http://wp-events-plugin.com/" target="_blank">Events Manager</a>, for them that wasn&#8217;t quite right for that project; but it almost perfectly fit Jessica&#8217;s needs, especially if she was willing to upgrade to the &#8220;pro&#8221; version.</p>
<p>And then, by coincidence again, <a href="https://twitter.com/#!/manage_kelley" target="_blank">Kelley Muir</a> &#8212; who coordinates the projects at GiveCamp &#8212; emailed me to say that she had asked this year&#8217;s nonprofits to identify possible second projects, and TEA said they&#8217;d like to have a content management system and general site redesign, too.</p>
<p>Jackpot! No need to reinvent the wheel; no need to even resize the wheel. We&#8217;re gonna put them in the Honda of the Web &#8212; good ol&#8217; WordPress &#8212; and slap some 20-inch rims (Events Manager) on it.</p>
<h3>Meet TEA</h3>
<p><img src="https://www.dougv.com/wp-content/uploads/2012/05/logo.jpg" alt="TEA logo" title="TEA logo" width="200" height="69" class="alignleft noborder size-full wp-image-4786" />I talked with Jessica and <strong>Christopher Timmel</strong>, TEA&#8217;s communications director, on the phone on Wednesday, to go over their needs and expectations, and knew instantly that I had lucked out a second time.</p>
<p>I&#8217;m a big fan of green space. You can&#8217;t live in Augusta, Maine and not like trees and rivers, after all.</p>
<p>But as a simple matter of public policy, I don&#8217;t think parks and open spaces get enough credit or resources, in spite of being a huge factor in quality of life. Understandably, because trees and grass pretty much grow on their own, it&#8217;s easy to put government dollars and effort elsewhere.</p>
<p>So when I see a group like TEA, I&#8217;m already a huge fan of their work.</p>
<p>Even better was that Jessica and Chris had a very clear picture of what they actually wanted. Even more rarely, what they wanted was actually what they needed. (It&#8217;s strange, but often, clients pitch requirements that don&#8217;t resolve their problems, or do so in ways that only make the problems worse or create new problems. Not so with Chris and Jessica.)</p>
<p>Plus, they&#8217;re personable, sincere and nice people. No coddling, wrangling or placating was in my weekend forecast. Not that it&#8217;s been a problem for me in past GiveCamps; just that any time you can get a client that&#8217;s positive about its problems, you need to celebrate.</p>
<h3>Meet The Team</h3>
<p>So I asked Kelley to assign a designer and a couple PHP people (since WordPress skinning requires a little knowledge of PHP, and I figured we might need to adjust some of the Events Manager code via overrides) to the project.</p>
<p>That&#8217;s how <a href="http://cmyung.com/" target="_blank">Christina Yung</a> and <strong>Jason Dufour</strong> were assigned to the project, and they were my third stroke of luck.</p>
<p><div id="attachment_4795" class="wp-caption alignright" style="width: 360px"><a href="https://www.dougv.com/wp-content/uploads/2012/05/IMG_0047.jpg"><img src="https://www.dougv.com/wp-content/uploads/2012/05/IMG_0047-350x235.jpg" alt="Jessica Pederson and Christina Yung" title="Jessica Pederson and Christina Yung" width="350" height="235" class="size-medium wp-image-4795" /></a><p class="wp-caption-text">Jessica Pederson, program director for The Esplanade Association, and Christina Yung, graphic designer, running through the old esplanadeassociation.org Web site on Friday night of GiveCamp 2012.</p></div>Christina primarily works in print, signage and branding, but has recently expanded into Web design. Jason is primarily an ASP.NET developer, but has PHP experience, and very strong jQuery and CSS skills. As anyone who&#8217;s ever skinned a WordPress site knows, the hard part is getting the CSS right, and the things you can&#8217;t quite get right in CSS, you can usually fix in jQuery.</p>
<p>An even better perk is that Jason and Christina work together at their day jobs, which means there would be no need to worry about chemistry or communication. And the weekend bore out that both were take-initiative-and-ownership types, which fits my management style perfectly: I like to provide an area of responsibility and a goal, then let people figure out, and do, what needs to get done to achieve it.</p>
<p>So two times I was a GiveCamp project lead, and two times Kelley assigned my team members who were skilled, friendly people who knew what to do and got done what needed to get done.</p>
<h3>The Project</h3>
<p>Even when you have a superior platform (WordPress); the right tools inside that platform (Events Manager); a client with full buy-in, all collateral organized and ready, and authority to make decisions on the fly (Chris and Jessica); and a team that&#8217;s got the skills and drive to get the job done (Jason and Christina), putting a Web site together over a weekend is a frenzy.</p>
<p>There were a few points where I thought we wouldn&#8217;t make it.</p>
<p>In addition to the usual bumps in any project road &#8212; misunderstandings, mistakes, unforeseen complications and unintended consequences are the hallmarks of every Web project &#8212; there were many, many steps in getting the content right.</p>
<p>Because of the kinds of information TEA needs to communicate, this WordPress site wasn&#8217;t header-footer-sidebar-index-css-and-we&#8217;re-done.</p>
<p>They needed fairly complicated page flow, and the calendar itself mutated a couple times as it became clearer what events would need a registration component, and which events they simply wanted to let people know about. And their information required a few different page templates and a couple extra sidebars, to ensure it showed up properly.</p>
<p>Additionally, I had the objective to use as few plugins (aside from Events Manager) and as little code as possible (especially in the templates themselves), because I wanted to ensure whomever helps them with technical needs going forward can make heads and tails out of what we did. So that meant every error we made in terms of approach, taxonomy and content was magnified, since we couldn&#8217;t simply find a plugin or write some code to fix the issue.</p>
<p>In the end, we would up with only eight plugins, three of which are standard in all my WordPress installs:</p>
<ul>
<li><a href="http://wordpress.org/extend/plugins/bluetrait-event-viewer/" target="_blank">Bluetrait Event Viewer (BTEV)</a>, which logs system events and is very useful for debugging, intrusion detection and the like;</li>
<li><a href="http://wordpress.org/extend/plugins/category-posts/" target="_blank">Category Posts Widget</a>, which &#8212; as its name suggests &#8212; allows you to show posts in a specific category as a sidebar widget;</li>
<li>Events Manager, a calendar and booking system I simply cannot flog hard enough;</li>
<li><a href="http://wordpress.org/extend/plugins/google-sitemap-generator/" target="_blank">Google XML Sitemaps</a>, to help with site indexing;</li>
<li><a href="http://wordpress.org/extend/plugins/html-sitemap/" target="_blank">HTML Page Sitemap</a>, which automatically generates a sitemap based on wp_list_pages();</li>
<li><a href="http://wordpress.org/extend/plugins/simple-lightbox/" target="_blank">Simple Lightbox</a>, for slideshow functionality (TEA has lots and lots of pictures);</li>
<li><a href="http://wordpress.org/extend/plugins/single-post-widget/" target="_blank">Single Post Widget</a>, which &#8212; as its name suggests &#8212; allows you to show a single post in a sidebar; and</li>
<li><a href="http://wordpress.org/extend/plugins/wp-super-cache/" target="_blank">WP Super Cache</a>, to reduce server load and improve site responsiveness.</li>
</ul>
<h3>The Results</h3>
<p>Jessica and Chris are happy with their new site. They&#8217;re going to take a week to finish migrating content, come up with a list of questions and tweaks, then present it to their board of directors for feedback.</p>
<p>But you don&#8217;t have to wait. Check out the before and after shots:</p>
<p><div id="attachment_4765" class="wp-caption alignleft" style="width: 204px"><a href="https://www.dougv.com/wp-content/uploads/2012/05/esplanade_before.png"><img class="size-medium wp-image-4765" title="esplanadeassociation.org, before the redesign" src="https://www.dougv.com/wp-content/uploads/2012/05/esplanade_before-194x350.png" alt="esplanadeassociation.org, before the redesign" width="194" height="350" /></a><p class="wp-caption-text">esplanadeassociation.org, before the redesign</p></div>&nbsp;<div id="attachment_4764" class="wp-caption alignleft" style="width: 326px"><a href="https://www.dougv.com/wp-content/uploads/2012/05/esplanade_after.png"><img class="size-medium wp-image-4764" title="esplanadeassociation.org, after the redesign" src="https://www.dougv.com/wp-content/uploads/2012/05/esplanade_after-316x350.png" alt="esplanadeassociation.org, after the redesign" width="316" height="350" /></a><p class="wp-caption-text">esplanadeassociation.org, after the redesign</p></div>
<div style="display: block; clear: both;"></div>
<p>As I quipped at the GiveCamp closing presentations, it looks good because Christina made it that way; it works because Jason made it that way; and it&#8217;s not done because I made it that way.</p>
<p>The site needs to go live still; a fair amount of content remains to be migrated in, there are a few cosmetic tweaks needed, and we didn&#8217;t run a complete walkthrough / debugging of the site because all the elements are not in place.</p>
<p>But that is not to detract at all from what was accomplished. While I intend to help TEA go live with this site, really it can be finished by anyone with basic HTML / CSS knowledge. Anything short of complete disapproval of the skin won&#8217;t be difficult to accommodate, and that skin is not going to be rejected. Not by sane people, anyway. </p>
<p>So I hit for the cycle: Great project; great client; great team; great results. GiveCamp 2012 was a complete success.</p>
<h3>A Round Of Applause</h3>
<p>I&#8217;d be remiss if I didn&#8217;t note, once again, what a great experience GiveCamp is on the whole.</p>
<p>There&#8217;s just something reaffirming and enjoyable in being around capable, motivated, positive people who are in it for the love. Sincerely, you meet the nicest and best people at GiveCamp.</p>
<p>You get fed well. (Thank you <a href="http://gmcr.com/" target="_blank">Green Mountain Coffee Roasters</a>, <a href="http://www.nakedpizza.biz/" target="_blank">Naked Pizza</a>, <a href="http://bgood.com/" target="_blank">b.good catering</a>, <a href="http://wholefoodsmarket.com/" target="_blank">Whole Foods</a>, <a href="http://capecodchips.com/" target="_blank">Cape Cod Chips</a>, <a href="http://ripvanwafels.com/wafel/" target="_blank">Rip van Wafels</a> and Cakes By Kelli.) You get all kinds of great swag (that&#8217;s generally true of any event hosted by <a href="http://blogs.msdn.com/b/jimoneil/" target="_blank">Jim O&#8217;Neil</a>, New England GiveCamp&#8217;s host and driving force, but is certainly true of GiveCamp). And you get to be in Cambridge, which is an amazing place.</p>
<p>I should also mention, after the disappointment of the Royal Sonesta hotel last year, that the best deal in town for a stay near MIT has to be the <a href="http://hamptoninn.hilton.com/en/hp/hotels/index.jhtml?ctyhocn=BOSCBHX" target="_blank">Hampton Inn</a> on Monsignor O&#8217;Brien Highway. Even though I figure I paid about $30 per hour to sleep there (11 hours of sleep @ $340 for two nights), it&#8217;s very clean, the bed is awesome, the staff is friendly and efficient, it&#8217;s 100 percent geared toward serving business travelers (free WiFi, lightning-fast checkin and automatic checkout), the hot breakfast is actually quite palatable and it&#8217;s only a one-mile walk up Third Street from <a href="http://microsoftcambridge.com/Default.aspx" target="_blank">NERD</a>, where GiveCamp is hosted.</p>
<p>So, everybody who&#8217;s taken part or helped make it happen, thanks. You done good. <em>Real</em> good. And if you couldn&#8217;t make it this year, GiveCamp 2013 is April 26-28; mark your calendars now!</p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/tea-time-new-england-givecamp-2012-recap" target="_blank">http://delicious.com/dougvdotcom/tea-time-new-england-givecamp-2012-recap</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2010/05/13/designers-and-developers-donate-your-time-talent-at-new-england-give-camp-june-11-13-2010/" rel="bookmark">Designers And Developers: Donate Your Time, Talent At New England GiveCamp, June 11-13, 2010</a> (30.2)</li>
				<li><a href="https://www.dougv.com/2011/05/03/new-england-givecamp-2011-what-a-weekend/" rel="bookmark">New England GiveCamp 2011: What A Weekend!</a> (24.2)</li>
				<li><a href="https://www.dougv.com/2010/06/10/at-new-england-givecamp-this-weekend/" rel="bookmark">At New England GiveCamp This Weekend</a> (23.4)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/coding-standards/" title="coding standards" rel="tag">coding standards</a>, <a href="https://www.dougv.com/tag/crowdsourcing/" title="crowdsourcing" rel="tag">crowdsourcing</a>, <a href="https://www.dougv.com/tag/ethics/" title="ethics" rel="tag">ethics</a>, <a href="https://www.dougv.com/tag/givecamp/" title="GiveCamp" rel="tag">GiveCamp</a>, <a href="https://www.dougv.com/tag/graphic-design/" title="graphic design" rel="tag">graphic design</a>, <a href="https://www.dougv.com/tag/jim-oneil/" title="Jim O&#039;Neil" rel="tag">Jim O&#039;Neil</a>, <a href="https://www.dougv.com/tag/marketing/" title="marketing" rel="tag">marketing</a>, <a href="https://www.dougv.com/tag/microsoft/" title="Microsoft" rel="tag">Microsoft</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/05/07/tea-time-new-england-givecamp-2012-recap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via ASP.NET</title>
		<link>https://www.dougv.com/2012/04/11/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-asp-net/</link>
		<comments>https://www.dougv.com/2012/04/11/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-asp-net/#comments</comments>
		<pubDate>Wed, 11 Apr 2012 14:02:01 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Databases]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Stored Procedures]]></category>
		<category><![CDATA[Transact-SQL]]></category>
		<category><![CDATA[Web Forms]]></category>
		<category><![CDATA[latitude / longitude]]></category>
		<category><![CDATA[regular expression]]></category>
		<category><![CDATA[ZIP Code database]]></category>

		<guid isPermaLink="false">https://www.dougv.com/?p=4664</guid>
		<description><![CDATA[If you provide a starting ZIP Code and a distance, this code will return all other ZIP codes within that distance.<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" rel="bookmark">Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL</a> (56.2)</li>
				<li><a href="https://www.dougv.com/2011/07/28/zip-codes-within-1000-mile-radius-of-19142/" rel="bookmark">zip codes within 1,000 mile radius of 19142</a> (32.2)</li>
				<li><a href="https://www.dougv.com/2012/02/22/all-zip-codes-100-miles-from-03251/" rel="bookmark">all zip codes 100 miles from 03251</a> (22.3)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>Some time ago I wrote a PHP-MySQL based solution to <a href="https://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" title="Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL">getting all ZIP Codes in a given radius from a known point / ZIP Code</a>. I&#8217;ve long intended to do an ASP.NET version of that post, and here it is.</p>
<p>I won&#8217;t bother revisiting the mechanics in detail. I do urge you to read the post on the PHP version of this solution, at least to familiarize yourself with the mechanics of what I am doing and the compromises I&#8217;ve taken in coming up with this solution.</p>
<p>I will note the following for the &#8220;get to the point&#8221; types:</p>
<ul>
<li>The first thing we need is to procure a geocoded database table of ZIP Codes. There are several out there; the one I am using is the <a href="http://sourceforge.net/projects/zips/" target="_blank">ZIP Code Database project</a>, available at Sourceforge. You&#8217;ll need to figure out how to get their CSV file into your SQL Server database; <a href="http://msdn.microsoft.com/en-us/library/ms188365.aspx" target="_blank">BULK INSERT</a> is an option, or you can script it.</li>
<li>The basic method I am going to use is to create a square. Specifically, I am going to:
<ul>
<li>ask the end user for a starting ZIP Code, and a radius from that point from which he would like other ZIP Codes to come;</li>
<li>create a square by selecting points North, South, East and West at the given distance from the starting point; then</li>
<li>query the database for all points that fall within that square (or, in other words, all points with latitudes less than North, greater than South, less than East and greater than West).</li>
</ul>
</li>
<li>I&#8217;m going to put my results in a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.aspx" target="_blank">GridView</a>. However, you could easily just use a <a href="http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx" target="_blank">DataReader</a> or <a href="http://msdn.microsoft.com/en-us/library/system.data.datatable.aspx" target="_blank">DataTable</a> to get the relevant records and do with them as you like.</li>
<li>The formulas I am using to compute longitude and latitude coordinates come from <a href="http://www.movable-type.co.uk/scripts/latlong.html" target="_blank">moveabletype.co.uk</a>.</li>
</ul>
<p><span id="more-4664"></span></p>
<h3>Step 1: Helper Functions</h3>
<p>Before we get into the nitty-gritty, we need to create two helper functions: One that converts degrees into radians, and another that reverses the process. (In trigonometry, angles are calculated in radians; a <a href="http://en.wikipedia.org/wiki/Radian" target="_blank">radian</a> is the ratio between the length of an arc and its radius. If an arc is as long as its radius, that&#8217;s 1<sup>c</sup>).</p>
<p>First, to convert from degrees to radians: </p>
<pre class="brush: vb; title: ; notranslate">
Function Deg2Rad(ByVal sglDegrees As Single) As Single
	Return sglDegrees * (Math.PI / 180.0)
End Function
</pre>
<p>And to go from radians to degrees:</p>
<pre class="brush: vb; title: ; notranslate">
Function Rad2Deg(ByVal sglRadians As Single) As Single
	Return sglRadians * (180.0 / Math.PI)
End Function
</pre>
<h3>Step 2: Functions To Plot Latitude And Longitude</h3>
<p>Now that we can move back and forth between degrees and radians, we can calculate the latitude and longitude of points at a known distance and bearing from an initial geocoordinate.</p>
<p>Given a known starting point, expressed as <em>lat1</em> and <em>lon1;</em> a known distance from that point, <em>d;</em> a known bearing from that point, <em>b;</em> and a known radius of the sphere over which we are travelling, <em>r;</em> we calculate a new geocoordinate, expressed as <em>lat2</em> and <em>lon2,</em> thus:</p>
<pre class="brush: plain; title: ; notranslate">
lat2 = asin(sin(lat1) * cos(d/r) + cos(lat1) * sin(d/r) * cos(b))
lon2 = lon1 + atan2(sin(b) * sin(d/r) * cos(lat1), cos(d/r) - sin(lat1)*sin(lat2))
</pre>
<div class="aside">Note that we have to convert our latitude, longitude and bearing variables into radians when we make these calculations; and we need to convert the results from radians into degrees.</div>
<p>So here&#8217;s a function that accepts, as arguments, a starting latitude, radius, bearing and distance; and returns the latitude at the provided bearing and distance from that starting latitude.</p>
<pre class="brush: vb; title: ; notranslate">
Function CalculateLatitudeCoordinate(ByVal sglLat1 As Single, ByVal intRadius As Integer, ByVal intBearing As Integer, ByVal intDistance As Integer) As Single
	Return Math.Asin(Math.Sin(sglLat1) * Math.Cos(intDistance / intRadius) + Math.Cos(sglLat1) * Math.Sin(intDistance / intRadius) * Math.Cos(intBearing))
End Function
</pre>
<p>And here&#8217;s a function that accepts, as arguments, a starting latitude and longitude, and ending latitude, radius, bearing and distance; and returns a longitude at the provided bearing and distance from the starting coordinates.</p>
<pre class="brush: vb; title: ; notranslate">
Function CalculateLongitudeCoordinate(ByVal sglLon1 As Single, ByVal sglLat1 As Single, ByVal sglLat2 As Single, intRadius As Integer, ByVal intBearing As Integer, ByVal intDistance As Integer) As Single
	Return sglLon1 + Math.Atan2(Math.Sin(intBearing) * Math.Sin(intDistance / intRadius) * Math.Cos(sglLat1), Math.Cos(intDistance / intRadius) - Math.Sin(sglLat1) * Math.Sin(sglLat2))
End Function
</pre>
<h3>Step 3: Create The Form</h3>
<p>To implement, we need to create a form with a few elements:</p>
<ul>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.textbox.aspx" target="_blank">TextBox</a> to accept an initial ZIP Code;</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.dropdownlist.aspx" target="_blank">DropDownList</a> to provide the desired distance from that point, which will serve to create our search area;</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.requiredfieldvalidator.aspx" target="_blank">RequiredFieldValidator</a>;</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.requiredfieldvalidator.aspx" target="_blank">RegularExpressionValidator</a>, to ensure we get a five-digit number as our input;</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.aspx" target="_blank">Button</a> to submit;</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.label.aspx" target="_blank">Label</a>, to display messages;</li>
<li>a GridView, to display the ZIP Codes we get from the database; and</li>
<li>a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.sqldatasource.aspx" target="_blank">SqlDataSource</a>, against which we will bind the ZIP Codes</li>
</ul>
<pre class="brush: xml; title: ; notranslate">
&lt;p&gt;
	Select all ZIP Codes within 

	&lt;asp:DropDownList runat=&quot;server&quot; ID=&quot;ddlDistance&quot;&gt;
		&lt;asp:ListItem Selected=&quot;True&quot;&gt;5&lt;/asp:ListItem&gt;
		&lt;asp:ListItem&gt;10&lt;/asp:ListItem&gt;
		&lt;asp:ListItem&gt;25&lt;/asp:ListItem&gt;
		&lt;asp:ListItem&gt;50&lt;/asp:ListItem&gt;
		&lt;asp:ListItem&gt;100&lt;/asp:ListItem&gt;
	&lt;/asp:DropDownList&gt;

	miles of ZIP Code

	&lt;asp:TextBox runat=&quot;server&quot; ID=&quot;tbZip&quot; Columns=&quot;5&quot; /&gt;
	&lt;asp:RequiredFieldValidator
		runat=&quot;server&quot;
		ID=&quot;rfvZip&quot;
		ControlToValidate=&quot;tbZip&quot;
		ErrorMessage=&quot;Please provide a ZIP Code&quot;
		CssClass=&quot;warning&quot;
		Display=&quot;Dynamic&quot;
	/&gt;
	&lt;asp:RegularExpressionValidator
		runat=&quot;server&quot;
		ID=&quot;revZip&quot;
		ControlToValidate=&quot;tbZip&quot;
		ValidationExpression=&quot;^[0-9]{5}$&quot;
		ErrorMessage=&quot;Please enter a valid five-digit ZIP Code&quot;
		CssClass=&quot;warning&quot;
		Display=&quot;Dynamic&quot;
	/&gt;

	&lt;asp:Button runat=&quot;server&quot; ID=&quot;btnZip&quot; Text=&quot;Get ZIP Codes&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;&lt;asp:Label runat=&quot;server&quot; ID=&quot;lblStatus&quot; Text=&quot;Status messages will appear here&quot; /&gt;&lt;/p&gt;

&lt;asp:GridView
	runat=&quot;server&quot;
	ID=&quot;gvZIP&quot;
	DataSourceID = &quot;sqlZip&quot;
	AutoGenerateColumns=&quot;false&quot;
	AllowSorting=&quot;true&quot;
	AllowPaging = &quot;true&quot;
	PageSize = &quot;20&quot;
	HeaderStyle-BackColor=&quot;Yellow&quot;
	HeaderStyle-Font-Bold=&quot;true&quot;
	HeaderStyle-HorizontalAlign=&quot;Center&quot;
	AlternatingRowStyle-BackColor=&quot;WhiteSmoke&quot;
	CellPadding=&quot;5&quot;
&gt;
	&lt;Columns&gt;
		&lt;asp:BoundField HeaderText=&quot;City&quot; DataField=&quot;cityname&quot; SortExpression=&quot;cityname&quot; /&gt;
		&lt;asp:BoundField HeaderText=&quot;State&quot; DataField=&quot;statecode&quot; SortExpression=&quot;statecode&quot; /&gt;
		&lt;asp:BoundField HeaderText=&quot;ZIP Code&quot; DataField=&quot;zip_code&quot; SortExpression=&quot;zip_code&quot; /&gt;
		&lt;asp:BoundField HeaderText=&quot;Latitude&quot; DataField=&quot;latitude&quot; SortExpression=&quot;latitude&quot; /&gt;
		&lt;asp:BoundField HeaderText=&quot;Longitude&quot; DataField=&quot;longitude&quot; SortExpression=&quot;longitude&quot; /&gt;
		&lt;asp:BoundField HeaderText=&quot;Distance&quot; DataField=&quot;distance&quot; SortExpression=&quot;distance&quot; /&gt;
	&lt;/Columns&gt;
&lt;/asp:GridView&gt;

&lt;asp:SqlDataSource
	runat=&quot;server&quot;
	ID=&quot;sqlZip&quot;
	SelectCommand=&quot;sp_get_zips_in_radius&quot;
	SelectCommandType=&quot;StoredProcedure&quot;
	ConnectionString=&quot;YOUR CONNECTION STRING&quot;
&gt;
	&lt;SelectParameters&gt;
		&lt;asp:Parameter Name=&quot;maxlat&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;minlat&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;maxlon&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;minlon&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;startlat&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;startlon&quot; DbType=&quot;Decimal&quot; DefaultValue=&quot;0.0&quot; /&gt;
		&lt;asp:Parameter Name=&quot;radius&quot; DbType=&quot;Int16&quot; DefaultValue=&quot;3959&quot; /&gt;
	&lt;/SelectParameters&gt;
&lt;/asp:SqlDataSource&gt;
</pre>
<div class="aside">Note that my SqlDataSource has <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.sqldatasource.selectparameters.aspx" target="_blank">SelectParameters</a>; I&#8217;ll use code behind to update those parameters with the coordinates I&#8217;ll figure out, also via code behind, and bind the GridView.</p>
<p>Although I am going to bind my data via codebehind, and therefore could have used a SqlDataReader, DataTable or the like as my GridView&#8217;s data source, I am using a SqlDataSource because I want to be able to page and sort my results, and I am too lazy to write code behind to do all that; those features are native to a GridView bound to a SqlDataSource.</p>
<p>If I didn&#8217;t want to page and sort, I&#8217;d just use a SqlDataReader and bind the GridView to that.</div>
<h3>Step 4: Get The Initial Point</h3>
<p>We need to get, from the database, the initial geocoordinates for the user-supplied ZIP Code. We do that with a stored procedure:</p>
<pre class="brush: sql; title: ; notranslate">
CREATE PROCEDURE [dbo].[sp_get_zip_code]
	@zip_code CHAR(5)
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT *
	FROM zip_codes
	WHERE zipcode = @zip_code
END
</pre>
<p>This query should return to us the city name, state, latitude and longitude for the specified ZIP Code. As always, we want to catch any exceptions in making the query, and we want to make sure we get a record from the database (that is, we can find the starting ZIP Code in the database).</p>
<p>If we can&#8217;t get starting coordinates, we&#8217;ll report that. </p>
<p>Otherwise, we&#8217;ll output details about the starting point to our Label control, and invoke a (yet-to-be-written) subroutine to populate the GridView.</p>
<pre class="brush: vb; title: ; notranslate">
Sub GetInitialCoordinates() Handles btnZip.Click
	'This subroutine requires a Label control named lblStatus

	'Prepare to connect to db and execute stored procedure
	Dim objConn As New SqlConnection(&quot;YOUR CONNECTION STRING&quot;)
	Dim objCmd As New SqlCommand(&quot;sp_get_zip_code&quot;, objConn)
	objCmd.CommandType = CommandType.StoredProcedure

	'we need to supply the ZIP code as an input parameter to our stored procedure
	objCmd.Parameters.Add(New SqlParameter(&quot;zip_code&quot;, SqlDbType.Char, 5))
	objCmd.Parameters(&quot;zip_code&quot;).Value = tbZip.Text

	'sglMinLat = south, sglMaxLat = north, sglMinLon = west, sglMaxLon = east
	Dim sglMinLat As Single
	Dim sglMaxLat As Single
	Dim sglMinLon As Single
	Dim sglMaxLon As Single

	Try
		'open connection
		objConn.Open()
		'put results into datareader
		Dim objReader As SqlDataReader
		objReader = objCmd.ExecuteReader()
		If objReader.HasRows Then
			'if starting point found, calculate box points
			objReader.Read()
			sglMinLat = Rad2Deg(CalculateLatitudeCoordinate(Deg2Rad(objReader(&quot;latitude&quot;)), 3959, Deg2Rad(180), ddlDistance.SelectedValue))
			sglMaxLat = Rad2Deg(CalculateLatitudeCoordinate(Deg2Rad(objReader(&quot;latitude&quot;)), 3959, Deg2Rad(0), ddlDistance.SelectedValue))
			sglMinLon = Rad2Deg(CalculateLongitudeCoordinate(Deg2Rad(objReader(&quot;longitude&quot;)), Deg2Rad(objReader(&quot;latitude&quot;)), Deg2Rad(sglMinLat), 3959, Deg2Rad(270), ddlDistance.SelectedValue))
			sglMaxLon = Rad2Deg(CalculateLongitudeCoordinate(Deg2Rad(objReader(&quot;longitude&quot;)), Deg2Rad(objReader(&quot;latitude&quot;)), Deg2Rad(sglMinLat), 3959, Deg2Rad(90), ddlDistance.SelectedValue))

			'report starting point details to lblStatus
			Dim strOut As String
			strOut = &quot;ZIP Code &quot; &amp; tbZip.Text &amp; &quot; is assigned to &quot; &amp; objReader(&quot;cityname&quot;) &amp; &quot;, &quot; &amp; objReader(&quot;statecode&quot;) &amp; &quot;.&lt;br /&gt;&quot;
			strOut &amp;= &quot;It is located at latitude &quot; &amp; objReader(&quot;latitude&quot;) &amp; &quot;, longitude &quot; &amp; objReader(&quot;longitude&quot;) &amp; &quot;.&lt;br /&gt;&lt;br /&gt;&quot;
			strOut &amp;= &quot;At a distance of &quot; &amp; ddlDistance.SelectedValue &amp; &quot; miles, the search box coordinates are:&lt;br /&gt;&quot;
			strOut &amp;= &quot;Maximum latitude (North): &quot; &amp; sglMaxLat &amp; &quot;&lt;br /&gt;&quot;
			strOut &amp;= &quot;Miniumum latitude (South): &quot; &amp; sglMinLat &amp; &quot;&lt;br /&gt;&quot;
			strOut &amp;= &quot;Maximum longitude (East): &quot; &amp; sglMaxLon &amp; &quot;&lt;br /&gt;&quot;
			strOut &amp;= &quot;Minimum longitude (West): &quot; &amp; sglMinLon &amp; &quot;&lt;br /&gt;&quot;
			lblStatus.Text = strOut

			'populate gridview
			PopulateGridView(sglMinLat, sglMaxLat, sglMinLon, sglMaxLon, objReader(&quot;latitude&quot;), objReader(&quot;longitude&quot;))
		Else
			'starting point not found
			lblStatus.Text = &quot;Error retrieving initial ZIP Code coordinates: No record found for &quot; &amp; tbZip.Text &amp; &quot;.&quot;
		End If
		objConn.Close()
		objCmd.Dispose()
		objConn.Dispose()
	Catch ex As Exception
		'technical problem running the query
		lblStatus.Text = &quot;Error executing database query for initial coordinates: &quot; &amp; ex.Message
	End Try
End Sub
</pre>
<h3>Step 5: Bind The GridView</h3>
<p>Lastly, we need to create a stored procedure that will accept the coordinates for our search box, and return all coordinates that fall within that box.</p>
<div class="aside">Notice that in addition to the North, South, East and West coordinates that form the search box, I am including the latitude and longitude of the ZIP Code that acts as our starting point, as well as the radius of the Earth. </p>
<p>That&#8217;s because I am going to calculate, on the fly, the distance from my starting point to the ZIP Codes returned by the query. </p>
<p>Given two known points, expressed as <em>lat1,</em> <em>lon1,</em> <em>lat2</em> and <em>lon2;</em> and a known radius of the sphere on which they are located, expressed as <em>r;</em> the distance between those points is found via this formula:</p>
<pre class="brush: plain; title: ; notranslate">distance = acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)) * r</pre>
<p>Again, we have to convert our latitude and longitude coordinates from degrees to radians; but note that we do not need to convert the result of the expression from radians to degrees. I will, however, round that result down to a precision of 2.<br />
</div>
<pre class="brush: sql; title: ; notranslate">
CREATE PROCEDURE [dbo].[sp_get_zips_in_radius]

@maxlat DECIMAL(9, 6),
@minlat DECIMAL(9, 6),
@maxlon DECIMAL(9, 6),
@minlon DECIMAL(9, 6),
@startlat DECIMAL(9, 6),
@startlon DECIMAL(9, 6),
@radius INT

AS
SELECT zipcode, statecode, latitude, longitude, cityname,
ROUND(ACOS(SIN(RADIANS(@startlat)) * SIN(RADIANS(latitude)) + COS(RADIANS(@startlat)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS(@startlon))) * @radius, 2) AS distance
FROM zip_codes
WHERE latitude &lt; @maxlat AND latitude &gt; @minlat AND longitude &lt; @maxlon AND longitude &gt; @minlon
ORDER BY distance, zipcode, statecode, cityname
</pre>
<p>Now that we have the stored procedure ready to go, we just need to update the values of our SqlDataSource&#8217;s SelectParameters, then bind the GridView:</p>
<pre class="brush: vb; title: ; notranslate">
Sub PopulateGridView(ByVal sglMinLat As Single, ByVal sglMaxLat As Single, ByVal sglMinLon As Single, ByVal sglMaxLon As Single, ByVal sglStartLat As Single, ByVal sglStartLon As Single)
	sqlZip.SelectParameters(&quot;minlat&quot;).DefaultValue = sglMinLat
	sqlZip.SelectParameters(&quot;maxlat&quot;).DefaultValue = sglMaxLat
	sqlZip.SelectParameters(&quot;minlon&quot;).DefaultValue = sglMinLon
	sqlZip.SelectParameters(&quot;maxlon&quot;).DefaultValue = sglMaxLon
	sqlZip.SelectParameters(&quot;startlat&quot;).DefaultValue = sglStartLat
	sqlZip.SelectParameters(&quot;startlon&quot;).DefaultValue = sglStartLon

	gvZIP.DataBind()
End Sub
</pre>
<p>And that&#8217;s all there is to it. You can see a working demo here: <a href="http://dougv.net/demos/zip_code_distance" target="_blank">http://dougv.net/demos/zip_code_distance</a></p>
<p>You can download the code here: <a href='https://www.dougv.com/wp-content/uploads/2012/04/zip_code_distance.zip'>https://www.dougv.com/wp-content/uploads/2012/04/zip_code_distance.zip</a></p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-asp-net" target="_blank">http://delicious.com/dougvdotcom/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-asp-net</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" rel="bookmark">Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL</a> (56.2)</li>
				<li><a href="https://www.dougv.com/2011/07/28/zip-codes-within-1000-mile-radius-of-19142/" rel="bookmark">zip codes within 1,000 mile radius of 19142</a> (32.2)</li>
				<li><a href="https://www.dougv.com/2012/02/22/all-zip-codes-100-miles-from-03251/" rel="bookmark">all zip codes 100 miles from 03251</a> (22.3)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/latitude-longitude/" title="latitude / longitude" rel="tag">latitude / longitude</a>, <a href="https://www.dougv.com/tag/regular-expression/" title="regular expression" rel="tag">regular expression</a>, <a href="https://www.dougv.com/tag/zip-code-database/" title="ZIP Code database" rel="tag">ZIP Code database</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/04/11/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-asp-net/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How To Increment A Counter In MySQL Based On A Radio Button Click</title>
		<link>https://www.dougv.com/2012/04/06/how-to-increment-a-counter-in-mysql-based-on-a-radio-button-click/</link>
		<comments>https://www.dougv.com/2012/04/06/how-to-increment-a-counter-in-mysql-based-on-a-radio-button-click/#comments</comments>
		<pubDate>Sat, 07 Apr 2012 00:42:06 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[arrays]]></category>
		<category><![CDATA[global constants]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[radio buttons]]></category>

		<guid isPermaLink="false">https://www.dougv.com/?p=4635</guid>
		<description><![CDATA[Asked recently on Formspring: how to increment count in database on clicking radio button There are a few ways to go about this. I&#8217;ll demonstrate two: a traditional, PHP / MySQL only, postback approach, and a jQuery version that uses AJAX to asynchronously record and update the counts. Just to be clear: In order to [...]<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2008/12/09/a-simple-page-click-count-system-using-php-and-mysql/" rel="bookmark">A Simple Page Click Count System Using PHP And MySQL</a> (22.3)</li>
				<li><a href="https://www.dougv.com/2006/11/26/a-simple-php-script-mysql-too-to-track-radio-station-song-requests-part-4/" rel="bookmark">A Simple PHP Script (MySQL, Too) To Track Radio Station Song Requests, Part 4</a> (20.4)</li>
				<li><a href="https://www.dougv.com/2006/11/26/a-simple-php-script-mysql-too-to-track-radio-station-song-requests-part-3/" rel="bookmark">A Simple PHP Script (MySQL, Too) To Track Radio Station Song Requests, Part 3</a> (20.1)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.formspring.me/dougvdotcom/q/312751702639910317" target="_blank">Asked recently on Formspring</a>:</p>
<blockquote><p>how to increment count in database on clicking radio button</p></blockquote>
<p>There are a few ways to go about this. I&#8217;ll demonstrate two: a traditional, PHP / MySQL only, postback approach, and a <a href="http://jquery.com/" target="_blank">jQuery</a> version that uses AJAX to asynchronously record and update the counts.</p>
<p>Just to be clear: In order to complete this solution, we have to use both JavaScript and a server-side scripting language. We use JavaScript to intercept the user clicking the radio button, but process the fact that the button was clicked on the server.</p>
<p>Also, for the purpose of this tutorial, I&#8217;ll assume that the radio button involved is part of a group. That is, we have several radio buttons, all with the same name, but different values, e.g.:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;form id=&quot;myform&quot; name=&quot;myform&quot; method=&quot;post&quot;&gt;
	&lt;p&gt;Select a color:&lt;/p&gt;
	&lt;label id=&quot;l_red&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_red&quot; name=&quot;color_name&quot; value=&quot;red&quot; /&gt;Red&lt;/label&gt; (&lt;label id=&quot;c_red&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_green&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_green&quot; name=&quot;color_name&quot; value=&quot;green&quot; /&gt;Green&lt;/label&gt; (&lt;label id=&quot;c_green&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_blue&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_blue&quot; name=&quot;color_name&quot; value=&quot;blue&quot; /&gt;Blue&lt;/label&gt; (&lt;label id=&quot;c_blue&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_black&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_black&quot; name=&quot;color_name&quot; value=&quot;black&quot; /&gt;Black&lt;/label&gt; (&lt;label id=&quot;c_black&quot;&gt;0&lt;/label&gt;)
&lt;/form&gt;
</pre>
<p><span id="more-4635"></span></p>
<h3>Example 1: PHP / MySQL Postback</h3>
<p>The easiest way to process a click is right on the page that contains the radio button, via a simple <a href="http://en.wikipedia.org/wiki/Postback" target="_blank">postback</a>.</p>
<p>We begin with a MySQL table that will hold our count. It has two columns: color_name, which will contain unique values and thus can act as our table&#8217;s primary key; and color_count, the number of times that color has been clicked.</p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE IF NOT EXISTS `colorcounter` (
  `colorname` varchar(5) NOT NULL,
  `colorcount` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`colorname`)
)
</pre>
<p>Next, we query the database for the current counts, which we&#8217;ll display in our form, later. We&#8217;ll dump the results set into an associative array, which will make outputting the counts in our form a little easier.</p>
<pre class="brush: php; title: ; notranslate">
$link = mysql_connect('server', 'user', 'password') or die('Cannot connect to database server');
mysql_select_db('database') or die('Cannot select database');

// click-count increment routine will go here; more on that shortly ...

// get current color click counts
$rs = mysql_query(&quot;SELECT * FROM colorcounter&quot;) or die ('Cannot process SQL count totals query');
if(mysql_num_rows($rs) &gt; 0) {
	while($row = mysql_fetch_array($rs)) {
		$count[$row['colorname']] = $row['colorcount'];
	}
}
</pre>
<p>We now need to set up our form to be ready to process the user click, and to display the click count records we previously received.</p>
<ul>
<li>We set the form to submit to itself.</li>
<li>We add an onclick event to each radio button, instructing it to submit the form. (The logic that will process the click follows shortly; we&#8217;re just assuming that every time the form is submitted, we need to increment a counter.)</li>
<li>We display the current click counts after each radio button.</li>
</ul>
<pre class="brush: xml; title: ; notranslate">
&lt;form id=&quot;myform&quot; name=&quot;myform&quot; method=&quot;post&quot; action=&quot;&lt;?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?&gt;&quot;&gt;
	&lt;p&gt;Select a color:&lt;/p&gt;
	&lt;label id=&quot;l_red&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_red&quot; name=&quot;color_name&quot; value=&quot;red&quot; onclick=&quot;this.form.submit();&quot; /&gt;Red&lt;/label&gt; (&lt;label id=&quot;c_red&quot;&gt;&lt;?php echo $count['red']; ?&gt;&lt;/label&gt;) |
	&lt;label id=&quot;l_green&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_green&quot; name=&quot;color_name&quot; value=&quot;green&quot; onclick=&quot;this.form.submit();&quot; /&gt;Green&lt;/label&gt; (&lt;label id=&quot;c_green&quot;&gt;&lt;?php echo $count['green']; ?&gt;&lt;/label&gt;) |
	&lt;label id=&quot;l_blue&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_blue&quot; name=&quot;color_name&quot; value=&quot;blue&quot; onclick=&quot;this.form.submit();&quot; /&gt;Blue&lt;/label&gt; (&lt;label id=&quot;c_blue&quot;&gt;&lt;?php echo $count['blue']; ?&gt;&lt;/label&gt;) |
	&lt;label id=&quot;l_black&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_black&quot; name=&quot;color_name&quot; value=&quot;black&quot; onclick=&quot;this.form.submit();&quot; /&gt;Black&lt;/label&gt; (&lt;label id=&quot;c_black&quot;&gt;&lt;?php echo $count['black']; ?&gt;&lt;/label&gt;)
&lt;/form&gt;
</pre>
<p>Finally, we need to add some code to the page that will see if the form has been submitted, and increment the appropriate count.</p>
<pre class="brush: php; title: ; notranslate">
// if this is a postback ...
if(isset($_POST['color_name'])) {
	// create array of acceptable values
	$ok = array('red', 'green', 'blue', 'black');
	// if we have an acceptable value for color_name ...
	if(in_array($_POST['colorname'], $ok)) {
		// update the counter for that color
		$q = mysql_query(&quot;UPDATE colorcounter SET colorcount = colorcount + 1 WHERE colorname = '&quot; . $_POST['color_name'] . &quot;'&quot;) or die (&quot;Error updating count for &quot; . $_POST['color_name']);
	}
}
</pre>
<p>Note that this code block should appear <em>before</em> the code block that gets the current click counts (where I included the comment in the first block of PHP code), in order to ensure the counts are up-to-the-minute.</p>
<p>You can see a working demo here: <a href="http://www.dougv.com/demo/ajax_radio_clickcount/index.php" target="_blank">http://www.dougv.com/demo/ajax_radio_clickcount/index.php</a></p>
<h3>Example 2: Using AJAX</h3>
<p>The previous example has a number of drawbacks, not the least of them being that each time you click on a radio button, the entire form posts back to the server. </p>
<p>That limits its usefulness quite a bit. We could work around this problem a few ways; perhaps making sure we <a href="https://www.dougv.com/2009/06/13/retaining-values-in-a-form-following-php-postback-and-clearing-form-values-after-successful-php-form-processing/" title="Retaining Values In A Form Following PHP Postback And Clearing Form Values After Successful PHP Form Processing">repopulate our form fields following the postback</a>, and only performing final processing when the submit button is clicked (by adding an onclick event to that button, which hands off the results to a JavaScript function or a different processing page).</p>
<p>That&#8217;s an awful lot of work for very little benefit. It makes way more sense to leverage AJAX, and update our click counts without having to post the form itself back to the server.</p>
<p>JavaScript &#8212; more specifically, jQuery &#8212; to the rescue!</p>
<div class="aside">I could have written a traditional AJAX solution here. However, it&#8217;s wordy to do that in a way that ensures browser comparability. </p>
<p>To me, it makes sense to encumber the jQuery library whenever you create an AJAX script, rather than reinventing the wheel with an old-school <a href="http://www.w3schools.com/xml/xml_http.asp" target="_blank">XMLHTTPRequest</a> object that has to be written several ways to ensure it works in whatever browser you encounter.</div>
<p>To get our jQuery solution to work, we need to create two PHP &#8220;helper&#8221; pages: one that will update our click counts whenever a radio button is selected, and the other that will get the current click counts, both on initial page load and following a click.</p>
<h3>The get_counts.php Helper Page</h3>
<p>Let&#8217;s start by calling the helper page that gets our click counts get_counts.php. It works almost exactly like the code block that gets the counts in our PHP postback example, except this page is going to output our results in <a href="http://www.json.org/" target="_blank">JSON</a>.</p>
<p>We&#8217;re putting the results out that way because it&#8217;s very easy for jQuery to process data in JSON format. </p>
<div class="aside"><strong>A crash course in JSON:</strong> In the end, JSON is just a fancy way to create associative arrays in JavaScript. Or, more simply, it&#8217;s an easy way to supply a whole bunch of key-value pairs to a JavaScript function.</p>
<p>Or, to put it even another way, think of JSON as the JavaScript version of an XML file. It&#8217;s just a way to organize data into columns and rows.</p>
<p>JSON is actually a bit more complex than that, but the way we&#8217;re using it, JSON is just a recordset, exactly like we would get from a MySQL query. It&#8217;s just written differently.</div>
<p>Let&#8217;s look  page will expect to see a querystring variable named &#8220;color,&#8221; which indicates which value needs to be incremented. The page code itself will be very similar to the processing code in our postback version.</p>
<pre class="brush: php; title: ; notranslate">
$link = mysql_connect('server', 'user', 'password') or die('Cannot connect to database server');
mysql_select_db('database') or die('Cannot select database');

// get new count totals, pass as JSON
$rs = mysql_query(&quot;SELECT * FROM colorcounter&quot;) or die('Cannot get updated click counts');
if(mysql_num_rows($rs) &gt; 0) {
	$out = &quot;{ &quot;;
	while($row = mysql_fetch_array($rs)) {
		$out .= &quot;\&quot;$row[colorname]\&quot; : $row[colorcount], &quot;;
	}
	$out = substr($out, 0, strlen($out) - 2);
	$out .= &quot; }&quot;;

	header(&quot;Content-type: application/json&quot;);
	echo $out;
}
</pre>
<h3>The increment_counter.php Helper Page</h3>
<p>Now that we have a way to get the counts, we need a way to increment them. Once again, we make a PHP helper page &#8212; increment_counter.php &#8212; that works fundamentally the same as the code block that increments the click counts in our PHP postback page, except that it calls upon GET, instead of POST. </p>
<p>(Yes, you can use <a href="http://api.jquery.com/jQuery.ajax/" target="_blank">jQuery to make POST and GET AJAX requests</a>. When I&#8217;m sending a single variable that just needs to meet a range, I prefer to use GET.)</p>
<pre class="brush: php; title: ; notranslate">
$link = mysql_connect('server', 'user', 'password') or die('Cannot connect to database server');
mysql_select_db('database') or die('Cannot select database');

// if this is a postback ...
if(isset($_GET['color'])) {
	// create array of acceptable values
	$ok = array('red', 'green', 'blue', 'black');
	// if we have an acceptable value for color_name ...
	if(in_array($_GET['color'], $ok)) {
		// update the counter for that color
		$q = mysql_query(&quot;UPDATE colorcounter SET colorcount = colorcount + 1 WHERE colorname = '&quot; . $_GET['color'] . &quot;'&quot;) or die (&quot;Error updating count for &quot; . $_GET['color']);
	}
}
</pre>
<h3>The Form And JavaScript</h3>
<p>With our helper pages out of the way, we can proceed to work on the form and the jQuery JavaScript needed to make this work.</p>
<p>As usual, our first step is to procure and include a copy of jQuery on our page. Lately, I prefer to simply link to the version hosted by the <a href="https://developers.google.com/speed/libraries/devguide" target="_blank">Google Libraries API</a>, but you can <a href="http://docs.jquery.com/Downloading_jQuery" target="_blank">download jQuery directly</a> and run it off your server if you prefer. Either way, call it somewhere in your page&#8217;s head section:</p>
<pre class="brush: xml; title: ; notranslate">&lt;script type=&quot;text/javascript&quot; src=&quot;https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js&quot;&gt;&lt;/script&gt;</pre>
<p>Next, I want to strip the form back to its basic components. </p>
<pre class="brush: xml; title: ; notranslate">
&lt;form id=&quot;myform&quot; name=&quot;myform&quot; method=&quot;post&quot;&gt;
	&lt;p&gt;Select a color:&lt;/p&gt;
	&lt;label id=&quot;l_red&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_red&quot; name=&quot;color_name&quot; value=&quot;red&quot; /&gt;Red&lt;/label&gt; (&lt;label id=&quot;c_red&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_green&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_green&quot; name=&quot;color_name&quot; value=&quot;green&quot; /&gt;Green&lt;/label&gt; (&lt;label id=&quot;c_green&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_blue&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_blue&quot; name=&quot;color_name&quot; value=&quot;blue&quot; /&gt;Blue&lt;/label&gt; (&lt;label id=&quot;c_blue&quot;&gt;0&lt;/label&gt;) |
	&lt;label id=&quot;l_black&quot;&gt;&lt;input type=&quot;radio&quot; id=&quot;r_black&quot; name=&quot;color_name&quot; value=&quot;black&quot; /&gt;Black&lt;/label&gt; (&lt;label id=&quot;c_black&quot;&gt;0&lt;/label&gt;)
&lt;/form&gt;
</pre>
<p>Notice that I no longer have onclick events assigned to each radio button. That&#8217;s because I&#8217;ll wire up that event via <a href="http://api.jquery.com/ready/" target="_blank">$(document).ready</a>.</p>
<p>More on that shortly. First, I want to declare a <a href="https://www.dougv.com/2006/12/18/variable-scope-made-simple/" title="Variable Scope Made Simple" target="_blank">global variable</a>, lastChecked. It will keep track of the value of the last radio button that was checked. </p>
<p>This will keep our page from recording multiple clicks on the same radio button, over and over again; clicks won&#8217;t be recorded unless, and until, a new value is clicked. In other words, if I just keep clicking Red, it will only be recorded once; if I then click Blue, that will be incremented, and clicking Red again will increment its value, once again.</p>
<pre class="brush: jscript; title: ; notranslate">var lastClicked = '';</pre>
<p>I next want to write JavaScript functions for each of my helper pages. The first, getTotals(), will &#8212; as its name suggests &#8212; send an AJAX request to get the current click totals. It will then assign those totals to the labels after each radio button.</p>
<pre class="brush: jscript; title: ; notranslate">
function getTotals() {
	// function to get click counts as JSON from helper page
	// expects get_count.php to be in same directory level

	$.ajax({
		type: &quot;GET&quot;,
		url: &quot;get_count.php&quot;,
		dataType: &quot;json&quot;,
		error: function(xhr, status, msg) {
			alert(&quot;Failed to get click counts: &quot; + msg);
		}
	})
	.done(function(data) {
		// loop through JSON variables, assign to count labels
		$.each(data, function(key, value) {
			var tmp = &quot;#c_&quot; + key;
			$(tmp).text(value);
		});
	});
}
</pre>
<p>I can now write the JavaScript function that will increment click counts. It&#8217;s named, quite cleverly, processClick, and takes as its argument the radio button (obj, for &#8220;object&#8221;) that was clicked.</p>
<pre class="brush: jscript; title: ; notranslate">

function processClick(obj) {
	// function to increment click count via ajax
	// expects increment_count.php to be in same directory level

	if(lastClicked != obj.val()) { // don't count clicks on currently active radio button
		lastClicked = obj.val(); // set currently clicked radio button to this one

		var qs = &quot;color=&quot; + obj.val(); // set query string value

		$.ajax({
			type: &quot;GET&quot;,
			url: &quot;increment_count.php&quot;,
			data: qs,
			error: function(xhr, status, msg) {
				alert(&quot;Failed to process click count: &quot; + msg);
			}
		})
		.done(function() {
			getTotals(); // update totals on successful processing
		});
	}
}
</pre>
<p>Finally, we want to use $(document).ready to initially populate the click total labels, and to assign processClick as the event handler for clicks on all the radio buttons on the page.</p>
<pre class="brush: jscript; title: ; notranslate">

$(document).ready(function() {
	getTotals(); // get click totals on initial page load

	$(document).ready(function() {
		// add click incrementing event handler to all radio buttons on page
		$('input:radio').click(function() {
			processClick($(this));
		});
	});
});
</pre>
<div class="aside">No doubt some of you have noticed that I pass the entire radio button object to my processClick function, when all I really need to pass is its value. I see it as six of one, half-dozen of another, but if there&#8217;s a compelling argument against sending the entire object vs. its value, leave a comment and let&#8217;s discuss it. I&#8217;m always happy to clean up bad code.</div>
<p>You can see a working example here: <a href="http://www.dougv.com/demo/ajax_radio_clickcount/example2.htm" target="_blank">http://www.dougv.com/demo/ajax_radio_clickcount/example2.htm</a></p>
<h3>Notes And Code</h3>
<p>A few notes about this project:</p>
<p>It&#8217;s not overly difficult for someone to click spam what I have here, either by simply clicking a radio button multiple times, or by calling the increment_count.php helper page multiple times.</p>
<p>A way around that would be to create single-use tokens for each iteration of the form, so that only one click per rendering of the form would be allowed.</p>
<p>You could do that by creating a session variable or cookie, if you&#8217;re using the PHP postback example; setting a Boolean in that session / cookie to be true the first time the form is submitted; and refusing to process the click if that session / cookie Boolean is true.</p>
<p>Or, for more security, you could add another MySQL table, which counts the number of times your form has been rendered. It would consist of an autoincremented primary key, which would act as an identifier for this instance of the form; and an tinyint column for recording a Boolean, which would indicate if the form had been submitted. </p>
<p>Every time your form is rendered, you would insert a new record in your form-rendering table, with a Boolean value of false indicating that the form has not yet been processed, then use mysql_insert_id to get the primary key value of the record you just created.</p>
<p>Using <a href="http://php.net/manual/en/book.mcrypt.php" target="_blank">mcrypt</a>, you would then create a single-use token by encrypting that record ID with a secret key only you know, and pass that to whatever method you are using to increment the counter. </p>
<p>That processing code would decrypt the token, make sure the record with that ID has a Boolean false as its &#8220;form has been submitted&#8221; value. If not, the code would not process the click; but if so, the code would process the click, then mark the form&#8217;s Boolean value as true.</p>
<p>An added benefit of this method is that it also inhibits forgeries and cross-site scripting attacks, since the only way to create legitimate tokens is via your secret key. (Theoretically, a hacker could crack your key, but use a sufficient passphrase and a good crypto algorithm, and the chances of anyone even taking a crack at it, nonetheless succeeding, are infinitesimal.)</p>
<p>Sounds more complex than it is. I&#8217;ve got demonstrating how to make a single-use token like this in Evernote blog column ideas. If significant interest is expressed, I&#8217;ll move it to the top of the list.</p>
<p>You can download the code in this post here: <a href='https://www.dougv.com/wp-content/uploads/2012/04/ajax_radio_clickcount.zip'>https://www.dougv.com/wp-content/uploads/2012/04/ajax_radio_clickcount.zip</a></p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/how-to-increment-a-counter-in-mysql-based-on-a-radio-button-click" target="_blank">http://delicious.com/dougvdotcom/how-to-increment-a-counter-in-mysql-based-on-a-radio-button-click</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2008/12/09/a-simple-page-click-count-system-using-php-and-mysql/" rel="bookmark">A Simple Page Click Count System Using PHP And MySQL</a> (22.3)</li>
				<li><a href="https://www.dougv.com/2006/11/26/a-simple-php-script-mysql-too-to-track-radio-station-song-requests-part-4/" rel="bookmark">A Simple PHP Script (MySQL, Too) To Track Radio Station Song Requests, Part 4</a> (20.4)</li>
				<li><a href="https://www.dougv.com/2006/11/26/a-simple-php-script-mysql-too-to-track-radio-station-song-requests-part-3/" rel="bookmark">A Simple PHP Script (MySQL, Too) To Track Radio Station Song Requests, Part 3</a> (20.1)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/arrays/" title="arrays" rel="tag">arrays</a>, <a href="https://www.dougv.com/tag/global-constants/" title="global constants" rel="tag">global constants</a>, <a href="https://www.dougv.com/tag/google/" title="Google" rel="tag">Google</a>, <a href="https://www.dougv.com/tag/radio-buttons/" title="radio buttons" rel="tag">radio buttons</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/04/06/how-to-increment-a-counter-in-mysql-based-on-a-radio-button-click/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Making A Simple WordPress Shortcode Plugin</title>
		<link>https://www.dougv.com/2012/03/16/making-a-simple-wordpress-shortcode-plugin/</link>
		<comments>https://www.dougv.com/2012/03/16/making-a-simple-wordpress-shortcode-plugin/#comments</comments>
		<pubDate>Fri, 16 Mar 2012 19:42:53 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress Plugins]]></category>
		<category><![CDATA[arrays]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[html entities]]></category>
		<category><![CDATA[metadata]]></category>
		<category><![CDATA[notepad++]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4526</guid>
		<description><![CDATA[If you&#8217;ve been reading this blog for any length of time, you&#8217;ve noticed my penchant for asides &#8212; brief digressions in which I explain a term, offer advice, or explain why I&#8217;m doing something a certain way. Until yesterday, I was doing that via raw HTML, by adding a div tag and assigning it to [...]<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2010/02/03/hacking-wp-plugins-used-to-remove-plugin-version-numbers/" rel="bookmark">Hacking WP-PluginsUsed To Remove Plugin Version Numbers</a> (16)</li>
				<li><a href="https://www.dougv.com/2008/08/25/fixing-various-issues-with-the-sociable-plug-in-for-wordpress/" rel="bookmark">Fixing Various Issues With The Sociable Plug-In For WordPress</a> (14.7)</li>
				<li><a href="https://www.dougv.com/2010/02/04/blog-changes-new-themes-new-syntax-plugin-several-plugins-deactivated/" rel="bookmark">Blog Changes: New Themes, New Syntax Plugin, Several Plugins Deactivated</a> (13.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve been reading this blog for any length of time, you&#8217;ve noticed my penchant for asides &#8212; brief digressions in which I explain a term, offer advice, or explain why I&#8217;m doing something a certain way.</p>
<p>Until yesterday, I was doing that via raw HTML, by adding a div tag and assigning it to the CSS class &#8220;aside,&#8221; which is defined in my theme&#8217;s style.css file:</p>
<pre class="brush: xml; title: ; notranslate">&lt;div class=&quot;aside&quot;&gt;This content will appear as an aside.&lt;/div&gt;</pre>
<p>And that makes the text above look like this:</p>
<div class="aside">This content will appear as an aside.</div>
<p>Well, I&#8217;m just as lazy as the next guy, and just as careless, too. So sometimes I was forgetting to close that div, or was misspelling it, or otherwise making a general mess by typing a simple div tag. </p>
<p>Which got me to thinking: Why not make a WordPress shortcode plugin, to abbreviate and simplify this repetitive task? </p>
<p>Making your own shortcode is an excellent way to learn the basics of <a href="http://codex.wordpress.org/Writing_a_Plugin" target="_blank">writing WordPress plugins</a>. And once you get the hang of it, you&#8217;ll find WordPress plugin authoring isn&#8217;t all that hard to do, yet will make you infinitely more marketable as a Web developer.<br />
<span id="more-4526"></span></p>
<h3>An Overview Of WordPress Shortcodes</h3>
<p>As the name implies, a WordPress shortcode is a way to use a simple, short directive to apply formatting, or append code, to a WordPress post or page.</p>
<p>More often than not, shortcodes are provided as part of a larger plugin solution. </p>
<p>For example, I use <a href="http://wordpress.org/extend/plugins/syntaxhighlighter/" target="_blank">SyntaxHighlighter Evolved</a> for code markup. </p>
<p>By typing a simple shortcode &#8212; &#91;vbnet&#93;, &#91;js&#93;, &#91;php&#93;, etc. &#8212; I can quickly apply <a href="http://code.google.com/p/syntaxhighlighter/" target="_blank">SyntaxHighlighter</a> to my code samples. SyntaxHighlighter Evolved will also do me the favor of converting HTML entities, so I don&#8217;t have to do that by hand.</p>
<p>For example, this:</p>
<pre>&#91;php&#93;
$foo = '&lt;p&gt;soup &amp; sandwich&lt;/p&gt;';
echo $foo;
&#91;/php&#93;</pre>
<p>Becomes this:</p>
<pre class="brush: php; title: ; notranslate">
$foo = '&lt;p&gt;soup &amp; sandwich&lt;/p&gt;';
echo $foo;
</pre>
<div class="aside">One thing a shortcode can&#8217;t do is prevent the WordPress visual editor from converting HTML entities. In other words, if you enter raw HTML in the HTML editor, then switch to the visual editor, it will probably mangle your code. </p>
<p>There are <a href="http://codex.wordpress.org/Writing_Code_in_Your_Posts#Frequent_Code_User">a number of plugins and strategies</a> meant to prevent this from happening. I&#8217;ve tried a few and found them problematic. That&#8217;s why I pretty much edit text exclusively in the HTML editor. It&#8217;s just easier and more reliable.</p>
<p>Of course, if you don&#8217;t usually blog things that require special coding, sticking with the visual editor is fine. It handles shortcodes without incident, provided what you put inside the shortcode isn&#8217;t raw HTML.</div>
<p>Another example of how a shortcode can be used to add elements or affect style is WordPress&#8217;s built-in <a href="http://codex.wordpress.org/CSS#WordPress_Generated_Classes" target="_blank">&#91;caption&#93; shortcode</a>.</p>
<p>If you look at your post in the WordPress HTML editor, after uploading and adding a captioned photo, the code block that contains the photo will look something like this:</p>
<pre>&#91;caption id="attachment_4535" align="alignnone" width="585" caption="Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet © 2011"&#93;&lt;img src="http://www.dougv.com/wp-content/uploads/2012/03/Blake_Lively_FNP_EW_0186946.jpg" alt="Blake Lively" title="The TIME 100 Gala: TIME&#039;S 100 Most Influential People In The World" width="585" height="840" class="size-full wp-image-4535" /&gt;&#91;/caption&#93;</pre>
<p>As you can see, the &#91;caption&#93; shortcode takes a series of attribute arguments &#8212; id, align, width and caption &#8212; and wraps itself around the selected image.</p>
<p>When the post renders on the screen, the HTML produced looks like this:</p>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;attachment_4535&quot; class=&quot;wp-caption alignnone&quot; style=&quot;width: 595px&quot;&gt;&lt;img src=&quot;http://www.dougv.com/wp-content/uploads/2012/03/Blake_Lively_FNP_EW_0186946.jpg&quot; alt=&quot;Blake Lively&quot; title=&quot;The TIME 100 Gala: TIME&amp;#039;S 100 Most Influential People In The World&quot; width=&quot;585&quot; height=&quot;840&quot; class=&quot;size-full wp-image-4535&quot; /&gt;&lt;p class=&quot;wp-caption-text&quot;&gt;Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet © 2011&lt;/p&gt;&lt;/div&gt;</pre>
<p>So, the &#91;caption&#93; shortcode:</p>
<ul>
<li>outputs a containing div;</li>
<li>assigns the id of the caption as the id of that containing div;</li>
<li>adds the wp-caption CSS class to the div;</li>
<li>assigns the align attribute as a CSS class for that div;</li>
<li>sets the width of the image, plus 10 pixels, as the width of the caption container (to allow for 5 px padding left and right); and</li>
<li>places the value of the caption attribute into a paragraph element, which is made a child element of the caption div.</li>
</ul>
<p>The final product looks like this:</p>
<div id="attachment_4535" class="wp-caption alignnone" style="width: 595px"><img src="http://www.dougv.com/wp-content/uploads/2012/03/Blake_Lively_FNP_EW_0186946.jpg" alt="Blake Lively" title="The TIME 100 Gala: TIME&#039;S 100 Most Influential People In The World" width="585" height="840" class="size-full wp-image-4535" /><p class="wp-caption-text">Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet © 2011</p></div>
<p>Obviously, a WordPress shortcode is a very powerful way to render formulaic content. Any time you find yourself repetitively writing the same HTML or text, over and over again, you should consider making a shortcode, especially if hand-coding that pattern is time-consuming or accident-prone.</p>
<h3>An Overview Of WordPress Plugins</h3>
<p>A WordPress plugin is basically a way to intercept <a href="http://codex.wordpress.org/Plugin_API/Action_Reference" target="_blank">any of the many events that take place when working with WordPress</a>, and to get WordPress to either alter how it goes about that task, add additional steps, or not do what it would normally do.</p>
<p>Usually, a plugin watches for when a post or page is created, and either adds some content, changes content somehow, or performs additional tasks related to the content being created or changed. </p>
<p>For example, there are plugins that will <a href="http://wordpress.org/extend/plugins/tags/twitter" target="_blank">post a tweet</a> any time you create a new post. Other plugins will optimize your post&#8217;s <a href="http://wordpress.org/extend/plugins/tags/seo" target="_blank">search engine indexing</a>; figure out <a href="http://wordpress.org/extend/plugins/tags/related" target="_blank">which previous posts are related to this post</a> and hyperlink to them; <a href="http://wordpress.org/extend/plugins/tags/tags" target="_blank">automatically tag</a> your post with relevant terms; etc.</p>
<p>Some plugins don&#8217;t deal with posts at all. There are plugins that extend and improve the built-in <a href="http://wordpress.org/extend/plugins/tags/users" target="_blank">user management</a> system; work with <a href="http://wordpress.org/extend/plugins/tags/custom-post" target="_blank">special post types</a>; provide <a href="http://wordpress.org/extend/plugins/tags/cart" target="_blank">shopping carts</a> or <a href="http://wordpress.org/extend/plugins/tags/forum" target="_blank">forums</a>; or even fundamentally change the functionality of WordPress.</p>
<p>A plugin might leverage one event in the WordPress lifecycle, or several events. It might do one thing &#8212; such as our plugin &#8212; or scores of things. </p>
<p>Really, the sky is the limit. By design, WordPress is very extensible, which is a big part of the reason why it&#8217;s so popular.</p>
<p>&#8220;There&#8217;s a plugin for that&#8221; is pretty much cliche when it comes to WordPress. But in those cases when there isn&#8217;t a plugin for that, you can make one yourself.</p>
<h3>Your First WordPress Plugin In Three Easy Steps</h3>
<p>So let&#8217;s make a really basic WordPress shortcode plugin &#8212; one that will apply a specific style to the text it contains.</p>
<p>First, let&#8217;s put together the CSS for our &#91;aside&#93; shortcode. We&#8217;ll call that CSS style, appropriately, .aside:</p>
<pre class="brush: css; title: ; notranslate">
div.aside {
	padding: 25px 50px;
	background: #efe;
	font-size: 12px !important;
	line-height: 150%;
	text-align: justify;
}
</pre>
<p>Put this CSS block in <a href="http://codex.wordpress.org/Theme_Development#Theme_Stylesheet" target="_blank">your theme&#8217;s style.css file</a>.</p>
<p>Next, we need to fire up a text editor &#8212; Notepad or <a href="http://support.apple.com/kb/HT2523" target="_blank">TextEdit</a> will do fine; I use <a href="http://notepad-plus-plus.org/" target="_blank">Notepad++</a> &#8212; and write the plugin that will provide our shortcode functionality.</p>
<div class="aside">Whatever text editor you use to write a plugin needs to save its files in <a href="http://stackoverflow.com/questions/701882/what-is-ansi-format" target="_blank">ANSI encoding</a>. In most plain-text editors, that&#8217;s the default encoding scheme.</p>
<p>If your text editor saves files in UTF-8, for example, when you activate a plugin, WordPress will report an error along the lines of:</p>
<pre class="brush: plain; title: ; notranslate">The plugin generated X characters of unexpected output during activation. If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.</pre>
<p>To fix that, set your text editor to save files in ANSI encoding, copy and paste your plugin code into new text files, replace your wrongly encoded plugin files with those new files, and reinstall the plugins.</div>
<p>According to the <a href="http://codex.wordpress.org/Plugin_API" target="_blank">WordPress Codex</a>, in order to create a plugin, we first need to provide a PHP comment block that contains some metadata: The name of our plugin, some author and version information, plus a description.</p>
<p>If we don&#8217;t include this metadata, we can&#8217;t manage it in the Plugins area of the WordPress admin section. So let&#8217;s do that first.</p>
<pre class="brush: php; title: ; notranslate">
/*
Plugin Name: Aside Shortcode
Plugin URI: http://www.dougv.com/2012/03/16/making-a-simple-wordpress-shortcode-plugin/
Description: A simple plugin that adds an &quot;aside&quot; shortcode to WordPress
Version: 1.0
Author: Doug Vanderweide
Author URI: http://www.dougv.com
License: GPL3
*/
</pre>
<p>With that out of the way, we can actually create our shortcode. We do that with a simple PHP function.</p>
<p>Our function should be named something other than what we want to use as a shortcode. In other words, I want to use &#91;aside&#93; as my shortcode; so I don&#8217;t want to call this function aside. Instead, I&#8217;ll name it aside_shortcode.</p>
<pre class="brush: php; title: ; notranslate">
function aside_shortcode($atts, $content = null) {
   return '&lt;div class=&quot;aside&quot;&gt;' . $content . '&lt;/div&gt;';
}
</pre>
<p>Note the two arguments. I&#8217;ll discuss those shortly.</p>
<p>As you can see, our function simply adds a div tag, with the aside class, around the content contained inside our shortcode tags. </p>
<p>We now need to give our shortcode a name, and tell it to use this function when it finds those shortcode tags. That&#8217;s handled via the add_shortcode function, which is built into WordPress:</p>
<pre class="brush: php; title: ; notranslate">add_shortcode('aside', 'aside_shortcode');</pre>
<p>So, here&#8217;s what our entire plugin PHP file looks like:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/*
Plugin Name: Aside Shortcode
Plugin URI: http://www.dougv.com/2012/03/16/making-a-simple-wordpress-shortcode-plugin/
Description: A simple plugin that adds an &quot;aside&quot; shortcode to WordPress
Version: 1.0
Author: Doug Vanderweide
Author URI: http://www.dougv.com
License: GPL3
*/

/*
Copyright 2012 Doug Vanderweide

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 3, as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

function aside_shortcode($atts, $content = null) {
   return '&lt;div class=&quot;aside&quot;&gt;' . $content . '&lt;/div&gt;';
}

add_shortcode('aside', 'aside_shortcode');
?&gt;
</pre>
<p>We just save this file with the name aside_shortcode.php.</p>
<p>Final step: Install and activate the plugin. </p>
<p>We can do that one of two ways:</p>
<ol>
<li>Use FTP to create an aside_shortcut folder inside our wp-content/plugins folder; then upload our aside_shortcut.php file into that new folder.</li>
<li>Add aside_shortcut.php to a ZIP file; then use the Add New feature, under Plugins in the WordPress admin section, to upload and automatically create the appropriate directory.</li>
</ol>
<p>With the plugin uploaded, we just need to go to the Plugins page in the WordPress admin section and activate it. </p>
<p><strong>Congratulations! You&#8217;re officially a WordPress developer.</strong></p>
<h3>More On WordPress Shortcodes</h3>
<p>I previously noted that aside_shortcode took two arguments: $attrs and $content. </p>
<p>(Technically, a shortcode function can take a third argument, $code, which is a reference to the shortcode name itself. But do me a favor and forget about that. There&#8217;s a reason for it, but it&#8217;s very specialized and almost always inapplicable.)</p>
<p>If I were writing a shortcode that had attributes / arguments &#8212; for example, such as this:</p>
<pre>&#91;caption id="attachment_4535" align="alignnone" width="585" caption="Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet © 2011"&#93;</pre>
<p>&#8211; then, when I wrote my shortcode handler function, $attrs would be an associative array of all the attributes that were added to my shortcode:</p>
<pre class="brush: php; title: ; notranslate">
$attrs['id'] = 'attachment_4535';
$attrs['align'] = 'alignnone';
$attrs['width'] = 585;
$attrs['caption'] = 'Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet &amp;copy; 2011';
</pre>
<p>In our &#91;aside&#93; shortcode, we&#8217;re not using attributes / arguments, so $attrs is a zero-length array.</p>
<p>But suppose you want to write a shortcode that can apply any of several CSS classes to a selection of text. Let&#8217;s call that shortcode &#8220;styler,&#8221; and let&#8217;s suppose it takes an argument with the name &#8220;class,&#8221; which will be the CSS class you want to assign to the enclosed text, via a span tag.</p>
<p>We can change our shortcode function to handle that:</p>
<pre class="brush: php; title: ; notranslate">
function styler_shortcode($atts, $content = null) {
   return '&lt;span class=&quot;' . $attrs['class'] . '&quot;&gt;' . $content . '&lt;/span&gt;';
}

add_shortcode('styler', 'styler_shortcode');
</pre>
<p>So, if we had this in our post content:</p>
<pre>Four score and &#91;styler class="big"&#93;seven years&#91;/styler&#93; ago, our fathers &#91;styler class="small"&#93;brought&#91;/styler&#93; forth on this &#91;styler class="green"&#93;continent&#91;/styler&#93; ...</pre>
<p>it would render this HTML:</p>
<pre class="brush: xml; title: ; notranslate">Four score and &lt;span class=&quot;big&quot;&gt;seven years&lt;/span&gt; ago, our fathers &lt;span class=&quot;small&quot;&gt;brought&lt;/span&gt; forth on this &lt;span class=&quot;green&quot;&gt;continent&lt;/span&gt; ...</pre>
<p>Again, you&#8217;d just need to add the relevant CSS classes to your theme&#8217;s style.css file, and those styles would be applied to the rendered text. </p>
<p>Actually, you could write your plugin in a way that it either adds the relevant CSS to your WordPress installation&#8217;s head, or references a stylesheet specific to the plugin; or even provide an admin settings panel that lets end users define styles. </p>
<p>That&#8217;s more advanced stuff, so I won&#8217;t get into it here. Just know that again, the possibilities are endless.</p>
<div class="aside">There&#8217;s a security risk in using the $attrs array. Namely, we&#8217;re not sure the attributes sent are ones we expect or want. In other words, people can send arbitrary arguments to our shortcode; and that can contaminate the output.</p>
<p>As the Shortcode API documentation notes, you can protect against this by using the built-in shortcode_attrs() function, which sanitizes the $attrs array before processing. As this is an introduction, I won&#8217;t get into that. I&#8217;ll just caution you that if your blog is used by multiple authors, any time you create a shortcode that relies on attributes, you&#8217;ll want to sanitize them first.</div>
<p>By now, you&#8217;ve probably figured out that $content represents all the text that appears between opening and closing shortcode tags. </p>
<p>In other words, given this:</p>
<pre>&#91;caption id="attachment_4535" align="alignnone" width="585" caption="Blake Lively At The TIME 100 Gala, 26 April 2011. Photo credit: Flynet © 2011"&#93;&lt;img src="http://www.dougv.com/wp-content/uploads/2012/03/Blake_Lively_FNP_EW_0186946.jpg" alt="Blake Lively" title="The TIME 100 Gala: TIME&#039;S 100 Most Influential People In The World" width="585" height="840" class="size-full wp-image-4535" /&gt;&#91;/caption&#93;</pre>
<p>$content is:</p>
<pre class="brush: xml; title: ; notranslate">&lt;img src=&quot;http://www.dougv.com/wp-content/uploads/2012/03/Blake_Lively_FNP_EW_0186946.jpg&quot; alt=&quot;Blake Lively&quot; title=&quot;The TIME 100 Gala: TIME&amp;#039;S 100 Most Influential People In The World&quot; width=&quot;585&quot; height=&quot;840&quot; class=&quot;size-full wp-image-4535&quot; /&gt;</pre>
<p>The default value of $content is null because in some cases, we may want to write a shortcode that simply renders some pre-determined content on the screen.</p>
<p>For example, suppose you have a couple standard lines of text you use to conclude a post:</p>
<pre class="brush: xml; title: ; notranslate">
Copyright &amp;copy; 2012 &lt;a href=&quot;http://www.twitter.com/#!/dougvdotcom/&quot;&gt;Doug Vanderweide&lt;/a&gt;.
I distribute code under the &lt;a href=&quot;http://www.gnu.org/licenses/gpl-3.0.html&quot;&gt;GNU GPL version 3&lt;/a&gt;.
I herd u liek mudkipz.
</pre>
<p>Since these lines are static, we can call them via a very simple shortcode. We&#8217;ll call that shortcode &#8220;kicker,&#8221; which will take an ID attribute of 1, 2 or 3, for whichever line we want to output; if no such attribute is specified, the third line will be used.</p>
<p>Thus, our shortcode function would be:</p>
<pre class="brush: php; title: ; notranslate">
function kicker_shortcode($atts, $content = null) {
	switch($attrs['id']) {
		case 1:
			$out = 'Copyright &amp;copy; 2012 &lt;a href=&quot;http://www.twitter.com/#!/dougvdotcom/&quot;&gt;Doug Vanderweide&lt;/a&gt;.';
			break;
		case 2:
			$out = 'I distribute code under the &lt;a href=&quot;http://www.gnu.org/licenses/gpl-3.0.html&quot;&gt;GNU GPL version 3&lt;/a&gt;.';
			break;
		default:
			$out = 'I herd u liek mudkipz.';
	}
   return $out;
}

add_shortcode('kicker', 'kicker_shortcode');
</pre>
<p>After installation, we call that shortcode in our posts or pages thus:</p>
<pre>Thanks for reading! &#91;kicker id="1"&#93;</pre>
<p>As in, no need for content / containing shortcode tags. Wherever we put that single shortcode tag, there will be our output:</p>
<pre class="brush: xml; title: ; notranslate">Thanks for reading! Copyright &amp;copy; 2012 &lt;a href=&quot;http://www.twitter.com/#!/dougvdotcom/&quot;&gt;Doug Vanderweide&lt;/a&gt;.</pre>
<p>You can download the sample shortcode plugins here: <a href='http://www.dougv.com/wp-content/uploads/2012/03/wordpress_shortcodes.zip'>http://www.dougv.com/wp-content/uploads/2012/03/wordpress_shortcodes.zip</a></p>
<div class="aside">I have added three separate plugin files to the download code, each representing one shortcode. But if you want to have a single plugin that contains multiple shortcodes, by all means, combine the PHP code into a single file. </p>
<p>In other words, you could strip out the PHP comments from two of these three plugin files, copy and paste the functions into one of them, change the plugin name of that file to be something more appropriate (e.g., &#8220;Style Shortcodes&#8221;), and install a that single plugin file. If you copy and paste correctly, all three shortcodes will be available in a single file.</div>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/making-a-simple-wordpress-shortcode-plugin" target="_blank">http://delicious.com/dougvdotcom/making-a-simple-wordpress-shortcode-plugin</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2010/02/03/hacking-wp-plugins-used-to-remove-plugin-version-numbers/" rel="bookmark">Hacking WP-PluginsUsed To Remove Plugin Version Numbers</a> (16)</li>
				<li><a href="https://www.dougv.com/2008/08/25/fixing-various-issues-with-the-sociable-plug-in-for-wordpress/" rel="bookmark">Fixing Various Issues With The Sociable Plug-In For WordPress</a> (14.7)</li>
				<li><a href="https://www.dougv.com/2010/02/04/blog-changes-new-themes-new-syntax-plugin-several-plugins-deactivated/" rel="bookmark">Blog Changes: New Themes, New Syntax Plugin, Several Plugins Deactivated</a> (13.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/arrays/" title="arrays" rel="tag">arrays</a>, <a href="https://www.dougv.com/tag/blogging/" title="blogging" rel="tag">blogging</a>, <a href="https://www.dougv.com/tag/html-entities/" title="html entities" rel="tag">html entities</a>, <a href="https://www.dougv.com/tag/metadata/" title="metadata" rel="tag">metadata</a>, <a href="https://www.dougv.com/tag/notepad/" title="notepad++" rel="tag">notepad++</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/03/16/making-a-simple-wordpress-shortcode-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Displaying Selected YouTube Data API Thumbnails On A Web Page Via ASP.NET Web Forms</title>
		<link>https://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/</link>
		<comments>https://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/#comments</comments>
		<pubDate>Thu, 15 Mar 2012 23:37:41 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Social Media]]></category>
		<category><![CDATA[Web Forms]]></category>
		<category><![CDATA[YouTube]]></category>
		<category><![CDATA[YouTube Data API]]></category>
		<category><![CDATA[bit.ly]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[RSS]]></category>
		<category><![CDATA[XPath]]></category>
		<category><![CDATA[XSLT]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4325</guid>
		<description><![CDATA[Describes how to query the YouTube Data API, process its response XML, and display thumbnails hyperlinked to youtube.com video pages.<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/" rel="bookmark">Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page</a> (64.7)</li>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24.5)</li>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (23.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>Previously, I blogged about &#8220;<a href="http://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/" title="Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page">Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page</a>,&#8221; when you know the video IDs of the thumbnails you want to hyperlink.</p>
<p>A reader recently asked me how to hyperlink YouTube video thumbnails based on searching for a keyword. I promised to address that, so here goes.</p>
<p>Interestingly enough, searching the <a href="https://developers.google.com/youtube/" target="_blank">YouTube Data API</a> is accomplished in a <a href="http://www.infoq.com/articles/rest-introduction" target="_blank">REST</a>-like manner quite similar to the methodology I used for <a href="http://www.dougv.com/2009/07/02/shortening-urls-with-the-bit-ly-api-via-asp-net/" title="Shortening URLs With The bit.ly API Via ASP.NET">shortening URLs in ASP.NET via the bit.ly API</a>. </p>
<ul>
<li>Form a simple request URL to the YouTube Data API that contains the appropriate search parameters;</li>
<li>Use a <a href="http://msdn.microsoft.com/en-us/library/system.net.webrequest.aspx" target="_blank">WebRequest</a> to send that URL to Google, which returns an XML document with results;</li>
<li>Use <a href="http://msdn.microsoft.com/en-us/library/system.net.webresponse.aspx" target="_blank">WebResponse</a> to dump that stream into an <a href="http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.aspx" target="_blank">XmlDocument</a>;</li>
<li>Use <a href="http://www.w3schools.com/xpath/default.asp" target="_blank">XPath</a> and <a href="http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.aspx" target="_blank">XmlNode</a>&#8216;s SelectNodes method to recursively get the thumbnails from each entry; and</li>
<li>Bind up a pile of <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.hyperlink.aspx" target="_blank">Hyperlink</a> controls, which are added dynamically to a <a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.panel.aspx" target="_blank">Panel</a> control.</li>
</ul>
<p>Sounds more complicated than it actually is. Let&#8217;s do it.<br />
<span id="more-4325"></span></p>
<h3>Overview Of The YouTube Data API</h3>
<p>First step in leveraging the YouTube Data API: <a href="https://code.google.com/apis/youtube/dashboard" target="_blank">Get a Youtube Data API developer key</a>.</p>
<div class="aside">As of this writing, you can keyword search query the YouTube Data API without supplying a developer key and get a useful response. However, Google wants you to supply a key, and a time may come where you must supply one. So take the 30 seconds required to register a key.</div>
<p>With our developer key in hand, we can proceed to build a simple keyword query. In this case, we&#8217;re going to get the top 12 videos that meet the search criteria &#8220;Miss Maine.&#8221; </p>
<p>For our purposes, &#8220;top 12 videos&#8221; means the 12 videos YouTube deems most relevant to the exact search term &#8220;Miss Maine.&#8221; In other words, our search is going to work exactly as though we had typed &#8220;Miss Maine&#8221; (including the double quotes) into the search box at youtube.com; the results we get back should be the same as the first 12 results we would get via a default search on youtube.com. (For the usual vague reasons, this won&#8217;t always be the case, but the results sets will be similar.)</p>
<p>The YouTube Data API tells us that, <a href="https://developers.google.com/youtube/2.0/reference#Searching_for_videos" target="_blank">to do a search query, we can GET a request to its servers</a>, with our search parameters as querystring variables, and Google will return to us an XML document containing records that match our query.</p>
<p>Because we&#8217;re not requesting user-specific data, we don&#8217;t need to authenticate our requests; as previously mentioned, we don&#8217;t even need to send along our developer key. (But, again, being courteous and thorough, we will send it, as requested.)</p>
<p>So, the methodology we use to get our records is exactly the same as we used to shorten a URL via the bit.ly API; we&#8217;re going to create a URL containing all our parameters, then make a WebRequest to the YouTube Data API; we&#8217;ll get its returned XML via a WebResponse, and push that into an XmlDocument, from which we can extract the information we want. </p>
<div class="aside">As we build this solution, let&#8217;s keep some non-technical things in mind:</p>
<ul>
<li>Many of the videos on YouTube aren&#8217;t very good, in the aesthetic, intellectual or civil senses. In other words, much of what is on YouTube is terrible.</li>
<li>A sizable number of YouTube videos are spam.</li>
<li>It&#8217;s <em>de rigueur</em> to keyword spam video descriptions, especially if the video is spam.</li>
<li>Trolling is mandatory for YouTube video comments, and you&#8217;ll probably be linking directly to that.</li>
</ul>
<p>We can mitigate the damage, somewhat, by applying the smartSearch filter, being very specific with our search term, including specific words we don&#8217;t want returned in our results, restricting the categories in which we want to search &#8230; in other words, being as specific as we can about what we want to see.</p>
<p>However, I&#8217;ll bet a dollar to doughnuts that if you request any sizable result set on any generic search term, your results set is going to contain items you wish weren&#8217;t in there. That&#8217;s just the nature of the thing, and there&#8217;s little that can be done about it.</p></div>
<h3>Querying The YouTube Data API</h3>
<p>There are three distinct phases in our solution: query the API, get the information we need from its response, and put that information on the page. Therefore, we will use three distinct functions and subroutines to do the work. </p>
<p>First up, a function to submit our query. It accepts as arguments our developer key and a formatted querystring, and it puts the response into an XML document for us. If the request fails, it will report as much in a Label control, and return an empty XML document.</p>
<pre class="brush: vb; title: ; notranslate">
Function QueryYouTubeDataAPI(ByVal strAPIKey As String, ByVal strQuery As String) As XmlDocument
	'Returns empty XMLDocument on failure, results XML on success
	'This function requires your page to have a label control named lblStatus for error reporting

	'build URL to shorten method resource
	Dim strUri As New StringBuilder(&quot;https://gdata.youtube.com/feeds/api/videos?&quot;)
	strUri.Append(&quot;v=2&quot;)
	strUri.Append(&quot;&amp;&quot;)
	strUri.Append(strQuery)
	strUri.Append(&quot;&amp;key=&quot;)
	strUri.Append(Server.HtmlEncode(strAPIKey))
	strUri.Append(&quot;&amp;prettyprint=true&quot;) 'adds line breaks &amp; white space to response; useful for debugging

	Dim objRequest As HttpWebRequest
	Dim objResponse As WebResponse
	Dim objXML As New XmlDocument() 'This is the document the function will return

	Try
		'create request for shorten resource
		objRequest = WebRequest.Create(strUri.ToString)
		'since we are passing querystring variables, our method is get
		objRequest.Method = &quot;GET&quot;
		'act as though we are sending a form
		objRequest.ContentType = &quot;application/x-www-form-urlencoded&quot;
		'don't wait for a 100 Continue HTTP response
		objRequest.ServicePoint.Expect100Continue = False
		'since we are using get, we need not send a request body; set content-length to 0
		objRequest.ContentLength = 0

		'read the Data API response into XML document
		objResponse = objRequest.GetResponse()
		objXML.Load(objResponse.GetResponseStream())
	Catch ex As Exception
		lblStatus.Text = &quot;Error querying YouTube Data API. Message: &quot; &amp; ex.Message
	End Try

	'send XML Document
	Return objXML
End Function
</pre>
<div class="aside"><strong>A note about error trapping:</strong> The error trapping I have used here does not check whether or not the YouTube Data API returns a sensible result. It only checks that we formed a request and the API responded.</p>
<p>If there is a problem with the query string you send to the YouTube Data API, or your request is somehow malformed, the API will return to you the 25 most popular videos for the day, as of the time of the request.</p>
<p>You can check to see if the YouTube Data API is seeing your request properly by looking for the /feed/link rel=&#8217;self&#8217; node:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;feed xmlns='http://www.w3.org/2005/Atom' xmlns:app='http://www.w3.org/2007/app' xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:yt='http://gdata.youtube.com/schemas/2007' gd:etag='W/&amp;quot;CkYNSH07cSp7I2A9WhVSGEg.&amp;quot;'&gt;
	&lt;!-- ... previous nodes --&gt;
	&lt;link rel='self' type='application/atom+xml' href='https://gdata.youtube.com/feeds/api/videos?q=%22Miss+Maine%22&amp;amp;start-index=1&amp;amp;max-results=12&amp;amp;duration=short&amp;amp;safeSearch=strict&amp;amp;orderby=relevance_lang_en&amp;amp;v=2'/&gt;
	&lt;!-- additional nodes ... --&gt;
&lt;/feed&gt;
</pre>
<p>If the URL in that node doesn&#8217;t resemble the one you sent, most likely your querystring is malformed; but possibly, there are other errors in your request URL.
</p></div>
<h3>Creating The Query String</h3>
<p>You&#8217;ll notice that I am not very specific about the query string argument passed to the previous function. That&#8217;s because I want to give you maximum flexibility, and the best way to do that is to allow for a free-form query string to be supplied. </p>
<p>So let&#8217;s look at how I&#8217;ll build my sample query string. I&#8217;ll do that, of course, with key-value pairs. That is, I will specify the parameter I want to send, and set it as equal to something.</p>
<p>First up, the search term. We&#8217;re going to use &#8220;Miss Maine.&#8221; So, the first part of our query string is:</p>
<pre class="brush: plain; title: ; notranslate">q=&quot;Miss Maine&quot;</pre>
<div class="aside">Note that the YouTube Data API says all parameters should be URL encoded. Our function will take care of that for us when we create the WebRequest, so don&#8217;t bother encoding your query string in advance, or you&#8217;ll get errors. Just pass it to the function as natural text.</div>
<p>Technically, we can stop here and let the default API request variables take over. But I want to be more specific about my search results, so let&#8217;s add some more parameters.</p>
<p>Next, I&#8217;ll limit my responses to 12 records, by appending that key-value pair to my string:</p>
<pre class="brush: plain; title: ; notranslate">q=&quot;Miss Maine&quot;&amp;max-results=12</pre>
<p>I&#8217;d like only short (under 4 minutes) videos:</p>
<pre class="brush: plain; title: ; notranslate">q=&quot;Miss Maine&quot;&amp;max-results=12&amp;duration=short</pre>
<p>To cull at least some offensive material, I&#8217;ll use strict safeSearch:</p>
<pre class="brush: plain; title: ; notranslate">q=&quot;Miss Maine&quot;&amp;max-results=12&amp;duration=short&amp;safeSearch=strict</pre>
<p>Finally, I&#8217;m going to specifically ask for English language videos that best match my search term. So my completed query string is:</p>
<pre class="brush: plain; title: ; notranslate">q=&quot;Miss Maine&quot;&amp;max-results=12&amp;duration=short&amp;safeSearch=strict&amp;orderby=relevance_lang_en</pre>
<div class="aside">You may be wondering why I am not using the <a href="http://code.google.com/p/google-gdata/" target="_blank">GData .NET Library</a> to accomplish this search. That&#8217;s an excellent option in general, but it involves installing components on your local machine and Web server. </p>
<p>For many Web developers, adding DLLs to a Web server or installing assemblies on a workstation can be problematic, if not prohibited outright. So I want to use as many built-in resources as possible. However, if you can install the GData API, consider going that route.</p></div>
<h3>Examining The XML Response</h3>
<p>Assuming our request goes through &#8212; that is, our Web server actually delivers the request to YouTube, which in turn sends a response back &#8212; we now have an XML document in storage that contains the information we requested. (Or, in the event of a malformed request, the default results set previously described.)</p>
<p>You can see the XML document returned for my demo query string here: <a href="http://www.dougv.net/demos/youtube_data_api_thumbs/videos.xml" target="_blank">http://www.dougv.net/demos/youtube_data_api_thumbs/videos.xml</a></p>
<p>In our case, we want some specific data for each video:</p>
<ul>
<li>Its title;</li>
<li>the default thumbnail; and</li>
<li>the URL to its player page on youtube.com.</li>
</ul>
<p>We get back lots more useful information in the XML response: The name of the uploader, the run time of the video in seconds, its likes and dislikes count, the categories and keywords assigned to the video, the date and time it was uploaded, its view count, etc. But for this solution, we&#8217;ll only use the video&#8217;s title, thumbnail and URL.</p>
<p>Although the actual XML document contains much more data per entry, for our purposes, the relevant structure of the document looks like this (I&#8217;ve stripped out extraneous attributes):</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;feed&gt;
	&lt;entry&gt;
		&lt;title&gt;&lt;/title&gt;
		&lt;link rel='alternate' /&gt;
		&lt;media:group&gt;
			&lt;media:thumbnail /&gt;
		&lt;/media:group&gt;
	&lt;/entry&gt;
&lt;/feed&gt;
</pre>
<p>In practice, the parts of an entry (that is, an individual record) that we want to use look like this:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;entry gd:etag='W/&amp;quot;DU8AQ347eCp7I2A9WhRWEEQ.&amp;quot;'&gt;
	&lt;title&gt;Miss Maine USA 2011&lt;/title&gt;
	&lt;link rel='alternate' type='text/html' href='https://www.youtube.com/watch?v=STm4GUqwLVo&amp;amp;feature=youtube_gdata'/&gt;
	&lt;media:group&gt;
		&lt;media:thumbnail url='http://i.ytimg.com/vi/STm4GUqwLVo/default.jpg' height='90' width='120' time='00:00:43.500' yt:name='default'/&gt;
	&lt;/media:group&gt;
&lt;/entry&gt;
</pre>
<h3>Traversing The XML Response Document</h3>
<div class="aside">There are a number of ways in ASP.NET to traverse / select nodes from an XML document. </p>
<p>The way most Web resources will tell you to do it is with LINQ to XML. LINQ allows us to make a SQL-like query of the XML document, and receive in return a SQL-like recordset, which we can then use to ouput our data.</p>
<p>LINQ to XML is definitely the way to go if you&#8217;re going to work with a large XML document, or need to extract data in a very complicated way (e.g, get various node attributes; select nodes based on data contained in other nodes). </p>
<p>LINQ to XML would work here, too, but I think it&#8217;s a bit of overkill, considering we only need three values from 12 records. So I&#8217;m just going to use the built-in parsing functions that are part of XmlDocument, a class we have already encumbered. Also, I&#8217;m more used to using XPath than I am LINQ.</p>
<p>Another way is to create an XML Stylesheet (<a href="http://www.w3schools.com/xsl/" target="_blank">XSLT</a>) which will &#8220;flattened out&#8221; the XML document into 12 nodes (each representing one record) with three attributes (each representing the title, URL and thumbnail for that record). I describe that methodology at &#8220;<a href="http://www.dougv.com/2006/11/11/using-national-weather-service-xml-feeds-with-aspnet-adonet-and-xsl/" title="Using National Weather Service XML Feeds With ASP.NET, ADO.NET And XSL" target="_blank">Using National Weather Service XML Feeds With ASP.NET, ADO.NET And XSL</a>.&#8221; </p>
<p>But I am trying to limit the number of things you have to learn at one time to accomplish this task. In my estimation, it&#8217;s a bit easier to hack together an XPath expression than to write an XSLT, so this is how I&#8217;m proceeding.</p></div>
<p>We&#8217;ll need three XPath expressions that will give us, for each of the 12 videos returned:</p>
<ol>
<li>the inner text of title,</li>
<li>the inner text of the link node that has the attribute &#8216;alternate&#8217;, and </li>
<li>the url attribute of whichever media:thumbnail node that has the additional attribute of yt:name=&#8217;default&#8217;.</li>
</ol>
<p>The XML returned by the YouTube Data API uses several namespaces (atom, media, yt, etc.). Therefore, we first have to make reference to those namespaces in order to traverse the response XML.</p>
<p>We do that by creating an XmlNamespaceManager, then adding the specification URLs to it:</p>
<pre class="brush: vb; title: ; notranslate">
'add namespaces so we can traverse this thing
Dim xmlNSM As New XmlNamespaceManager(xmlDoc.NameTable)
xmlNSM.AddNamespace(&quot;atom&quot;, &quot;http://www.w3.org/2005/Atom&quot;)
xmlNSM.AddNamespace(&quot;media&quot;, &quot;http://search.yahoo.com/mrss/&quot;)
xmlNSM.AddNamespace(&quot;yt&quot;, &quot;http://gdata.youtube.com/schemas/2007&quot;)
</pre>
<p>(We got these URLs from the root (&#8220;feed&#8221;) node&#8217;s attributes. That&#8217;s where they always appear, any time an XML document references namespace(s).</p>
<div class="aside">An <a href="http://www.w3.org/TR/REC-xml-names/" target="_blank">XML namespace</a> is basically a description of what elements an XML document will have, the kinds of data each node will contain, and other useful technical information that helps a parser understand what it is looking at and how to consume it.</div>
<p>Now that we have the namespaces referenced, we can go ahead and create our XPath arguments. Here they are:</p>
<pre class="brush: vb; title: ; notranslate">
'let's get our entry node values
Dim xmlTitleNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/atom:title&quot;, xmlNSM)
Dim xmlURLNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/media:group/media:player&quot;, xmlNSM)
Dim xmlThumbNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/media:group/media:thumbnail[@yt:name='default']&quot;, xmlNSM)
</pre>
<p>Note that I use the namespace:node syntax to select the nodes I want. If I didn&#8217;t do that, the XML parser wouldn&#8217;t understand what I meant if I said simply, &#8220;feed&#8221;, for example. I have to let the parser know which namespace the element &#8220;feed&#8221; belongs to, so it can find it.</p>
<p>With our nodes on hand, we just need to iterate each XmlNodeList, get our values, and pass them to a subroutine that adds hyperlinked thumbnails to our page.</p>
<p>We can use a simple For loop here, because by design, the number of title, thumbnail and hyperlink nodes will be the same, and the nodes will be in the same order in all three XmlNodeLists. (That is, the first node in each XmlNodeList will be for the first record; the title, URL and thumbnail values will match up, because they were listed in order in the XML document.)</p>
<pre class="brush: vb; title: ; notranslate">
'For loop will iterate them; by definition, counts are the same for each XmlNodeList
For I = 0 To xmlTitleNodes.Count - 1
	strTitle = xmlTitleNodes.Item(I).InnerText
	strURL = xmlURLNodes.Item(I).Attributes(&quot;url&quot;).Value
	strThumb = xmlThumbNodes.Item(I).Attributes(&quot;url&quot;).Value

	CreateHyperlinkedThumb(strTitle, strURL, strThumb)
Next
</pre>
<p>Here&#8217;s what the entire subroutine looks like:</p>
<pre class="brush: vb; title: ; notranslate">
Sub MakeThumbnailLinks(ByVal strAPIKey As String, ByVal strQuery As String)
	'create XML document we will parse
	Dim xmlDoc As New XmlDocument()
	xmlDoc = QueryYouTubeDataAPI(strAPIKey, strQuery)

	'add namespaces so we can traverse this thing
	Dim xmlNSM As New XmlNamespaceManager(xmlDoc.NameTable)
	xmlNSM.AddNamespace(&quot;atom&quot;, &quot;http://www.w3.org/2005/Atom&quot;)
	xmlNSM.AddNamespace(&quot;media&quot;, &quot;http://search.yahoo.com/mrss/&quot;)
	xmlNSM.AddNamespace(&quot;yt&quot;, &quot;http://gdata.youtube.com/schemas/2007&quot;)

	'let's get our entry node values
	Dim xmlTitleNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/atom:title&quot;, xmlNSM)
	Dim xmlURLNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/media:group/media:player&quot;, xmlNSM)
	Dim xmlThumbNodes As XmlNodeList = xmlDoc.SelectNodes(&quot;/atom:feed/atom:entry/media:group/media:thumbnail[@yt:name='default']&quot;, xmlNSM)

	'for debugging, we'll also get the url that represents what the Data API actually used
	Dim objNode As XmlNode = xmlDoc.SelectSingleNode(&quot;/atom:feed/atom:link[@rel='self']&quot;, xmlNSM)
	lblStatus.Text = &quot;&lt;strong&gt;Request URL returned by YouTube Data API:&lt;/strong&gt; &quot; &amp; Server.HtmlEncode(objNode.Attributes(&quot;href&quot;).Value)

	'strings to store values
	Dim strTitle As String
	Dim strURL As String
	Dim strThumb As String

	'looping variable
	Dim I As Integer

	'For loop will iterate them; by definition, counts are the same for each XmlNodeList
	For I = 0 To xmlTitleNodes.Count - 1
		strTitle = xmlTitleNodes.Item(I).InnerText
		strURL = xmlURLNodes.Item(I).Attributes(&quot;url&quot;).Value
		strThumb = xmlThumbNodes.Item(I).Attributes(&quot;url&quot;).Value

		CreateHyperlinkedThumb(strTitle, strURL, strThumb)
	Next
End Sub
</pre>
<h3>Creating Thumbs From Results</h3>
<p>The CreateHyperlinkedThumb subroutine takes the title, URL and thumbnail strings we got, applies them as properties to a Hyperlink control, and adds those controls to a Panel on our page.</p>
<pre class="brush: vb; title: ; notranslate">
Sub CreateHyperlinkedThumb(strTitle As String, strURL As String, strThumb As String)
	'create hyperlink
	Dim ctlLink As New HyperLink()

	'set values
	ctlLink.Text = strTitle
	ctlLink.ToolTip = strTitle
	ctlLink.NavigateUrl = strURL
	ctlLink.ImageUrl = strThumb
	ctlLink.CssClass = &quot;margin-5&quot;
	ctlLink.Target = &quot;video&quot;

	'add to panel
	pnlThumbs.Controls.Add(ctlLink)
End Sub
</pre>
<p>And with that, we&#8217;re done!</p>
<p>To invoke, we simply call the MakeThumbnailLinks subroutine, passing to it our YouTube Data API developer key and the querystring we built:</p>
<pre class="brush: vb; title: ; notranslate">MakeThumbnailLinks(&quot;YOUTUBE_DATA_API_DEVELOPER_KEY&quot;, &quot;q=&quot;&quot;Miss Maine&quot;&quot;&amp;max-results=12&amp;duration=short&amp;safeSearch=strict&amp;orderby=relevance_lang_en&quot;)</pre>
<p>You can see a working demo here: <a href="http://www.dougv.net/demos/youtube_data_api_thumbs/Example2.aspx" target="_blank">http://www.dougv.net/demos/youtube_data_api_thumbs/Example2.aspx</a></p>
<p>The code for this solution, as well as &#8220;<a href="http://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/" title="Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page" target="_blank">Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page</a>,&#8221; can be downloaded here: <a href='http://www.dougv.com/wp-content/uploads/2012/03/youtube_data_api_thumbs.zip'>http://www.dougv.com/wp-content/uploads/2012/03/youtube_data_api_thumbs.zip</a></p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms" target="_blank">http://delicious.com/dougvdotcom/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/" rel="bookmark">Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page</a> (64.7)</li>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24.5)</li>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (23.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/bit-ly/" title="bit.ly" rel="tag">bit.ly</a>, <a href="https://www.dougv.com/tag/google/" title="Google" rel="tag">Google</a>, <a href="https://www.dougv.com/tag/microsoft/" title="Microsoft" rel="tag">Microsoft</a>, <a href="https://www.dougv.com/tag/rest/" title="REST" rel="tag">REST</a>, <a href="https://www.dougv.com/tag/rss/" title="RSS" rel="tag">RSS</a>, <a href="https://www.dougv.com/tag/xpath/" title="XPath" rel="tag">XPath</a>, <a href="https://www.dougv.com/tag/xslt/" title="XSLT" rel="tag">XSLT</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Preloading Images With JavaScript</title>
		<link>https://www.dougv.com/2012/03/12/preloading-images-with-javascript/</link>
		<comments>https://www.dougv.com/2012/03/12/preloading-images-with-javascript/#comments</comments>
		<pubDate>Mon, 12 Mar 2012 23:10:43 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[arrays]]></category>
		<category><![CDATA[coding standards]]></category>
		<category><![CDATA[DOM images]]></category>
		<category><![CDATA[elegance]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4491</guid>
		<description><![CDATA[In my recent travels through old blog posts, I&#8217;ve noticed a number of occasions where I&#8217;ve discussed how to preload images. Almost all those examples are stupid or just plain wrong. For that, you have my apologies, and I aim to rectify those mistakes with this post. First, why would we want to preload an [...]<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2007/04/04/displaying-static-random-html-and-images-on-a-web-page-via-ajax/" rel="bookmark">Displaying Static, Random HTML And Images On A Web Page Via AJAX</a> (17)</li>
				<li><a href="https://www.dougv.com/2007/06/14/showing-a-larger-image-on-thumbnail-mouseover-with-javascript-dom/" rel="bookmark">Showing A Larger Image On Thumbnail MouseOver with JavaScript / DOM</a> (15.6)</li>
				<li><a href="https://www.dougv.com/2009/06/14/the-simplest-ways-to-do-image-rollover-effects-javascript-and-css-only/" rel="bookmark">The Simplest Ways To Do Image Rollover Effects: JavaScript And CSS-Only</a> (15.4)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p><div id="attachment_4494" class="wp-caption alignright" style="width: 210px"><img src="/wp-content/uploads/2012/03/anne+hathaway-Hot-Sexy-Gallery-Photos5.jpg" alt="anne+hathaway" title="anne+hathaway" width="200" height="302" class="size-full wp-image-4494" /><p class="wp-caption-text">Anne Hathaway</p></div>In my recent travels through old blog posts, I&#8217;ve noticed a number of occasions where I&#8217;ve discussed how to <a href="/tag/dom-images/">preload images</a>.</p>
<p>Almost all those examples are stupid or just plain wrong. For that, you have my apologies, and I aim to rectify those mistakes with this post.</p>
<p>First, why would we want to preload an image? Simply put, we intend to show it later on our Web page &#8212; either as a result of a mouseover, or a click, or some other sort of Document Object Model (<a href="http://www.w3schools.com/htmldom/default.asp">DOM</a>) event. </p>
<p>For example, maybe we want to <a href="http://www.dougv.com/2007/06/14/showing-a-larger-image-on-thumbnail-mouseover-with-javascript-dom/" title="Showing A Larger Image On Thumbnail MouseOver with JavaScript / DOM">mouseover a series of thumbnails, and show a larger version of that image in the same place</a>.</p>
<p>Rather than making the end user wait for a new image to load as a result of doing something on a Web page, it makes sense to load the image we intend to show in advance, so it will display almost instantaneously as a result of an event.</p>
<p>I&#8217;ll first show why two of my previous methods for preloading images are wrong or dumb, then describe two correct ways to preload images: via basic JavaScript and via <a href="http://jquery.com/" target="_blank">jQuery</a>. </p>
<p>The lovely <a href="http://www.imdb.com/name/nm0004266/" target="_blank">Anne Hathaway</a> will be our model.<br />
<span id="more-4491"></span></p>
<h3>The Wrong Way: A Single img Tag And JavaScript src Loop</h3>
<p>I previously favored preloading images by applying a single HTML img tag on a page, one without a src attribute and also hidden via CSS:</p>
<pre class="brush: xml; title: ; notranslate">&lt;img id=&quot;hiddenImg&quot; src=&quot;&quot; alt=&quot;&quot; style=&quot;display: none;&quot; /&gt;</pre>
<p>Then, I would create a preloadImages() function, which took as its arguments the paths to the images I wanted to preload. I would then loop through those arguments, setting the src attribute for hiddenImg to be the paths passed to the function:</p>
<pre class="brush: jscript; title: ; notranslate">

function preloadImages() {
	var img = document.getElementById('hiddenImage');
	var i;

	for(i = 0; i &lt; arguments.length; i++) {
		img.src = arguments[i];
	}
}

window.onload = function() {
	preloadImages('i/d01.jpg', 'i/d02.jpg', 'i/d03.jpg', 'i/d04.jpg', 'i/d05.jpg');
}
</pre>
<p>Unfortunately for me, this simply doesn&#8217;t work. And the reason why it doesn&#8217;t work is because, if I pass more than one image to the preloadImages() function, they simply don&#8217;t have enough time to load before the next image is called.</p>
<p>In short, the only image that actually preloads under this methodology is the last one in the arguments list. And that pretty much makes this approach useless.</p>
<p>You can see what I am talking about here: <a href="http://www.dougv.com/demo/js_preload_images/wrong.htm" target="demo">http://www.dougv.com/demo/js_preload_images/wrong.htm</a></p>
<h3>The Dumb Way: Using HTML And CSS</h3>
<p>Better in the technical sense, but just as bad in the practical sense, is using HTML and CSS alone to preload images.</p>
<p>In this case, we simply add, to the end of the page, a bunch of img tags that bring in the pictures we intend to display via JavaScript, based on a DOM event. We either place them inside a hidden div or assign those images to a CSS class that sets them to display: none.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;hiddenImages&quot; style=&quot;display: none;&quot;&gt;
	&lt;img src=&quot;i/a01.jpg&quot; alt=&quot;&quot; /&gt;
	&lt;img src=&quot;i/a02.jpg&quot; alt=&quot;&quot; /&gt;
	&lt;img src=&quot;i/a03.jpg&quot; alt=&quot;&quot; /&gt;
	&lt;img src=&quot;i/a04.jpg&quot; alt=&quot;&quot; /&gt;
	&lt;img src=&quot;i/a05.jpg&quot; alt=&quot;&quot; /&gt;
&lt;/div&gt;
</pre>
<p>You can see an example here: <a href="http://www.dougv.com/demo/js_preload_images/index.htm" target="demo">http://www.dougv.com/demo/js_preload_images/index.htm</a></p>
<p>While this is better than the wrong way, because at least all the images manage to get preloaded, this is dumb. </p>
<p>Suppose our client has disabled JavaScript. This approach doesn&#8217;t care; the images for our JavaScript-driven effects will still load in the background. </p>
<p>In other words, we encumber all the overhead of bringing in effect images for an effect that will not be seen. That&#8217;s wasteful. Maybe it&#8217;s no big deal, given that the average user has plenty of computing power and bandwidth to spare, but it&#8217;s inelegant. And by now, you know that little puts a burr in my butt more than wasteful code.</p>
<h3>Example 1: Using JavaScript&#8217;s appendChild() Method</h3>
<p>The proper way to preload images with JavsScript (and this time, I mean it) is to:</p>
<ul>
<li>create an HTML div that is hidden;</li>
<li>dynamically create an img element for each image we want to preload;</li>
<li>assign each of those dynamically created elements a src attribute that is a path to an image we want to preload; then</li>
<li>append that dynamic img element to the hidden div.</li>
</ul>
<p>So first, the HTML:</p>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;hiddenImages&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;</pre>
<p>And next, the JavaScript function:</p>
<pre class="brush: jscript; title: ; notranslate">
function preloadImages() {
	var hiddenDiv = document.getElementById('hiddenImages');
	var i;
	var img;

	for(i = 0; i &lt; arguments.length; i++) {
		img = document.createElement('img');
		img.src = arguments[i];
		img.alt = '';
		hiddenDiv.appendChild(img);
	}
}
</pre>
<p>To invoke this, we simply call the function:</p>
<pre class="brush: jscript; title: ; notranslate">preloadImages('i/b01.jpg', 'i/b02.jpg', 'i/b03.jpg', 'i/b04.jpg', 'i/b05.jpg');</pre>
<p>Witness this in action: <a href="http://www.dougv.com/demo/js_preload_images/example1.htm" target="demo">http://www.dougv.com/demo/js_preload_images/example1.htm</a></p>
<p>I prefer to put the preloadImages() function in the head of the page, and call it just before the closing body tag. </p>
<p>That way, the page gets a chance to load all its elements, before we start calling the images that we intend to show later. In other words, the page gets a chance to at least start loading the images it&#8217;s supposed to be displaying at first glance, before it starts loading the images we&#8217;ll show based on clicks / mouseovers / whatever.</p>
<h3>Example 2: Using jQuery And document.ready</h3>
<p>Of course, no discussion about JavaScript these days is complete without showing a jQuery version. So here&#8217;s mine.</p>
<p>In our case, we can use the same basic methodology as in Example 1. However, there are a few changes:</p>
<ul>
<li>Because jQuery has the .hide() method built-in, we&#8217;ll just hide each image as we create it. That means the div into which we are adding these images need not be hidden at all.</li>
<li>We need to call $(document).ready() sometime after we create the hiddenImages div. Otherwise, jQuery doesn&#8217;t see where it&#8217;s supposed to add the images it&#8217;s creating, and thus silently fails. So, for simplicity&#8217;s sake, I am adding both the preloadImages() function and my call to $(document).ready() just before the closing body tag.</li>
</ul>
<p>So here&#8217;s our hiddenImages div (again, no need to hide it):</p>
<pre class="brush: xml; title: ; notranslate">&lt;div id=&quot;hiddenImages&quot;&gt;&lt;/div&gt;</pre>
<p>And here&#8217;s the JavaScript we place, sometime after it, to preload:</p>
<pre class="brush: jscript; title: ; notranslate">
function preloadImages() {
	for(var i = 0; i &lt; arguments.length; i++) {
		$('&lt;img /&gt;').attr({ src: arguments[i], alt: '' }).appendTo('#hiddenImages').hide();
	}
}

$(document).ready(preloadImages('i/c01.jpg', 'i/c02.jpg', 'i/c03.jpg', 'i/c04.jpg', 'i/c05.jpg'));
</pre>
<p>Working example: <a href="http://www.dougv.com/demo/js_preload_images/example2.htm" target="demo">http://www.dougv.com/demo/js_preload_images/example2.htm</a></p>
<div class="aside">It&#8217;s worth noting that there are a number of image effect examples and plugins at the <a href="http://docs.jquery.com/Tutorials" target="_blank">jQuery tutorial site</a>.</div>
<p>So, whether you prefer to go the old-school JavaScript route, or to get newfangled with jQuery, the proper way to preload an image for DOM-event-based display is to create image elements via JavaScript, then add those elements to some other element on the page &#8212; either an element that&#8217;s already hidden, or by hiding the images as they are created.</p>
<p>You can download the source code here: <a href='http://www.dougv.com/wp-content/uploads/2012/03/js_preload_images.zip'>http://www.dougv.com/wp-content/uploads/2012/03/js_preload_images.zip</a></p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/preloading-images-with-javascript" target="_blank">http://delicious.com/dougvdotcom/preloading-images-with-javascript</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2007/04/04/displaying-static-random-html-and-images-on-a-web-page-via-ajax/" rel="bookmark">Displaying Static, Random HTML And Images On A Web Page Via AJAX</a> (17)</li>
				<li><a href="https://www.dougv.com/2007/06/14/showing-a-larger-image-on-thumbnail-mouseover-with-javascript-dom/" rel="bookmark">Showing A Larger Image On Thumbnail MouseOver with JavaScript / DOM</a> (15.6)</li>
				<li><a href="https://www.dougv.com/2009/06/14/the-simplest-ways-to-do-image-rollover-effects-javascript-and-css-only/" rel="bookmark">The Simplest Ways To Do Image Rollover Effects: JavaScript And CSS-Only</a> (15.4)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/arrays/" title="arrays" rel="tag">arrays</a>, <a href="https://www.dougv.com/tag/coding-standards/" title="coding standards" rel="tag">coding standards</a>, <a href="https://www.dougv.com/tag/dom-images/" title="DOM images" rel="tag">DOM images</a>, <a href="https://www.dougv.com/tag/elegance/" title="elegance" rel="tag">elegance</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/03/12/preloading-images-with-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Displaying Selected YouTube Video Thumbnails On An ASP.NET Web Forms Page</title>
		<link>https://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/</link>
		<comments>https://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/#comments</comments>
		<pubDate>Thu, 08 Mar 2012 16:27:59 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[API]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Web Forms]]></category>
		<category><![CDATA[YouTube Data API]]></category>
		<category><![CDATA[arrays]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[hotlinking]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Stack Overflow]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4251</guid>
		<description><![CDATA[Describes how to hyperlink YouTube video thumbnails on an ASP.NET Web Forms Page, linking to those videos on YouTube.com.<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24)</li>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (23.8)</li>
				<li><a href="https://www.dougv.com/2008/08/21/displaying-an-image-stored-in-a-sql-server-database-on-an-aspnet-page-using-vbnet/" rel="bookmark">Displaying An Image Stored In A SQL Server Database On An ASP.NET Page Using VB.NET</a> (22.5)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>I received a kind email recently from a reader, thanking me for my article titled &#8220;<a href="http://www.dougv.com/2009/06/13/retaining-values-in-a-form-following-php-postback-and-clearing-form-values-after-successful-php-form-processing/" title="Retaining Values In A Form Following PHP Postback And Clearing Form Values After Successful PHP Form Processing">Retaining Values In A Form Following PHP Postback And Clearing Form Values After Successful PHP Form Processing</a>&#8220;. </p>
<p>He also asked how he could use the <a href="https://developers.google.com/youtube/2.0/developers_guide_protocol_audience" target="_blank">YouTube Data API</a> to search for videos by keyword; display thumbnails of those videos on an ASP.NET page; then either hyperlink to those videos on YouTube, or display them on his page via the <a href="https://developers.google.com/youtube/js_api_reference" target="_blank">YouTube Player API</a>.</p>
<p>I&#8217;ll address that specific question in an upcoming post. First, I want to show how to do what the questioner asks if you already know the video IDs of specific YouTube videos you want to show on a page.</p>
<p>If you know the video ID(s) for the YouTube videos you want to display on a page, you can call them directly from YouTube&#8217;s image servers, thanks to a predictable URL and naming scheme, and hotlinking.<br />
<span id="more-4251"></span><br />
As <a href="http://stackoverflow.com/questions/2068344/how-to-get-thumbnail-of-YouTube-video-link-using-YouTube-api" target="_blank">this thread at Stack Overflow</a> notes, every YouTube video has at least three thumbnails, numbered 1.jpg to 3.jpg. These images are 120px by 60px and are roughly taken at the start, middle, and end of the video, respectively.</p>
<p>The file named default.jpg is the default thumbnail at 120px x 90px resolution. Two others &#8212; 0.jpg and hqdefault.jpg &#8212; are 480px x 360px. All three of these default images are copies of whichever of the three video thumbnails has been set as the default frame capture for the video.</p>
<p>Finally, a maximum resolution version, available as maxresdefault.jpg, is available if there is a high-definition (720p or 1080p) version of the video. It measures 1280px x 720px. When there isn&#8217;t a high definition version of the video, this appears as the generic YouTube thumbnail icon, at 120px x 90px resolution.</p>
<p>So, here are all the thumbnail images available for <a href="http://www.YouTube.com/watch?v=TvwJMa5b1Qg" target="_blank">Lisa Hannigan&#8217;s &#8220;What&#8217;ll I Do&#8221; music video</a> (hover over each image to see its name):</p>
<p><img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/default.jpg" alt="default.jpg" title="default.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/1.jpg" alt="1.jpg" title="1.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/2.jpg" alt="2.jpg" title="2.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/3.jpg" alt="3.jpg" title="3.jpg" /></p>
<p><img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/0.jpg" alt="0.jpg" title="0.jpg" />  <img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/hqdefault.jpg" alt="hqdefault.jpg" title="hqdefault.jpg" /></p>
<p><img src="http://img.YouTube.com/vi/TvwJMa5b1Qg/maxresdefault.jpg" alt="maxresdefault.jpg" title="maxresdefault.jpg" style="width:100%;" /> </p>
<p>And here are the thumbnails for <a href="http://www.YouTube.com/watch?v=VUUSdvwEC_Y" target="_blank">a decidedly low-res video for The Monkees&#8217;s song, &#8220;Last Train To Clarksville&#8221;</a>:</p>
<p><img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/default.jpg" alt="default.jpg" title="default.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/1.jpg" alt="1.jpg" title="1.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/2.jpg" alt="2.jpg" title="2.jpg" />&nbsp;<img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/3.jpg" alt="3.jpg" title="3.jpg" /></p>
<p><img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/0.jpg" alt="0.jpg" title="0.jpg" /> <img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/hqdefault.jpg" alt="hqdefault.jpg" title="hqdefault.jpg" /></p>
<p><img src="http://img.YouTube.com/vi/VUUSdvwEC_Y/maxresdefault.jpg" alt="maxresdefault.jpg" title="maxresdefault.jpg" /></p>
<h3>ASP.NET Web Forms Code To Display And Hyperlink Known YouTube Video Thumbnails</h3>
<p>Here&#8217;s some simple ASP.NET Web Forms code to add hyperlinked thumbnails for five different YouTube videos, the IDs of which we know, to a Panel control. (I&#8217;m assuming here that the Panel control is named pnlThumbs.)</p>
<pre class="brush: vb; title: ; notranslate">
Sub AddVideoThumbs()
	'create ArrayList of known video IDs
	Dim arrIDs As New ArrayList
	arrIDs.Add(&quot;STxXS5lLunE&quot;)
	arrIDs.Add(&quot;Mwrl6bWfvrc&quot;)
	arrIDs.Add(&quot;4nTo8rjo-lM&quot;)
	arrIDs.Add(&quot;eVVXtknZVf0&quot;)
	arrIDs.Add(&quot;pxg113O_SRI&quot;)

	'some temp string variables for our processing loop
	Dim strTemp As String
	Dim strLink As String
	Dim strSrc As String

	For Each strTemp In arrIDs
		strSrc = &quot;http://img.YouTube.com/vi&quot; &amp; strTemp &amp; &quot;/default.jpg&quot; 'path to default thumbnail
		strLink = &quot;http://www.YouTube.com/watch?v=&quot; &amp; strTemp 'path to video on YouTube

		Dim objLink As New HyperLink 'create hyperlink
		objLink.NavigateUrl = strLink
		objLink.ImageUrl = strSrc
		objLink.Text = strTemp 'sets alt text to ensure XHTML compliance
		objLink.Target = &quot;video&quot; 'will open all videos in same, new window / tab
		objLink.CssClass = &quot;margin-0-5&quot; 'a specific-to-me CSS class to space out the thumbnails

		pnlThumbs.Controls.Add(objLink)
	Next
End Sub
</pre>
<p>You can see this in action here: <a href="http://www.dougv.net/demos/YouTube_data_api_thumbs/Default.aspx" target="demo">http://www.dougv.net/demos/youtube_data_api_thumbs/Default.aspx</a></p>
<p>In an upcoming post, I will describe how to use the YouTube Data API to search for videos, display their thumbnails on a page, and hyperlink to those videos. I&#8217;ll also package all the demo code together for download at that time.</p>
<div class="aside">Update, 15 March 2012: The second part of this tutorial, and downloadable demo code for both examples, is available at <a href="http://www.dougv.com/2012/03/15/displaying-selected-youtube-data-api-thumbnails-on-a-web-page-via-asp-net-web-forms/" title="Displaying Selected YouTube Data API Thumbnails On A Web Page Via ASP.NET Web Forms">Displaying Selected YouTube Data API Thumbnails On A Web Page Via ASP.NET Web Forms</a>.&#8221;</div>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page" target="_blank">http://delicious.com/dougvdotcom/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2011/04/24/automatically-hash-tagging-text-with-asp-net-web-forms-vb-net/" rel="bookmark">Automatically Hash Tagging Text With ASP.NET Web Forms (VB.NET)</a> (24)</li>
				<li><a href="https://www.dougv.com/2011/12/25/parent-child-dropdownlist-controls-in-asp-net-web-forms-vb-net/" rel="bookmark">Parent-Child DropDownList Controls In ASP.NET Web Forms (VB.NET)</a> (23.8)</li>
				<li><a href="https://www.dougv.com/2008/08/21/displaying-an-image-stored-in-a-sql-server-database-on-an-aspnet-page-using-vbnet/" rel="bookmark">Displaying An Image Stored In A SQL Server Database On An ASP.NET Page Using VB.NET</a> (22.5)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/arrays/" title="arrays" rel="tag">arrays</a>, <a href="https://www.dougv.com/tag/google/" title="Google" rel="tag">Google</a>, <a href="https://www.dougv.com/tag/hotlinking/" title="hotlinking" rel="tag">hotlinking</a>, <a href="https://www.dougv.com/tag/microsoft/" title="Microsoft" rel="tag">Microsoft</a>, <a href="https://www.dougv.com/tag/stack-overflow/" title="Stack Overflow" rel="tag">Stack Overflow</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/03/08/displaying-selected-youtube-video-thumbnails-on-an-asp-net-web-forms-page/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Converting Latitude And Longitude Coordinates Between Decimal And Degrees, Minutes, Seconds</title>
		<link>https://www.dougv.com/2012/03/07/converting-latitude-and-longitude-coordinates-between-decimal-and-degrees-minutes-seconds/</link>
		<comments>https://www.dougv.com/2012/03/07/converting-latitude-and-longitude-coordinates-between-decimal-and-degrees-minutes-seconds/#comments</comments>
		<pubDate>Wed, 07 Mar 2012 23:18:18 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[converters]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[latitude / longitude]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4315</guid>
		<description><![CDATA[PHP functions that convert latitude and longitude coordinates between decimal and degrees - minutes - seconds (DMS).<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2009/07/13/calculating-the-bearing-and-compass-rose-direction-between-two-latitude-longitude-coordinates-in-php/" rel="bookmark">Calculating The Bearing And Compass Rose Direction Between Two Latitude / Longitude Coordinates In PHP</a> (43.3)</li>
				<li><a href="https://www.dougv.com/2006/12/20/displaying-a-random-yahoo-search-every-30-seconds-with-javascript-and-php/" rel="bookmark">Displaying A Random Yahoo! Search Every 30 Seconds With JavaScript And PHP</a> (15.4)</li>
				<li><a href="https://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" rel="bookmark">Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL</a> (5.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>I received an email yesterday from a reader who needed help with implementing my blog post, &#8220;<a href="http://www.dougv.com/2009/07/13/calculating-the-bearing-and-compass-rose-direction-between-two-latitude-longitude-coordinates-in-php/" title="Calculating The Bearing And Compass Rose Direction Between Two Latitude / Longitude Coordinates In PHP">Calculating The Bearing And Compass Rose Direction Between Two Latitude / Longitude Coordinates In PHP</a>.&#8221;</p>
<p>His problem: He has latitude and longitude coordinates in his database tables that are in degrees-minutes-seconds (DMS) format, rather than decimal format. In other words, his coordinates look more like <a href="http://maps.google.com/maps?q=55%C2%B045%E2%80%9906%E2%80%B3N,+37%C2%B037%E2%80%9904%E2%80%B3E&#038;ie=UTF8&#038;hnear=0x46b54a56b84a9be7:0x68ec5d2a865073a,%2B55%C2%B0+45'+6.05%22,+%2B37%C2%B0+37'+1.87%22&#038;gl=us&#038;t=h&#038;z=14" target="_blank">55&deg; 45&#8242; 06&#8243; N, 37&deg; 37&#8242; 04&#8243; E</a> than <a href="http://maps.google.com/maps?q=55.751667,+37.617778&#038;hl=en&#038;sll=37.0625,-95.677068&#038;sspn=59.249168,135.263672&#038;t=h&#038;z=16" title="The Kremlin" target="_blank">55.751667, 37.617778</a>.</p>
<p>So, my reader needed a way to convert back and forth between the two.</p>
<p>Fortunately, that&#8217;s easily done with a couple PHP functions. And since there&#8217;s not a lot that comes up in a Google search about how to make those conversions in PHP (lots of standalone converters, some JavaScript code, little PHP), I&#8217;ll fill the void here.</p>
<p>I should also note this function can be used for my other PHP latitude / longitude post, &#8220;<a href="http://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" title="Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL">Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL</a>.&#8221;<br />
<span id="more-4315"></span></p>
<h3>Converting From DMS To Decimal</h3>
<p>The easier of the two conversions is going from degrees-minutes-seconds to decimal. That&#8217;s because just as in an hour of time, each degree has 60 minutes, and each minute has 60 seconds. </p>
<p>So, to convert from DMS to decimal, we simply start with the degrees; then add the minutes value divided by 60, and the seconds value divided by 3600 (60 * 60). If the coordinates are South (latitude) or West (longitude), we need to make the decimal coordinate negative; North and East coordinates are positive.</p>
<p>Thus, a North latitude or East longitude is converted as:</p>
<pre class="brush: php; title: ; notranslate">$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);</pre>
<p>A South latitude or West longitude is calculated as:</p>
<pre class="brush: php; title: ; notranslate">$decimal = ($degrees + ($minutes / 60) + ($seconds / 3600)) * -1;</pre>
<p>Here&#8217;s a simple function that makes these conversions. It takes, as arguments, the degrees, minutes and seconds of either a latitude or longitude coordinate, as well as its direction (N, S, E or W), and returns a decimal response.</p>
<pre class="brush: php; title: ; notranslate">
function DMS2Decimal($degrees = 0, $minutes = 0, $seconds = 0, $direction = 'n') {
	//converts DMS coordinates to decimal
	//returns false on bad inputs, decimal on success

	//direction must be n, s, e or w, case-insensitive
	$d = strtolower($direction);
	$ok = array('n', 's', 'e', 'w');

	//degrees must be integer between 0 and 180
	if(!is_numeric($degrees) || $degrees &lt; 0 || $degrees &gt; 180) {
		$decimal = false;
	}
	//minutes must be integer or float between 0 and 59
	elseif(!is_numeric($minutes) || $minutes &lt; 0 || $minutes &gt; 59) {
		$decimal = false;
	}
	//seconds must be integer or float between 0 and 59
	elseif(!is_numeric($seconds) || $seconds &lt; 0 || $seconds &gt; 59) {
		$decimal = false;
	}
	elseif(!in_array($d, $ok)) {
		$decimal = false;
	}
	else {
		//inputs clean, calculate
		$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);

		//reverse for south or west coordinates; north is assumed
		if($d == 's' || $d == 'w') {
			$decimal *= -1;
		}
	}

	return $decimal;
}
</pre>
<p>You can test this function here: <a href="http://www.dougv.com/demo/php_lat_lon_conversion/dms2decimal.php">http://www.dougv.com/demo/php_lat_lon_conversion/dms2decimal.php</a></p>
<p>You call this function as you would any built-in PHP function. For example, assuming you have DMS coordinate values stored in the PHP variables $deg, $min, $sec and $dir, you could assign a value to a decimal equivalent variable, $dec, thus:</p>
<pre class="brush: php; title: ; notranslate">$dec = DMS2Decimal($deg, $min, $sec, $dir);</pre>
<h3>Converting Decimal To DMS</h3>
<p>Going from decimal coordinates to degrees-minutes-seconds is a bit more complicated.</p>
<ul>
<li>Set the DMS direction to South (negative latitude), West (negative longitude), North (positive latitude) or East (positive longitude);</li>
<li>set the decimal to its absolute (i.e., unsigned) value;</li>
<li>set degrees to be the <a href="http://php.net/manual/en/function.floor.php" target="_blank">floor</a> of the decimal;</li>
<li>subtract degrees from decimal, to get its fractional portion;</li>
<li>multiply that by 3600, to get the total number of seconds;</li>
<li>divide the seconds by 60, to get the total number of minutes;</li>
<li>subtract the total number of minutes, times 60, from seconds; then</li>
<li>set seconds value to its floor.</li>
</ul>
<p>To accomplish this task via a PHP function, we will pass to it six arguments: the decimal coordinate; variables for degrees, minutes, seconds and direction; and a Boolean indicating if this is a latitude or longitude coordinate.</p>
<p>Four of our six arguments &#8212;  degrees, minutes, seconds and direction &#8212; will be <a href="http://php.net/manual/en/language.references.pass.php" target="_blank">passed by reference</a>. What that means to a layman is, the values of those arguments will be set by the function, without the need to specifically set them via an evaluation.</p>
<p>In other words, in PHP, you usually set a variable value via a function like this:</p>
<pre class="brush: php; title: ; notranslate">$foo = myfunction($bar);</pre>
<p>But we&#8217;ll do it this way:</p>
<pre class="brush: php; title: ; notranslate">myfunction($bar, $foo);</pre>
<p>An example of that in practice follows the function code.</p>
<pre class="brush: php; title: ; notranslate">
function DecimalToDMS($decimal, &amp;$degrees, &amp;$minutes, &amp;$seconds, &amp;$direction, $type = true) {
	//set default values for variables passed by reference
	$degrees = 0;
	$minutes = 0;
	$seconds = 0;
	$direction = 'X';

	//decimal must be integer or float no larger than 180;
	//type must be Boolean
	if(!is_numeric($decimal) || abs($decimal) &gt; 180 || !is_bool($type)) {
		return false;
	}

	//inputs OK, proceed
	//type is latitude when true, longitude when false

	//set direction; north assumed
	if($type &amp;&amp; $decimal &lt; 0) {
		$direction = 'S';
	}
	elseif(!$type &amp;&amp; $decimal &lt; 0) {
		$direction = 'W';
	}
	elseif(!$type) {
		$direction = 'E';
	}
	else {
		$direction = 'N';
	}

	//get absolute value of decimal
	$d = abs($decimal);

	//get degrees
	$degrees = floor($d);

	//get seconds
	$seconds = ($d - $degrees) * 3600;

	//get minutes
	$minutes = floor($seconds / 60);

	//reset seconds
	$seconds = floor($seconds - ($minutes * 60));
}
</pre>
<p>You can see this in action here: <a href="http://www.dougv.com/demo/php_lat_lon_conversion/index.php">http://www.dougv.com/demo/php_lat_lon_conversion/index.php</a></p>
<p>Let&#8217;s suppose you have the variables $deg, $min, $sec, $dir, for degrees, minutes, seconds and direction, respectively. You also have a decimal coordinate stored in $dec. </p>
<p>This is how you would invoke the function above to assign values to your DMS variables:</p>
<pre class="brush: php; title: ; notranslate">
//if your decimal coordinate is a latitude:
DecimalToDMS($dec, $deg, $min, $sec, $dir, true);

//if your decimal coordinate is a longitude:
DecimalToDMS($dec, $deg, $min, $sec, $dir, false);
</pre>
<p>You can download the demo code here: <a href='http://www.dougv.com/wp-content/uploads/2012/03/php_lat_lon_conversion.zip'>http://www.dougv.com/wp-content/uploads/2012/03/php_lat_lon_conversion.zip</a></p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/converting-latitude-and-longitude-coordinates-between-decimal-and-hours-minutes-seconds" target="_blank">http://delicious.com/dougvdotcom/converting-latitude-and-longitude-coordinates-between-decimal-and-hours-minutes-seconds</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2009/07/13/calculating-the-bearing-and-compass-rose-direction-between-two-latitude-longitude-coordinates-in-php/" rel="bookmark">Calculating The Bearing And Compass Rose Direction Between Two Latitude / Longitude Coordinates In PHP</a> (43.3)</li>
				<li><a href="https://www.dougv.com/2006/12/20/displaying-a-random-yahoo-search-every-30-seconds-with-javascript-and-php/" rel="bookmark">Displaying A Random Yahoo! Search Every 30 Seconds With JavaScript And PHP</a> (15.4)</li>
				<li><a href="https://www.dougv.com/2009/03/27/getting-all-zip-codes-in-a-given-radius-from-a-known-point-zip-code-via-php-and-mysql/" rel="bookmark">Getting All ZIP Codes In A Given Radius From A Known Point / ZIP Code Via PHP And MySQL</a> (5.9)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/converters/" title="converters" rel="tag">converters</a>, <a href="https://www.dougv.com/tag/google/" title="Google" rel="tag">Google</a>, <a href="https://www.dougv.com/tag/latitude-longitude/" title="latitude / longitude" rel="tag">latitude / longitude</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/03/07/converting-latitude-and-longitude-coordinates-between-decimal-and-degrees-minutes-seconds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Disable Windows Antivirus When Installing aspell English Dictionary</title>
		<link>https://www.dougv.com/2012/02/29/disable-windows-antivirus-when-installing-aspell-english-dictionary/</link>
		<comments>https://www.dougv.com/2012/02/29/disable-windows-antivirus-when-installing-aspell-english-dictionary/#comments</comments>
		<pubDate>Wed, 29 Feb 2012 19:49:35 +0000</pubDate>
		<dc:creator>Doug Vanderweide</dc:creator>
				<category><![CDATA[Help Desk]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[developer tools]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[MSDN]]></category>
		<category><![CDATA[notepad++]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://www.dougv.com/?p=4244</guid>
		<description><![CDATA[When installing Kevin Atkison's English dictionary for aspell, disable your antivirus program, or the install will silently fail.<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2008/04/21/adobe-creative-suite-3-automatic-updates-and-windows-vista-issues/" rel="bookmark">Adobe Creative Suite 3 Automatic Updates And Windows Vista Issues</a> (15.2)</li>
				<li><a href="https://www.dougv.com/2008/12/29/how-to-take-a-screen-shot-windows-mac-os-x-and-linux/" rel="bookmark">How To Take A Screen Shot: Windows, Mac OS X And Linux</a> (14.1)</li>
				<li><a href="https://www.dougv.com/2008/01/01/a-windows-shell-script-to-automatically-rename-and-move-images-to-a-new-folder/" rel="bookmark">A Windows Shell Script To Automatically Rename And Move Images To A New Folder</a> (13.5)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.
	</div>
]]></description>
			<content:encoded><![CDATA[<p>A lesson I learned the hard way today, while installing <a href="http://aspell.net/win32/" target="_blank">aspell</a> support for Notepad++:</p>
<p>If you&#8217;re installing <a href="http://wordlist.sourceforge.net/" target="_blank">Kevin Atkison&#8217;s English dictionary</a> for aspell, you need to disable your antivirus program (at least, if you&#8217;re using <a href="http://www.avast.com/en-us/index" target="_blank">Avast</a>, as I am). </p>
<p>If you don&#8217;t, the dictionary installer can&#8217;t write its unpacked files to disk and will fail silently. As in, it just plain closes, and Notepad++ will report something along the lines of &#8220;Aspell and/or dictionaries are missing.&#8221;</p>
<p>FYI.</p>
<p>Also, if you haven&#8217;t heard of <a href="http://notepad-plus-plus.org/" target="_blank">Notepad++</a>, you should check it out. It&#8217;s an open-source, GPL-licensed Win32 text editor. (It runs perfectly fine in Win64).</p>
<p>Highly extensible via plugins, translated to all kinds of languages, exceptionally powerful, with support for syntax highlighting in just about every programming language under the Sun and syntax checking for a fair number of them, too. </p>
<p>It&#8217;s pretty much the only tool I use any more for Web coding, even when writing <a href="http://www.asp.net/web-forms" target="_blank">ASP.NET Web Forms</a>. (I still use <a href="http://msdn.microsoft.com/en-us/vstudio/aa718325" target="_blank">Visual Studio</a> for some Windows coding. But Notepad++ has completely replaced <a href="http://www.adobe.com/products/dreamweaver.html" target="_blank">Dreamweaver</a>.)</p>
<p>All links in this post on delicious: <a href="http://delicious.com/dougvdotcom/disable-windows-antivirus-when-installing-aspell-english-dictionary" target="_blank">http://delicious.com/dougvdotcom/disable-windows-antivirus-when-installing-aspell-english-dictionary</a></p>
<div class="yarpp">
	<h5>Related Posts</h5>
		<ol>
				<li><a href="https://www.dougv.com/2008/04/21/adobe-creative-suite-3-automatic-updates-and-windows-vista-issues/" rel="bookmark">Adobe Creative Suite 3 Automatic Updates And Windows Vista Issues</a> (15.2)</li>
				<li><a href="https://www.dougv.com/2008/12/29/how-to-take-a-screen-shot-windows-mac-os-x-and-linux/" rel="bookmark">How To Take A Screen Shot: Windows, Mac OS X And Linux</a> (14.1)</li>
				<li><a href="https://www.dougv.com/2008/01/01/a-windows-shell-script-to-automatically-rename-and-move-images-to-a-new-folder/" rel="bookmark">A Windows Shell Script To Automatically Rename And Move Images To A New Folder</a> (13.5)</li>
			</ol>
	<p class="note">The numbers inside parentheses are relevance scores. Scoring is based, in order of priority, on title, category, content and tags. The higher the score, the more likely that post relates to this post.</p>
	</div>

	Tags: <a href="https://www.dougv.com/tag/developer-tools/" title="developer tools" rel="tag">developer tools</a>, <a href="https://www.dougv.com/tag/microsoft/" title="Microsoft" rel="tag">Microsoft</a>, <a href="https://www.dougv.com/tag/msdn/" title="MSDN" rel="tag">MSDN</a>, <a href="https://www.dougv.com/tag/notepad/" title="notepad++" rel="tag">notepad++</a>, <a href="https://www.dougv.com/tag/open-source/" title="open source" rel="tag">open source</a>, <a href="https://www.dougv.com/tag/visual-studio/" title="Visual Studio" rel="tag">Visual Studio</a><br />
]]></content:encoded>
			<wfw:commentRss>https://www.dougv.com/2012/02/29/disable-windows-antivirus-when-installing-aspell-english-dictionary/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

