Category Archives: {Code}

I’ve been learning a lot of Reactive Extensions (Rx) lately, and in the spirit of learning I decided to share some 984368initial thoughts on the concepts and give a very light overview.  Rx is very deep, but hopefully this article gives you an initial understanding and examples to solidify the knowledge.

If you haven’t heard of Rx before, I highly recommend reading Erik Meijer’s blog post in which he describes an alternate way of thinking about events.  In short, he describes events (mouse clicks, keyboard events) as a stream of data.  Think of an array where you have a list of items but with the added element of time in between each item.  For example, as your mouse moves it creates a stream of coordinates. (The dots represent time passing)

mouseMoves = [….{x: 45, y: 45}…….{x: 45, y: 50}…..{x: 50, y: 50}…]

See, mouseMoves is just an array with time between the items.  This is what is known as an Observable.

Once we have an observable, we can do all kinds of things to the stream of data.  We can filter it for certain items, we can map over each data item and transform it to something else, we can merge it with another Observable to create a new data stream.   The documentation for the javascript implementation, gives you just a taste of what you can do.

A Practical Example – Mouse Moves

Let’s say that we want to add a listener to our page that detects whenever the mouse hovers over an image tag.  In addition, while we want to know whenever the mouse is hovering over the image, we also want to throttle the number events actually occurring.  The following Rx code accomplishes this in just a few lines of code.

Let’s take this line by line to see just what is happening.

After querying the page for a list of image tags, we create two new Observables by attaching them to the images DOM list.  These observables are now listening to all mouseover and mouseout events.

Now that we are listening to events, we want to filter these events to only return every 300ms (as opposed to very fast which it would do right now) and only while we are still hovered over an image.  Here’s how this code works. We loop over the list of mouseover events streaming in using ‘map’. For each event we occur, we create a new Observable that is delayed by 300ms.  And lastly, we filter that nested observable with the ‘takeUntil‘ operator so that we only return a valid event if the mouse has not left the image.  At this point, the event is then returned to the outer ‘map‘ result for the final operator, ‘switchLatest.’ Switch latest takes in a stream of events and returns only the most recent event. This operator is especially useful for ajax calls, where you may have made a few requests, but only care about the data from the most recent request made.  Likewise, this returns the most recent mouseover event that was valid.

Now that we’ve created our observable with its propers filers, we simply ‘forEach‘ or ‘subscribe’ over the stream as if it were an array.

That’s it!  We can now listen for mouseover events on images like an array.

This is just scratching the surface of Rx and we haven’t even touched on how Observables automatically unsubscribe themselves from events or handle errors.  If you’d like to learn and play more with observables, I highly recommend giving Jafar Husain’s interactive tutorial a try.

Been taking a look at ReactJS over the last few days, which is an interesting view library from the folks over at Facebook.  What I really like about it so far is the “componentization” of  objects on a page.  The general idea being,  any part of the page should be able to stand on its own.  Think widgets.  In theory this is a nice idea until components need to interact.  For example, a list of items with a search box that filters the results.  Component 1 is the search box, Component 2 is the list of items.  In the traditional MVC pattern, the controller would be responsible for handling this interaction.  In react, you would create a parent component which would handle the passing of state change to the list component.  It doesn’t really matter what the parent component is, just that its children are the list and the search box.

Considering the issues I have with the traditional MVC pattern for front end web apps, I’m interested in how this approach by ReactJS performs in the real world at scale.  Especially on very interactive web apps.  In past large web apps, I’ve used an event bus to facilitate the communication between components and thereby reduce tight coupling.  My initial concern is that there will be hooks and callbacks being passed through each component in ReactJS, making the code a challenge.

Time to dig in and see the real world results.

I recently read The Principles of Object-Oriented JavaScript by Nicholas Zakas and was enthralled by his patterns for prototypal inheritance of both instantiated and non-instantiated objects.   As I put each of these patterns to practice I noticed an interesting “discrepancy” between the the way each was linked to its parent.  I thought it was interesting enough for me to dig into, so it might make an interesting blog post.

Example 1: Instantiated Object Inheritance

In the example below, we create our parent class “Animal” which is created via the function method.  We attach a simple method to the prototype which retrieves the private “species” variable.

Then we create a “Dog” subclass by creating it via the function method, then linking the two classes with

This overrides the default Dog prototype with the Animal prototype, resulting in the correct prototype chain.  Here is a very abbreviated version of the prototype chain.

Screen Shot 2014-05-14 at 4.43.33 PM

Note the very important constructor attachment required after we override the prototype.

Pretty common sequence to chain these two classes together.  Any changes made to Animal will get automatically chained to Dog.  Now let’s take a look at the non-instantiated example.

Example 2: Non-instantiated Object Inheritance

In this example we’ll use a IIFE (Immediately-invoked functional expression) to create our parent class, with our retrieval function as above.  This pattern is common for singleton type use cases.

Now notice how we chain the prototypes of the Animal and Dog classes, specifically what is passed to the Object.create() function.

No mention of a prototype!  Why not?  Why are we attaching the object itself instead of its prototype?

The reason is because of what Animal is in this second example.  In example 1, Animal was created via the function literal resulting in an object with a full prototype chain inherited from Function. In the second example, animal was created via the inline object declaration “{}” resulting in an object with a prototype of “null”. To put it shortly, an object only has a prototype property if it was created via function literal.

Screen Shot 2014-05-14 at 4.43.40 PM

So if you called the following code, you would get a big fat error because animal has no prototype property.

So in example 2, the Animal object itself is what gets chained to myDog. MyDog  can now add its own methods without modifying the original Animal object.

 

I occasionally read through discussion boards that show off various artwork, but often find myself sifting through pages of text till I finally find the images I’m looking for.  So rather than continually sift by hand, I build this tool to ease the process.

DEMO

Here’s how it works. First off, rather than download all images to my server and sort them out there, we simply generate a new “lens” on the existing page, then let the client’s browser verify the images in javascript.  Once we have the images loaded, we can remove all images smaller than 120×120 pixels (avatars and such) and display the results in a easy to view format.  Lastly, we show the original size artwork scaled down, so that we can easily “right click to save.” The other feature I threw in was the ability to navigate by pages, so if the discussion board your reading supports paging, the extractor should figure it out and let you view pages by using the right and left arrows.

Need to check if your users are using a “supported” browser for your site? Don’t like how jQuery handles browser version numbers and chrome. Here’s a bit of javascript to make your life easier by putting version numbers in human readable format. Just update the numbers to match what you consider “supportable” for your site.

 function is_supported_browser()
 {
 var userAgent = navigator.userAgent.toLowerCase();

// Is this a version of IE?
 if($j.browser.msie)
 {
 userAgent = $j.browser.version;
 version = userAgent.substring(0,userAgent.indexOf('.'));
 if ( version >= 8 ) return true;
 }

// Is this a version of Chrome?
 $j.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
 if($j.browser.chrome)
 {
 userAgent = userAgent.substring(userAgent.indexOf('chrome/') +7);
 version = userAgent.substring(0,userAgent.indexOf('.'));
 if (version >= 13) return true;
 }

// Is this a version of Safari?
 if($j.browser.safari)
 {
 userAgent = userAgent.substring(userAgent.indexOf('version/') +8);
 version = userAgent.substring(0,userAgent.indexOf('.'));
 if (version >= 5) return true;
 }

// Is this a version of firefox?
 if($j.browser.mozilla && navigator.userAgent.toLowerCase().indexOf('firefox') != -1)
 {
 userAgent = userAgent.substring(userAgent.indexOf('firefox/') +8);
 version = userAgent.substring(0,userAgent.indexOf('.'));
 if (version >= '3.6') return true;
 }
 return false;
 }
 

So I’ve had quite a few questions about how I do AJAX caching in Titanium Appcelerator. So, here’s my solution.

First, I needed some persistent storage with a very general structure. I could have gone with flat files, but I decided to make use of the built-in SQLite engine, so that I could easily access entries and delete old data.

Database

var db_conn = Ti.Database.open('app_db1');
db_conn.execute('CREATE TABLE IF NOT EXISTS <code>remote_cache</code>
                 (key TEXT, type TEXT, valid_till TEXT, value TEXT,
                 PRIMARY KEY (key, type))');

Next, I need a wrapper class for my ajax calls. Note the “get” function which is the main function call. Here is the signature and explaination:

get: function (url, params, type, valid_for, callback, skip_cache)

  • url : string : the full url to call
  • params : object : a simple object with name/value parse for POST variables
  • type : string : the cache domain for this call. Usefull for clearing all cache data for a specific domain
  • valid_for : date string : the amount of time the cache should remain valid for
  • callback : function : the function to call on completion or fail of the request
  • skip_cache : boolean : an option to bypass the cache and perform write through

AJAX Class


var ajax =
{
	http_conn: Ti.Network.createHTTPClient(),
	url:'',
	type:'',
	params:{},
	key:'',
	valid_for: '+1 hour',
	callback: null,
	debug:true,
	get: function (url, params, type, valid_for, callback, skip_cache)
	{
		this.key = MD5(url + '|' +   JSON.stringify(params));
		this.url = url;
		this.params = params;
		this.valid_for = valid_for;
		this.type = type;

		this.callback = callback;

		if (ajax.debug) Titanium.API.info('Ajax Call');
		if (skip_cache)
		{
			this.remote_grab();
		}
		else if (!this.local_grab())
		{
			this.remote_grab();
		}

	},
	abort : function ()
	{
		this.http_conn.abort();
	},

	local_grab: function ()
	{
		if (ajax.debug) Titanium.API.info('Checking Local key:' + this.key);

		var rows = db_conn.execute('SELECT valid_till, value FROM remote_cache WHERE key = ? AND type = ? AND valid_till > datetime(\'now\')',  this.key, this.type);//, '', '+this.valid_for+')');//',  this.key, 'date(\'now\', '+this.valid_for+')');
		var result = false;
		if (rows.isValidRow())
		{
			if (ajax.debug) Titanium.API.info('Local cache found with expiration:' + rows.fieldByName('valid_till'));
			var result = JSON.parse(rows.fieldByName('value'));
			rows.close();

			this.callback(result);
			result = true;
		}
		rows.close();
		return result;
	},

	remote_grab: function ()
	{
		if (ajax.debug) Titanium.API.info('Calling Remote: ' + this.url + ' - PARAMS: '+ JSON.stringify(this.params));

		this.abort();
		this.http_conn.setTimeout(10000);

		// catch errors
		this.http_conn.onerror = function(e)
		{
			//alert('Call failed');
			ajax.callback({result:false});
			Ti.API.info(e);
		};

		// open connection
		this.http_conn.open('POST', this.url);

		// act on response
		var key = this.key;
		var valid_for = this.valid_for;
		var callback = this.callback;
		var type = this.type;

		this.http_conn.onload = function()
		{
			if (ajax.debug) Titanium.API.info('Response from server:' + this.responseText);
			if (this.responseText != 'null')
			{
				var response = JSON.parse(this.responseText);
				if (response.result == true || (response && response.length > 0))
				{
					ajax.update_local(key, type, valid_for, response);
					callback(response);
				}
				else if (response.result == false && response.error  && response.error.length > 0)
				{
					callback({result:false,error:response.error});
				}
				else
				{
					callback({result:false,error:'Invalid Result (1)'});
				}
			}
			else
			{
				callback({result:false,error:'Invalid Result (2)'});
			}
		};

		// Send the HTTP request
		this.http_conn.send(this.params);

	},

	update_local: function (key, type, valid_for, response)
	{
		if (ajax.debug) Ti.API.info('Updating Cache: KEY: ' + key + ' TYPE: ' + type +' - FOR: '+valid_for+', ' + JSON.stringify(response));
		db_conn.execute('DELETE FROM remote_cache WHERE (valid_till <= datetime(\'now\') OR key = ?) AND type = ?', key, type);
		db_conn.execute('INSERT INTO remote_cache ( key, type, valid_till, value ) VALUES(?,?,datetime(\'now\',?),?)',key, type, valid_for, JSON.stringify(response));
	}
};

I recently began using Titanium Appcelerator to build an iPhone app for WorthMonkey. One of the features I love in any application, is the ability to search a table using remote data. However, this is very difficult to pull off in Appcelerator, because it’s table search work only on local data. So here’s how I got around that limitation to make a true remote search table.

First, obviously it quite easy to add a search bar to a table, so I did that then attached a listener on the “change” event. But when you attempt to do your remote call here, the built in filter fires first and overlays the current table, so updating the table contents afterward is of no help. So, the trick is to fire the filter event again, AFTER you’ve filled the table. So on line 70, I tell the filter to fire again, and on line 24 I put a check in to make sure we don’t start infinite calls. Enjoy!

//SEARCH BAR
var search = Titanium.UI.createSearchBar({
	barColor:'#77B121',
	height:43,
	hintText:'What\'s It Worth?',
	top:0
});

//AUTOCOMPLETE TABLE
var table_data = [];
var autocomplete_table = Titanium.UI.createTableView({
	search: search,
	scrollable: true,
	top:0
});
win.add(autocomplete_table);

//
// SEARCH BAR EVENTS
//
var last_search = null;
search.addEventListener('change', function(e)
{
	if (search.value.length > 2 && search.value !=  last_search)
	{
		clearTimeout(timers['autocomplete']);
		timers['autocomplete'] = setTimeout(function()
		{
			last_search =search.value;
			auto_complete(search.value);
		}, 300);
	}
	return false;
});

function auto_complete(search_term)
{
	if (search_term.length > 2)
	{
		var url = 'YOURURL' + escape( search_term);
		var ajax_cache_domain = 'autocomplete';
		var params = {};
		var cache_for = '+7 days';
		ajax.get(url, params, ajax_cache_domain, cache_for, function (response)
		{
			if (typeof(response) == 'object')
			{
				var list = response;
				// Empty array &quot;data&quot; for our tableview
				table_data = [];

				for (var i = 0; i < list.length; i++)
				{
					//Ti.API.info('row data - ' + data[i].value);
					var row = Ti.UI.createTableViewRow(
					{
						height: 40,
						title: list[i].value.replace(/^\s+|\s+$/g,""),
						hasDetail:true
					});

					// apply rows to data array
					table_data.push(row);

				};

				// set data into tableView

				autocomplete_table.setData(table_data);
				search.value = search.value;
			}
			else
			{
				alert(response.error);
			}

		});
	}
}

Original Post

So I wanted to build a link parser like the one on Facebook, but didn’t find one that suited me. So I built one. My code is based off the code found here, but I rewrote much of it to be cleaner and to return JSON rather than HTML.

Code Change – Feb 2nd, 2011
Added some refinements to the cleaning mechanism and greatly speed up image parser.

HTML

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<style>
	#atc_bar{width:500px;}
	#attach_content{border:1px solid #ccc;padding:10px;margin-top:10px;}
	#atc_images {width:100px;height:120px;overflow:hidden;float:left;}
	#atc_info {width:350px;float:left;height:100px;text-align:left; padding:10px;}
	#atc_title {font-size:14px;display:block;}
	#atc_url {font-size:10px;display:block;}
	#atc_desc {font-size:12px;}
	#atc_total_image_nav{float:left;padding-left:20px}
	#atc_total_images_info{float:left;padding:4px 10px;font-size:12px;}
</style>
<br /><br /><br /><br />

<div align="center">
	<h1>Parse a Link Like Facebook with PHP and Jquery</h1>
	<div id="atc_bar" align="center">
		Paste Link Here: <input type="text" name="url" size="40" id="url" value="" />
		<input type="button" name="attach" value="Parse" id="attach" />
		<input type="hidden" name="cur_image" id="cur_image" />
		<div id="loader">

			<div align="center" id="atc_loading" style="display:none"><img src="load.gif" alt="Loading" /></div>
			<div id="attach_content" style="display:none">
				<div id="atc_images"></div>
				<div id="atc_info">

					<label id="atc_title"></label>
					<label id="atc_url"></label>
					<br clear="all" />
					<label id="atc_desc"></label>
					<br clear="all" />
				</div>
				<div id="atc_total_image_nav" >
					<a href="#" id="prev"><img src="prev.png"  alt="Prev" border="0" /></a><a href="#" id="next"><img src="next.png" alt="Next" border="0" /></a>
				</div>

				<div id="atc_total_images_info" >
					Showing <span id="cur_image_num">1</span> of <span id="atc_total_images">1</span> images
				</div>
				<br clear="all" />
			</div>
		</div>
		<br clear="all" />
	</div>
</div>

JavaScript


<script>

	$(document).ready(function(){

		// delete event
		$('#attach').bind("click", parse_link);

		function parse_link ()
		{
			if(!isValidURL($('#url').val()))
			{
				alert('Please enter a valid url.');
				return false;
			}
			else
			{
				$('#atc_loading').show();
				$('#atc_url').html($('#url').val());
				$.post("fetch.php?url="+escape($('#url').val()), {}, function(response){

					//Set Content
					$('#atc_title').html(response.title);
					$('#atc_desc').html(response.description);
					$('#atc_price').html(response.price);

					$('#atc_total_images').html(response.total_images);

					$('#atc_images').html(' ');
					$.each(response.images, function (a, b)
					{
						$('#atc_images').append('<img src="'+b.img+'" width="100" id="'+(a+1)+'">');
					});
					$('#atc_images img').hide();

					//Flip Viewable Content
					$('#attach_content').fadeIn('slow');
					$('#atc_loading').hide();

					//Show first image
					$('img#1').fadeIn();
					$('#cur_image').val(1);
					$('#cur_image_num').html(1);

					// next image
					$('#next').unbind('click');
					$('#next').bind("click", function(){

						var total_images = parseInt($('#atc_total_images').html());
						if (total_images > 0)
						{
							var index = $('#cur_image').val();
							$('img#'+index).hide();
							if(index < total_images)
							{
								new_index = parseInt(index)+parseInt(1);
							}
							else
							{
								new_index = 1;
							}

							$('#cur_image').val(new_index);
							$('#cur_image_num').html(new_index);
							$('img#'+new_index).show();
						}
					});

					// prev image
					$('#prev').unbind('click');
					$('#prev').bind("click", function(){

						var total_images = parseInt($('#atc_total_images').html());
						if (total_images > 0)
						{
							var index = $('#cur_image').val();
							$('img#'+index).hide();
							if(index > 1)
							{
								new_index = parseInt(index)-parseInt(1);;
							}
							else
							{
								new_index = total_images;
							}

							$('#cur_image').val(new_index);
							$('#cur_image_num').html(new_index);
							$('img#'+new_index).show();
					 	}
					});
				});
			}
		};
	});

	function isValidURL(url)
	{
		var RegExp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;

		if(RegExp.test(url)){
			return true;
		}else{
			return false;
		}
	}
</script>

PHP

$url = urldecode($_REQUEST['url']);
$url = checkValues($url);
$return_array = array();

$base_url = substr($url,0, strpos($url, "/",8));
$relative_url = substr($url,0, strrpos($url, "/")+1);

// Get Data
$cc = new cURL();
$string = $cc->get($url);
$string = str_replace(array("\n","\r","\t",'</span>','</div>'), '', $string);

$string = preg_replace('/(<(div|span)\s[^>]+\s?>)/',  '', $string);
if (mb_detect_encoding($string, "UTF-8") != "UTF-8")
	$string = utf8_encode($string);

// Parse Title
$nodes = extract_tags( $string, 'title' );
$return_array['title'] = trim($nodes[0]['contents']);

// Parse Base
$base_override = false;
$base_regex = '/<base[^>]*'.'href=[\"|\'](.*)[\"|\']/Ui';
preg_match_all($base_regex, $string, $base_match, PREG_PATTERN_ORDER);
if(strlen($base_match[1][0]) > 0)
{
	$base_url = $base_match[1][0];
	$base_override = true;
}

// Parse Description
$return_array['description'] = '';
$nodes = extract_tags( $string, 'meta' );
foreach($nodes as $node)
{
	if (strtolower($node['attributes']['name']) == 'description')
		$return_array['description'] = trim($node['attributes']['content']);
}

// Parse Images
$images_array = extract_tags( $string, 'img' );
$images = array();
for ($i=0;$i<=sizeof($images_array);$i++)
{
	$img = trim(@$images_array[$i]['attributes']['src']);
	$width = preg_replace("/[^0-9.]/", '', $images_array[$i]['attributes']['width']);
	$height = preg_replace("/[^0-9.]/", '', $images_array[$i]['attributes']['height']);

	$ext = trim(pathinfo($img, PATHINFO_EXTENSION));

	if($img && $ext != 'gif')
	{
		if (substr($img,0,7) == 'http://')
			;
		else	if (substr($img,0,1) == '/' || $base_override)
			$img = $base_url . $img;
		else
			$img = $relative_url . $img;

		if ($width == '' && $height == '')
		{
			$details = @getimagesize($img);

			if(is_array($details))
			{
				list($width, $height, $type, $attr) = $details;
			}
		}
		$width = intval($width);
		$height = intval($height);

		if ($width > 199 || $height > 199 )
		{
			if (
				(($width > 0 && $height > 0 && (($width / $height) < 3) && (($width / $height) > .2))
					|| ($width > 0 && $height == 0 && $width < 700)
					|| ($width == 0 && $height > 0 && $height < 700)
				)
				&& strpos($img, 'logo') === false )
			{
				$images[] = array("img" => $img, "width" => $width, "height" => $height, 'area' =>  ($width * $height),'offset' => $images_array[$i]['offset']);
			}
		}

	}
}
$return_array['images'] = array_values(($images));
$return_array['total_images'] = count($return_array['images']);

header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json');

echo json_encode($return_array);
exit;