GreggHz http://www.gregghz.com A blog about programming and things. en-us Wed, 22 Feb 2012 17:23:31 -0700 Wed, 22 Feb 2012 17:23:31 -0700 greggory.hz@gmail.com greggory.hz@gmail.com Mootools Video Gallery Tutorial http://www.gregghz.com/post/mootools-video-gallery-tutorial <p>Recently I did some work for the folks over at <a href="http://www.thegoodline.com">The Good Line</a>. As part of the project, I built a fairly simple video gallery for them. The gallery handles vimeo videos and uses the <a href="http://mootools.net/">mootools javascript framework</a>. Today I'd like to take a few minutes and outline how the <a href="http://www.thegoodline.com/work.php">gallery</a> was created.</p> <p>I should note that is a bit of server side magic going on that makes some of what I'm doing here quite a bit easier, however, in this post I would like to focus on the client side issues since the server side stuff is less interesting and any average programmer would come to similar solutions that I have found.</p> <p>So to start, let's take a look at the html for the gallery. First the container for the actual player (this is the part that shows up after you click a video thumbnail):</p> <pre class="brush: xml"> &lt;div id="video_wrap" style="display: none;"> &lt;div id="video"> &lt;iframe id="vid_frame" src="" width="990" height="555" frameborder="0" webkitAllowFullScreen allowFullScreen>&lt;/iframe> &lt;/div> &lt;div id="title">&lt;/div> &lt;div id="description">&lt;/div> &lt;/div> </pre> <p>The `display: none` makes sure that the player is hidden when the page loads. The three children divs (video, title and description) are what will hold the data from each video. When a video is loaded, the iframe's src attribute will be updated to be the appropriate Vimeo url, the title div will be updated with the title of the video and the description div will be updated to contain the description.</p> <p>Now let's look at the html for each thumbnail.</p> <pre class="brush: xml"> &lt;div class="thumb alpha grid_1" data-vid-id="28961992" data-title="Fairbourne Consulting - SPEC AD" data-description=""> &lt;img src="http://thegoodline.com/images/slir/w188-h106-c188:106/images/thumbs/28961992_200.jpg" title="Fairbourne Consulting" alt="Fairbourne Consulting" /> &lt;div class="vid_title">Fairbourne Consulting&lt;/div> &lt;/div> </pre> <p>There are minor things worth mentioning here. First, the main div here uses the data-* attributes that html5 allows for storing arbitrary data. While it's not quite standards compliant if you're not using html, any browser will handle this data correctly. These data attributes hold the video id, title and description of the video. The image here is just a normal thumbnail. In my case, I used <a href="http://code.google.com/p/smart-lencioni-image-resizer/">SLIR</a> to dynamically generate the thumbnails from base images. This is required, but it makes life much easier (unfortunately, it's also beyond the scope of this article).</p> <p>Each thumbnail uses the same markup with the data modified to match the video it is a thumbnail for. There are some important css classes I'm using though. You can see three of the classes used above (thumb, alpha, grid_1) and one not used above (omega). Below is the css for these classes:</p> <pre class="brush: css"> .grid_1 { width: 188px; float: left; margin-left: 6px; margin-right: 6px; } .alpha { margin-left: 0px; } .omega { margin-right: 0px; } .thumb { width: 188px; cursor: pointer; } </pre> <p>`grid_1` is what places each thumbnail into a column. `alpha` should be used when the thumbnail starts a new row. `omega` should be used when a thumbnail ends a row. `thumb` is used to make sure the thumbnails behave like links (the cursor turns into the pointer when hovered).</p> <p>The remaining css is mostly cosmetic issues and should be much trouble to work out.</p> <p>Now the interesting part. First of all, mootools must be included for this to work properly. A simple and quick way to do this is to use the <a href="http://code.google.com/apis/libraries/devguide.html#mootools">Google Libraries API</a> like this:</p> <pre class="brush: xml"> &lt;script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/mootools/1.4.1/mootools-yui-compressed.js">&lt;/script> </pre> <p>The rest of the javascript will be inserted inside script tags just before the closing &lt;/body&gt; tag.</p> <pre class="brush: js"> var setVideo = function (src, title, description, callback) { $('vid_frame').addEvent('load', function (event) { callback(event); $('vid_frame').removeEvents('load'); }); $('vid_frame').set('src', src); $('title').set('html', title); $('description').set('html', description); } </pre> <p>setVideo is a helper function that loads a new video into the player and issues the passed in callback. The parameters are simple enough to figure out I think. It also sets up an event listener so that when the new video finishes loading, an arbitrary callback is called. I'll explain what to do with that shortly.</p> <p>Here's the function that will run when a thumbnail is clicked:</p> <pre class="brush: js"> var onThumbClick = function (event) { var src = 'http://player.vimeo.com/video/'+this.get('data-vid-id')+'?title=0&byline=0&portrait=0&autoplay=0&color=ffffff'; var title = this.get('data-title'); var desc = this.get('data-description'); if ($('video_wrap').getStyle('display') == 'none') { setVideo(src, title, desc, function () { $('video_wrap').reveal(); }); } else { var fade = new Fx.Tween($('video_wrap'), { property: 'opacity', link: 'chain', onComplete: function () { setVideo(src, title, desc, function () { $('video_wrap').fade('in'); new Fx.Tween($('video_wrap'), { property: 'opacity', link: 'chain' }).start(100); }); } }).start(0); } }; </pre> <p>Now we need to make sure all of the thumbnails are clickable and that we call onThumbClick when the thumbnails are clicked. We're also binding thumb to the this variable for convenience.</p> <pre class="brush: js"> $$('.thumb').each(function (thumb) { addEvent('click', onThumbClick.bind(this)); }); </pre> <p>And that's it. Not so bad. You can see the finished result here: <a href="http://thegoodline.com/work.php">The Good Line</a></p> http://www.gregghz.com/post/mootools-video-gallery-tutorial Google Docs Sync for Linux http://www.gregghz.com/post/google-docs-sync-for-linux <p> Google-Docs-Sync is a small python project I started back in January of this year. Since then, I think it has grown into a somewhat useful (although incomplete) tool. It's Ubuntu only for now, but I occasionally bug a Mac user friend and a Windows user friend to help with the porting. I haven't had any luck yet. </p> <p> This blog post will serve as a sort of quick start guide. While this information will probably stay relevant, the most up to date info will be found in the project's <a href="https://github.com/greggoryhz/Google-Docs-Sync/blob/master/README">README</a> file on <a href="https://github.com/greggoryhz/Google-Docs-Sync">github</a>. </p> <p> You can download everything you need here: <a href="https://github.com/greggoryhz/Google-Docs-Sync/tarball/master">Google-Docs-Sync</a>. </p> <ol> <li>Download the file using the link above and extract it where ever you would like. I use /home/gregg/workspace/.</li> <li>Open a terminal and change to the Google-Docs-Sync directory. For me the command is `cd /home/gregg/workspace/Google-Docs-Sync`.</li> <li>To start syncing, just type `./main.py` without the backticks (`). It will download all files from the Google Docs server. Any time you make changes, those changes will be sync'd back to the server.</li> </ol> <p> This will get you started. use the --help flag to get a view of all available options. Some include things like downloading files by title and running the sync in daemon mode (so the process isn't attached to the terminal it was started by). </p> <p> There are probably a handful of solutions out there that do something similar. In fact I *know* that this is not the first time someone has written a doc sync program like this. But the last one I remember seeing fell out of support a long time ago. With any luck, this project will continue to evolve and improve based on feedback and contributions from users. </p> <p> Currently, it does not detect changes made remotely. To pick up those changes you need to stop and restart the program. </p> http://www.gregghz.com/post/google-docs-sync-for-linux Teach Yourself HTML and CSS http://www.gregghz.com/post/teach-yourself-html-and-css <p><strong><a href="http://greggoryhz.appspot.com/css_tutor">See the actual app here.</a></strong></p> <p> My <a href="http://patrishin.blogspot.com/">wife</a> is a designer. Recently she has expressed interest in learning html and css since it would help her tremendously at work. So we agreed that I would teach her. I'm not a great teacher, so I wanted to make sure everything was as painless as possible. In my mind, the least useful/fun part when learning to work with html and css is dealing with the editors and opening the page in a browser and refreshing the page and checking for errors and refreshing the page and making changes and saving your work and refreshing the browser and so on. I decided that I can get rid of most of the grid of learning css and html by creating a simple web app. This web app (linked above) allows you to edit css in one pane, edit html in a second pane, and see the results in a third pane below the first two. This way, the flow is reduced to editing html or css. That's it. It's all in real time so you don't have to reload the page or save any files or open them in a browser. </p> <p> The implementation of this tool was a rather simple affair. As of writing this blog post, I have spent maybe 3 hours total working on it. Part of why it was fairly simple is because of the awesome open source community and the tools they provide. Specifically, this project uses <a href="http://jquery.com">jQuery</a>, <a href="http://codemirror.net/">CodeMirror</a>, <a href="http://960.gs">960.gs</a>, and <a href="http://www.blueprintcss.org/">Blueprint</a> (everything but the grid). Without these tools, this project would have been MUCH more difficult to even get it to the rudimentary state it is currently in. I'm particularly impressed with CodeMirror and they editor. I think it really adds a level of polish to the app. </p> <p> This project is very much in its infancy. I have a number of ideas for how to improve it. First of all, I would like for it to have an undo and redo functionality. Thanks to CodeMirror, this should be relatively trivial to implement. The bigger struggle for me is defining the UI for undo and redo. Since there are two editors, they each have their own undo stack and so it needs to be clear which one you are "undoing" at any given time. I would also like to have multiple different save slots. That way each user (identified by something saved in localStorage or perhaps server-side authentication) would be able to have a few different experiments they are working with. Things like tutorial progress or a code snippet you'd like to hold on to would be nice to be able to save. I'm sure there are plenty of features that would be useful that I haven't thought of yet. If anyone comes up with anything they would like to see, <a href="mailto:greggory.hz@gmail.com">email me</a> and let's talk about it. </p> <p> This project has re-inspired an old idea I had as well. When I first got my <a href="http://en.wikipedia.org/wiki/Google_Chrome_OS#Cr-48_prototype_hardware">Cr-48</a> the first deficiency I noticed was the lack of a useful IDE. I started on a web based IDE back then, but never got very far. With the experience I've gained from this simple project, I've decided that I would give it a go once more. I have a very early prototype ready, but it still needs a lot of work and will likely be months before anything substantial is released. </p> <p> Oh, by the way, and excellent reference for learning html and css can be found over at <a href="http://htmldog.com/">HTML Dog</a>. Check it out. </p> http://www.gregghz.com/post/teach-yourself-html-and-css Syft.co and Image Search http://www.gregghz.com/post/syft-co-and-image-search <p> Yes. <a href="http://www.syft.co">Syft.co</a>. Not <a href="http://www.syft.com">Syft.com</a>. Now that we have that out of the way, we can move on to more interesting issues. </p> <p> Syft is a joint project between myself and <a href="http://fieldstudy.tumblr.com">Keenan Cummings</a>. I handle the implementation and technical aspects of the site. In the current state, Syft is relatively simple (by most any standard). It uses the Google Image Search API and runs a user-supplied query against a number of pre-selected sites and returns the results. So right now, not much all that interesting is going on. It seems to be a pretty standard use of Google's API. Unfortunately Google recently <a href="http://googlecode.blogspot.com/2011/05/spring-cleaning-for-some-of-our-apis.html">announced</a> that they are deprecating this API. And they aren't providing an alternative. I believe that gives me three years to come up with something else. </p> <p> Alternative APIs exist, but I haven't yet found any that provide the flexibility and freedom that Syft will demand (especially as it grows). As a result, I have decided to create my own image search engine from scratch. I have absolutely no experience with this kind of project, so it will be quite the adventure. Below I'm going to outline my plans. This will provide me an opportunity to refine the idea into something tangible and also to receive feedback from anyone who may be interested. </p> <p> The search engine will be made up of 3 key components (since I don't have any experience with this, I'm just making up the names of the various components). </p> <ol> <li>Collector</li> <li>Parser</li> <li>Ranker</li> </ol> <p> Let's go over each of these one at a time. </p> <h2>1. Collector</h2> <p> The Collector is probably the simplest component of Syft Search. Its job is to take a pre-generated list of URLs and simply download and store the markup for each of those pages. Initially this list of URLs will be the list of hand selected sites that have a high likelihood of containing high quality images. Later, this list will be updated by the Parser to keep everything updated. Each downloaded page will be stored (likely using some sort of compression) in a datastore for the parser to process later. </p> <h2>2. Parser</h2> <p> The Parser sifts through the pages stored by the Collector and generates raw data about the page. It finds and saves all anchors that point to relative URLs or URLs on the same domain and stores them to be processed later by the Collector. This allows the Collector to always have more URLs to process while the Collector sends more pages to the Parser to be processed. In addition to finding new URLs to be processed, the parser is also responsible for finding all images on the page and storing information like their location in an images info datastore. </p> <h2>3. Ranker</h2> <p> The Ranker processes the image information stored by the Parser. It uses this information to find the best keywords and phrases for each image. The keywords are ranked by relative importance to their respective image. This ranking is based mostly on where each keyword or phrase appears in relation to each image. Things like the alt and title tags would be worth a lot more than text found in a footer or far away from the image. When a user runs a search, this information is used to determine what the best result set for the users specific query is and which images are most relevant. </p> <p> This is a very high level overview of the architecture of the Syft search engine. Much of the implementation details still need to be worked out. The datastore will likely be hosted by Google App Engine, but Amazon provides a possible alternative depending on the specific needs. As far as scalability is concerned, Syft Search has a very specific use case and won't require the type of crawling and indexing that a general purpose search engine requires. Each site that we'll be crawling is hand selected, so by the very nature of this process, it will be an extremely small index in comparison to a general purpose internet search engine. This should work to my advantage. That said, careful attention will need to be paid to the efficiency of actually running the queries. As much data as possible will need to be cached, and the algorithms used should be very finely tuned. We don't want users waiting around for results to show up. </p> <p> I'm very excited about this project. To get a sneak peak of the progress, head over to <a href="http://www.syft.co">syft.co</a> and sign up for the beta by entering your email address. We'll be sending out invites shortly. </p> http://www.gregghz.com/post/syft-co-and-image-search