40win/FTW

a home for my bits

There Be Dragons

The Legend Begins

On a recent project, I needed to wire up drag-and-drop functionality. I had some things in a list, and I needed to be able to sort them using the mouse. Not only did I need to drag things around inside of a list, I needed to drag things from one list of things, to another list of similar things. Typical drag’n’drop stuff.

So I spent some time looking for a community project that gave me the desired functionality.

From the looks of it, the defacto drag’n’drop bits all revolve around jQuery UI. My project doesn’t currently have a dependency on jQuery UI, so I really didn’t want to go that route. However, I also decided that if it was the best route, then I would add jQuery UI to our project.

My Quest: Find the Best Dragon

The word “Best” is a loaded word. Given different contexts, two different options could alternately become the best option. My decision of which library is the best would be based on: Does this library allow me to build the best possible experience for my users?

As the search continued, my buddy Trevor and I stumbled into a little gem, built by none other than the benevolent Brian T. Ford. My boy Brian has a github repo called Dragon Drop. My initial impression was that it may be too simple for my needs. And I almost walked away from it. But then I took another look at it, and really fell in love with it.

The following are reasons that Dragon Drop. Seemed like a great choice.

  1. No jQuery UI dependency.
  2. The library is extremely easy to read, which meant that adding to or modding the it would be easy.
  3. It embraces AngularJS’s API.

I downloaded it, added it to my project and immediately had drag’n’drop functionality, without some crazy, sick overhead. It was beautiful.

Train the Dragon

The easiest way to explain why Dragon Drop is such an awesome solution is: It is an awesome place to start. I was able to quickly make some changes to his code, and have it work exactly like I needed it to.

After an hour of working on it, I had a pretty amazing drag’n’drop bit going. I had made numerous changes to Brian’s project, and some of them made great sense. I would have felt really bad if I didn’t at least offer to submit a pull request with some of my changes. However, I didn’t want to force my changes onto Brian, so I chatted him on G+ prior to submiting the PR.

I asked him if he wanted to take his repo to the next level or keep it simple, as it was. He quickly replied, but differently than I thought he might. He mentioned that he had thought a bit about what Dragon should turn into, or if it should altogether stay the same. While he wasn’t certain of the direction of Dragon, he wanted to at least see the PR (I still need to submit it).

After thinking about it for a few days, I think that Dragon should stay as it is today, which is A Great Place To Start. It doesn’t include all of the features that you will need, but it doesn’t bombard you with functionality that you will never use either. If you need to add a feature, the library is only a few hundred lines long, so you can just add it.

Before mentioning some of the changes that I made, let me mention some of the reasons I like Dragon:

  1. It is simple to use, as it extends the functionality of the ng-repeat directive.
  2. By default it allowed me to drag from on list to another.
  3. No external dependencies. Just AngularJS and jQuery.
  4. Literally, all you have to do it add it to your project and it just works.

Now let me list some of the changes that I made to Dragon, each if which was very simple to do:

  1. By default, Dragon removes the item you are dragging from it’s source list as soon as you click it to start dragging. I didn’t want it to remove the item from the source list until I dropped it onto a new list. This change was very easy to make (even if the new list was the same list).
  2. By default, dropping the item places it at the bottom of the list. It doesn’t respect the order that you dropped it in. I changed it to respect the drop order. If you drop an item on top of the item that is second on the list, your item will become the second item on the list, and everything else will shift down.
  3. By default, Dragon started firing the Drag method as soon as you initially clicked the item on the DOM. This ruined the ability to click the item without dragging it. So, I made the drag stuff not happen on the mousedown event. You have to move the mouse one pixel before it will start the drag process. So, now I can have ngClicks on items in a Dragon list.
  4. By default, Dragon doesn’t have an onDrop event callback to notify you of a drop event. I added an onDrop callback so that I don’t have to $watch for changes. I can simply subscribe via a callback.
  5. No, I didn’t not remove the comment Dragon. As mentioned in the comments, he is very important to the library.
  6. The item that is beig dragged loses some of it’s styling from the parent. To minimize this, I made the Dragon respect the aspect of the original item.
  7. Dragon didn’t allow me to use a filter on my list. Using a filter would ruin the drop functionality. I modded it to allow for filters to be used in the ng-repeatish syntax.

There are a few additional reasons. The more important thing is that you understand how easy it was for me, a non-guru, to make these changes. I actually enjoyed making them. Reading through Brian’s code was a cool experience as well. It is brilliant! I have never seen angular used like he uses it, and I loved it. I feel like a better dev because of it.

Test Flight

Prereqs to learning how to use Dragon are understanding ng-repeat. Once you understand that, you are there.

Once you have downloaded and added the Dragon to your project, you merely do the following in you markup:

index.html
1
2
3
4
5
<div class="list-container">
  <ul>
    <li btf-dragon="thing in things"></li>
  </ul>
</div>

This will give you a list of things. Each of the things may be dragged around.

If you dat]re to make two lists, you can drag from one to the next, like so:

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<span class="list1"> Set One
  <div class="list-container">
    <ul>
      <li btf-dragon="thing in setOne"></li>
    </ul>
  </div>
</span>
<span class="list2"> Set Two
  <div class="list-container">
    <ul>
      <li btf-dragon="thing in setTwo"></li>
    </ul>
  </div>
</span>

As you drag a thing, it follows your mouse, as you would expect. By dropping a thing from setOne into setTwo, the thing in the array will be removed from setOne and added to setTwo, and the DOM will subsequently reflect that change in the model. It is all very genius, and very simple.

If you don’t believe me, go check it out here.

Special thanks to Brian. Make sure and tweet him if you agree that he is completely legit for sharing his art with us.

Easy Search With Angular

Simple, yet powerful

This evening I needed to add some searching to a section of the project I am working on. Having search isn’t part of the scope, and has never come up as a Must Have feature. However, several team members and I quickly found that our feature would suffer without it.

I took a crack at it, and it only took me a few minutes, so I figured I should blog it and share it. Also, I don’t want to have to rehash how easy it is with anyone. I want a place I can send someone to understand it.

Background

Suppose that we have the following controller, which houses a list of some of my favorite things:

SearchController
1
2
3
4
5
6
7
8
9
10
11
12
13
angular.module('app').controller('SearchController', function($scope){
  $scope.things = [
      'Techno'
      ,'Ice Fishing'
      ,'Audis'
      ,'Kids'
      ,'Ender\'s Game'
      ,'Domo'
      ,'Angular'
      ,'Google'
      ,'XBox'
    ];
});

and a UI that looks like this: <div class=’bogus-wrapper’>

<figcaption>index.html</figcaption><div class=”highlight”><table><tr><td class=”gutter”><pre class=”line-numbers”>1 2 3 4 5 6 </pre></td><td class=’code’><pre><fieldset ng-controller="SearchController"> <legend>Demo Easy Search</legend> <ul> <li ng-repeat="thing in things"></li> </ul> </fieldset> </pre></td></tr></table></div>
</div> It simple lists out the things.

The section of my project is significantly more elaborate than a simple list of strings, but for simplicity’s sake, we are going to do this demo with a list of bloody thing-strings.

Step 1: Add a filter

I added the following filter to my project: <div class=’bogus-wrapper’>

<figcaption>thingFilter.js</figcaption><div class=”highlight”><table><tr><td class=”gutter”><pre class=”line-numbers”>1 2 3 4 5 6 7 </pre></td><td class=’code’><pre>angular.module('app').filter('thingFilter', function(){ return function(list, term){ return list.filter(function(thing){ return thing.toLowerCase().indexOf(term.toLowerCase()) >= 0 }); } }); </pre></td></tr></table></div>
</div>

If you filter with an empty term, it returns the full list. Otherwise, it toLowerCase()’s everything and compares the things to the search term. Pretty simple.

Step 2: Add a Search field

Now add the following to your UI: <div class=’bogus-wrapper’>

<figcaption>index.html mark:3</figcaption><div class=”highlight”><table><tr><td class=”gutter”><pre class=”line-numbers”>1 2 3 4 5 6 7 </pre></td><td class=’code’><pre><fieldset ng-controller="SearchController"> <legend>Demo Easy Search</legend> <label for="search">Search <input type="text" name="search" id="search" ng-model="search" placeholder="type here..."/></label> <ul> <li ng-repeat="thing in things"></li> </ul> </fieldset> </pre></td></tr></table></div>
</div> On line three, we have a new search input that will bind it’s value to a $scope variable called search.

Step 3: Combine the two

Now we just have to filter the things through the thingFilter with the search term, like so

index.html mark:5
1
2
3
4
5
6
7
<fieldset ng-controller="SearchController">
  <legend>Demo Easy Search</legend>
  <label for="search">Search <input type="text" name="search" id="search" ng-model="search" placeholder="type here..."/></label>
  <ul>
    <li ng-repeat="thing in things | thingFilter:search"></li>
  </ul>
</fieldset>

On line 5, you can see that we have piped our things list through the thingFilter, using the search term as the matching text. This will instantly remove unmatched items from the DOM. Very easy, very fast. Once the filter exists, all you need is a search field and the use the filter pipe to filter it. Two lines of code. Pretty cool.

HERE is a live demo

Until next time, where I will explain how to use a similar method to easily implement infinite scrolling in Angular. It turns out that the best solution is also the easiest one. Once I built several infinite scrolls in Angular, I found that using filters is the easiest way to do it.

$http.delete Problem

Angular’s $http.delete returns a 415 Unsupported Media Type

So, I have fought this issue a few times, and I decided that it’s about time to take to the blog and talk about it. I am doing this for a few reasons. I am sick of looking at it for 30-40 minutes trying to find the answer.

  1. I absolutely don’t want to have to re-remember this again. This was not the first time I have run into this problem.
  2. I know that others need the help as well. I didn’t find any solid answers.
  3. Let’s get some pressure on the Angular team to see if we can get it fixed.

Perhaps this blog can accomplish at least a few of those.

The problem

If you have made calls to $http.delete, and have gotten an “415 Unsupported Media Type” response from your server, that sucks. I know, cause it has happened to me a few times. It takes me a while to remember why, but I eventually re-discover why I am getting the problem. This is what I eventually remember.

My $http.delete usually looks like this:

$http.delete
1
$http.delete('http://www.foo.com/1234').then(someCallback);

Which gives more a result of something like this:

console
1
DELETE http://www.foo.com/1234 415 (Unsupported Media Type)

If you check out the request in your Dev Tools, the Content-Type has something like application/xml or text/plain, which isn’t what your server is expecting. Your server is expecting application/json, so it returns to you a 415 Unsupported Media Type.

If you want to take it one step further, if you use jQuery to do a delete to the same endpoint, jQuery’s delete will work. The headers will have the correct Content-Type. This will piss you off if you try it, so you may want to avoid it.

If this ever happens to you, just punch yourself in the face. Then come over here and punch me in the face.

It’s Angular’s fault

In Angular’s source, there are a few lines of code that will make you want to quit trying in life and go back to developing for IE (not really, calm down). They are as follows: <div class=’bogus-wrapper’>

<figcaption>angular.js start:9743 </figcaption><div class=”highlight”><table><tr><td class=”gutter”><pre class=”line-numbers”>1 2 3 </pre></td><td class=’code’><pre>if (lowercase(header) === 'content-type') { delete headers[header]; } </pre></td></tr></table></div>
</div>

So, essentially, this means that if you set a content-type onto your delete headers, it will delete them. Yeah… it will just delete them. Which in really helpful, cause we all know that you usually post text/plain to your endpoints. Am I right?

If you comment out this code, your delete will work. HOWEVER, that is not what I am recommending you do. I did that as a troubleshooting step only. So, leave it be. You don’t want to mod your angular source file.

The Fix

Angular tries to discover what your content type is. So, when you do a delete with no request body, it can’t find what your request body wants to be. So, instead of defaulting to application/json it defaults to text/plain. So, all you need to do it add something to the body. I tried this, and it worked. Check it:

bogus data in $http.delete
1
2
3
4
//Pass a config obj to your delete call, and give it a data property
$http.delete('http://www.foo.com/1234', {
	data: {foo:'bar'}	
}).then(someCallback);

Adding the data property to the config tells Angular to include that as your request body.

That’s all I had to do. Once I had some dummy data in my request, angular was able to detect that my Content-Type needed to be application/json, and the request worked fine.

If you had other solutions for the same issue, link to them below please. Thanks!

Web Gestures With getUserMedia: Part1

A few weeks ago I read a blog post by Tim Taubert where he talked about building a green screen with his webcam, a canvas and a green sheet of paper. His demo ran in Firefox, and it was pretty slick. Here his a video, check it out. <iframe src=”http://player.vimeo.com/video/51593914?badge=0” width=”500” height=”191” frameborder=”0” webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>

A few days later I went to the October meeting of the Utah Google Developers Group where one of the presenters (Mike Heath) did a separate demo where he used the getUserMedia webcam feed and a canvas to attempt to detect movement. He was looking for hand movement, but wasn’t able to successfully reduce the amount of noise in the background where movement would appear to happen due to random lighting changes.

After watching his demo, I thought about combinin the two demos to build a motion detection screen using a green sheet of paper. Tonight I finished part 1 of that demo. It is pretty nifty.

What does it do?

So, what did I manage to get up and running this evening? As you will see, when I move the green card around the screen, there is a floating div that follows it’s movement on the page. As I move the card up on the webcam feed, the div will move up. As I move the div down/sideways/up/back the div follows the card on the page. Here is a video of what I got going. <iframe width=”420” height=”315” src=”http://www.youtube.com/embed/h13LgUV_ZZg” frameborder=”0” allowfullscreen></iframe>

There is a bit going on here. While most of my code is new, I did shamelessly take the rgb2hsl function from Tim’s live demo. The rest of it is mine. Let me walk you through it.

1. Get the webcam feed

You should note upfront that this is a demo that should be run in Chrome. It uses the webkitGetUserMedia, which will fail in IE, FF and Safari.

To get started we need some basic HTML. Here is what that will look like:

[Basic HTML]
1
2
3
4
5
6
7
8
9
10
<html>
	<head>
		<script type="text/javascript" src="main.js"></script>
	</head>
	<body style="background-color:red;">
		<video id="v" width="320" height="240"/>
		<canvas id="c" width="320" height="240"></canvas>
		<div id="highlight"></div>
	</body>
</html>

Now that we have that html, let’s lay some tracks to feed the webcam into our video element.

[getUserMedia] []
1
2
3
4
5
6
7
8
9
10
11
var v = document.querySelector("#v"),
	c = document.querySelector("#c"),
	x = c.getContext("2d"),
	hl = document.querySelector("#highlight"),
	pixels;

navigator.webkitGetUserMedia({video:true},function(stream){
	v.src = URL.createObjectURL(stream);
	v.play();
	setTimeout(draw,200);
});

On line ‘7’ you can see that we make our call to ‘webkitGetUserMedia’, passing ‘{video:true}’ as our first param, and a callback function as our second parameter. Once the method is invoked the user is asked if they would like to share their webcam feed. If the user accepts, then the callback is called. In our callback, we create an ObjectUrl from the video stream and then set that to the ‘src’ of the video. After which we tell the video to play, and then call a setTimeout, which will run our ‘draw’ method. You haven’t seen the draw method, but it contains all of our code. Let’s break it down a step at a time.

2. Draw the video feed onto our canvas

The first few lines of our ‘draw’ method will draw the video onto a canvas. Here is how we do that:

[Load Video Into Canvas] []
1
2
3
4
5
6
	var w = v.width,
		h = v.height;
	
	//Draw the current video frame to the canvas
	x.drawImage(v, 0, 0, (w), h);
	

3. Find Green Pixels in the Canvas

Now that we have the canvas loaded up with a snapshot from the video, we need to find the green pixels so that we can track them. We track them for two reasons. The first is so that we can turn them transparent, so that you can get the nice visual feedback on the page and see which pixels it detects as green, and second so that we can then go through and look through different neighborhoods of pixels and score the different parts of the screen to see which ones have the most concentration of green pixels. We will get to that in a second. For now, take a look at how we detect green pixels:

[Find Green Pixels] []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
	var pixels = x.getImageData(0, 0, w, h);
	var pixLength = pixels.data.length / 4;
	
	//map: make two dimensional array to store which pixels detect green
	//scores: 2d array to store the 5x5 scores for each pixel. Each pixel
	//	gets a score of the summary of the green pixels around it. It looks
	//	at the 5 pixels to the left, right, above and below the pixel. The
	//	pixel gets the score of the sum of that total.
	var map = new Array(w);
	var scores = new Array(w);
	for(var i = 0; i < w; i++){
		map[i] = new Array(h);
		scores[i] = new Array(h);
	}
	//load the map with 1 and 0 for green and non-green pixels respectively
	for(var i = 0; i < pixLength; i++){
		var index = i*4;
		var r = pixels.data[index],
			g = pixels.data[index+1],
			b = pixels.data[index+2];
		var hsl = rgb2hsl(r, g, b),
			ha = hsl[0],
			s = hsl[1],
			l = hsl[2];
		
		var left = Math.floor(i%w);
		var top = Math.floor(i/w);
			
		if (ha >= 70 && ha <= 180 &&
			s >= 25 && s <= 90 &&
			l >= 20 && l <= 95) {
				
				pixels.data[i * 4 + 3] = 0;
			map[left][top] = 1;
		}else{
			map[left][top] = 0;
		}
	}

Let’s walk throuh these lines of code. We have over 30 lines of code here, and I don’t want to lose anyone.

First we pull the imageData out of the canvas. The ‘imageData’ and an object that holds all the RBG and Alpha values for each pixel in our canvas. We pull that out so that we can analyze it in a few lines.

Second, on line 9 and 10 we create two new 2-dimensional Arrays. These Arrays are as wide as the canvas and as tall as the canvas. They are the same dimensions as the canvas. The ‘map’ array[] will be used because I want to score each pixel with a 1 or a 0. If the pixel was green it gets a score of 1. If it was not green, it gets a score of zero. On lines 18-24, I turn the RGB into HSL values. Then on line 29, I verify if it was a green-ish pixel or not. If it was green-ish enough, the map gets a value of 1 for that pixel. If not, it gets a value of 0. I didn’t use the ‘scores’ array[], but I will in a minute.

4. Find the section with the most green-ish pixels

So at this point we have our map, with is a 2D array that has a 1 or a 0 for each pixel in our image. How do we use this to find the part of the screen with the most green-ish pixels? We are going to check out the entire neighborhood. That’s right, we are going to perform a Neighborhood Operation to find out what the surrounding pixels look like. If I have a pixel that is surrounded by all green-ish stuff, we will add up his 1 and the 1’s of all of his neighbors, and that will be his score. So, each pixel will get scored based on the summed values ofitself and it’s neighbors. Once we do that, we just have to go through the scores and find the first pixel with the highest score (if other later pixels tie, we ignore them). See the code for perhaps a better explanation:

[Sum Each Pixel by Neighborhood] []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
	//sum the score for each pixel
	for(var j = 5; j < h-5; j++){
		for(var i = 5; i < w-5; i++){
			var l5 = map[i-5][j],
				l4 = map[i-4][j],
				l3 = map[i-3][j],
				l2 = map[i-2][j],
				l1 = map[i-1][j],
				r1 = map[i+1][j],
				r2 = map[i+2][j],
				r3 = map[i+3][j],
				r4 = map[i+4][j],
				r5 = map[i+5][j],
				u5 = map[i][j-5],
				u4 = map[i][j-4],
				u3 = map[i][j-3],
				u2 = map[i][j-2],
				u1 = map[i][j-1],
				d1 = map[i][j+1],
				d2 = map[i][j+1],
				d3 = map[i][j+1],
				d4 = map[i][j+1],
				d5 = map[i][j+1],
				self = map[i][j];
			//console.log(i,j);
			scores[i][j] = l5+l4+l3+l2+l1+r1+r2+r3+r4+r5+u5+u4+u3+u2+u1+d1+d2+d3+d4+d5+self;
		}
	}

Now the ‘scores’ array is populated with the highest scores. Now we just have to go find the highest scoring pixel and set move our ‘highlight’ div to a position relative to that, adjusted from the size of the canvas to the size of the entire screen.

[Find The Highest Score] []
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	//Find the pixel closest to the top left that has the highest score. The
	//	pixel with the highest score is where the highlight box will appear.
	var targetx = 0;
	var targety = 0;
	var targetscore = 0;
	for(var i = 5; i < w-5; i++){
		for(var j = 5; j < h-5; j++){
			if(scores[i][j] > targetscore){
				targetx = i,
				targety = j;
				targetscore = scores[i][j];
			}
		}
	}
	hl.style.left = ""+Math.floor(document.width*(targetx/v.width))+"px";
	hl.style.top = ""+Math.floor(document.height*(targety/v.height))+"px";
	x.putImageData(pixels, 0, 0);


	setTimeout(draw,200);

Notice that on lines 169 and 170 I translate the location of the pixel inside the canvas into a location on the page.

At that point, I set the image on the canvas back to the updated images, and I call another setTimeout so that this will happen all over again.

Feel free to checkout the LIVE DEMO!. NOTE: You will need something very chartreuse green in order to get it to work correctly.