Classynote

Over the course of studying for mid-terms, I create a little ruby on rails application designed to help students collaboratively take notes and study. Its still in a really early stage of development, but I found it immensely useful in preparation for my mid-terms this year. Heres a little sneak peak at the homepage — expect more to come.

[EDIT] This project has since been renamed “Classynote” and is now online at http://classynote.com

cloudnote

jQuery UI Nested Sortables 2

Working on a recent project, I needed a jQuery plugin that would me to visually rearrange a hierarchy of HTML elements. Although I did find a couple possible solutions in the jQuery plugin directory, all the existing solutions made the nesting algorithm extremely jumpy and temperamental. I did, however, stumble across a plugin written for the deprecated jQuery Interface library called Nested Sortables. This plugin had the exact behavior I was looking for — nested sortables with a threshold that kept the nesting very smooth. Unfortunately, the outdated library which this code relied on made it an inelegant solutoin. Over a weekend, I decided to port the module over to jQuery UI, a process which eventually led me to rewrite everything except for the nesting algorithm itself. Although I took out many of the old plugin’s features in the process (namely, RTL support), the final product is very stable and lightweight. In hopes that someone else might find the code useful, here it is:

$.fn.nestedSortable = function(options) {
  1.  
  2.  var settings = $.extend({
  3.   nestable: 'li',
  4.   container: 'ul',
  5.   indent: 30,
  6.   handle: null,
  7.   opacity: 1,
  8.   placeholderClass: 'placeholder',
  9.   helperClass: 'helper',
  10.   appendTo: 'parent',
  11.   start: function() {},
  12.   stop: function() {},
  13.   drag: function() {},
  14.   maxDepth: null
  15.  }, options);
  16.  settings.snapTolerance = settings.indent * 0.4;
  17.  
  18.  this.each(function() {
  19.  
  20.   // The top level nestable list container
  21.   var $root = $(this);
  22.  
  23.   // An element placed to preview the location of the dragged element in its new position
  24.   var $placeholder = $('<div/>', {
  25.    "class":  settings.placeholderClass
  26.   });
  27.  
  28.   // Use the mouseover live event to bind to nestables as they are created
  29.  
  30.   $root.find(settings.nestable).live("mouseover", function() {
  31.  
  32.    var $this = $(this);
  33.  
  34.    // Check if the element has already been set up as a nestable
  35.    if (!$this.data("nestable")) {
  36.  
  37.     // Make the element draggable
  38.     $this.draggable({
  39.  
  40.      // Transfer over some settings to jQuery's draggable implementation
  41.      opacity: settings.opacity,
  42.      handle: settings.handle,
  43.      appendTo: settings.appendTo,
  44.  
  45.      // Creates a helper element
  46.      helper: function() {
  47.       // Create a helper that is a clone of the original (with a few little tweaks)
  48.       return $this.clone().width($this.width()).addClass(settings.helperClass);
  49.      },
  50.  
  51.      // When dragging starts
  52.      start: function() {
  53.  
  54.       // Hide the original and initialize the placeholder ontop of the starting position
  55.       $this.hide().after($placeholder);
  56.  
  57.       // Find the deepest nested child
  58.       var maxChildDepth = 0;
  59.       $this.find(settings.nestable).each(function(index, child) {
  60.        var $child = $(child);
  61.        var childDepth = $child.parentsUntil($this).filter(settings.nestable).length;
  62.        if (childDepth > maxChildDepth) {
  63.         maxChildDepth = childDepth;
  64.        }
  65.       });
  66.       $this.data("maxChildDepth", maxChildDepth);
  67.  
  68.       // Run a custom start function specitifed in the settings
  69.       settings.start.apply(this);
  70.  
  71.      },
  72.  
  73.      // When dragging ends
  74.      stop: function(event, ui) {
  75.       // Replace the placeholder with the original
  76.       $placeholder.after($this.show()).remove();
  77.       // Run a custom stop function specitifed in the settings
  78.       settings.stop.apply(this);
  79.      },
  80.  
  81.      // Each "step" during the drag
  82.      drag: function (event, ui) {
  83.  
  84.       // Cycle through all nestables to find the item directly underneath the helper
  85.       var largestY = 0;
  86.       var depth;
  87.       var maxChildDepth = $this.data("maxChildDepth");
  88.       var underItems = $.grep($root.find(settings.nestable), function(item) {
  89.  
  90.        $item = $(item);
  91.  
  92.        // Is the item being  checked underneath the one being dragged?
  93.        if (!(($item.offset().top < ui.position.top) && ($item.offset().top > largestY))) {
  94.         return false;
  95.        }
  96.  
  97.        // Is the item being checked on the same nesting level as the dragged item?
  98.        if ($item.offset().left – settings.snapTolerance >= ui.position.left) {
  99.         return false;
  100.        }
  101.  
  102.        // Make sure the item being checked is not part of the helper
  103.        if (ui.helper.find($item).length) {
  104.         return false;
  105.        }
  106.  
  107.        // Make sure the item complies with max depth rules
  108.        if (settings.maxDepth !== null) {
  109.         depth = $item.parentsUntil($root).filter(settings.container).length + maxChildDepth;
  110.         if (depth – 1 > settings.maxDepth) {
  111.          return false;
  112.         }
  113.        }
  114.  
  115.        // If we've got this far, its a match
  116.        largestY = $item.offset().top;
  117.        return true;
  118.  
  119.       });
  120.  
  121.       var underItem = underItems.length ? $(underItems.pop()) : null;
  122.  
  123.       // If there is no item directly underneath the helper, check if the helper is over the first list item
  124.       if (underItem === null) {
  125.  
  126.        var firstItem = $root.find(settings.nestable + ":first");
  127.  
  128.        if ((firstItem.offset().top < ui.position.top + $(this).height()) && (firstItem.offset().top > ui.position.top)) {
  129.         firstItem.closest(settings.container).prepend($placeholder);
  130.        }
  131.  
  132.        // Place the placeholder inside or after the item underneath, depending on their relative x coordinates
  133.       } else {
  134.  
  135.        // Should the dragged item be nested?
  136.        if ((underItem.offset().left + settings.indent – settings.snapTolerance < ui.position.left) && (settings.maxDepth === null || depth <= settings.maxDepth)) {
  137.         underItem.children(settings.container).prepend($placeholder);
  138.  
  139.         // … or should it just be placed after
  140.        } else  {
  141.         underItem.after($placeholder);
  142.        }
  143.  
  144.       }
  145.  
  146.       // Run a custom drag callback specitifed in the settings
  147.       settings.drag.apply(this);
  148.      }
  149.  
  150.     }).data("nestable", true);
  151.    }
  152.   });
  153.  });
  154.  return this;
  155. };

The options you must pass to this plugin are:
indent: (default: 30) – The horizontal distance (in px) between levels of child nestables, usually the “margin-left” or “padding-left” of the lists which contain the child elements.

The options you may pass to this plugin are:
nestable: (default: ‘li’) – A selector which identifies the draggable and nestable elements within the list
container: (default: ‘ul’) – Each “nestable” element must have a container inside it for accepting children. This selector identifies that sub-list
start: A function to run on when dragging begins
stop: A function to run when the dragging ends
drag: A function to run at each movement of the mouse
handle: – A selector identifying a drag handle
opacity: (default: 1) – The opacity of the dragged element’s helper
placeholderClass: (default: ‘placeholder’) – The class of the placeholder – the temporary element created to show the final position of the helper as you drag it across the list.
maxDepth: (default: null) – The maximum depth at which items can be nested.

The HTML must be set up like this for the plugin to work:

  1. <ul>
  2.  <li>
  3.       List Item Content</li>
  4.  <li>
  5. <ul></ul>
  6. </li>
  7. </ul>

DSU Web Design Club

A few months ago, I started a web design club for middle schoolers at my school district’s student union. Because middle school was the time that I became fascinated with computer programming I was excited about the idea of exposing some young kids to the challenges and joys of web design. After four weeks of meetings, I’m proud to post their first little web page online. Everyone involved seemed to enjoy themselves and I hope that basics they learned will enable them to experiment with more advanced topics on their own.

Duxbury National Honor Society

I have spent the last few days learning Ruby on Rails. Armed with the famous “Agile Development with Rails” and a long weekend, I had no difficulty getting a feel for the language and framework; however I knew I would not truly understand Rails until I created a sample application with it. Finding an unusual lack of projects to work on, I decided to build (completely unbeknownst to them) a prototype of a management tool for a National Honor Society chapter.

Currently, I would assume most NHS programs are entirely paper/email based which I speculate would create an enormous amount of work for those in charge. The tool I allows a chapter to handle meeting attendance, news distribution, volunteer hours, and an events calender (with RSVP) in a simple and clean online interface. Although my school is unlikely to deploy this software, I’d love to help out anyone who’s interested.

Rails itself is amazing. Coming from a background of unwieldy and verbose PHP applications, its clean syntax is a breath of fresh air. Concise, small, and elegant, the Ruby language makes programming so much fun; I can see why ROR has become such a popular framework so quickly.

Bohdan Wojciechowski

My grandfather, a retired chemical engineering professor, thinks about the future. In the past five years, he has written several social commentary books about his vision of the extrapolated long-term progress of the human race. His books needed a distribution platform, and centralized place on the internet, so I designed and built this web site. The design was inspired by the family crest, seen in the upper righthand corner, which he proudly displayed on the cover of most of these books. Working with only one or two low-resolution images of the Jelita crest, I created a scalable vector version ready for the 21st century. The image below the crest, although it is now static, will rotate through a library of historic, and modern, family photographs which will be linked to their appropriate section on the family history time line.

The web site is in the final stages of approval, so stay tuned for links.

My Vision for Ambrosia CMS

They say that all new technology begins as a science and ends as an art. On the surface, web design seems to be a poster child for this cliché: what was once a rarity requiring a large amount of technical knowledge is now a key component of any business or organization. If you’re not on the web, you do not exist to the modern consumer. Like any revolution, the rush to the internet has fostered an enormous market for software and services which aid small organizations who could not otherwise afford a web site. I am sure there are thousands of organizations who are perfectly happy making their own web sites with these tools but my experience in the world of web design tells a different story.

Despite their enticing claims, all the tools currently on the market fit into one of two buckets:

  • Easy to use but rigid (Wordpress, Weebly, Tango CMS, TYPOlite, Wix, Joomla)
  • Complex and technical yet flexible (Drupal, CMS Made Simple, MODx, Expression Engine)

Every software package is either designed for the non-technical user or the developer. In reality, however, a website is almost always the result of collaboration between three major groups of people and any tool will alienate at least one of them:

  • The client
  • The web developer
  • The designer
    Read more

Stocks API

Problem: My dad likes to manage his stock portfolio by hand. To do this, he has a complex excel spreadsheet he uses to track and analyze the price fluctuations for each of the stocks he owns. Unfortunately, as his portfolio has grown, the labor of manually updating these spreadsheets has grown overwhelming and using someone else’s commercial software was not an option.

Solution: Faced with this technical dilemma, he did what any reasonable person would do faced with a technical challenge: run to the nearest computer geek. In this case, I happened to be closest. Upon hearing the problem, I instinctively assumed this was a common enough problem that it had been solved before — maybe it was even built into Excel. As it turns out, you can fetch the current stock price for any given stock through excel, but you cannot get the long term historical prices which we needed. In an hour, I turned out a small PHP script which can fetch the most recent 200 stock proces from Yahoo and format them into an excel-friendly XML feed.
Read more

DuxNews

Although my high school has a couple of excellent media classes (namely the news paper journalism and TV station) it lacks a good distribution medium. The news paper is still handed out once a month by hand and all of the video news segments are put up on local access cable TV. It is fun to recieve a paper newspaper, and seeing yourself on real cable TV is pretty cool but neither is terribly practical. The newsprint costs money and creates litter. Almost nobody thinks to check the local cable access channel every week to see if anything new is up. Besides, this is the 21st century!

With this in mind, I began playing around with the open source content management system Drupal and my imagination was running wild! I envisioned a web portal for all students to share news, have discussions, and even coordinate events! With the excitement that grips all web developers when they delve into web 2.0 projects, I single handedly designed and coded a prototype website and rallied support among the teachers who would be affected by this project. Needless to say the design went through many iterations but the final result is pretty slick.

You can play around with a demo here, but I warn you, its a little slow.

DuxSwim Montage

Partly because I love swim team, partly because I needed a segment for Dragon TV, I was inspired to bring a video camera to the Duxbury vs Pembroke swim meet and put together this montage. This video became important ammunition for the Friends of the Percy Walker fighting against the town proposal to close the pool.

Eco Friendly Bags

On a particularity slow day in Dragon TV, my student video journalism class, I went into one of those rare one day production frenzies and created this commercial. The idea was to advertise the product, an eco-friendly shopping bag, as a way to “save the world” and become a superhero.