Category Archives: html5

HTML5 Canvas and Particle Systems

13 Mar 2015

I’ve been doing a lot of canvas stuff lately which reminded me of some things that I’ve always wanted to try. In particular I’ve always meant to find time to try writing a particle system using HTML5 Canvas.

Its pretty easy to do – the idea is that we render a shape or a number of shapes to a canvas using window.requestAnimationFrame to recalculate the positions of the particles it each iteration. Before each render to the canvas we wipe the canvas clean then render the shapes to their new locations. That’s all there is to it.

I wrote two experiments at creating a particle system – in one I had the particles take care of themselves separate from any thing else – they essentially moved themselves around the canvas. The second attempt has a system that updates all of the new particle coordinates before rendering them all to the canvas. There are some subtle differences and effects that can be achieved. In almost all cases the second “style” of updating the canvas is preferred.

Before we go any further I’m assuming that you are using a modern web browser – I’ve not bothered with supporting lesser browsers.

Experiment One

The methodology in this one is that each particle takes care of itself. That is, it calculates its own position within the canvas and writes itself to the canvas regardless of whatever else might be happening. The caveat here is that we always need to remove the previously rendered shape before plotting the new one else we end up drawing lines on the screen.

You might think that this would be easy to do as we always know the coordinate of the previous shape and can simply erase it. Shapes, however, are anti-aliased. The outer-most anti-aliased edge of the shape (a “ghost”) is always left behind when we attempt to erase only the portion of the canvas where the previously plotted shape was. You can enlarge the bounding area of the shape to be sure to remove all of it but then you see “empty borders” around shapes as they cross each other.

The point is that even though this looks cool its impractical for most purposes.

The first example doesn’t bother to erase the previously plotted shape. As a result we have a series lines – but lines that have opacity and compositing so that we end up with something cool.

The example on the right does attempt to erase the previously plotted shape but as I mentioned above you can still see the “ghost” of that previous shape which leaves a sort of trail behind it as it moves about the screen.

Experiment Two

This one approaches Canvas animation the way its usually done. First calculate the new position of all shapes, wipe the entire canvas clean, and then write all the shapes to the canvas, repeat.




I wont go through any exhaustive description of how to do things – the described workflow above and the source code below should be all that you need to give it a try for yourself.

;(function(ns){
	
	var _parts = [];
	var _cvs = null;
	var _ctx = null;
	var _bgColor = null;
	
	ns.setupParts = function(cvsID,bgColor){
		_cvs = document.getElementById(cvsID);
		_ctx = _cvs.getContext('2d');
		_bgColor = bgColor;
	}
	
	ns.addParts = function(o){
		_parts.push(o);
	}
	
	ns.updateCanvasWithParts = function(){
		_ctx.clearRect(0,0,_cvs.width,_cvs.height);
		if (_bgColor){
			_ctx.fillStyle = _bgColor;
			_ctx.fillRect(0,0,_cvs.width,_cvs.height);
		}
		for (var i=0;i<_parts.length;i++){
			_ctx.fillStyle = _parts[i].color;
			_ctx.globalCompositeOperation = _parts[i].comp;
			_ctx.globalAlpha = _parts[i].alpha;
			_ctx.fillRect(_parts[i].x, _parts[i].y,_parts[i].height,_parts[i].width);
			_parts[i].update();
		}
		requestAnimationFrame(ns.updateCanvasWithParts);
	}
	
	ns.particle = function(config){
		var that = this;
		this.vx = config.omni ? (Math.random() < 0.5 ? config.vx * -1: config.vx) : config.vx;
		this.vy = config.omni ? (Math.random() < 0.5 ? config.vy * -1: config.vy) : config.vy;
		this.x = config.x;
		this.y = config.y;
		this.originX = config.x;
		this.originY = config.y;
		this.starfield = config.starfield;
		this.color = config.color;
		this.bgColor = config.bgColor;
		this.alpha = config.alpha;
		this.comp = config.comp;
		this.size = config.size;
		this.height = config.uniform ? config.size : Math.round(Math.random() * config.size);
		this.width = config.uniform ? config.size : Math.round(Math.random() * config.size);
		this.update = function(){
			if (!that.starfield){
				if (that.x > _cvs.height - that.height){
					that.vx = that.vx * -1;
				} else if (that.x < 0){
					that.vx = Math.abs(that.vx);
				}
				if (that.y > _cvs.width - that.width){
					that.vy = that.vy * -1;
				} else if (that.y < 0){
					that.vy = Math.abs(that.vy);
				}
			} else {
				if (that.x > _cvs.height + that.size || that.y > _cvs.width + that.size ||
					that.x < -that.size || that.y < -that.size){
					that.x = that.originX;
					that.y = that.originY;
				}
			}				
			that.x = that.x + that.vx;
			that.y = that.y + that.vy;
		}
	}

})(this.particles2 = this.particles2 || {});

document.addEventListener('DOMContentLoaded', function(e){	
	particles2.setupParts('cvs1','#000');
	for (var i=0;i<500;i++){
		var color = Math.floor(Math.random()*16777215).toString(16);
		var p = new particles2.particle({
			color: '#' + color,
			comp: null,
			alpha:1,
			x:(Math.random() * 400),
			y:(Math.random() * 400),
			vx:(Math.random() * 2),
			vy:(Math.random() * 2),
			size:(Math.random() * 6),
			uniform: true,
			omni:false,
			starfield:false
		});
		particles2.addParts(p);
	}
	particles2.updateCanvasWithParts();
});

Hashbang View Routing with History Support

25 Jul 2014

In the world of web-apps we commonly create our applications as “single-page websites”. That is, the page never reloads – we opt instead to manipulate the DOM based on user input to give the look of a seamless native application. Frequently we may need to have the ability to go “back” to the previous view that the user was on. This might be because the UI contains a “Back” button or maybe we’re on Android and want to enable the software back button.

It might seem trivial to just hard-code a back-button but its common to have templated views capable of displaying many different kinds of information and simply going “back” ignores the state of the view. You may think that all you need to do is to declare a variable somewhere that holds a string or object that tells you what your previous view contained and then branch off of that. True, that would work – how about going back multiple steps? Again, you can hack something together I’m sure….

At some point in the past someone was thinking about the same thing and hit upon the idea that the browser’s built-in history object can be used to track an application’s view history. As our app is a single-page application we aren’t loading completely new pages so the history object will just “sit” on the current page/url. Its possible however to update the history object without loading a new page by using “anchor references” which become part of the page’s history but do not cause a page reload.

For example, we can change this:

  • index.html

To this:

  • index.html#hello

An anchor is meant as a quick way to jump down to a specific part of a long page, thus its name. In our case, its counterpart does not exist on the page so merely changing the url of our application to include an anchor does nothing other than to update the page’s history object. When applied to our purposes the anchor will have a special meaning and so moving forward we’ll call it by its technical name – a hash fragment or just hash for short.

Knowing that the page’s history object includes hashes might not seem very interesting. Consider though that there is an event called window.onhashchange that is fired every time the hash changes. Consider next that we have a method of updating the hash in the form of window.location.hash. Further that we can use window.history.back() to traverse the history object which triggers the window.onhashchange and we soon realize that we have the essential ingredients to create a hash-based view router: 1) a method of keeping track of views, 2) a method of going back in our view history, 3) a method of knowing when the hash changes, and 4) a method of changing the hash.

You may have noted that the title of the article contains the word “Hashbang” – this is what a hashbang looks like: #!

The purpose of the “bang” (the exclamation point) is to function as a flag to Google’s bots that the URL they have just parsed should be considered as a link, not an anchor to something else lower on the page. If we use hashbangs in our single-page applications on the web Google will know that its an *application* and not just a single page and will index our links and treat them as individual pages.

For clarity’s sake, here are some hashbang examples:

  • index.html#!home
  • index.html#!products
  • index.html#!productDetail

For web-apps the hashbang isn’t as useful but its part of how we do things none-the-less. You’ll see HTML5 frameworks all over use them as they don’t know if your intent is to build a web-app for a mobile device or to build a single-page app that will live on the internet. As a result its baked in as a hashbang instead of just a hash and so as I said its just how things are done. Contrary to what my mother says I’ve decided that just because everyone else does it that it does in fact mean that I should do it too.

However, if you’ve read what html5doctor.com has to say on the topic you would walk away with a completely different perception:

You may have already seen articles fussing over the adoption of the “hashbang” (#!) pattern on sites like Twitter. This technique updates the address bar with a fragment identifier that can then be used by JavaScript to determine which page and state should be displayed.

This works as a method of creating a bookmarkable, shareable URL for a page’s state in the absence of a standard API.

…It’s ugly. It’s a hack and it looks like one…

…The hashbang was never intended to be a long-term solution, so don’t rely on it….
~ html5doctor.com

“so don’t rely on it”…. the exclamation point’s presence is by the whim of the developer or baked in by a framework. Its presence or lack thereof is actually immaterial to our purposes – if you elect not to use it you still have a fragment identifier that can serve as a “bookmarkable, shareable URL for a page’s state”. A hashbang’s only use as I understand it is to help Google index our one-page websites instead of giving Google a “locked door” that it’s bots can’t get past.

According to Google:

…AJAX URLs containing hash fragments with #! are eligible to be crawled and indexed by the search engine.
~ Google, via “Making AJAX Applications Crawlable

It is true that this Google-ism isn’t part of the official HTML5 spec – nor should it be as it has nothing to do with HTML5. As the hashbang is used more often than not by various frameworks, Twitter, Bing, and other web-properties its repeated use for this particular purpose amounts to a de facto standard for ***indexing AJAX-based single-page applications*** though admittedly not one that is part of any “official” standard.

The inclusion of the “bang” on the topic of Hash-based View Routing is more convention than anything else. And the convention is that “hash” and “bang” go together – use it or not, its up to you. If you use it within your app I’m pretty sure you can “rely on it”.

Sample App

The code presented here is functional and is based on a mythical Audi hybrid app. Click the image to view the app which contains 4 views used to illustrate the topic of this article.

Methodology

This is how I handled tracking view state – it makes sense to me and definitely fits the jQuery way of doing things with data locked into DOM elements via data attributes. Other frameworks like backbone.js or angular.js provide an abstraction for managing view history. I wanted to get past the abstraction that a framework provides and see about doing it myself.

Triggering a view change

Here’s the run-down on triggering a view change:

  1. A button is touched – the associated event object and its data attributes are broadcast to all subscribers of the touchend event
  2. The triggerViewChange method looks at the “action” data attribute – if it equals “updateHash” then the the event object is pushed to the _events array for future use and the page’s hash is set via window.location.hash (_events is described in more detail later in this article)
  3. Setting the hash itself causes the hashchange to fire
  4. notifyHashChange is listening for hashchange events and now receives the event and notifies the methods that have subscribed to it. Review the first code sample provided later on this page – the init() method – there are four functions subscribed to the hashchange event and they all create views. Those functions are:
    • audi.controller.showDefaultView()
    • audi.controller.showModelsView()
    • audi.controller.showModelsDrillDownView()
    • audi.controller.showCarDetailView()
  5. Each of the functions subscribed to the hashchange inspects the window’s hash – if it corresponds to a specific key then the function knows it is the view that must be rendered to the screen
    • Recall that the event object that triggered all of this was added to the end of the _events array – when a view changes it inspects that last item of this array for the desired event object and inspects the HTML5 data attributes it needs in order to know how to configure the content.

That’s it, a new view has been triggered and is now displayed, we’ve updated the _events array and the windows’s location object has a record of where we went.

Going back in the view history

You can simulate clicking a button by using Chrome’s console and entering “window.history.back()” provided that you created a history to begin with by touching the different navigation buttons.

Here’s the rundown on what happens when window.history.back() is invoked by hand via the console or by an in-app “back” button:

  1. showPreviousView() is triggered – this function will:
    • Remove the last array item from _events
    • window.history.back() is invoked
  2. The hashchange event is fired and everything wired to it does its thing:
    • The hash is now different – the four functions subscribed to this event are notified and each inspect the hash
    • The function whose key matches the hash fires and the appropriate view is rendered
    • As before the view in question looks at the last item in the _events array. Since we removed one from the end of the array (step 2 above) the last array item is now in-synch with our view. Everything the view needs to know about this prior view is now available to it and so it configures its content appropriately

Methodology in Practice

What I’m going to show here uses my own personal convention for building apps. To get a refresher for how I approach things you should first read “The Pub/Sub Pattern and Event Delegation in jQuery“.

Lets skip all of the setup – assuming you read the aforementioned article we will start with a bare-bones app – 4 views/pages – here’s all the code:

A short intro to the code is below.

Init the app (audi.js)

To initialize the app I have the following in the index.html

<script>
	$(function(){
		//document.addEventListener('deviceready', audi.init, false);
		audi.init();
	});
</script>

As you can see this is the hook to get things started. Once the document’s assets have been loaded jQuery’s ready event fires which in turn fires the app’s init() function.

init() sets up our controller by associating functions with the proper touch events and then by setting up all the listeners required by the application.

;(function(ns,$){
	/* init()
	 * This inits the application.
	 * @type {Function}
	 * @param {}
	 * @return {} Returns nothing
	 */
	ns.init = function(){
	audi.controller.subscribe('touchend',audi.controller.doButtonTouched);
	audi.controller.subscribe('touchend',audi.controller.doButton2Touched);
	audi.controller.subscribe('touchend',audi.controller.triggerViewChange);
        audi.controller.subscribe('touchend',audi.controller.showPreviousView);

        audi.controller.subscribe('hashchange',audi.controller.showDefaultView);
        audi.controller.subscribe('hashchange',audi.controller.showModelsView);
        audi.controller.subscribe('hashchange',audi.controller.showModelsDrillDownView);
        audi.controller.subscribe('hashchange',audi.controller.showCarDetailView);

	$('body').on('touchend','div',audi.controller.notify);
        $(window).on('hashchange',audi.controller.notifyHashChange);
        $(window).on('popstate',audi.util.showHideBackButton);

	window.location.hash = ' '; // make sure the hashchange event fires if the page is reloaded, useful in desktop browsers
        window.location.hash = 'defaultView';

	}
})(this.audi = this.audi || {},jQuery);

Of special note to us are lines 11 & 12 which assign our triggerViewChange and showPreviousView as subscribers to the touchend event. The buttons that exist in this sample application can do one of two things, either 1) change the view (move forward) or 2) go to the previous view (move backwards).

Another thing to note are lines 20 & 21.

Line 20 contains the setup for the hashchange listener. Every time the hash is updated the method “listening” to this event is fired. The 2 lines previously mentioned are responsible for changing the hash – again, they were triggerViewChange and showPreviousView.

Line 21 sets up a listener to the popstate event – I’m using this merely to control when my in-app back button should appear. You can view the source inside of audi.util.js to learn more.

The Controller

Below is a fragment of the controller code showing only the points of interest to our topic.

/** 
 * audi.controller 
 * @type {Object}
 * @return {} returns nothing
 * @name audi.controller
 * @namespace holds the event pub/sub system and button methods
 */
;(function(ns,$){

    var _uiUpdatePause = 5;
    var _subscriptions = {};
    var _events = []; 

	/* subscribe()
	 * This handles the event subscriptions.
	 * @type {Function}
	 * @param {string} eType - the event type
	 * @param {object} cb - the function reference
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.subscribe = function(eType,cb){
		if (!_subscriptions.hasOwnProperty(eType)){
			_subscriptions[eType] = [];
		}
		_subscriptions[eType].push(cb);
	};

	/* notify()
	 * This notifies the event subscribers (hashchange has a separate publisher).
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.notify = function(e){
		if (!$(e.target).data('action')){
			return;
		}
                if (audi.util.getIsScrolling()){
                       return;  
                }
		e.preventDefault();
		e.stopPropagation();

		var cbs = _subscriptions[e.type];
		for (var i=0;i<cbs.length;i++){
			cbs[i](e);
		}
	};

	/* notifyHashChange()
	 * This notifies the hashchange event subscribers.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
        ns.notifyHashChange = function(e){
            var cbs = _subscriptions[e.type];
		for (var i=0;i<cbs.length;i++){
			cbs[i](_events.slice(-1)[0]);
		}
        }

	/* triggerViewChange()
	 * Changes the location hash which will trigger the hashchange 
         * event which itself triggers the appropriate view update.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.triggerViewChange = function(e){
		if ($(e.target).data('action') != 'updateHash'){
			return;
		}
		var hash = $(e.target).data('hash');
		_events.push(e);
		setTimeout(function(ev){
			window.location.hash = hash;
		},_uiUpdatePause);
    	}
	/* showPreviousView()
	 * This is called by a hashchange event - it uses the window object's
         * history.back method to go to the previous view.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.showPreviousView = function(e){
		if ($(e.target).data('action') != 'showPreviousView'){
			return;
		}
        	var hash = window.location.hash;
        	if (hash == '#defaultView' || hash == 'modelDrillDownView'){
			return;   
		}
        	_events.pop(); // remove the current recorded event so that the previous one will be used.
		window.history.back();
	}

	/* showDefaultView()
	 * Handles the click event for the logo, this renders the default view.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.showDefaultView = function(e){
		if (window.location.hash !=  '#defaultView'){
			return;
		}
		audi.view.destroy_iScroll();
        	audi.view.renderDefaultView(_events.slice(-1)[0]);
	}

	/* showModelsView()
	 * Handles the click event for the models button.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.showModelsView = function(e){
		if (window.location.hash !=  '#modelsView'){
			return;
		}
		audi.view.destroy_iScroll();
        	$('#mainView').removeClass('splashBG');
        	audi.view.renderShowModels(_events.slice(-1)[0]);
	}

	/* showModelsDrillDownView()
	 * Handles the click event for the models button.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.showModelsDrillDownView = function(e){
		if (window.location.hash !=  '#modelDrillDownView'){
			return;
		}
		audi.view.destroy_iScroll();
        	audi.view.renderDrillDown(_events.slice(-1)[0]);
	}

	/* showCarDetailView()
	 * Handles the click event for the models button.
	 * @type {Function}
	 * @param {object} e - the event object
	 * @return {} Returns nothing
	 * @see audi.init()
	 */
	ns.showCarDetailView = function(e){
		if (window.location.hash !=  '#showCarDetailView'){
			return;
		}
		audi.view.destroy_iScroll();
		audi.view.renderCarDetail(_events.slice(-1)[0]);
	}
...

What is the “_events” variable used for?

You’ll see on line 12 above that I’ve declared a variable called _events and its purpose is to collect touch events in an array as they occur. Not all touch events, only ones that are “actionable” as determined by HTML 5 data attributes within some DOM elements (data-action or your own convention). Specific elements are in fact buttons and trigger view changes – and thus hashchange events. These buttons contain information on the view itself and so I hold the touchend events from our button divs (and thus their data attributes) in this array so that it can be passed to the relevant views when the hashchange event fires. Thus, if I go back in my browser history – effectively going backwards in my view history – I have the event that was used to create that previous view and by extension all of the information necessary to populate that prior view.

This _events array is managed by triggerViewChange() which pushes new event objects to the array and showPreviousView() which removes the last array item when the hashchange event fires.

The current event object is accessed like so: _events.slice(-1)[0]. Thus we are able to manage views via hash changes while feeding the views relevant data per the dom element that triggered the hashchange to begin with.

Of course we could choose instead to keep all the DOM-locked data attributes as part of our hash, something like this:

  • index.html#foo=bar&dog=brown&bacon=awesome

I didn’t use that approach because there’s no need as it adds complexity where its not needed. Did you notice how little effort was required to go back in our app’s history? By saving the touch event object I already have all the data represented in the above example. Everything is much simpler and requires less code without the need to parse hash fragments.

Of course, if you have a single-page web page (not a hybrid app) and need bookmarking support or deep linking then you should consider a hash fragment that contains name/value pairs representing whatever state your view requires.

Summary

Once you wrap your brain around the ideas here you realize its not so difficult. The “ah-ha” moment should have happened after you’ve read the Methodology section. If not and you’ve read this far go back and re-read it now that you have the code in your brain as a reference.

Download the entire working example. Note that it uses touch events so you have to enable them in your modern browser of choice.

Also, you can give this a whirl in PhoneGap too if you like, just package up the contents and do a build. You may want to edit the index.html so that the app builds its initial view on “device ready”.

Sencha Touch on Tizen!

14 Jun 2013

I just learned that Sencha Touch is coming to Tizen and is being made possible by a partnership between Sencha, Intel, and Samsung. This is super interesting considering a few things, the main one is that Intel had acquired App Mobi presumably for the same purpose – to provide developer tools for the fledgling operating system. While AppMobi is available today Sencha has not released a timeline for official Tizen support opting instead to mention that the feature will be coming to their toolset at a later date.

One of the features that makes Sencha a competitive toolkit for developing web apps is that you no longer need to leave the Sencha environment to package an app as they offer a native wrapper of their own via Sencha Cmd. Granted, it is young and may not have the breadth of features that PhoneGap has but the lure of not having to find the right mix of PhoneGap builds and PhoneGap plugins for a project, and discovering what is broken and the work-arounds, if any, is pretty compelling in my book.

While the open-source world can likely move faster to squash bugs than a for-profit entity I do like the implied support that comes with using the “all-in-one” development toolsets. Afterall, you’ve just bought their software so they have to offer support. Not so with the free open-sourced PhoneGap plugins. I should say though that you can get support for PhoneGap itself via a paid subscription program (Sencha has a similar support mechanism), otherwise you’re at the mercy of the open-source community for plugin support. True, they seem like a competent bunch but its still a risk and one that needs to be considered given Apple’s proclivity for changing their OS at whim. When that happens there is a cascade of issues and activity that ripples through the entire PhoneGap community. Its not fun being the end user (or customer) waiting for the kinks in that sometimes long chain to get fixed.

While Sencha Architect is a desktop app AppMobi’s approach foregoes that approach in favor of the cloud. The dev environment runs in the browser (Java) and they compile in the cloud, sort of like PhoneGap’s cloud compilation service (which, btw, has at last count 3 plugins supported which severely limits its use). Together both AppMobi and Sencha are offering solutions that are integrated and turn-key – no other software is needed and there are no other environments to learn/sign up for/install/troubleshoot, etc. Having created PhoneGap-based applications for the past two years I’m finding that such advantages are pretty attractive and I’m currently looking for a framework that has them.

As with anything in tech, the promise is one thing, actually delivering is another. PhoneGap is well entrenched and has the widest list of features (via plugins). The first Tizen device is hitting this year (said to be in Q3) and when it happens part of the announcement will undoubtedly be the developer options available. I for one will be reading forums and blogs for developer reactions to the those tools.

HTML5 is the way of the mobile future and has been for a while. Other HTML5-centric mobile OS’s are coming but none have a hardware backer on the scale of Samsung and Intel. Time will tell if Tizen will take off but its exciting to see the acquisitions and partnerships being made toward the initial product launch.

As for Intel’s involvement it is a forgone conclusion that the first Tizen device will have an Intel mobile chip in it (the revamped Atom?). Beyond the hardware and AppMobi tools they also seem to be offering a UI layer for Tizen currently known as “Obsidian”. The assumption here is that by offering a UI that would likely be optimized for the Intel silicon experience that they will be able to incentivize adoption of their chips into future Tizen devices.

It is interesting to note however that Samsung is also a chip manufacturer. As much as Intel would like to get into the phone/tablet market one can’t help but notice potential conflicts of interest in their partnership.

Launching the Facebook & Twitter Websites From a Web App

25 Mar 2013

Creating a link within your mobile HTML5/web apps is exactly like creating a link that launches in a new window, you simply do the following:

...
<a href="http://somewhere.com" target="_blank"></a>
...

Or via JavaScript:

...
<script language="javascript">
  window.open('http://somewhere.com');
</script>
...

The above does what one would expect in most cases – launching the URL in the device’s mobile browser. However, on iOS if we are trying to go to a Facebook or Twitter site such as in this example:

...
<a href="http://www.facebook.com/pepsi" target="_blank"></a>
...

…and the Facebook App is installed on the iOS device the Facebook App itself will launch instead and present the Facebook login screen – which is not what we want to happen. In this case getting around this iOS quirk is easy – create a proxy page on a server of your choice that will redirect Mobile Safari to the desired location.

For example you might have a link like this one within your web app:

...
<a href="http://myserver.com/redir.html" target="_blank"></a>
...

Given the above your proxy page would contain a single line of JavaScript:

...
document.location.href = 'http://www.facebook.com/pepsi'; // go to the branded pepsi facebook page
...

Pretty simple… but in my specific example I want to have a single proxy page handle multiple Facebook and Twitter cases. Given this information the link within my web app looks similar to this:

...
<a href="http://myserver.com/redir.html?b=abc&sn=fb" target="_blank"></a>
...

The convention that I’ve setup here is that I have a link to a specific brand (“b”) whose branded social network (“sn”) website that I want the user to go to. In my “redir.html” document I have some simple JavaScript to inspect the URL’s name/value pairs and thus branch to the desired brand’s specific social network website based on that information.

...
<script language="javascript">
  var loc = document.location.href.split('?');
  var args = loc[1].split('&');
  var brand = args[0].split('=');
  var sn = args[1].split('=');
  if (brand[1] == 'abc'){
    if (sn[1] == 'fb'){
      // go to the brand's facebook site
      document.location.href = 'http://www.facebook.com/abc';
    } else if (sn[1] == 'tw'){
      // go to the brand's twitter website
      document.location.href = 'http://www.twitter.com/abc';
    }
  } else if (brand[1] == 'xyz'){
    // etc...
  } 
  // etc.....
</script>
...

Intel Buys AppMobi

26 Feb 2013

I just got an email from Intel re its acquisition of AppMobi – Intel has just gotten itself developer tools presumably for its Tizen mobile operating system (of which Samsung is a partner). Here’s the text of the email:

February 25, 2013

Dear HTML5 Developer:

As a valued user of appMobi* HTML5* tools and services, I am pleased to let you know the developer tools and build system business portion of appMobi was acquired by Intel Corporation on February 21, 2013. In addition, Intel hired the tool development team from appMobi. With our combined development teams, we can extend the HTML5 tools capabilities.

While the names and location of the tools have changed, the same capabilities are there for you. You can continue your work, accessing the tools, your work files, and appMobi’s cloud services.

We do need you to sign up with Intel at http://software.intel.com/html5tools . It will take just a minute and you become part of Intel’s developer program, with access to the HTML5 tools and build system at no cost.

At Intel, we are committed to delivering HTML5 tools that inspire you to create and deploy more HTML5 cross platform apps. Our goal is to increase your productivity and expand your customer reach. Working together, Intel and appMobi will continue to serve your development needs.

I hope you will also visit http://software.intel.com/html5 to learn about the latest HTML5 developments from Intel.

We thank you for your continued support.

Sincerely,

Kevin J. Smith

Director, Developer Products Division/Web App

Logo

All content © 2012-2017.