Life as Clay

Archive for the ‘javascript’ Category

Nested arrays in d3.js

leave a comment »

I find nested arrays to be difficult to work with in d3.js. Every time I begin a new d3 project, I spend a few minutes confused about how to access the data. I built this small file to be able to play with the values and remind myself what they do.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font: 14px sans-serif;
  font-weight: bold;
}

 .chart {
    background-color: #efefef;
    shape-rendering: crispEdges;
 }

 .series {
    background-color: red;
 }

 .text0 {
    fill: red;
 }

 .text1 {
    fill: green;
 }

 .text2 {
    fill:steelblue;
 }

 .text3 {
    fill: yellow;
 }

 .text4 {
    fill: purple;
 }

 .text5 {
    fill:brown;
 }

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>


<script type="text/javascript">
var nestedArray = [[5, 10, 15, 20, 25],
                   [10, 20, 30, 40, 50],
                   [43, 34, 17, 9, 2]];


var chart = d3.select("body").append("svg")
            .attr("class", "chart")
            .attr("id", "chart")
            .attr("width", 550)
            .attr("height", 40 * 5);

var series = chart.selectAll(".series")
                .data(nestedArray)
                .enter().append("svg:g")
                .attr("class", "series");

var point  = series.selectAll(".point")
             .data(         function(d,i,j) { return d;          } )
             .enter().append("text")
             .text(         function(d,i,j) { return d;          } )
             .attr("class", function(d,i,j) { return "text" + j; } )
             .attr("x",     function(d,i,j) { return 10 * d;     } )
             .attr("y",     function(d,i,j) { return 40 * (j+1); } );

</script>
</body>
</html>
Advertisements

Written by Clay

September 15, 2013 at 17:16

Posted in Code, d3, javascript

Tagged with

Using Colorbox with Rails 3.1

with 2 comments

I’m in the midst of creating a mini-Facebook like Rails app that I can use to track events in my daughter’s life. Part of that includes the ability to attach photos to events. I wanted to use a friendly modal popup for displaying larger versions of photos, so I elected to go with ColorBox. It’s a jQuery plugin that is dead simple to use, especially on static pages.

For the “timeline” page, a dynamic page, I never know whether there will be photos to display and if there are, how they are grouped. The idea is that photos that are attached to the same event should all be part of one gallery.

Here’s what I did…

Anything that can end up on the timeline is a “mark” in my app. I use a partial when iterating through the marks to display photos that may be associated with the mark. Here is that partial:

<% if mark.images.count > 0 %>
	<div class="mark_images">
		<% for image in mark.images %>
				<a href="<%= image.pic(:large) %>" class="gallery_<%= mark.id %>" title="<%= image.caption %>"><%= image_tag(image.pic(:tiny), :title => image.caption, :class => "mark_image") %></a>
		<% end%>
	</div>
<% end %>

As you can see, I’m using thumbnail images as links. The images, by the way, are attached using the Paperclip gem from thoughtbot. The code above sets up the HTML that ColorBox needs in order to display the modal.

The rest of the work comes in javascript. I elected to put the ColorBox javascript at the end of my application.js file. It looks like this:

$(document).ready(function() {
	galleries = [];
	$('a[class^="gallery_"]').each( function() {
		if ($.inArray($(this).attr("class"), galleries) == -1) {
			$(this).colorbox({
				rel: $(this).attr("class"),
				maxWidth: "95%",
				maxHeight: "95%"
			});
			galleries.push($(this).attr("class"));
		}
	});
});

Here I create an empty array called galleries. I then find all link elements on the page that have a class that begins with “gallery_“. I iterate on each of them. If the class of the element is not in the galleries array, then I set up ColorBox for that class (gallery) and I add the class name to the galleries array so that I know not to set it up again.

Finally, I use the class of the gallery to relate the images that share the same class together, so that I get the navigational buttons on the modal viewbox.

To make this all work, you have to drop the ColorBox .js file into your assets/javascripts directory and you need to include both the .css file that you prefer (from the ColorBox downloadable examples) and the images that accompany it. I chose to use example 3. I had to modify the css image paths so that it would locate the ColorBox images in my assets/images directory. I elected to rename the enclosing folder to colorbox_images. The css file I ended up with looks like this:

/*
    ColorBox Core Style:
    The following CSS is consistent between example themes and should not be altered.
*/
#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
#cboxOverlay{position:fixed; width:100%; height:100%;}
#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
#cboxContent{position:relative;}
#cboxLoadedContent{overflow:auto;}
#cboxTitle{margin:0;}
#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
.cboxPhoto{float:left; margin:auto; border:0; display:block;}
.cboxIframe{width:100%; height:100%; display:block; border:0;}

/*
    User Style:
    Change the following styles to modify the appearance of ColorBox.  They are
    ordered & tabbed in a way that represents the nesting of the generated HTML.
*/
#cboxOverlay{background:#000;}
#colorbox{}
    #cboxContent{margin-top:20px;margin-bottom:20px}
        .cboxIframe{background:#fff;}
        #cboxError{padding:50px; border:1px solid #ccc;}
        #cboxLoadedContent{border:5px solid #000; background:#fff;}
        #cboxTitle{position:absolute; bottom:-15px; left:0; color:#ccc;}
        #cboxCurrent{position:absolute; top:-20px; right:0px; color:#ccc;}
        #cboxSlideshow{position:absolute; top:-20px; right:90px; color:#fff;}
        #cboxPrevious{position:absolute; top:50%; left:5px; margin-top:-32px; background:url(/assets/colorbox_images/controls.png) no-repeat top left; width:28px; height:65px; text-indent:-9999px;}
        #cboxPrevious:hover{background-position:bottom left;}
        #cboxNext{position:absolute; top:50%; right:5px; margin-top:-32px; background:url(/assets/colorbox_images/controls.png) no-repeat top right; width:28px; height:65px; text-indent:-9999px;}
        #cboxNext:hover{background-position:bottom right;}
        #cboxLoadingOverlay{background:#000;}
        #cboxLoadingGraphic{background:url(/assets/colorbox_images/loading.gif) no-repeat center center;}
        #cboxClose{position:absolute; top:5px; right:5px; display:block; background:url(/assets/colorbox_images/controls.png) no-repeat top center; width:38px; height:19px; text-indent:-9999px;}
        #cboxClose:hover{background-position:bottom center;}

I also chose to move the title to the bottom of the image because that way, it doesn’t bump up against the image count when viewed on a mobile device. That required setting a bottom margin for #cboxContent and changing this line: #cboxTitle{position:absolute; bottom:-15px; left:0; color:#ccc;}.

There are a variety of other options that can be used with the javascript call to alter the modal dialogue. It’s worth taking a look at the ColorBox page for some examples.

Overall, I’m really happy with this solution and probably will use it again in the future. If you’re interested in the app that I’m developing, you can find it on github, called Blytheline. It’s an early work in progress, so it’s a bit rough around the edges!

Written by Clay

November 29, 2011 at 12:53

Javascript snippet to display elapsed time in words

leave a comment »

Send in two strings in the format indicated and return a string that represents the time elapsed between the two inputs, in words.

function findDuration(startTime, endTime) {
	
	// Accepts input of two time stamps in this format:
	// 2011-11-22T12:40:00-05:00
	
	var year_start    	= parseInt(startTime.substring(0,4), 10);
	var month_start   	= parseInt(startTime.substring(5,7), 10);
	var day_start     	= parseInt(startTime.substring(8,10), 10);
	var hours_start   	= parseInt(startTime.substring(11,13), 10);
	var minutes_start 	= parseInt(startTime.substring(14,16), 10);
	
	var year_end    	= parseInt(endTime.substring(0,4), 10);
	var month_end   	= parseInt(endTime.substring(5,7), 10);
	var day_end     	= parseInt(endTime.substring(8,10), 10);
	var hours_end   	= parseInt(endTime.substring(11,13), 10);
	var minutes_end 	= parseInt(endTime.substring(14,16), 10);
	
    var years_total    	= 0;
    var months_total   	= 0;
    var days_total     	= 0;
    var hours_total    	= 0;
    var minutes_total  	= 0;
    
    var earlier_date = new Date();
    earlier_date.setFullYear(year_start);
    earlier_date.setMonth(month_start - 1);
    earlier_date.setDate(day_start);
    earlier_date.setHours(hours_start);
    earlier_date.setMinutes(minutes_start);
    
    var later_date = new Date();
    later_date.setFullYear(year_end);
    later_date.setMonth(month_end - 1);
    later_date.setDate(day_end);
    later_date.setHours(hours_end);
    later_date.setMinutes(minutes_end);
    
    var nTotalDiff 	= later_date.getTime() - earlier_date.getTime();
    var oDiff 		= new Object();
    
    oDiff.days 		= Math.floor(nTotalDiff/1000/60/60/24);
    nTotalDiff 	   -= oDiff.days*1000*60*60*24;
    
    oDiff.hours 	= Math.floor(nTotalDiff/1000/60/60);
    nTotalDiff 	   -= oDiff.hours*1000*60*60;
    
    oDiff.minutes 	= Math.floor(nTotalDiff/1000/60);
    nTotalDiff 	   -= oDiff.minutes*1000*60;
    
    oDiff.seconds 	= Math.floor(nTotalDiff/1000);
    
    oDiff.years 	= 0;
    if (oDiff.days > 364) {
        oDiff.years = Math.floor(oDiff.days / 365);
        oDiff.days 	= oDiff.days - (oDiff.years * 365) - 1;
    }
    
    var diff_string = '';
    if (oDiff.years > 0) {
        if (oDiff.years == 1) {
            diff_string += oDiff.years + " year ";
        } else {
            diff_string += oDiff.years + " years ";
        }
    }
    if (oDiff.days > 0) {
        if (oDiff.days == 1) {
            diff_string += oDiff.days + " day ";
        } else {
            diff_string += oDiff.days + " days ";
        }
    }
    if (oDiff.hours > 0) {
        if (oDiff.hours == 1) {
            diff_string += oDiff.hours + " hour ";
        } else {
            diff_string += oDiff.hours + " hours ";
        }
    }
    if (oDiff.minutes > 0) {
        if (oDiff.minutes == 1) {
            diff_string += oDiff.minutes + " minute ";
        } else {
            diff_string += oDiff.minutes + " minutes";
        }
    }

    // Add support for seconds here, if you want it.
    
    return diff_string;
}

Written by Clay

November 25, 2011 at 09:58

Posted in Code, javascript

Tagged with , ,