

/*!
 * tacky.js
 *
 * Copyright (c) 2011, ShopLive, LLC
 * http://tacky.com/copyright
 * 
 * Description:  The core Tacky Client JavaScript
 * 		
 */



var tacky = (function(window,document,undefined){
	
	//--  Private Variables  --
	var domain = "http://www.tacky.com";
	var publicMethods = null; //Defined at the bottom of tacky
	var externalUrl = decodeURIComponent(document.location.hash.replace(/^#/, ''));
	var isExternal = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/.test(externalUrl) ? true : false;
	var user = {}; //Contains the user data
	var urlParams = (function() {
	    var params={},e,a = /\+/g,r = /([^&=]+)=?([^&]*)/g;
	    var d = function (s) { return decodeURIComponent(s.replace(a, " ")); };
	    var q = window.location.search.substring(1);
	    while (e = r.exec(q)) { params[d(e[1])] = d(e[2]); }
	    return params;
	})();
	
	
	
	
	
	
	
	//--  Private Methods  --
	var isUrl = function(url) {
		var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
		return regexp.test(url);
	};
	
	var getDomain = function(url){
    	url = url || window.location.href;
    	return url.match(/:\/\/(.[^/]+)/)[1];
    };
    
	var stringify = function(obj){
	    var t = typeof (obj);  
	    if (t != "object" || obj === null) {
	        if (t == "string") obj = '"'+obj+'"';  
	        return String(obj);  
	    }  
	    else {  
	        var n, v, json = [], arr = (obj && obj.constructor == Array);  
	        for (n in obj) {  
	            v = obj[n]; t = typeof(v);  
	            if (t == "string") v = '"'+v+'"';  
	            else if (t == "object" && v !== null) v = stringify(v);  
	            json.push((arr ? "" : '"' + n + '":') + String(v));  
	        }  
	        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");  
	    }  
	};
	
	var destringify = function(jsonString){ 
		return eval("(" + jsonString + ")"); 
	};
	
	var addEvent = function(elem,type,func){
		if(elem.attachEvent){
			elem['e'+type+func] = func;
			elem[type+func] = function(){elem['e'+type+func]( window.event );};
			elem.attachEvent( 'on'+type, elem[type+func] );
		} else {
			elem.addEventListener(type,func,false);
		}
	};
	

	
    
	//--  Public Methods  --
	
	//Notification System
	var notify = (function(){
		var storage = [];
		
		var displayNotifications = function(){
			var n = $("#notify");
			n.css({left:($(window).width()-n.width())/2 + "px"});
			n.text(storage[0]);
			n.fadeIn("fast",function(){
				n.delay(2000).fadeOut("fast",function(){
					storage.shift();
					if(storage.length > 0){ displayNotifications(); }
				});
			});
		};
		
		var publicNotify = function(msg){
			if(storage.length == 0 || storage[storage.length-1] != msg){
				storage.push(msg);				
			}
			
			if(storage.length == 1){
				displayNotifications();
			}
		};
		return publicNotify;
		
	})();//End Notification System
	
	
	//Application Program Interface Communication
	var api = {
		send:function(apiCall,onComplete,data){
			data = data || {};
			onComplete = onComplete || function(){};
			$.ajax({url:domain+apiCall,dataType:"json",data:data,success:onComplete}).fail(function(jqXHR, textStatus){
				onComplete({success:false,message:textStatus});
			});
		},
		receive:function(callback){
			
		}
	};//End Application Program Interface Communication
	
	
	//Cross Domain Communication
	var cdc = {
		destSrc:externalUrl,
		destElem:window.parent,
		
		send:function(msg){
			if(!isExternal){ return; }
			if (window['postMessage']) {
	        	this.destElem['postMessage'](stringify(msg), this.destSrc.replace( /([^:]+:\/\/[^\/]+).*/, '$1'));
	        } else if (target_url) {
	        	this.destElem.location = this.destSrc.replace(/#.*$/, '') + '#' + (+new Date) + (this.cache_bust++) + '&' + stringify(msg);
	        }
		},
		receive:function(callback){
			if (window['postMessage']) {
	        	addEvent(window,"message",function(e){
	                callback(destringify(e.data));
	        	});
	         } else {
	             this.interval_id && clearInterval(this.interval_id);
	             this.interval_id = null;
	             this.interval_id = setInterval(function() {
	                 var hash = document.location.hash, re = /^#?\d+&/;
	                 if (hash !== this.last_hash && re.test(hash)) {
	                	 this.last_hash = hash;
	                     callback(destringify(hash.replace(re, '')));
	                 }
	             }, 10);
	         }
		}
	};//End Cross Domain Communication
	
	
	
	//Action Manager
	var actions = {
		storage:{},//{actionName:{callbacks:[],action:function(){}}}
		add:function(actionName,actionFunc){
			if(!this.storage[actionName]) { 
				this.storage[actionName] = {callbacks:[],action:function(args){ return args; }}; 
			}
			if(actionFunc){ this.storage[actionName].action = actionFunc; }
		},
		call:function(actionName,args){
			if(!this.storage[actionName]){ return; }
			args = this.storage[actionName].action(args);
			for(var i=0; i<this.storage[actionName].callbacks.length; i++){
				this.storage[actionName].callbacks[i](args,actionName);
			}
		},
		listen:function(actionName,callback){
			if(!this.storage[actionName]){ this.add(actionName); }
			this.storage[actionName].callbacks.push(callback);
		}
	};//End Action Manager
	
	
	
	//Client Panels
	var panels = {
		storage:{},//{panelId:{isOpen:false,group:null,...},...}
		init:function(){
			for(var panelId in this.storage){
				this.storage[panelId].elem = $("#"+panelId);
				this.storage[panelId].init.call(this.storage[panelId]);
				if(this.storage[panelId].isOpen){
					this.storage[panelId].elem.css({display:"block"});
				}
			}
		},
		get:function(panelId){
			return this.storage[panelId];
		},
		add:function(panelId, panelMethods){
			this.storage[panelId] = {isOpen:false,group:null,init:function(){},open:function(){},close:function(){}};
			for(var m in panelMethods){
				this.storage[panelId][m] = panelMethods[m];
			}
		},
		open:function(panelId,args){
			//Close any open dialogs in the same group
			if(this.storage[panelId].group != null){
				for(var p in this.storage){
					if(this.storage[p].group == this.storage[panelId].group){
						this.close(p,args);
					}
				}
			}
			
			//Open the panel
			this.storage[panelId].isOpen = true;
			this.storage[panelId].elem.css({display:"block"});
			this.storage[panelId].open.call(this.storage[panelId],args);
		},
		close:function(panelId,args){
			if(!this.storage[panelId].isOpen){ return; }
			this.storage[panelId].isOpen = false;
			this.storage[panelId].elem.css({display:"none"});
			this.storage[panelId].close.call(this.storage[panelId],args);
		},
		closeAll:function(args){
			for(var p in this.storage){
				this.close(p,args);
			}
		}
	};//End Client Panels
	
	
	
	
	//Client Dialogs
	var dialogs = {
		storage:{},//{dialogName:{elem:null,isOpen:false}}
		cover:null,
		isOpen:false,
		init:function(){
			this.cover = $("<div></div>").addClass("dialogCover").appendTo($(document.body));
			for(var d in this.storage){
				this.storage[d].elem = $("#"+d);
				this.storage[d].init.call(this.storage[d]);
			}
		},
		get:function(dialogId){
			return this.storage(dialogId);
		},
		add:function(dialogId,panelMethods){
			this.storage[dialogId] = {isOpen:false,init:function(){},open:function(){},close:function(){}};
			for(var m in panelMethods){
				this.storage[dialogId][m] = panelMethods[m];
			}
		},
		open:function(dialogId,args){
			if(!this.storage[dialogId] || this.storage[dialogId].isOpen){ return; }
			this.close(args);
			this.cover.fadeIn("fast");
			this.storage[dialogId].isOpen = true;
			this.isOpen = true;
			var left = Math.round($(window).width() - this.storage[dialogId].elem.width())/2;
			var top = Math.round($(window).height() - this.storage[dialogId].elem.height())/2;
			this.storage[dialogId].elem.css({left:left+"px",top:top+"px"});
			this.storage[dialogId].elem.fadeIn("fast");
			this.storage[dialogId].open.call(this.storage[dialogId],args); 
		},
		close:function(args){
			for(var d in this.storage){
				if(this.storage[d].isOpen){
					this.storage[d].elem.fadeOut("fast");
					this.storage[d].isOpen = false;
					this.storage[d].close.call(this.storage[d],args); 
					break;
				}
			}
			this.isOpen = false;
			this.cover.fadeOut("fast");
		}
	};//End Client Dialogs
	/*
	 //Handle page scroll and resize events for centering the iframe
		$(window).resize(function(){
			popupDetails.top = Math.floor(($(window).height()-popup.height())/2);
			popupDetails.scrollTop = $(window).scrollTop();
			popupDetails.left= Math.floor(($(window).width()-popup.width())/2);
			popup.css({top: (popupDetails.top + popupDetails.scrollTop) +"px", left:popupDetails.left+"px"});
		});
		$(window).scroll(function(){
			popupDetails.scrollTop = $(window).scrollTop();
			popup.css({top: (popupDetails.top + popupDetails.scrollTop) +"px"});
		});
		
		
	 */
	
	
	
	//Page Layouts
	var layouts = {
		waterflow:function(parent,colSize){
			var columns = []; //{left:0,height:0}
			//Setup Columns
			var numCols = Math.floor(parent.width()/colSize);
			var leftOffset = Math.floor((parent.width() - numCols*colSize)/2);
			for(var i=0; i<numCols; i++){
				columns.push({left:i*colSize+leftOffset,height:0});
			}
			
			var sortColumns = function(){
				columns.sort(function(a,b){ return a.height - b.height; });
			};

			this.add = function(child){
				sortColumns();
				child.css({position:"absolute",top:columns[0].height + "px",left:columns[0].left +"px"});
				columns[0].height += child.outerHeight(true);
			}
		}	
	};//End Page Layouts
	
	
	
	//Signin and Set User
	var signin = function(userId){
		api.send("/api/users/"+userId, function(json){
			if(json.success && json.user) {
				notify("Successful Signin to Tacky!");
				
				for(var u in json.user){ user[u] = json.user[u]; }
				user.boards = json.boards;
				
				actions.call("signedin");
			}
		});
	};
	
	
	
	//Register Default Action Listeners
	
	//--addTack--
	actions.listen("addTack",function(data){
		api.send("/api/users/"+user.id+"/boards/"+data.boardId+"/tacks/add", function(json){
			if(json.success) {
				for(var b=0; b<user.boards.length; b++){
					if(user.boards[b].id == data.boardId){
						user.boards[b].tacks.push(json.tack);
					}
				}
				
				cdc.send({updateTack:json.tack.sourceUrl,tackId:json.tack.id});
				notify("Tack Created");
				actions.call("tackAdded",data);
			} else {
				notify("Tack not created: " + json.message);
			}
		}, {
			sourceUrl:data.url,
			sourcePageUrl:data.pageUrl,
			sourceTitle:data.title,
			description:data.desc
		});
	});
	
	//--removeTack--
	actions.listen("removeTack",function(data){
		api.send("/api/users/"+user.id+"/tacks/"+data.tackId+"/remove",function(json){
			if(json.success){
				tacky.user.boards[data.boardIndex].tacks.splice(data.tackIndex,1);
				
				cdc.send({removeTack:data.tackId});
				notify("Tack Removed");
				actions.call("tackRemoved",data);
			} else {
				notify("Remove Tack Error: " + json.message);
			}
		});
	});
	
	//--updateTack--
	actions.listen("updateTack",function(data){
		api.send("/api/users/"+user.id+"/tacks/"+data.tackId+"/update",function(json){
			if(json.success){
				notify("Tack Updated");
				actions.call("tackUpdated",data);
			} else {
				notify("Update Tack Error: " + json.message);
			}
		},{
			description:data.desc,
			boardId:data.newBoardId
		});
	});
	
	//--addBoard--
	actions.listen("addBoard",function(data){
		api.send("/api/users/"+user.id+"/boards/add",function(json){
			if(json.success){
				tacky.user.boards.push(json.board);
				notify("Board Added!");
				data.board = json.board;
				actions.call("boardAdded",data);
			} else {
				notify("Board not added: " + json.message);
			}
		},{
			scope:data.scope,
			title:data.title,
			iconIndex:data.iconIndex
		});
	});
	
	//--removeBoard--
	actions.listen("removeBoard",function(data){
		api.send("/api/users/"+user.id+"/boards/"+data.boardId+"/remove",function(json){
			if(json.success){
				for(var i=0; i<tacky.user.boards.length; i++){
					if(tacky.user.boards[i].id == data.boardId){
						tacky.user.boards.splice(i,1);
						break;
					}
				}
				notify("Board Removed");
				actions.call("boardRemoved",data);
			} else {
				notify("Remove Board Error: " + json.message);
			}
		});
	});
	
	//--updateBoard
	actions.listen("updateBoard",function(data){
		api.send("/api/users/"+user.id+"/boards/"+data.boardId+"/update",function(json){
			if(json.success){
				for(var i=0; i<tacky.user.boards.length; i++){
					if(tacky.user.boards[i].id == data.boardId){
						if(data.title) { tacky.user.boards[i].title = data.title; }
						if(data.scope) { tacky.user.boards[i].scope = data.scope; }
						if(data.tackIds) { tacky.user.boards[i].tackIds = data.tackIds; }
						break;
					}
				}
				
				notify("Board Updated");
				actions.call("boardUpdated",data);
			} else {
				notify("Update Board Error: " + json.message);
			}
		},{
			scope:data.scope,
			title:data.title,
			tackIds:data.tackIds,
			tackOrder:data.tackOrder
		});
	});
	
	//--shareBoard
	actions.listen("shareBoard",function(data){
		api.send("/api/users/"+user.id+"/boards/"+data.boardId+"/share",function(json){
			if(json.success){
				notify("Board Shared");
				actions.call("boardShared",data);
			} else {
				notify("Sharing Board Error: " + json.message);
			}
		},{
			fb:data.fb,
			email:data.email
		});
	});
	
	//--getLayerTacks
	actions.add("getLayerTacks",function(data){
		api.send("/api/users/"+user.id+"/tacks", function(json){
			if(json.success){
				actions.call("receivedLayerTacks",json);
			} else {
				notify("Error: " + json.message);
			}
		},{
			domain:encodeURIComponent(getDomain(externalUrl))
		});
	});
	

	
	//--  Initialization  --
	$(function(){
		panels.init();
		dialogs.init();
		
		//Setup Cross Domain Receiver
		cdc.receive(function(data){
			if(data && data.actionName) {
				var actionName = data.actionName;
				data.receivedBy = "cdc";
				actions.call(actionName,data);
			}
		});
		
		//Setup API Receiver
		api.receive(function(data){
			if(data && data.actionName) {
				var actionName = data.actionName;
				data.receivedBy = "api";
				actions.call(actionName,data);
			}
		});
	
		
		//Signin the user
		api.send("/api/users/me",function(json){
			if(json.success){
				signin(json.user.id);
			} else {
				actions.call("signinFailed");
				//window.open("/oauth/request/facebook","FacebookLogin","width=580,height=320,toolbar=0,location=0,status=0,menubar=0,scrollbars=0,resizable=1");
			}
		});
	});
	
	
	//--  Return Public Methods  --
	publicMethods = {
		notify:notify,
		panels:panels,
		dialogs:dialogs,
		actions:actions,
		cdc:cdc,
		api:api,
		layouts:layouts,
		isExternal:isExternal,
		signin:signin,
		user:user,
		urlParams:urlParams,
		stringify:stringify,
		domain:domain
	};	
	return publicMethods;
	
})(window,document,undefined);
