/* Encalc js
 * Version 1.03
 * Last modified Sep 5, 2009
 * (c) Copyright 2008 Encalc.com. All Rights Reserved. 
 *
 * DEPENDENCIES: jQuery
 */


function rand(lower, upper) {
    /* Returns a random integer in [lower, upper) exclusive on upper.
        Uses the formula: (upper - lower)*random + lower  */
    return Math.floor(Math.random()*(upper-lower)) + lower;
}

function sanitize(str) {
	str=str.replace(/\</g, '&lt;');
	str=str.replace(/\>/g, '&gt;');
	return str;
}

// Random Example Class
var Example = {
	count: 0,
	randomExamples: [
		"expr=1%20marathon%20in%20miles&var1=&val1=&p=0",
			"expr=G%20M_1%20M_2%20%2F%20r%5E2&var1=M_1&val1=mass%20of%20the%20Sun&var2=M_2&val2=mass%20of%20the%20Earth&var3=r&val3=1%20astronomical%20unit&var4=&val4=&p=0",
		"expr=60%20kg%20in%20pounds&var1=&val1=&p=0",
		"expr=sin(30%20deg)%5E2%20%2B%20cos(30%20deg)%5E2&var1=&val1=&p=0",
		"expr=3*5-7&var1=&val1=&p=0",
		"expr=sin(pi/4)&var1=&val1=&p=1",
		"expr=sqrt(16)&var1=&val1=&p=0",
		"expr=v%20*%20t%20in%20inches&var1=v&val1=10%20miles%2Fhour&var2=t&val2=5%20seconds&var3=&val3=&p=0",
		"expr=m_e%20q_e%5E4%20%2F%20(32%20pi%5E2%20e_0%5E2%20h_bar%5E2%20n%5E2)%20in%20eV&var1=n&val1=1&var2=&val2=&p=0",
		"expr=100%20kg%20*%20(5%20m%2Fs)%5E2%20in%20Joules&var1=&val1=&p=1"
		],
	getExample: function(){
		Example.count++;
		if(Example.count >= Example.randomExamples.length) {
			Example.count = 0;
		}
		return Example.randomExamples[Example.count];
	},
	randomize: function(){
		var newArray = []
		while(Example.randomExamples.length>0) {
			var index=rand(0, Example.randomExamples.length);
			newArray.push(Example.randomExamples[index]);
			Example.randomExamples.splice(index,1);
		}
		Example.randomExamples=newArray;
	},
	
	_last: null // to avoid having to worry about missing commas at the end
};

var Calc = {
    currentlyLoading:   {
    	inUse: false,
    	expression: '',
    	variables: new Array(),
    	parameters: '',
    	resultId: ''
    },
    
    BASE_URL: 'http://www.encalc.com/',
    BASE_CALC_URL: 'http://www.encalc.com/',
    SCRIPT_PATH: 'engine/calculate', // relative to BASE_CALC_URL
    DEFAULT_EXPRESSION_TEXT: '2+2',
    uniqueResultNumber: 1,
    sidebarCount: 0,
    currentSidebar: 0,
    editableDescriptions: false,
    
    createNewVariable: function(defaultName, defaultValue) {
        var numVariables = $('.var-holder').length;
        numVariables++;
    
        var newSpan = document.createElement("span");
        newSpan.className="var-holder";
        
        var newVarName = document.createElement("input");
        newVarName.className="var-name";
        newVarName.type="text";
        if(defaultName) {
        	newVarName.value=defaultName;
        }
        newVarName.onchange=Calc.updateVariables;
        newVarName.autocorrect="off";
        newVarName.autocomplete="off";
        newVarName.name='var'+numVariables;
        newSpan.appendChild(newVarName);
        
        var equalsSpan = document.createElement("span");
        equalsSpan.className="var-equals";
        equalsSpan.innerHTML=" = ";
        newSpan.appendChild(equalsSpan);
        
        var newVarValue = document.createElement("input");
        newVarValue.className="var-value";
        newVarValue.type="text";
        if(defaultValue) {
        	newVarValue.value=defaultValue;
        }
        newVarValue.onchange=Calc.updateVariables;
        newVarValue.autocorrect="off";
        newVarValue.autocomplete="off";
        newVarValue.name='val'+numVariables;
        newSpan.appendChild(newVarValue);
                
        var newVarDesc = document.createElement('input');
        newVarDesc.className="var-desc";
        if(Calc.editableDescriptions) {
            $(newVarDesc).val('Description');
        } else {
            newVarDesc.style.display = 'none';
            $(this).addClass('locked');
            $(this).attr('readonly', true);
        }
        newVarDesc.name='vdes'+numVariables;
        newSpan.appendChild(newVarDesc);        
        
        newSpan.style.display = 'none';
        $("#variables")[0].appendChild(newSpan);
        $(newSpan).slideDown(250);
    },
    
    updateVariables: function() {
    	var deleteThese=new Array();
    	var numToDelete=-1; // always leave one empty
    	var numFilled=0;
        
    	$('.var-holder').each(function(i){
    			if(($(this).children('.var-name')[0].value=='') && ($(this).children('.var-value')[0].value=='')) {
    				deleteThese.push(this);
    				numToDelete++;
    			} else {
    				numFilled++;
    			}
           });
        
    	if(numFilled==$('.var-holder').length) {
    		Calc.createNewVariable();
    	}
        
    	$.each(deleteThese, function(i) {
    		if(numToDelete>0) {
    			$(this).remove();
    			numToDelete--;
    		}
    	});
        
        Calc.updateDescs();
    	Calc.standardizeFormVariables();
    },
    
    updateDescs: function() {
        $('.var-desc').each(function(i){
                if($(this).val()!='' && $(this).val()!='Description') {
                    this.style.display = 'block';
                }
            });
    },
    
    standardizeFormVariables: function() {
    	$('.var-holder').each(function(i){
                $(this).children('.var-name')[0].name='var'+(i+1);
                $(this).children('.var-value')[0].name='val'+(i+1);
                $(this).children('.var-desc')[0].name='vdes'+(i+1);
    		});
    },
    
    setUrl: function(params) {
    	var currentUrl=document.location.toString();
    	if(-1!=currentUrl.indexOf('?')) {
    		return; // don't put 2 values in for the url, it would be confusing...
    	}
        
    	currentUrl=currentUrl.split('#');
    	currentUrl=currentUrl[0];
    	currentUrl+='#';
    	currentUrl+=params;	

    	document.location = currentUrl;
    },
    
    setTitle: function(expression) {
        document.title = expression+' :: Encalc';
    },
    
    updateState: function() {
        Calc.setUrl(Calc.getParameters());
        Calc.setTitle(Calc.getExpression());
    },
    
    getParameters: function() {
        parameters = $('#form-input').serialize();
        parameters = parameters.replace(/\+/g, '%20'); // necessary... otherwise "2 + 3" -> "2+++3" when decoded 
    	/*if(parameters.indexOf('&p=')!=-1) {
    		// do nothing
    	} else {
    		var checkedValue = $('#precisionToggle').checked ? '1' : '0';
    		parameters += '&p=' + checkedValue;
    	}*/
    	return parameters;
    },
    
    getExpression: function() {
        return $('#expression').val();
    },
    
    createNewResult: function(defaultExpression) {
    	var myId="result"+Calc.uniqueResultNumber;
    	Calc.uniqueResultNumber++;
    	Calc.updateState();
    	
    	//tallySidebarCount();

    	$("#results").prepend('<br/>'); // added this to fix an IE bug

    	var newSpan = document.createElement("div");
    	newSpan.className="result-holder";
    	newSpan.id=myId;
    	newSpan.style.display='none';

    	var newExpression = document.createElement("a");
    	newExpression.className="result-expression";
    	newExpression.href=Calc.BASE_CALC_URL+'#'+Calc.currentlyLoading.parameters;
    	newExpression.title="Right click to copy url for this expression";
    	if(defaultExpression) {
    		newExpression.innerHTML=defaultExpression;
    	} else {
    		newExpression.innerHTML=' ';
    	}
    	newSpan.appendChild(newExpression);
    	newExpression.onclick=function() {
    		Calc.setFormFromLink(this);
    		this.blur();
    		return false;
    	};
        
        var newAnswer = document.createElement("div");
    	newAnswer.className="result-answer";
    	newSpan.appendChild(newAnswer);
        
    	var loadingImage = document.createElement("img");
    	loadingImage.src=Calc.BASE_URL+'images/loading1.gif';
    	newAnswer.appendChild(loadingImage);
    	    	
    	$("#results").prepend(newSpan);
    	$(newSpan).slideDown(250);
    	
    	loadingImage.style.display='none'; // do this after so that the slide down calculates its height correctly
    	$(loadingImage).fadeIn(2000);
    	
    	return myId;
    },
    
    
    simulateSubmitPress: function() {
    	$('#submit').addClass('pressed');
    	setTimeout("$('#submit').removeClass('pressed')", 120);
    },
    
    validate: function() {
    	var isAcceptable=true;
        
    	// do validation
        
    	return isAcceptable;
    },
    
    calculate: function() {
    	Calc.simulateSubmitPress();
        $('#submit').blur(); // it looks annoying otherwise
        
    	if(!Calc.validate()) {
    		return false;
    	}

    	if(Calc.currentlyLoading.inUse) {
    		return false; // one result at a time. could change this later
    	}
        
        Calc.currentlyLoading.inUse=true;	
    	Calc.currentlyLoading.expression=Calc.getExpression();
    	Calc.currentlyLoading.parameters=Calc.getParameters();
    	Calc.currentlyLoading.expression=sanitize(Calc.currentlyLoading.expression);
    	Calc.currentlyLoading.resultId=Calc.createNewResult(Calc.currentlyLoading.expression);
        
        
    	$.ajax({
    	    type: "GET",
    	    url: Calc.BASE_CALC_URL+Calc.SCRIPT_PATH,
    	    data: Calc.currentlyLoading.parameters,
    	    dataType: 'json',
    	    success: function(transport) {
    	        Calc.showResult(transport);
    	    },
    	    error: function(transport) {
    	        Calc.catchFailure();
    	    }
    	})
        
        SideBar.increment();
        
    	return false;
    },
    
    showResult: function(transport) {    	
    	result = transport.result+' ';
    	result=result.replace(/\^([^<]+?)\s/g, '<sup><span class="hidden-carat">^</span>$1</sup> ');
        
        currentResult = $('#'+Calc.currentlyLoading.resultId+' .result-answer')[0];
    	if(transport.result) {		
    		currentResult.innerHTML=' = '+result;		
    	} else {		
    		// there are warnings or errors
    		currentResult.innerHTML='';
    		currentResult.style.display='none'; // hide it (necessary for ie bug)
    	}
    	
    	if(transport.warnings) {
    		$.each(transport.warnings, function() { Calc.addWarning(this); });
    	}
    	if(transport.errors) {
    		$.each(transport.errors, function() { Calc.addError(this); });
    	}

    	Calc.currentlyLoading.inUse=false;
    },
    
    catchFailure: function() {
        $('#'+Calc.currentlyLoading.resultId+' .result-answer')[0].innerHTML='';
    	Calc.addError('The server did not respond properly.');
    	Calc.currentlyLoading.inUse=false;
    },
    
    addWarning: function(warning) {
    	var newSpan = document.createElement("span");
    	newSpan.className="result-warning";
    	if(warning) {
    		newSpan.innerHTML='<span class="warning-word">Warning:</span> '+warning;
    	}
        
    	$('#'+Calc.currentlyLoading.resultId).append(newSpan);
    },
    
    addError: function(error) {
    	var newSpan = document.createElement("span");
    	newSpan.className="result-error";
    	if(error) {
    		newSpan.innerHTML='<span class="error-word">Error:</span> '+error;
    	}
        
    	$('#'+Calc.currentlyLoading.resultId).append(newSpan);
    },
    
    setFormFromLink: function(link) {
    	var queryString;
    	if(typeof(link)=='string') {
    		queryString = Calc.getQuery(link);
    	} else {
    		queryString = Calc.getQuery(link.href);
    	}
    	if(''!=queryString) {
    		Calc.setFormToString(queryString);
    		Calc.updateState();
    	}
    },
    
    getQuery: function(str) {
        var url=str;
        if(''==url) {
        	url=document.location.toString();
        }
        var questionMarkIndex=url.indexOf('?');
        var poundIndex=url.indexOf('#');
        
        if(-1==questionMarkIndex && -1==poundIndex) {
        	return ''; // Nothing is given
        }

    	var queryString;
    	if(-1!=questionMarkIndex) {
    		queryString=url.substr(questionMarkIndex+1);
    	}
    	if(-1!=poundIndex) {
    		queryString=url.substr(poundIndex+1);
    	}

        return queryString;
    },
    
    clearAll: function() {
        $('#form-input input[type="text"]').each(function(i) {
               $(this).val(''); 
            });
    },
    
    numVariablesInString: function(string) {
        if(string=='') {
        	return 0;
        }

    	var current=1;
    	while(string.indexOf('&var'+current)!=-1) {
    		current++;
    		if(current>100) {
    			return 100; // Prevent infinite loop
    		}
    	}
	    return current-1;
    },
    
    setFormToString: function(string) {
        var calcForm = $('#form-input');
        
        var howManyVariables=Calc.numVariablesInString(string);
    	while(howManyVariables>$('.var-holder').length) {
    		Calc.createNewVariable(); // this increments the above value (numVariables)
    	}
        
        // ad hoc deserialize...
        
        Calc.clearAll();
        
        $('#precision-toggle').attr('checked', false); // default
        Calc.lockVarDescriptions(); // default
        
        var tokens = string.split('&');
        
        $.each(tokens, function(index) {
                    var data = this.split('=');
                    var id = data[0];
                    var value = decodeURIComponent(data[1]);
                    try {
                        var formElem = $('#form-input *[name="'+id+'"]')[0];
                        $(formElem).val(value);
                    } catch(e) {
                        // do nothing
                    }
                    
                    if(id=='p') {
                        if(value=='0') {
                            $('#precision-toggle').attr('checked', false);
                        } else if(value=='1') {
                            $('#precision-toggle').attr('checked', true);
                        }
                    }
                    
                    if(id=='d') {
                        if($('#editable-descriptions')) {
                            if(value=='0') {
                                $('#editable-descriptions').attr('checked', false);
                                Calc.lockVarDescriptions();
                            } else if(value=='1') {
                                $('#editable-descriptions').attr('checked', true);
                                Calc.editVarDescriptions();
                            }
                        }
                    }
                    
                    if(id=='h') {
                        if($('#hidden-equation')) {
                            if(value=='0') {
                                $('#hidden-equation').attr('checked', false);
                                $('#expression-container').slideDown(250);
                            } else if(value=='1') {
                                $('#hidden-equation').attr('checked', true);
                                $('#expression-container').slideUp(250);
                            }/* else {
                                // default
                                $('#hidden-equation').attr('checked', false);
                                $('#expression-container').slideDown(250);
                            }*/
                        }
                    }
                    
                });
        
    	Calc.updateVariables(); // this annoyingly resizes the screen
    },
    
    useExpressionFromUrl: function() {
        var queryString=Calc.getQuery(document.location.toString());
    	if(''!=queryString) {
    		Calc.setFormToString(queryString);
    		Calc.calculate();
    	}
    },
    
    autoCalculate: function() {
        try {
            if(autocalc) {
        		Calc.calculate();
        	}
        } catch(e) {
        	// nothing
        } finally {
        	// nothing
        }
    },
    
    useRandomExample: function(link) {
    	Calc.setFormFromLink(link);
    	Calc.calculate();
    	Calc.advanceRandomExample();
    },

    advanceRandomExample: function() {
    	var randomLink = $('#random-example')[0];
    	var newUrl=Calc.BASE_URL+'#'+Example.getExample();
    	randomLink.href=newUrl;
    },

    setRandomExamples: function() {
    	Example.randomize();
    	var randomLink = $('#random-example')[0];
    	if(randomLink) {
    	    randomLink.onclick=function() {
        		Calc.useRandomExample(this);
        		$('#actions').slideUp(250);
        		return false;
        	};
        	randomLink.title='Click to see a random example';
    	}
    	Calc.advanceRandomExample();
    },
    
    setEmbedLink: function() {
        var embedLink = $('#embed-link')[0];
        if(embedLink) {
            embedLink.onclick=function() {
                $('#embed-link')[0].href = 'http://www.encalc.com/embedded/?'+Calc.getParameters();
                return true; // follow through to the new href?
            }
        }
    },
    
    setSubmitLink: function() {
        var submitLinks = $('.submit-formula');
        submitLinks.each(function(i){
            this.onclick=function() {
                $.getJSON(Calc.BASE_CALC_URL+"submit-formula.php", {code:Calc.getParameters()}, function(json){alert('Thanks for submitting the formula!')});
                return false;
            };
        });
    },

    setHideEquation: function() {
        var hideButton = $('#hidden-equation')[0];
        if(hideButton) {
            hideButton.onclick=function() {
                if($('#hidden-equation')[0].checked) {
                    $('#expression-container').slideUp(250);
                } else {
                    $('#expression-container').slideDown(250);
                }
            }
        }
    },
    
    setMakeEditable: function() {
        var button = $('#editable-descriptions')[0];
        if(button) {
            button.onclick=function() {
                if($('#editable-descriptions')[0].checked) {
                    Calc.editVarDescriptions();
                } else {
                    Calc.lockVarDescriptions();
                }
            }
        }
    },
    
    
    setShowPrefs: function() {
        var links = $('.show-preferences');
        links.each(function(i){
            this.onclick=function() {
                $('#preferences').slideDown(250);
                return false;
            };
        });
    },
    
    setHidePrefs: function() {
        var links = $('.hide-preferences');
        links.each(function(i){
            this.onclick=function() {
                $('#preferences').slideUp(250);
                return false;
            };
        });
    },
    
    setShowActions: function() {
        var links = $('.show-actions');
        links.each(function(i){
            this.onclick=function() {
                $('#actions').slideDown(250);
                return false;
            };
        });
    },
    
    setHideActions: function() {
        var links = $('.hide-actions');
        links.each(function(i){
            this.onclick=function() {
                $('#actions').slideUp(250);
                return false;
            };
        });
    },

    editVarDescriptions: function() {
        $('.var-desc').each(function(i) {
                $(this).removeClass('locked');
                $(this).slideDown(250);
                $(this).attr('readonly', false);
                if($(this).val()=='' || $(this).val()=='Description') {
                    $(this).val('Description');
                }
           });
        Calc.editableDescriptions = true;
        return true;
    },
    
    lockVarDescriptions: function() {
        $('.var-desc').each(function(i) {
                $(this).addClass('locked');
                $(this).attr('readonly', true);
                if($(this).val()=='' || $(this).val()=='Description') {
                    $(this).slideUp(250);
                    $(this).val('');
                }
           });
           Calc.editableDescriptions = false;
           return true;
    },
    
    setBookmarkLinks: function() {
        $('.bookmark').each(function(i) {
                this.onclick=function() {
                    Calc.addBookmark();
                    return false;
                }
            });
    },
    
    addBookmark: function() {
        if(document.all) {
            window.external.AddFavorite(location.href,document.title);
        } else if(window.sidebar) {
            window.sidebar.addPanel(document.title,location.href,'');
        }
    },
    
    isInitialUrl: function() {
        try {
            return initialFormulaCode!='';
        } catch(e) {
            return false;
        }
    },
    
    initialize: function() {
        if(Calc.isInitialUrl()) {
            Calc.setFormToString(initialFormulaCode);
            Calc.calculate();
        } else {
            Calc.updateVariables();	
        	Calc.useExpressionFromUrl();	
        }
        //Calc.autoCalculate();
    	Calc.setRandomExamples();
    	Calc.setEmbedLink();
    	Calc.setHideEquation();
    	Calc.setMakeEditable();
    	Calc.setShowPrefs();
    	Calc.setHidePrefs();
    	Calc.setShowActions();
    	Calc.setHideActions();
    	Calc.setBookmarkLinks();
    	Calc.setSubmitLink();
    },
    
    _last: null // to avoid having to worry about missing commas at the end
};


var SideBar = {
    _count: 0,
    _currentIndex: 0,
    _modulo: 3,
    
    initialize: function() {
        
    },
    
    increment: function() {
        SideBar._count++;
        if(SideBar._count % SideBar._modulo == 0) {
            SideBar.rotate();
        }
    },
    
    rotate: function() {
        var oldIndex = SideBar._currentIndex;
        SideBar._currentIndex++;
        
        var sideBars = $('.side-bar');
        if (SideBar._currentIndex>=sideBars.length) {
            SideBar._currentIndex = 0;
        }
        sideBars.each(function(i){
                if(i==oldIndex) {
                    // push up the old side bar
                    $(this).slideUp(250);
                }
                setTimeout(SideBar.showCurrent, 250);
           });
    },
    
    showCurrent: function() {
        var currentSideBar = $('.side-bar')[SideBar._currentIndex];
        $(currentSideBar).slideDown(250);
    }
};

setTimeout(function() {	
        Calc.initialize();
    	SideBar.initialize();
    }, 100);
