/* ---------------------------------------------------
------------------------------------------------------

MARKETING MANCHESTER DATA VISUALISATION

Summary: Visualisation of twitter data discussing Manchester
Made by: mN (magneticNorth) http://mnatwork.com
For: Marketing Manchester http://marketingmanchester.com

Version: 1.7
Release: Dec 2009
Last Update: January 2010 (RayM @ mN) - Added datastore for search as opposed twitter.

Dependancies: Processing.js, jQuery 1.3+, excanvas (explorer canavas).

http://excanvas.sourceforge.net
http://processingjs.org
http://jquery.com

Instructions:
1. include the required files eg:
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<!--[if IE]><script type="text/javascript" src="excanvas.compiled.js"></script><![endif]-->  
<script type="text/javascript" src="processing.js"></script>  

2. Include this script following those files.

3. Include following Code in page:
<div id="vTwitter">
		<div id="visual">
		<canvas width="800" height="600" id="jspapp"></canvas>
		
		</div>
		<div id="messages">
		
		</div>
</div>

vTwitter can have any dimensions applied and the script will work within those dimensions.

4. Run the following call
	<script type="text/javascript">
		
		$(document).ready(function(){
			RunVisual();
		});
		
	</script>

-------------------------------------------------------
-----------------------------------------------------*/


/* ---------------------------------------------------
------------------------------------------------------

GLOBAL VARIABLES

The following variables are the globals for the script such as data holders and page data variables.
These are available globally within the script using window.n where n is the variable name.

-------------------------------------------------------
-----------------------------------------------------*/

// Default width and height variables.
var win_w= $(window).width()*0.99;
var win_h= $(window).height();
var right_space=150;
// JS Processing setup variable.
var jsp = {};
// Variable to hold processing functions
var p = {};

// Initial page of search results twitter.
var page=1;
// Number of results to grab from twitter at a time
var tw_num=100;
// Total pages available
var total_pages=1;

// Arrays to hold path creation information.
var path= new Array();	
var paths= new Array();	

// INTERVAL TIMERS
var find_reveal_int=0;
var random_s_int=0;
var twinkle_int=0;
var global_close=0;

// Variable to set if we are currently searching twitter results (don't do it while already searching).
var searching=false;
// Feed url for twitter search (NO LONGER USED - RayM Jan 2010).
var jsonFeed = "http://search.twitter.com/search.json"; 

// Holders for our data. Twitter is 

var data_o=new Array();
var loads={};

//var used=new Array();
var used={};	
var colors=new Array(
'#39105a',
'#ded903',
'#fd8e12',
'#219439',
'#02768a',
'#e61e5a');

var modifiers=new Array(
'0.3',
'-0.3',
'0.4',
'-0.5',
'0.1',
'-0.2');


/* ---------------------------------------------------
------------------------------------------------------

PROCESSING AND CREATION

The following functions are processing js functions and the make ribbons call which plots the paths as vertex shapes and add the data on top in the points.

Create paths creates a multi array of path points for processing to use.

-------------------------------------------------------
-----------------------------------------------------*/

jsp.setup= function() 
{
  
  
  p.size(win_w, win_h);
  p.strokeWeight(4);
  p.smooth();
  p.background('#FFFFFF');
  //p.frameRate(1);
  p.noLoop();
}

jsp.draw = function()
{
	
		makeRibbons(); 
	
}

function create_paths(){
	
	
  	var modifier=modifiers[random(modifiers.length-1)];	
	
	for(ix=0; ix < 8; ix++){
	
	
	var yPoint2 = (win_h/2)+(random(win_h)*modifier);
	var xPoint2 = (win_w/15)+(random(win_w/15));	
	
  	var n=0;
 	path[0]={"x":0 , "y":yPoint2};	
	
	for(i=1; i < 12; i++){
  	
		if(xPoint2 < (win_w-window.right_space)){

			path[i]={"x":xPoint2 , "y":yPoint2}; 
		
			yPoint2 = (win_h/2)+(random(win_h)*modifier);
			xPoint2 += (win_w/15)+(random(win_w/15));	
			modifier=modifiers[random(modifiers.length-1)];
			
		n++;
		}
	}	
	path[n+1]={"x":(win_w-window.right_space), "y": (win_h/2)+(random(win_h)*modifier)};
	window.paths[ix]=path;
	path=[];
	}
	
}





function makeRibbons() {

	var rd='';
	p.strokeWeight(1);
	p.noFill();
	var f = colors[random(colors.length-1)];
	$.each(window.paths, function( i, pObj){
	
  	p.stroke(f);
  	p.beginShape();	

	var ct=1;
	var totals=pObj.length;
  	$.each(pObj, function(n, obj){
  		//var rd=next_unused(random(window.data_o.length-1));
   		//window.used.push(rd);
  		
  		if(n > 0){
  			p.curveVertex(obj.x , obj.y);
  		} else {
  			p.vertex(obj.x , obj.y);
  		}
  		if(ct < totals){ place_mix(obj.x , obj.y, window.used); }
		ct++;
		window.used++;
  	});
  	
	p.endShape();
	
	f = colors[random(colors.length-1)];  
  	
  	});
  	
  	window.find_reveal_int=setInterval('find_and_reveal()',500);
  	window.twinkle_int=setInterval('twinkle()',500);
  	if(random_s_int==0){
  		window.random_s_int=setTimeout('random_select()',10000);
  	}
  
}


/* ---------------------------------------------------
------------------------------------------------------

ANIMATION and CORE FUNCTIONS

These functions create the animation and control the call of new data etc of the visualisation. 
Also housekeeping functions such as Random to generate a random number.



-------------------------------------------------------
-----------------------------------------------------*/

function random_new(){
	// This is the function that adds a new tweet to the page each time one is removed.
	//var rd=next_unused(random(window.data_o.length-1));
	// first random path selection
	var path_r=random(window.paths.length-1);
	
	var point= random(window.paths[path_r].length-1);

	var x= window.paths[path_r][point].x;
	var y= window.paths[path_r][point].y;
	
  	//window.used.push(rd);
	place_mix(x , y, window.used);
	window.used++;
	find_and_reveal();
	check_tw_data();
	
}

function check_tw_data(){
	if(window.used > ((window.tw_num*window.loads)-window.tw_num) ){
		if(!window.searching){
			more_tweets();
		}
	}

	}

function number_found (random_number,number_array) {

    for (var element=0; element<number_array.length; element++) {

        if (random_number==number_array[element]) {
            return (-1);
	}
   }

    return (random_number);
}

function next_unused(random_n){
	if(number_found(random_n, window.used)==-1){
  			return (next_unused(random(window.data_o.length-1)));
  		} else{
  			return (random_n);
  		}
}

function random(limit){
	
	var x = Math.floor(Math.random()*(limit+1));
	return x;
}


function find_and_reveal(){
	
	var sequence=$('div.thumbnail:hidden');	
    if(sequence.length < 1){
    	clearInterval(window.find_reveal_int);
    } else{
    	 $(sequence[random(sequence.length-1)]).fadeIn('slow');
    }   		 
}

function twinkle(){
	
	var sequence=$('div.thumbnail:visible');	
    if(sequence.length < 1){
    	clearInterval(window.twinkle_int);
    } else{
    	 var z_index=random(sequence.length)+10;
    	 $(sequence[random(sequence.length-1)]).css({'z-index': z_index });
    }    
   		 
}

function random_select(){
	var sequence2=$('div.thumbnail:visible');	
    if(sequence2.length > 0){
    
    
    var random_s = $(sequence2[random(sequence2.length-1)]);
    var id_pass=$(random_s).attr('id');
    
    var x = $(random_s).attr('offsetLeft');
	var y = $(random_s).attr('offsetTop');
    
    		//dim_background();
			show_tweet(id_pass, x, y);
			
   } else{
   		clearInterval(window.random_s_int);
   }
}

function close_random(){
	var r_s_int=0;
	$('#player').fadeOut('slow', function(){
		$(this).remove();
	})
	$('#visual').animate({'opacity': '1',   'filter': 'alpha(opacity = 100)'}, 200);
	
	if(r_s_int==0){
  		window.random_s_int=setTimeout('random_select()',3000);
  	}
}

function place_mix(x ,y, n){
	
	var y_pos=Math.round(y);
	var x_pos=Math.round(x);
	var right_point=(window.win_w - window.right_space)-300;
	if(x_pos > right_point){
	
	var place='right';
	x_pos=Math.round((window.win_w-x))+(random(window.win_w/10));
	}else{
	var place='left';
	}
	
	if(place=='left' && x_pos < 100){
		x_pos=x_pos+(random(window.win_w/6));
	}
	
	
	if(y_pos > (win_h-20)){
		y_pos -=25;
	}
	
	var type='twitter';
	
		var display='<div class="thumbnail t_'+ type +'" style="top:' + y_pos + 'px; '+ place +': ' + x_pos + 'px;" id="' + n + '"><a href="#"><img src="' + window.data_o[n].profile_image_url + '" width="18" height="18"/></a></div>';
	
	
	$('#visual').append(display);
	
	
}



function show_tweet(n, x, y){
	if(y > (win_h-50)){
		
		y -=65;
		
	}
	
	var right_point=(window.win_w - window.right_space)-300;
	
	if(x > right_point){
		x= x-350;
	}else{

	}


	
	var start_x= x + 400;
	var start_y= y;
	var image_src=window.data_o[n].profile_image_url;
	var title=window.data_o[n].from_user;
	var tweet=window.data_o[n].text;
	var link=window.data_o[n].id;
	var f = colors[random(colors.length-1)]; 
	var image='<p><a href="http://twitter.com/'+title+'" rel="external" style="color: '+ f +'; font-weight:bold;">'+title+'</a><br/><em>&quot; ' + tweet +' &quot;</em></p>';
	
	$('#player').remove();
	$('#visual').append('<div id="player" style="left:'+ start_x +'px; top:'+ start_y +'px;opacity:0;filter: alpha(opacity = 0); z-index:2000;">'+ image +'</div>');

	$('#player').animate({ "left" : x,"opacity": "1", "filter": "alpha(opacity = 100)"}, 500, function(){
		$('#' + n).remove();
		
		random_new();
		var timeout= tweet.length*60;
		window.global_close=setTimeout('close_random()', timeout);
  		
	});
	
}

function dim_background()
{
	$('#visual').animate({ "opacity": 0.2, "filter": "alpha(opacity = 20)"}, 200);    
}


/* ---------------------------------------------------
------------------------------------------------------

START UP AND GRAB DATA FUNCTIONS.

The following functions create the initial load of data (which also initialises the processing init_p), the load of more data and the initialisation of processing.
RunVisual is the call made from the executing script to start the whole process.

-------------------------------------------------------
-----------------------------------------------------*/

function startup(){
	$('#visual, #messages').css({ 'width': win_w + 'px', 'height': win_h + 'px' });
	
	$.ajax({
        url: 'http://fabricapi.mnatwork.com/fabricservice.svc/en/json/TwitterDatastore/MarketingManchester/pages/'+ window.tw_num +'?apikey=TWITTER_DATASTORE' ,
        dataType: "jsonp",
        data:{},
        timeout: 5000,
        success: function(data, status){
            window.total_pages=data.results.pages;
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERROR: " + textStatus);
            alert("ERROR: " + errorThrown);
          
        }
    });

	
		 
	 window.searching=true;
	   
    $.ajax({
        url: 'http://fabricapi.mnatwork.com/fabricservice.svc/en/json/TwitterDatastore/MarketingManchester/'+ window.page + '/' + window.tw_num +'?apikey=TWITTER_DATASTORE' ,
        dataType: "jsonp",
        data:{},
        timeout: 5000,
        success: function(data, status){
            var twitter=data.results;
	 		data_o= $.shuffle(twitter);
	 		window.loads=1;
	 		window.used=0;
	 		window.page++;
	 		
			init_p();
			window.searching=false;
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERROR: " + textStatus);
            alert("ERROR: " + errorThrown);
            window.searching=false;
        }
    });

	
}

function more_tweets(){
	window.searching=true;
	
	$.ajax({
        url: 'http://fabricapi.mnatwork.com/fabricservice.svc/en/json/TwitterDatastore/MarketingManchester/'+ window.page + '/' + window.tw_num +'?apikey=TWITTER_DATASTORE' ,
        dataType: "jsonp",
        data:{},
        timeout: 5000,
        success: function(data, status){
            var twitter=data.results;
            twitter=$.shuffle(twitter);
	 		data_o= data_o.concat(twitter);
	 		window.loads++;
	 		if(window.page < window.total_pages){
	 			window.page++;
	 		} else{
	 			window.page=1;
	 		}
	 		window.searching=false;
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERROR: " + textStatus);
            alert("ERROR: " + errorThrown);
            window.searching=false;
        }
    });
}

function init_p(){
	create_paths();
	
	p = Processing("jspapp");
	p.setup = jsp.setup;
	p.draw = jsp.draw;
	p.init();
	$('#jspapp').fadeIn('slow');
	
}



function RunVisual(){
	
	window.win_w=$('#vTwitter').width();
	window.win_h=$('#vTwitter').height();
	
	window.right_space=Math.round((window.win_w-950)/2);
	startup();
	$('#messages, #jspapp').hide();
	$('#vTwitter').css({ 'overflow': 'hidden' });

	$('.t_twitter a').live('click', function(){
			clearInterval(window.random_s_int);
			clearInterval(window.global_close);
			var id_pass=$(this).parents('div').attr('id');
			var x = $(this).parents('div').attr('offsetLeft') ;
			var y = $(this).parents('div').attr('offsetTop') ;
			//dim_background();
			show_tweet(id_pass, x, y);
			
			return false;
	});
		

}

/*
 * jQuery shuffle
 *
 * Copyright (c) 2008 Ca-Phun Ung <caphun at yelotofu dot com>
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://yelotofu.com/labs/jquery/snippets/shuffle/
 *
 * Shuffles an array or the children of a element container.
 * This uses the Fisher-Yates shuffle algorithm <http://jsfromhell.com/array/shuffle [v1.0]>
 */
 
(function($){

	$.fn.shuffle = function() {
		return this.each(function(){
			var items = $(this).children().clone(true);
			return (items.length) ? $(this).html($.shuffle(items)) : this;
		});
	}
	
	$.shuffle = function(arr) {
		for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
		return arr;
	}
	
})(jQuery);


