/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Version: 1.1.0-pre
 * Requires jQuery 1.3+
 * Docs: http://docs.jquery.com/Plugins/livequery
 */

(function($) {

$.extend($.fn, {
	livequery: function(type, fn, fn2) {
		var self = this, q;

		// Handle different call patterns
		if ($.isFunction(type))
			fn2 = fn, fn = type, type = undefined;

		// See if Live Query already exists
		$.each( $.livequery.queries, function(i, query) {
			if ( self.selector == query.selector && self.context == query.context &&
				type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) )
					// Found the query, exit the each loop
					return (q = query) && false;
		});

		// Create new Live Query if it wasn't found
		q = q || new $.livequery(this.selector, this.context, type, fn, fn2);

		// Make sure it is running
		q.stopped = false;

		// Run it immediately for the first time
		q.run();

		// Contnue the chain
		return this;
	},

	expire: function(type, fn, fn2) {
		var self = this;

		// Handle different call patterns
		if ($.isFunction(type))
			fn2 = fn, fn = type, type = undefined;

		// Find the Live Query based on arguments and stop it
		$.each( $.livequery.queries, function(i, query) {
			if ( self.selector == query.selector && self.context == query.context &&
				(!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped )
					$.livequery.stop(query.id);
		});

		// Continue the chain
		return this;
	}
});

$.livequery = function(selector, context, type, fn, fn2) {
	this.selector = selector;
	this.context  = context;
	this.type     = type;
	this.fn       = fn;
	this.fn2      = fn2;
	this.elements = [];
	this.stopped  = false;

	// The id is the index of the Live Query in $.livequery.queries
	this.id = $.livequery.queries.push(this)-1;

	// Mark the functions for matching later on
	fn.$lqguid = fn.$lqguid || $.livequery.guid++;
	if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++;

	// Return the Live Query
	return this;
};

$.livequery.prototype = {
	stop: function() {
		var query = this;

		if ( this.type )
			// Unbind all bound events
			this.elements.unbind(this.type, this.fn);
		else if (this.fn2)
			// Call the second function for all matched elements
			this.elements.each(function(i, el) {
				query.fn2.apply(el);
			});

		// Clear out matched elements
		this.elements = [];

		// Stop the Live Query from running until restarted
		this.stopped = true;
	},

	run: function() {
		// Short-circuit if stopped
		if ( this.stopped ) return;
		var query = this;

		var oEls = this.elements,
			els  = $(this.selector, this.context),
			nEls = els.not(oEls);

		// Set elements to the latest set of matched elements
		this.elements = els;

		if (this.type) {
			// Bind events to newly matched elements
			nEls.bind(this.type, this.fn);

			// Unbind events to elements no longer matched
			if (oEls.length > 0)
				$.each(oEls, function(i, el) {
					if ( $.inArray(el, els) < 0 )
						$.event.remove(el, query.type, query.fn);
				});
		}
		else {
			// Call the first function for newly matched elements
			nEls.each(function() {
				query.fn.apply(this);
			});

			// Call the second function for elements no longer matched
			if ( this.fn2 && oEls.length > 0 )
				$.each(oEls, function(i, el) {
					if ( $.inArray(el, els) < 0 )
						query.fn2.apply(el);
				});
		}
	}
};

$.extend($.livequery, {
	guid: 0,
	queries: [],
	queue: [],
	running: false,
	timeout: null,

	checkQueue: function() {
		if ( $.livequery.running && $.livequery.queue.length ) {
			var length = $.livequery.queue.length;
			// Run each Live Query currently in the queue
			while ( length-- )
				$.livequery.queries[ $.livequery.queue.shift() ].run();
		}
	},

	pause: function() {
		// Don't run anymore Live Queries until restarted
		$.livequery.running = false;
	},

	play: function() {
		// Restart Live Queries
		$.livequery.running = true;
		// Request a run of the Live Queries
		$.livequery.run();
	},

	registerPlugin: function() {
		$.each( arguments, function(i,n) {
			// Short-circuit if the method doesn't exist
			if (!$.fn[n]) return;

			// Save a reference to the original method
			var old = $.fn[n];

			// Create a new method
			$.fn[n] = function() {
				// Call the original method
				var r = old.apply(this, arguments);

				// Request a run of the Live Queries
				$.livequery.run();

				// Return the original methods result
				return r;
			}
		});
	},

	run: function(id) {
		if (id != undefined) {
			// Put the particular Live Query in the queue if it doesn't already exist
			if ( $.inArray(id, $.livequery.queue) < 0 )
				$.livequery.queue.push( id );
		}
		else
			// Put each Live Query in the queue if it doesn't already exist
			$.each( $.livequery.queries, function(id) {
				if ( $.inArray(id, $.livequery.queue) < 0 )
					$.livequery.queue.push( id );
			});

		// Clear timeout if it already exists
		if ($.livequery.timeout) clearTimeout($.livequery.timeout);
		// Create a timeout to check the queue and actually run the Live Queries
		$.livequery.timeout = setTimeout($.livequery.checkQueue, 20);
	},

	stop: function(id) {
		if (id != undefined)
			// Stop are particular Live Query
			$.livequery.queries[ id ].stop();
		else
			// Stop all Live Queries
			$.each( $.livequery.queries, function(id) {
				$.livequery.queries[ id ].stop();
			});
	}
});

// Register core DOM manipulation methods
$.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');

// Run Live Queries when the Document is ready
$(function() { $.livequery.play(); });

})(jQuery);

/*
 * jQuery Form Plugin
 * version: 2.24 (10-MAR-2009)
 * @requires jQuery v1.2.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;(function($) {

/*
    Usage Note:
    -----------
    Do not use both ajaxSubmit and ajaxForm on the same form.  These
    functions are intended to be exclusive.  Use ajaxSubmit if you want
    to bind your own submit handler to the form.  For example,

    $(document).ready(function() {
        $('#myForm').bind('submit', function() {
            $(this).ajaxSubmit({
                target: '#output'
            });
            return false; // <-- important!
        });
    });

    Use ajaxForm when you want the plugin to manage all the event binding
    for you.  For example,

    $(document).ready(function() {
        $('#myForm').ajaxForm({
            target: '#output'
        });
    });

    When using ajaxForm, the ajaxSubmit function will be invoked for you
    at the appropriate time.
*/

/**
 * ajaxSubmit() provides a mechanism for immediately submitting
 * an HTML form using AJAX.
 */
$.fn.ajaxSubmit = function(options) {
    // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
    if (!this.length) {
        log('ajaxSubmit: skipping submit process - no element selected');
        return this;
    }

    if (typeof options == 'function')
        options = { success: options };

    // clean url (don't include hash vaue)
    var url = this.attr('action') || window.location.href;
    url = (url.match(/^([^#]+)/)||[])[1];
    url = url || '';

    options = $.extend({
        url:  url,
        type: this.attr('method') || 'GET'
    }, options || {});

    // hook for manipulating the form data before it is extracted;
    // convenient for use with rich editors like tinyMCE or FCKEditor
    var veto = {};
    this.trigger('form-pre-serialize', [this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
        return this;
    }

    // provide opportunity to alter form data before it is serialized
    if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSerialize callback');
        return this;
    }

    var a = this.formToArray(options.semantic);
    if (options.data) {
        options.extraData = options.data;
        for (var n in options.data) {
          if(options.data[n] instanceof Array) {
            for (var k in options.data[n])
              a.push( { name: n, value: options.data[n][k] } );
          }
          else
             a.push( { name: n, value: options.data[n] } );
        }
    }

    // give pre-submit callback an opportunity to abort the submit
    if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
        log('ajaxSubmit: submit aborted via beforeSubmit callback');
        return this;
    }

    // fire vetoable 'validate' event
    this.trigger('form-submit-validate', [a, this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
        return this;
    }

    var q = $.param(a);

    if (options.type.toUpperCase() == 'GET') {
        options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
        options.data = null;  // data is null for 'get'
    }
    else
        options.data = q; // data is the query string for 'post'

    var $form = this, callbacks = [];
    if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
    if (options.clearForm) callbacks.push(function() { $form.clearForm(); });

    // perform a load on the target only if dataType is not provided
    if (!options.dataType && options.target) {
        var oldSuccess = options.success || function(){};
        callbacks.push(function(data) {
            $(options.target).html(data).each(oldSuccess, arguments);
        });
    }
    else if (options.success)
        callbacks.push(options.success);

    options.success = function(data, status) {
        for (var i=0, max=callbacks.length; i < max; i++)
            callbacks[i].apply(options, [data, status, $form]);
    };

    // are there files to upload?
    var files = $('input:file', this).fieldValue();
    var found = false;
    for (var j=0; j < files.length; j++)
        if (files[j])
            found = true;

    // options.iframe allows user to force iframe mode
   if (options.iframe || found) {
       // hack to fix Safari hang (thanks to Tim Molendijk for this)
       // see:  http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
       if (options.closeKeepAlive)
           $.get(options.closeKeepAlive, fileUpload);
       else
           fileUpload();
       }
   else
       $.ajax(options);

    // fire 'notify' event
    this.trigger('form-submit-notify', [this, options]);
    return this;


    // private function for handling file uploads (hat tip to YAHOO!)
    function fileUpload() {
        var form = $form[0];

        if ($(':input[name=submit]', form).length) {
            alert('Error: Form elements must not be named "submit".');
            return;
        }

        var opts = $.extend({}, $.ajaxSettings, options);
		var s = jQuery.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);

        var id = 'jqFormIO' + (new Date().getTime());
        var $io = $('<iframe id="' + id + '" name="' + id + '" src="about:blank" />');
        var io = $io[0];

        $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });

        var xhr = { // mock object
            aborted: 0,
            responseText: null,
            responseXML: null,
            status: 0,
            statusText: 'n/a',
            getAllResponseHeaders: function() {},
            getResponseHeader: function() {},
            setRequestHeader: function() {},
            abort: function() {
                this.aborted = 1;
                $io.attr('src','about:blank'); // abort op in progress
            }
        };

        var g = opts.global;
        // trigger ajax global events so that activity/block indicators work like normal
        if (g && ! $.active++) $.event.trigger("ajaxStart");
        if (g) $.event.trigger("ajaxSend", [xhr, opts]);

		if (s.beforeSend && s.beforeSend(xhr, s) === false) {
			s.global && jQuery.active--;
			return;
        }
        if (xhr.aborted)
            return;

        var cbInvoked = 0;
        var timedOut = 0;

        // add submitting element to data if we know it
        var sub = form.clk;
        if (sub) {
            var n = sub.name;
            if (n && !sub.disabled) {
                options.extraData = options.extraData || {};
                options.extraData[n] = sub.value;
                if (sub.type == "image") {
                    options.extraData[name+'.x'] = form.clk_x;
                    options.extraData[name+'.y'] = form.clk_y;
                }
            }
        }

        // take a breath so that pending repaints get some cpu time before the upload starts
        setTimeout(function() {
            // make sure form attrs are set
            var t = $form.attr('target'), a = $form.attr('action');

			// update form attrs in IE friendly way
			form.setAttribute('target',id);
			if (form.getAttribute('method') != 'POST')
				form.setAttribute('method', 'POST');
			if (form.getAttribute('action') != opts.url)
				form.setAttribute('action', opts.url);

            // ie borks in some cases when setting encoding
            if (! options.skipEncodingOverride) {
                $form.attr({
                    encoding: 'multipart/form-data',
                    enctype:  'multipart/form-data'
                });
            }

            // support timout
            if (opts.timeout)
                setTimeout(function() { timedOut = true; cb(); }, opts.timeout);

            // add "extra" data to form if provided in options
            var extraInputs = [];
            try {
                if (options.extraData)
                    for (var n in options.extraData)
                        extraInputs.push(
                            $('<input type="hidden" name="'+n+'" value="'+options.extraData[n]+'" />')
                                .appendTo(form)[0]);

                // add iframe to doc and submit the form
                $io.appendTo('body');
                io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
                form.submit();
            }
            finally {
                // reset attrs and remove "extra" input elements
				form.setAttribute('action',a);
                t ? form.setAttribute('target', t) : $form.removeAttr('target');
                $(extraInputs).remove();
            }
        }, 10);

        var nullCheckFlag = 0;

        function cb() {
            if (cbInvoked++) return;

            io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);

            var ok = true;
            try {
                if (timedOut) throw 'timeout';
                // extract the server response from the iframe
                var data, doc;

                doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;

                if ((doc.body == null || doc.body.innerHTML == '') && !nullCheckFlag) {
                    // in some browsers (cough, Opera 9.2.x) the iframe DOM is not always traversable when
                    // the onload callback fires, so we give them a 2nd chance
                    nullCheckFlag = 1;
                    cbInvoked--;
                    setTimeout(cb, 100);
                    return;
                }

                xhr.responseText = doc.body ? doc.body.innerHTML : null;
                xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
                xhr.getResponseHeader = function(header){
                    var headers = {'content-type': opts.dataType};
                    return headers[header];
                };

                if (opts.dataType == 'json' || opts.dataType == 'script') {
                    var ta = doc.getElementsByTagName('textarea')[0];
                    xhr.responseText = ta ? ta.value : xhr.responseText;
                }
                else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
                    xhr.responseXML = toXml(xhr.responseText);
                }
                data = $.httpData(xhr, opts.dataType);
            }
            catch(e){
                ok = false;
                $.handleError(opts, xhr, 'error', e);
            }

            // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
            if (ok) {
                opts.success(data, 'success');
                if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
            }
            if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
            if (g && ! --$.active) $.event.trigger("ajaxStop");
            if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');

            // clean up
            setTimeout(function() {
                $io.remove();
                xhr.responseXML = null;
            }, 100);
        };

        function toXml(s, doc) {
            if (window.ActiveXObject) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = 'false';
                doc.loadXML(s);
            }
            else
                doc = (new DOMParser()).parseFromString(s, 'text/xml');
            return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
        };
    };
};

/**
 * ajaxForm() provides a mechanism for fully automating form submission.
 *
 * The advantages of using this method instead of ajaxSubmit() are:
 *
 * 1: This method will include coordinates for <input type="image" /> elements (if the element
 *    is used to submit the form).
 * 2. This method will include the submit element's name/value data (for the element that was
 *    used to submit the form).
 * 3. This method binds the submit() method to the form for you.
 *
 * The options argument for ajaxForm works exactly as it does for ajaxSubmit.  ajaxForm merely
 * passes the options argument along after properly binding events for submit elements and
 * the form itself.
 */
$.fn.ajaxForm = function(options) {
    return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
        $(this).ajaxSubmit(options);
        return false;
    }).each(function() {
        // store options in hash
        $(":submit,input:image", this).bind('click.form-plugin',function(e) {
            var form = this.form;
            form.clk = this;
            if (this.type == 'image') {
                if (e.offsetX != undefined) {
                    form.clk_x = e.offsetX;
                    form.clk_y = e.offsetY;
                } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
                    var offset = $(this).offset();
                    form.clk_x = e.pageX - offset.left;
                    form.clk_y = e.pageY - offset.top;
                } else {
                    form.clk_x = e.pageX - this.offsetLeft;
                    form.clk_y = e.pageY - this.offsetTop;
                }
            }
            // clear form vars
            setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
        });
    });
};

// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
    this.unbind('submit.form-plugin');
    return this.each(function() {
        $(":submit,input:image", this).unbind('click.form-plugin');
    });

};

/**
 * formToArray() gathers form element data into an array of objects that can
 * be passed to any of the following ajax functions: $.get, $.post, or load.
 * Each object in the array has both a 'name' and 'value' property.  An example of
 * an array for a simple login form might be:
 *
 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
 *
 * It is this array that is passed to pre-submit callback functions provided to the
 * ajaxSubmit() and ajaxForm() methods.
 */
$.fn.formToArray = function(semantic) {
    var a = [];
    if (this.length == 0) return a;

    var form = this[0];
    var els = semantic ? form.getElementsByTagName('*') : form.elements;
    if (!els) return a;
    for(var i=0, max=els.length; i < max; i++) {
        var el = els[i];
        var n = el.name;
        if (!n) continue;

        if (semantic && form.clk && el.type == "image") {
            // handle image inputs on the fly when semantic == true
            if(!el.disabled && form.clk == el)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
            continue;
        }

        var v = $.fieldValue(el, true);
        if (v && v.constructor == Array) {
            for(var j=0, jmax=v.length; j < jmax; j++)
                a.push({name: n, value: v[j]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: n, value: v});
    }

    if (!semantic && form.clk) {
        // input type=='image' are not found in elements array! handle them here
        var inputs = form.getElementsByTagName("input");
        for(var i=0, max=inputs.length; i < max; i++) {
            var input = inputs[i];
            var n = input.name;
            if(n && !input.disabled && input.type == "image" && form.clk == input)
                a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
        }
    }
    return a;
};

/**
 * Serializes form data into a 'submittable' string. This method will return a string
 * in the format: name1=value1&amp;name2=value2
 */
$.fn.formSerialize = function(semantic) {
    //hand off to jQuery.param for proper encoding
    return $.param(this.formToArray(semantic));
};

/**
 * Serializes all field elements in the jQuery object into a query string.
 * This method will return a string in the format: name1=value1&amp;name2=value2
 */
$.fn.fieldSerialize = function(successful) {
    var a = [];
    this.each(function() {
        var n = this.name;
        if (!n) return;
        var v = $.fieldValue(this, successful);
        if (v && v.constructor == Array) {
            for (var i=0,max=v.length; i < max; i++)
                a.push({name: n, value: v[i]});
        }
        else if (v !== null && typeof v != 'undefined')
            a.push({name: this.name, value: v});
    });
    //hand off to jQuery.param for proper encoding
    return $.param(a);
};

/**
 * Returns the value(s) of the element in the matched set.  For example, consider the following form:
 *
 *  <form><fieldset>
 *      <input name="A" type="text" />
 *      <input name="A" type="text" />
 *      <input name="B" type="checkbox" value="B1" />
 *      <input name="B" type="checkbox" value="B2"/>
 *      <input name="C" type="radio" value="C1" />
 *      <input name="C" type="radio" value="C2" />
 *  </fieldset></form>
 *
 *  var v = $(':text').fieldValue();
 *  // if no values are entered into the text inputs
 *  v == ['','']
 *  // if values entered into the text inputs are 'foo' and 'bar'
 *  v == ['foo','bar']
 *
 *  var v = $(':checkbox').fieldValue();
 *  // if neither checkbox is checked
 *  v === undefined
 *  // if both checkboxes are checked
 *  v == ['B1', 'B2']
 *
 *  var v = $(':radio').fieldValue();
 *  // if neither radio is checked
 *  v === undefined
 *  // if first radio is checked
 *  v == ['C1']
 *
 * The successful argument controls whether or not the field element must be 'successful'
 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
 * The default value of the successful argument is true.  If this value is false the value(s)
 * for each element is returned.
 *
 * Note: This method *always* returns an array.  If no valid value can be determined the
 *       array will be empty, otherwise it will contain one or more values.
 */
$.fn.fieldValue = function(successful) {
    for (var val=[], i=0, max=this.length; i < max; i++) {
        var el = this[i];
        var v = $.fieldValue(el, successful);
        if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
            continue;
        v.constructor == Array ? $.merge(val, v) : val.push(v);
    }
    return val;
};

/**
 * Returns the value of the field element.
 */
$.fieldValue = function(el, successful) {
    var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
    if (typeof successful == 'undefined') successful = true;

    if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
        (t == 'checkbox' || t == 'radio') && !el.checked ||
        (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
        tag == 'select' && el.selectedIndex == -1))
            return null;

    if (tag == 'select') {
        var index = el.selectedIndex;
        if (index < 0) return null;
        var a = [], ops = el.options;
        var one = (t == 'select-one');
        var max = (one ? index+1 : ops.length);
        for(var i=(one ? index : 0); i < max; i++) {
            var op = ops[i];
            if (op.selected) {
				var v = op.value;
				if (!v) // extra pain for IE...
                	v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
                if (one) return v;
                a.push(v);
            }
        }
        return a;
    }
    return el.value;
};

/**
 * Clears the form data.  Takes the following actions on the form's input fields:
 *  - input text fields will have their 'value' property set to the empty string
 *  - select elements will have their 'selectedIndex' property set to -1
 *  - checkbox and radio inputs will have their 'checked' property set to false
 *  - inputs of type submit, button, reset, and hidden will *not* be effected
 *  - button elements will *not* be effected
 */
$.fn.clearForm = function() {
    return this.each(function() {
        $('input,select,textarea', this).clearFields();
    });
};

/**
 * Clears the selected form elements.
 */
$.fn.clearFields = $.fn.clearInputs = function() {
    return this.each(function() {
        var t = this.type, tag = this.tagName.toLowerCase();
        if (t == 'text' || t == 'password' || tag == 'textarea')
            this.value = '';
        else if (t == 'checkbox' || t == 'radio')
            this.checked = false;
        else if (tag == 'select')
            this.selectedIndex = -1;
    });
};

/**
 * Resets the form data.  Causes all form elements to be reset to their original value.
 */
$.fn.resetForm = function() {
    return this.each(function() {
        // guard against an input with the name of 'reset'
        // note that IE reports the reset function as an 'object'
        if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
            this.reset();
    });
};

/**
 * Enables or disables any matching elements.
 */
$.fn.enable = function(b) {
    if (b == undefined) b = true;
    return this.each(function() {
        this.disabled = !b;
    });
};

/**
 * Checks/unchecks any matching checkboxes or radio buttons and
 * selects/deselects and matching option elements.
 */
$.fn.selected = function(select) {
    if (select == undefined) select = true;
    return this.each(function() {
        var t = this.type;
        if (t == 'checkbox' || t == 'radio')
            this.checked = select;
        else if (this.tagName.toLowerCase() == 'option') {
            var $sel = $(this).parent('select');
            if (select && $sel[0] && $sel[0].type == 'select-one') {
                // deselect all other options
                $sel.find('option').selected(false);
            }
            this.selected = select;
        }
    });
};

// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
    if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
        window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
};

})(jQuery);


//tooltip used by Vijay */
(function($){var helper={},current,title,tID,IE=$.browser.msie&&/MSIE\s(5\.5|6\.)/.test(navigator.userAgent),track=false;$.tooltip={blocked:false,defaults:{delay:200,fade:false,showURL:true,extraClass:"",top:15,left:15,id:"tooltip"},block:function(){$.tooltip.blocked=!$.tooltip.blocked;}};$.fn.extend({tooltip:function(settings){settings=$.extend({},$.tooltip.defaults,settings);createHelper(settings);return this.each(function(){$.data(this,"tooltip",settings);this.tOpacity=helper.parent.css("opacity");this.tooltipText=this.title;$(this).removeAttr("title");this.alt="";}).mouseover(save).mouseout(hide).click(hide);},fixPNG:IE?function(){return this.each(function(){var image=$(this).css('backgroundImage');if(image.match(/^url\(["']?(.*\.png)["']?\)$/i)){image=RegExp.$1;$(this).css({'backgroundImage':'none','filter':"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+image+"')"}).each(function(){var position=$(this).css('position');if(position!='absolute'&&position!='relative')$(this).css('position','relative');});}});}:function(){return this;},unfixPNG:IE?function(){return this.each(function(){$(this).css({'filter':'',backgroundImage:''});});}:function(){return this;},hideWhenEmpty:function(){return this.each(function(){$(this)[$(this).html()?"show":"hide"]();});},url:function(){return this.attr('href')||this.attr('src');}});function createHelper(settings){if(helper.parent)return;helper.parent=$('<div id="'+settings.id+'"><h3></h3><div class="body"></div><div class="url"></div></div>').appendTo(document.body).hide();if($.fn.bgiframe)helper.parent.bgiframe();helper.title=$('h3',helper.parent);helper.body=$('div.body',helper.parent);helper.url=$('div.url',helper.parent);}function settings(element){return $.data(element,"tooltip");}function handle(event){if(settings(this).delay)tID=setTimeout(show,settings(this).delay);else
show();track=!!settings(this).track;$(document.body).bind('mousemove',update);update(event);}function save(){if($.tooltip.blocked||this==current||(!this.tooltipText&&!settings(this).bodyHandler))return;current=this;title=this.tooltipText;if(settings(this).bodyHandler){helper.title.hide();var bodyContent=settings(this).bodyHandler.call(this);if(bodyContent.nodeType||bodyContent.jquery){helper.body.empty().append(bodyContent)}else{helper.body.html(bodyContent);}helper.body.show();}else if(settings(this).showBody){var parts=title.split(settings(this).showBody);helper.title.html(parts.shift()).show();helper.body.empty();for(var i=0,part;(part=parts[i]);i++){if(i>0)helper.body.append("<br/>");helper.body.append(part);}helper.body.hideWhenEmpty();}else{helper.title.html(title).show();helper.body.hide();}if(settings(this).showURL&&$(this).url())helper.url.html($(this).url().replace('http://','')).show();else
helper.url.hide();helper.parent.addClass(settings(this).extraClass);if(settings(this).fixPNG)helper.parent.fixPNG();handle.apply(this,arguments);}function show(){tID=null;if((!IE||!$.fn.bgiframe)&&settings(current).fade){if(helper.parent.is(":animated"))helper.parent.stop().show().fadeTo(settings(current).fade,current.tOpacity);else
helper.parent.is(':visible')?helper.parent.fadeTo(settings(current).fade,current.tOpacity):helper.parent.fadeIn(settings(current).fade);}else{helper.parent.show();}update();}function update(event){if($.tooltip.blocked)return;if(event&&event.target.tagName=="OPTION"){return;}if(!track&&helper.parent.is(":visible")){$(document.body).unbind('mousemove',update)}if(current==null){$(document.body).unbind('mousemove',update);return;}helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");var left=helper.parent[0].offsetLeft;var top=helper.parent[0].offsetTop;if(event){left=event.pageX+settings(current).left;top=event.pageY+settings(current).top;var right='auto';if(settings(current).positionLeft){right=$(window).width()-left;left='auto';}helper.parent.css({left:left,right:right,top:top});}var v=viewport(),h=helper.parent[0];if(v.x+v.cx<h.offsetLeft+h.offsetWidth){left-=h.offsetWidth+20+settings(current).left;helper.parent.css({left:left+'px'}).addClass("viewport-right");}if(v.y+v.cy<h.offsetTop+h.offsetHeight){top-=h.offsetHeight+20+settings(current).top;helper.parent.css({top:top+'px'}).addClass("viewport-bottom");}}function viewport(){return{x:$(window).scrollLeft(),y:$(window).scrollTop(),cx:$(window).width(),cy:$(window).height()};}function hide(event){if($.tooltip.blocked)return;if(tID)clearTimeout(tID);current=null;var tsettings=settings(this);function complete(){helper.parent.removeClass(tsettings.extraClass).hide().css("opacity","");}if((!IE||!$.fn.bgiframe)&&tsettings.fade){if(helper.parent.is(':animated'))helper.parent.stop().fadeTo(tsettings.fade,0,complete);else
helper.parent.stop().fadeOut(tsettings.fade,complete);}else
complete();if(settings(this).fixPNG)helper.parent.unfixPNG();}})(jQuery);

/*!
 * URL Utils - v1.0 - 7/10/2009
 * http://benalman.com/
 * 
 * Copyright (c) 2009 "Cowboy" Ben Alman
 * Licensed under the MIT license
 * http://benalman.com/about/license/
 */

// Script: URL Utils
//
// Version: 1.0, Date: 7/10/2009
// 
// Tested with jQuery 1.3.2 in Internet Explorer 6-8, Firefox 3, Safari 3-4,
// Chrome, Opera 9.
// 
// Home       - http://benalman.com/projects/jquery-url-utils-plugin/
// Source     - http://benalman.com/code/javascript/jquery/jquery.ba-url.js
// (Minified) - http://benalman.com/code/javascript/jquery/jquery.ba-url.min.js (3.9kb)
// Unit Tests - http://benalman.com/code/unittest/url.html
// 
// About: License
// 
// Copyright (c) 2009 "Cowboy" Ben Alman
// 
// Licensed under the MIT license
// 
// http://benalman.com/about/license/

(function($) {
  '$:nomunge'; // Used by YUI compressor.
  
  var url_regexp,
    tag_attributes = {},
    
    // A few constants.
    undefined,
    TRUE = true,
    FALSE = false,
    
    // Some convenient shortcuts.
    aps = Array.prototype.slice,
    loc = document.location,
    
    // Internal plugin method references.
    p_urlTagAttrList,
    p_urlInternalHost,
    p_urlInternalRegExp,
    p_isUrlInternal,
    p_isUrlExternal,
    p_urlFilter,
    p_urlFilterSelector,
    p_setFragment,
    
    // Reused internal strings.
    str_urlInternal     = 'urlInternal',
    str_urlExternal     = 'urlExternal',
    str_queryString     = 'queryString',
    str_fragment        = 'fragment',
    str_update          = 'update',
    str_passQueryString = 'passQueryString',
    str_passFragment    = 'passFragment',
    str_fragmentChange  = 'fragmentChange',
    
    // fragmentChange event handler
    timeout_id,
    last_fragment;
  
  
  // A few commonly used bits, broken out to help reduce minified file size.
  
  function is_string( arg ) {
    return typeof arg === 'string';
  };
  
  function is_object( arg ) {
    return typeof arg === 'object';
  };
  
  function curry() {
    var args = aps.call( arguments ),
      func = args.shift();
    
    return function() {
      return func.apply( this, args.concat( aps.call( arguments ) ) );
    };
  };
  
  // Work-around for an annoying Firefox bug where document.location.hash gets
  // urldecoded by default.
  
  function get_fragment() {
    return loc.href.replace( /^[^#]*#?/, '' );
  };
  
  
  // Method: jQuery.urlTagAttrList
  // 
  // Get the internal "Default URL attribute per tag" list, or augment the list
  // with additional tag-attribute pairs, in case the defaults are insufficient.
  // 
  // This list contains the default attributes for the <jQuery.fn.urlInternal>
  // and <jQuery.fn.urlExternal> methods, as well as the <:urlInternal> and
  // <:urlExternal> selector filters, to determine which URL will be tested for
  // internal- or external-ness. In the <jQuery.fn.queryString>,
  // <jQuery.fn.fragment>, <jQuery.fn.passQueryString> and
  // <jQuery.fn.passFragment> methods, this list is used to determine which URL
  // will be modified.
  // 
  // Default List:
  // 
  //  TAG    - URL ATTRIBUTE
  //  a      - href
  //  img    - src
  //  form   - action
  //  base   - href
  //  script - src
  //  iframe - src
  //  link   - href
  // 
  // Usage:
  // 
  //  jQuery.urlTagAttrList( [ tag_attr_obj ] );                             - -
  // 
  // Arguments:
  // 
  //  tag_attr_obj - (Object) An list of tag names and associated default
  //    attribute names in the format { tag: 'attr', tag: 'attr', ... }.
  // 
  // Returns:
  // 
  //  (Object) The current internal "Default URL attribute per tag" list.
  
  $.urlTagAttrList = p_urlTagAttrList = function( attr_obj ) {
    return $.extend( tag_attributes, attr_obj );
  };
  
  // Initialize the tag_attributes object with some reasonable defaults.
  
  p_urlTagAttrList({
    a: 'href',
    img: 'src',
    form: 'action',
    base: 'href',
    script: 'src',
    iframe: 'src',
    link: 'href'
  });
  
  // Get the default attribute for the specified DOM element, if it exists.
  
  function get_attr( elem ) {
    var n = elem.nodeName;
    return n ? tag_attributes[ n.toLowerCase() ] : '';
  };
  
  
  // Section: URL Internal / External
  // 
  // Method: jQuery.urlInternalHost
  // 
  // Constructs the regular expression that matches an absolute-but-internal
  // URL from the current page's protocol, hostname and port, allowing for
  // an optional hostname (if specified). For example, if the current page is
  // http://benalman.com/anything, specifying an alt_hostname of 'www' would
  // yield this pattern:
  // 
  // > /^http:\/\/(?:www\.)?benalman.com\//i
  // 
  // This pattern will match URLs beginning with both http://benalman.com/ and
  // http://www.benalman.com/. Specifying an empty alt_hostname will disable any
  // alt-hostname matching.
  // 
  // Note that the plugin is initialized by default to an alt_hostname of 'www'.
  // Should you need more control, <jQuery.urlInternalRegExp> may be used to
  // completely override this absolute-but-internal matching pattern.
  // 
  // Usage:
  // 
  //  jQuery.urlInternalHost( [ alt_hostname ] );                            - -
  // 
  // Arguments:
  // 
  //  alt_hostname - (String) An optional alternate hostname to use when testing
  //    URL absolute-but-internal-ness.
  // 
  // Returns:
  // 
  //  (RegExp) The absolute-but-internal pattern, as a RegExp.
  
  $.urlInternalHost = p_urlInternalHost = function( alt_hostname ) {
    alt_hostname = alt_hostname
      ? '(?:' + alt_hostname + '\\.)?'
      : '';
    
    var re = new RegExp( '^' + alt_hostname + '(.*)', 'i' ),
      pattern = '^' + loc.protocol + '//'
        + loc.hostname.replace(re, alt_hostname + '$1')
        //+ 'benalman.com'.replace(re, alt_hostname + '$1') // For testing on stage.benalman.com
        + (loc.port ? ':' + loc.port : '') + '/';
    
    return p_urlInternalRegExp( pattern );
  };
  
  
  // Method: jQuery.urlInternalRegExp
  // 
  // Set or get the regular expression that matches an absolute-but-internal
  // URL.
  // 
  // Usage:
  // 
  //  jQuery.urlInternalRegExp( [ re ] );                                    - -
  // 
  // Arguments:
  // 
  //  re - (String or RegExp) The regular expression pattern. If not passed,
  //    nothing is changed.
  // 
  // Returns:
  // 
  //  (RegExp) The absolute-but-internal pattern, as a RegExp.
  
  $.urlInternalRegExp = p_urlInternalRegExp = function( re ) {
    if ( re ) {
      url_regexp = is_string( re )
        ? new RegExp( re, 'i' )
        : re;
    }
    
    return url_regexp;
  };
  
  
  // Initialize url_regexp with a reasonable default.
  
  p_urlInternalHost( 'www' );
  
  
  // Method: jQuery.isUrlInternal
  // 
  // Test whether or not a URL is internal. Non-navigating URLs (ie. #anchor,
  // javascript:, mailto:, news:, tel:, im: or non-http/-https protocol://
  // links) are not considered internal.
  // 
  // Usage:
  // 
  //  jQuery.isUrlInternal( url );                                           - -
  // 
  // Arguments:
  // 
  //   url - (String) a URL to test the internal-ness of.
  // 
  // Returns:
  // 
  //  (Boolean) true if the URL is internal, false if external, or undefined if
  //  the URL is non-navigating.
  
  $.isUrlInternal = p_isUrlInternal = function( url ) {
    
    // non-navigating: url is nonexistent
    if ( !url ) { return undefined; }
    
    // internal: url is absolute-but-internal (see $.urlInternalRegExp)
    if ( url_regexp.test(url) ) { return TRUE; }
    
    // external: url is absolute (begins with http:// or https://)
    if ( /^https?:\/\//i.test(url) ) { return FALSE; }
    
    // non-navigating: url begins with # or scheme:
    if ( /^(?:#|[a-z\d.-]+:)/i.test(url) ) { return undefined; }
    
    return TRUE;
  };
  
  
  // Method: jQuery.isUrlExternal
  // 
  // Test whether or not a URL is external. Non-navigating URLs (ie. #anchor,
  // mailto:, javascript:, or non-http/-https protocol:// links) are not
  // considered external.
  // 
  // Usage:
  // 
  //  jQuery.isUrlExternal( url );                                           - -
  // 
  // Arguments:
  // 
  //   url - (String) a URL to test the external-ness of.
  // 
  // Returns:
  // 
  //  (Boolean) true if the URL is external, false if internal, or undefined if
  //  the URL is non-navigating.
  
  $.isUrlExternal = p_isUrlExternal = function( url ) {
    var result = p_isUrlInternal( url );
    
    return typeof result === 'boolean'
      ? !result
      : result;
  };
  
  
  // Method: jQuery.fn.urlInternal
  // 
  // Filter a jQuery collection of elements, returning only elements that have
  // an internal URL (as determined by <jQuery.isUrlInternal>). If URL cannot
  // be determined, remove the element from the collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').urlInternal( [ attr ] );                            - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    test internal-ness against. See <jQuery.urlTagAttrList> for a list of
  //    default attributes.
  // 
  // Returns:
  // 
  //  (jQuery) A filtered jQuery collection of elements.
  
  // Method: jQuery.fn.urlExternal
  // 
  // Filter a jQuery collection of elements, returning only elements that have
  // an external URL (as determined by <jQuery.isUrlExternal>). If URL cannot
  // be determined, remove the element from the collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').urlExternal( [ attr ] );                            - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    test external-ness against. See <jQuery.urlTagAttrList> for a list of
  //    default attributes.
  // 
  // Returns:
  // 
  //  (jQuery) A filtered jQuery collection of elements.
  
  p_urlFilter = function( selector, attr ) {
    return this.filter( ':' + selector + (attr ? '(' + attr + ')' : '') );
  };
  
  $.fn[str_urlInternal] = curry( p_urlFilter, str_urlInternal );
  $.fn[str_urlExternal] = curry( p_urlFilter, str_urlExternal );
  
  
  // Method: :urlInternal
  // 
  // Filter a jQuery collection of elements, returning only elements that have
  // an internal URL (as determined by <jQuery.isUrlInternal>). If URL cannot
  // be determined, remove the element from the collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').filter(':urlInternal');                             - -
  //  jQuery('selector').filter(':urlInternal(attr)');                       - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    test internal-ness against. See <jQuery.urlTagAttrList> for a list of
  //    default attributes.
  // 
  // Returns:
  // 
  //  (jQuery) A filtered jQuery collection of elements.
  
  // Method: :urlExternal
  // 
  // Filter a jQuery collection of elements, returning only elements that have
  // an external URL (as determined by <jQuery.isUrlExternal>). If URL cannot
  // be determined, remove the element from the collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').filter(':urlExternal');                             - -
  //  jQuery('selector').filter(':urlExternal(attr)');                       - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    test external-ness against. See <jQuery.urlTagAttrList> for a list of
  //    default attributes.
  // 
  // Returns:
  // 
  //  (jQuery) A filtered jQuery collection of elements.
  
  p_urlFilterSelector = function( func, elem, i, match ) {
    var a = match[3] || get_attr( elem );
    
    return a ? !!func( $(elem).attr(a) ) : FALSE;
  };
  
  $.expr[':'][str_urlInternal] = curry( p_urlFilterSelector, p_isUrlInternal );
  $.expr[':'][str_urlExternal] = curry( p_urlFilterSelector, p_isUrlExternal );
  
  
  // Section: URL Query String / Fragment
  // 
  // Method: jQuery.queryString (deserialize)
  // 
  // Deserialize any params string or the current document's query string into
  // an object. Multiple sequential values will be converted into an array, ie.
  // 'n=a&n=b&n=c' -> { n: ['a', 'b', 'c'] }.
  // 
  // Usage:
  // 
  //  jQuery.queryString( [ params_str ] [ , cast_values ] );                - -
  // 
  // Arguments:
  // 
  //  params_str - (String) A stand-alone params string or a URL containing
  //    params to be deserialized. If omitted, defaults to the current page
  //    query string (document.location.search).
  //  cast_values - (Boolean) If true, converts any numbers or true, false,
  //    null, and undefined to their appropriate literal. Defaults to false.
  // 
  // Returns:
  // 
  //  (Object) The deserialized params string.
  
  // Method: jQuery.queryString (serialize)
  // 
  // Serialize an object into a params string. Arrays will be converted to
  // multiple sequential values, ie. { n: ['a', 'b', 'c'] } -> 'n=a&n=b&n=c'
  // (this method is just a wrapper for jQuery.param).
  // 
  // Usage:
  // 
  //  jQuery.queryString( params_obj );                                      - -
  // 
  // Arguments:
  // 
  //  params_obj - (Object) An object to be serialized. Note: A JSON string (or
  //    some analog) should be used for deep structures, since nested data
  //    structures other than shallow arrays can't be serialized into a query
  //    string in a meaningful way.
  // 
  // Returns:
  // 
  //  (String) A params string with urlencoded data in the format 'a=b&c=d&e=f'.
  
  // Method: jQuery.queryString (build url)
  // 
  // Merge a URL (with or without a pre-existing params) plus any object or
  // params string into a new URL.
  // 
  // Usage:
  // 
  //  jQuery.queryString( url, params [ , merge_mode ] );                    - -
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL, optionally containing a query string and/or
  //    #anchor, or fragment.
  //  params - (String or Object) Either a serialized params string or a data
  //    object to be merged into the URL.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params argument will override any params in url.
  //    * 1: any params in url will override params argument.
  //    * 2: params argument will completely replace any params in url.
  // 
  // Returns:
  // 
  //  (String) A URL with urlencoded params in the format 'url?a=b&c=d&e=f'.
  
  // Method: jQuery.fragment (deserialize)
  // 
  // Deserialize any params string or the current document's fragment into an
  // object. Multiple sequential values will be converted into an array, ie.
  // 'n=a&n=b&n=c' -> { n: ['a', 'b', 'c'] }.
  // 
  // Usage:
  // 
  //  jQuery.fragment( [ params_str ] [ , cast_values ] );                   - -
  // 
  // Arguments:
  // 
  //  params_str - (String) A stand-alone params string or a URL containing
  //    params to be deserialized. If omitted, defaults to the current page
  //    fragment (document.location.hash).
  //  cast_values - (Boolean) If true, converts any numbers or true, false,
  //    null, and undefined to their appropriate literal. Defaults to false.
  // 
  // Returns:
  // 
  //  (Object) The deserialized params string.
  
  // Method: jQuery.fragment (serialize)
  // 
  // Serialize an object into a params string. Arrays will be converted to
  // multiple sequential values, ie. { n: ['a', 'b', 'c'] } -> 'n=a&n=b&n=c'
  // (this method is just a wrapper for jQuery.param).
  // 
  // Usage:
  // 
  //  jQuery.fragment( params_obj );                                         - -
  // 
  // Arguments:
  // 
  //  params_obj - (Object) An object to be serialized. Note: A JSON string (or
  //    some analog) should be used for deep structures, since nested data
  //    structures other than shallow arrays can't be serialized into a query
  //    string in a meaningful way.
  // 
  // Returns:
  // 
  //  (String) A params string with urlencoded data in the format 'a=b&c=d&e=f'.
  
  // Method: jQuery.fragment (build url)
  // 
  // Merge a URL (with or without a pre-existing params) plus any object or
  // params string into a new URL.
  // 
  // Usage:
  // 
  //  jQuery.fragment( url, params [ , merge_mode ] );                       - -
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL, optionally containing a query string and/or
  //    #anchor, or fragment.
  //  params - (String or Object) Either a serialized params string or a data
  //    object to be merged into the URL.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params argument will override any params in url.
  //    * 1: any params in url will override params argument.
  //    * 2: params argument will completely replace any params in url.
  // 
  // Returns:
  // 
  //  (String) A URL with urlencoded params in the format 'url#a=b&c=d&e=f'.
  
  function p_params( fragment_mode, arg0, arg1, arg2 ) {
    var params;
    
    if ( is_string(arg1) || is_object(arg1) ) {
      // Build URL.
      return build_url( arg0, arg1, arg2, fragment_mode );
      
    } else if ( is_object(arg0) ) {
      // Serialize.
      return $.param( arg0 );
      
    } else if ( is_string(arg0) ) {
      // Deserialize.
      return deserialize( arg0, arg1, fragment_mode );
      
    } else {
      // Deserialize document query string / fragment.
      params = fragment_mode
        ? get_fragment()
        : loc.search;
      
      return deserialize( params, arg0, fragment_mode );
    }
  };
  
  $[str_queryString] = curry( p_params, 0 );
  $[str_fragment]    = curry( p_params, 1 );
  
  // Method: jQuery.fn.queryString
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing params) plus any object or params string into a new
  // URL, which is then set into that attribute. Like <jQuery.queryString (build
  // url)>, but for all elements in a jQuery collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').queryString( [ attr, ] params [ , merge_mode ] );   - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.urlTagAttrList> for a list of default
  //    attributes.
  //  params - (String or Object) Either a serialized params string or a data
  //    object to be merged into the URL.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params argument.
  //    * 2: params argument will completely replace any params in attr URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  // Method: jQuery.fn.fragment
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing params) plus any object or params string into a new
  // URL, which is then set into that attribute. Like <jQuery.fragment (build
  // url)>, but for all elements in a jQuery collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').fragment( [ attr, ] params [ , merge_mode ] );      - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.urlTagAttrList> for a list of default
  //    attributes.
  //  params - (String or Object) Either a serialized params string or a data
  //    object to be merged into the URL.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params argument.
  //    * 2: params argument will completely replace any params in attr URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  function p_fn_params() {
    var attr,
      params,
      merge_mode,
      args = aps.call( arguments ),
      fragment_mode = args.shift();
    
    if ( is_string(args[1]) || is_object(args[1]) ) {
      attr = args.shift();
    }
    params = args.shift();
    merge_mode = args.shift();
    
    return this.each(function(){
      
      var that = $(this),
        a = attr || get_attr( this ),
        url = a && that.attr( a ) || '';
      
      url = p_params( fragment_mode, url, params, merge_mode );
      that.attr( a, url );
      
    });
  };
  
  $.fn[str_queryString] = curry( p_fn_params, 0 );
  $.fn[str_fragment]    = curry( p_fn_params, 1 );
  
  // Method: jQuery.passQueryString
  // 
  // Merge a URL (with or without pre-existing params) plus the document query
  // string into a new URL, optionally omitting specified params or parsing the
  // document params with a callback function pre-merge.
  // 
  // Usage:
  // 
  //  jQuery.passQueryString( url [ , parse ] [ , merge_mode ] );            - -
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL, optionally containing a query string and/or
  //    #anchor, or fragment.
  //  parse - (Array or Function) An optional array of key names to -not- merge
  //    into the URL, or a function that returns the object that will be merged
  //    into url (the document params are deserialized and passed to this
  //    function as its only argument).
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: document params will override any params in url.
  //    * 1: any params in url will override document params.
  //    * 2: document params will completely replace any params in url.
  // 
  // Returns:
  // 
  //  (String) A URL with urlencoded params in the format 'url?a=b&c=d&e=f'.
  
  // Method: jQuery.passFragment
  // 
  // Merge a URL (with or without pre-existing params) plus the document
  // fragment into a new URL, optionally omitting specified params or parsing
  // the document params with a callback function pre-merge.
  // 
  // Usage:
  // 
  //  jQuery.passFragment( url [ , parse ] [ , merge_mode ] );               - -
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL, optionally containing a query string and/or
  //    #anchor, or fragment.
  //  parse - (Array or Function) An optional array of key names to -not- merge
  //    into the URL, or a function that returns the object that will be merged
  //    into url (the document params are deserialized and passed to this
  //    function as its only argument).
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: document params will override any params in url.
  //    * 1: any params in url will override document params.
  //    * 2: document params will completely replace any params in url.
  // 
  // Returns:
  // 
  //  (String) A URL with urlencoded params in the format 'url#a=b&c=d&e=f'.
  
  function p_passParams() {
    var args = aps.call( arguments ),
      fragment_mode = args.shift(),
      url = args.shift(),
      params = p_params( fragment_mode );
    
    if ( $.isFunction(args[0]) ) {
      params = args.shift()( params );
    } else if ( $.isArray(args[0]) ) {
      $.each(args.shift(), function(i,v){
        delete params[v];
      });
    }
    
    return p_params( fragment_mode, url, params, args.shift() );
  };
  
  $[str_passQueryString] = curry( p_passParams, 0 );
  $[str_passFragment]    = curry( p_passParams, 1 );
  
  
  // Method: jQuery.fn.passQueryString
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing params) plus the document query string into a new
  // URL (optionally parsing the document params with a callback function
  // pre-merge), which is then set into that attribute. Like
  // <jQuery.passQueryString>, but for all elements in a jQuery collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').passQueryString( [ attr ] [ , parse ] [ , merge_mode ] );  - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.urlTagAttrList> for a list of default
  //    attributes.
  //  parse - (Array or Function) An optional array of key names to -not- merge
  //    into the URL, or a function that returns the object that will be merged
  //    into url (the document params are deserialized and passed to this
  //    function as its only argument).
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: document params will override any params in attr URL.
  //    * 1: any params in attr URL will override document params.
  //    * 2: document params will completely replace any params in attr URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  // Method: jQuery.fn.passFragment
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing params) plus the document fragment into a new URL
  // (optionally parsing the document params with a callback function
  // pre-merge), which is then set into that attribute. Like
  // <jQuery.passFragment>, but for all elements in a jQuery collection.
  // 
  // Usage:
  // 
  //  jQuery('selector').passFragment( [ attr ] [ , parse ] [ , merge_mode ] );     - -
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.urlTagAttrList> for a list of default
  //    attributes.
  //  parse - (Array or Function) An optional array of key names to -not- merge
  //    into the URL, or a function that returns the object that will be merged
  //    into url (the document params are deserialized and passed to this
  //    function as its only argument).
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: document params will override any params in attr URL.
  //    * 1: any params in attr URL will override document params.
  //    * 2: document params will completely replace any params in attr URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  function p_fn_passParams() {
    var attr,
      args = aps.call(arguments),
      fragment_mode = args.shift();
    
    if ( is_string(args[0]) ) {
      attr = args.shift();
    }
    
    return this.each(function(){
      
      var that = $(this),
        a = attr || get_attr( this ),
        url = a && that.attr( a ) || '';
      
      url = p_passParams.apply( this, [fragment_mode, url].concat(args) );
      that.attr( a, url );
      
    });
  };
  
  $.fn[str_passQueryString] = curry( p_fn_passParams, 0 );
  $.fn[str_passFragment]    = curry( p_fn_passParams, 1 );
  
  
  // Deserialize a params string, optionally preceded by a url? or ?, or
  // followed by an #anchor (or if fragment_mode, optionally preceded by a url#
  // or #) into an object, optionally casting numbers, null, true, false, and
  // undefined values appropriately.
  
  function deserialize( params, cast_values, fragment_mode ) {
    var p,
      key,
      val,
      obj = {},
      cast_types = { 'null': null, 'true': TRUE, 'false': FALSE },
      decode_uri_component = decodeURIComponent,
      re = fragment_mode
        ? /^.*[#]/
        : /^.*[?]|#.*$/g;
    
    params = params.replace( re, '' ).replace( /\+/g, ' ' ).split('&');
    
    while ( params.length ) {
      
      p = params.shift().split('=');
      key = decode_uri_component( p[0] );
      
      if ( p.length === 2 ) {
        val = decode_uri_component( p[1] );
        
        if ( cast_values ) {
          if ( val && !isNaN(val) ) {
            val = Number( val );
          } else if ( val === 'undefined' ) {
            val = undefined;
          } else if ( cast_types[val] !== undefined ) {
            val = cast_types[val];
          }
        }
        
        if ( $.isArray(obj[key]) ) {
          obj[key].push( val );
        } else if ( obj[key] !== undefined ) {
          obj[key] = [obj[key], val];
        } else {
          obj[key] = val;
        }
        
      } else if ( key ) {
        obj[key] = cast_values
          ? undefined
          : '';
      }
    }
    
    return obj;
  };
  
  // Merge a URL (with or without a pre-existing params string and/or #anchor,
  // or fragment) plus any object or params string into a new URL.
  
  function build_url( url, params, merge_mode, fragment_mode ) {
    var qs,
      re = fragment_mode
        ? /^([^#]*)[#]?(.*)$/
        : /^([^#?]*)[?]?([^#]*)(#?.*)/,
      matches = url.match( re ),
      url_params = deserialize( matches[2], 0, fragment_mode ),
      hash = matches[3] || '';

    if ( is_string(params) ) {
      params = deserialize( params, 0, fragment_mode );
    }
    
    if ( merge_mode === 2 ) {
      qs = params;
    } else if ( merge_mode === 1 ) {
      qs = $.extend( {}, params, url_params );
    } else {
      qs = $.extend( {}, url_params, params );
    }
    
    qs = $.param( qs );
    return matches[1] + ( fragment_mode ? '#' : qs || !matches[1] ? '?' : '' ) + qs + hash;
  };
  
  
  // Method: jQuery.setFragment
  // 
  // Set the document fragment. Will trigger the <fragmentChange> event if 
  // <jQuery.fragmentChange> has been enabled, and the new fragment is actually
  // different than the previous fragment.
  // 
  // Usage:
  // 
  //  jQuery.setFragment( [ params [ , merge_mode ] ] );                     - -
  // 
  // Arguments:
  // 
  //  params - (String or Object) Either a serialized params string or a data
  //    object to set as the current document's fragment. If omitted, sets the
  //    document fragment to # (this may cause your browser to scroll).
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params argument will override any params in document fragment.
  //    * 1: any params in document fragment will override params argument.
  //    * 2: params argument will completely replace any params in document
  //      fragment.
  // 
  // Returns:
  // 
  //  Nothing.
  
  $.setFragment = p_setFragment = function( params, merge_mode ) {
    var frag = is_object( params )
      ? p_params( TRUE, params )
      : (params || '').replace( /^#/, '' );
    
    frag = params
      ? build_url( loc.hash, '#' + frag, merge_mode, 1 )
      : '#';
    
    //loc.hash = frag; // Safari 3 & Chrome barf if frag === '#'.
    loc.href = loc.href.replace( /#.*$/, '' ) + frag;
  };
  
  
  // Method: jQuery.fragmentChange
  // 
  // Enable or disable the polling loop that watches the document fragment for
  // changes and triggers the <fragmentChange> event. The event object passed to
  // a bound event callback has the property "fragment" which contains the
  // fragment params string. Disabled by default.
  // 
  // Note: When this is enabled for the first time, a hidden IFRAME is written
  // into the body for Internet Explorer 6 and 7 to enable fragment-based
  // browser history.
  // 
  // Usage:
  // 
  //  jQuery.fragmentChange( [ state ] );                                    - -
  // 
  // Arguments:
  // 
  //  state - (Boolean or Number) If true, a polling loop is started with the
  //    default delay of 100 and the fragmentChange event is enabled. If omitted
  //    or false, the polling loop is stopped and the fragmentChange event is
  //    disabled. A zero-or-greater numeric polling loop delay in milliseconds
  //    may also be specified.
  // 
  // Returns:
  // 
  //  Nothing.
  
  // Event: fragmentChange
  // 
  // Fired when the document fragment changes, provided <jQuery.fragmentChange>
  // has been enabled.
  // 
  // Usage:
  // 
  // > $(document).bind('fragmentChange', function(e) {
  // >   var params = $.fragment( e.fragment );
  // >   ...
  // > });
  
  $[str_fragmentChange] = function( delay ) {
    if ( delay === TRUE ) { delay = 100; }
    
    if ( timeout_id ) {
      clearTimeout( timeout_id );
      timeout_id = null;
    }
    
    if ( typeof delay === 'number' ) {
      last_fragment = get_fragment();
      
      if ( $.isFunction(ie_history) ) {
        ie_history = ie_history();
      }
      
      (function loopy(){
        var event,
          frag = get_fragment(),
          ie_frag = ie_history[str_fragment]( last_fragment );
        
        if ( frag !== last_fragment ) {
          ie_history[str_update]( frag, ie_frag );
          
          last_fragment = frag;
          
          event = $.Event( str_fragmentChange );
          event[str_fragment] = frag;
          
          $(document).trigger( event );
          
        } else if ( ie_frag !== last_fragment ) {
          p_setFragment( ie_frag, 2 );
        }
        
        timeout_id = setTimeout( loopy, delay < 0 ? 0 : delay );
      })();
    }
  };
  
  // Handle fragment-based browser history in IE 6-7.
  
  function ie_history() {
    var iframe,
      browser = $.browser,
      that = {};
    
    that[str_update] = that[str_fragment] = function( val ){ return val; };
    
    if ( browser.msie && browser.version < 8 ) {
      
      that[str_update] = function( frag, ie_frag ) {
        var doc = iframe.document;
        if ( frag !== ie_frag ) {
          doc.open();
          doc.close();
          doc.location.hash = '#' + frag;
        }
      };
      
      that[str_fragment] = function() {
        return iframe.document.location.hash.replace( /^#/, '' );
      };
      
      iframe = $('<iframe/>').hide().appendTo( 'body' )
        .get(0).contentWindow;
      
      that[str_update]( get_fragment() );
    }
    
    return that;
  };
  
})(jQuery);


(function($) {
  var currentRadius, multiplier;
 
  function parseOptions(options) {
    return {
      RADIUS: (options.radius || 20),
      DURATION: (options.duration || 500),
      TEXT_COLOR: (options.textColor || '#fff'),
      HALO_COLOR: (options.haloColor || '#777')
    }
  }
 
  function currentRadius(elem) {
    if (prop = elem.style['text-shadow']) {
      return parseInt(prop.match(/0 0 (\d+)px/));
    } else {
      return 0;
    }
  }
 
  function stepTextShadow(fx) {
    if (fx.state == 0) {
      fx.start = currentRadius(fx.elem);
    }
 
    updatedRadius = fx.end.begin ?
      parseInt(fx.end.radius * fx.pos) :
      parseInt(fx.end.radius - (fx.end.radius * fx.pos))
 
    if (fx.end.begin || (fx.state < 1)) {
      $(fx.elem).css('text-shadow', fx.end.color + ' 0 0 ' + updatedRadius + 'px');
    } else {
      $(fx.elem).css('text-shadow', $(fx.elem).data('glow.originalGlow'));
    }
  }
 
  function addGlow(opts) {
    var opts = parseOptions(opts || { });
 
    function startGlow() {
      $(this).stop().animate({
        color: opts.TEXT_COLOR,
        textShadow: {
          begin: true,
          color: opts.HALO_COLOR,
          radius: opts.RADIUS
        }
      }, opts.DURATION);
    }
 
    function startFade() {
      $(this).stop().animate({
        color: $(this).data('glow.originColor'),
        textShadow: {
          begin: false,
          color: opts.HALO_COLOR,
          radius: opts.RADIUS
        }
      }, opts.DURATION);
    }
 
    with($(this)) {
      bind('mouseenter', startGlow);
      bind('mouseleave', startFade);
      data('glow.originColor', css('color'));
      data('glow.originalGlow', (css('text-shadow') || 'none'));
    }
 
    return this;
  }
 
  $.fx.step['textShadow'] = stepTextShadow;
  $.fn.addGlow = addGlow;
})(jQuery);

(function(jQuery) {
/*
* jQuery Color Animations
* Copyright 2007 John Resig
* Released under the MIT and GPL licenses.
*/
 
// We override the animation for all of these color styles
jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
    jQuery.fx.step[attr] = function(fx){
        if ( fx.state == 0 ) {
            fx.start = getColor( fx.elem, attr );
            fx.end = getRGB( fx.end );
        }
 
        fx.elem.style[attr] = "rgb(" + [
            Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
            Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
            Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
        ].join(",") + ")";
    }
});
 
// Color Conversion functions from highlightFade
// By Blair Mitchelmore
// http://jquery.offput.ca/highlightFade/
 
// Parse strings looking for color tuples [255,255,255]
function getRGB(color) {
    var result;
 
    // Check if we're already dealing with an array of colors
    if ( color && color.constructor == Array && color.length == 3 )
        return color;
 
    // Look for rgb(num,num,num)
    if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
        return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
 
    // Look for rgb(num%,num%,num%)
    if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
        return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
 
    // Look for #a0b1c2
    if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
        return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
 
    // Look for #fff
    if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
        return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
 
    // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
    if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
        return colors['transparent']
 
    // Otherwise, we're most likely dealing with a named color
    return colors[jQuery.trim(color).toLowerCase()];
}
 
function getColor(elem, attr) {
    var color;
 
    do {
        color = jQuery.curCSS(elem, attr);
 
        // Keep going until we find an element that has color, or we hit the body
        if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
            break;
 
        attr = "backgroundColor";
    } while ( elem = elem.parentNode );
 
    return getRGB(color);
};
 
// Some named colors to work with
// From Interface by Stefan Petre
// http://interface.eyecon.ro/
 
var colors = {
  aqua:[0,255,255],
  azure:[240,255,255],
  beige:[245,245,220],
  black:[0,0,0],
  blue:[0,0,255],
  brown:[165,42,42],
  cyan:[0,255,255],
  darkblue:[0,0,139],
  darkcyan:[0,139,139],
  darkgrey:[169,169,169],
  darkgreen:[0,100,0],
  darkkhaki:[189,183,107],
  darkmagenta:[139,0,139],
  darkolivegreen:[85,107,47],
  darkorange:[255,140,0],
  darkorchid:[153,50,204],
  darkred:[139,0,0],
  darksalmon:[233,150,122],
  darkviolet:[148,0,211],
  fuchsia:[255,0,255],
  gold:[255,215,0],
  green:[0,128,0],
  indigo:[75,0,130],
  khaki:[240,230,140],
  lightblue:[173,216,230],
  lightcyan:[224,255,255],
  lightgreen:[144,238,144],
  lightgrey:[211,211,211],
  lightpink:[255,182,193],
  lightyellow:[255,255,224],
  lime:[0,255,0],
  magenta:[255,0,255],
  maroon:[128,0,0],
  navy:[0,0,128],
  olive:[128,128,0],
  orange:[255,165,0],
  pink:[255,192,203],
  purple:[128,0,128],
  violet:[128,0,128],
  red:[255,0,0],
  silver:[192,192,192],
  white:[255,255,255],
  yellow:[255,255,0],
  transparent: [255,255,255]
};
})(jQuery);

/*
 * jQuery validation plug-in 1.6
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
 * http://docs.jquery.com/Plugins/Validation
 *
 * Copyright (c) 2006 - 2008 Jörn Zaefferer
 *
 * $Id: jquery.validate.js 6403 2009-06-17 14:27:16Z joern.zaefferer $
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($) {

$.extend($.fn, {
	// http://docs.jquery.com/Plugins/Validation/validate
	validate: function( options ) {

		// if nothing is selected, return nothing; can't chain anyway
		if (!this.length) {
			options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
			return;
		}

		// check if a validator for this form was already created
		var validator = $.data(this[0], 'validator');
		if ( validator ) {
			return validator;
		}
		
		validator = new $.validator( options, this[0] );
		$.data(this[0], 'validator', validator); 
		
		if ( validator.settings.onsubmit ) {
		
			// allow suppresing validation by adding a cancel class to the submit button
			this.find("input, button").filter(".cancel").click(function() {
				validator.cancelSubmit = true;
			});
			
			// when a submitHandler is used, capture the submitting button
			if (validator.settings.submitHandler) {
				this.find("input, button").filter(":submit").click(function() {
					validator.submitButton = this;
				});
			}
		
			// validate the form on submit
			this.submit( function( event ) {
				if ( validator.settings.debug )
					// prevent form submit to be able to see console output
					event.preventDefault();
					
				function handle() {
					if ( validator.settings.submitHandler ) {
						if (validator.submitButton) {
							// insert a hidden input as a replacement for the missing submit button
							var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
						}
						validator.settings.submitHandler.call( validator, validator.currentForm );
						if (validator.submitButton) {
							// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
							hidden.remove();
						}
						return false;
					}
					return true;
				}
					
				// prevent submit for invalid forms or custom submit handlers
				if ( validator.cancelSubmit ) {
					validator.cancelSubmit = false;
					return handle();
				}
				if ( validator.form() ) {
					if ( validator.pendingRequest ) {
						validator.formSubmitted = true;
						return false;
					}
					return handle();
				} else {
					validator.focusInvalid();
					return false;
				}
			});
		}
		
		return validator;
	},
	// http://docs.jquery.com/Plugins/Validation/valid
	valid: function() {
        if ( $(this[0]).is('form')) {
            return this.validate().form();
        } else {
            var valid = true;
            var validator = $(this[0].form).validate();
            this.each(function() {
				valid &= validator.element(this);
            });
            return valid;
        }
    },
	// attributes: space seperated list of attributes to retrieve and remove
	removeAttrs: function(attributes) {
		var result = {},
			$element = this;
		$.each(attributes.split(/\s/), function(index, value) {
			result[value] = $element.attr(value);
			$element.removeAttr(value);
		});
		return result;
	},
	// http://docs.jquery.com/Plugins/Validation/rules
	rules: function(command, argument) {
		var element = this[0];
		
		if (command) {
			var settings = $.data(element.form, 'validator').settings;
			var staticRules = settings.rules;
			var existingRules = $.validator.staticRules(element);
			switch(command) {
			case "add":
				$.extend(existingRules, $.validator.normalizeRule(argument));
				staticRules[element.name] = existingRules;
				if (argument.messages)
					settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
				break;
			case "remove":
				if (!argument) {
					delete staticRules[element.name];
					return existingRules;
				}
				var filtered = {};
				$.each(argument.split(/\s/), function(index, method) {
					filtered[method] = existingRules[method];
					delete existingRules[method];
				});
				return filtered;
			}
		}
		
		var data = $.validator.normalizeRules(
		$.extend(
			{},
			$.validator.metadataRules(element),
			$.validator.classRules(element),
			$.validator.attributeRules(element),
			$.validator.staticRules(element)
		), element);
		
		// make sure required is at front
		if (data.required) {
			var param = data.required;
			delete data.required;
			data = $.extend({required: param}, data);
		}
		
		return data;
	}
});

// Custom selectors
$.extend($.expr[":"], {
	// http://docs.jquery.com/Plugins/Validation/blank
	blank: function(a) {return !$.trim("" + a.value);},
	// http://docs.jquery.com/Plugins/Validation/filled
	filled: function(a) {return !!$.trim("" + a.value);},
	// http://docs.jquery.com/Plugins/Validation/unchecked
	unchecked: function(a) {return !a.checked;}
});

// constructor for validator
$.validator = function( options, form ) {
	this.settings = $.extend( {}, $.validator.defaults, options );
	this.currentForm = form;
	this.init();
};

$.validator.format = function(source, params) {
	if ( arguments.length == 1 ) 
		return function() {
			var args = $.makeArray(arguments);
			args.unshift(source);
			return $.validator.format.apply( this, args );
		};
	if ( arguments.length > 2 && params.constructor != Array  ) {
		params = $.makeArray(arguments).slice(1);
	}
	if ( params.constructor != Array ) {
		params = [ params ];
	}
	$.each(params, function(i, n) {
		source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
	});
	return source;
};

$.extend($.validator, {
	
	defaults: {
		messages: {},
		groups: {},
		rules: {},
		errorClass: "error",
		validClass: "valid",
		errorElement: "label",
		focusInvalid: true,
		errorContainer: $( [] ),
		errorLabelContainer: $( [] ),
		onsubmit: true,
		ignore: [],
		ignoreTitle: false,
		onfocusin: function(element) {
			this.lastActive = element;
				
			// hide error label and remove error class on focus if enabled
			if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
				this.errorsFor(element).hide();
			}
		},
		onfocusout: function(element) {
			if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
				this.element(element);
			}
		},
		onkeyup: function(element) {
			if ( element.name in this.submitted || element == this.lastElement ) {
				this.element(element);
			}
		},
		onclick: function(element) {
			// click on selects, radiobuttons and checkboxes
			if ( element.name in this.submitted )
				this.element(element);
			// or option elements, check parent select in that case
			else if (element.parentNode.name in this.submitted)
				this.element(element.parentNode)
		},
		highlight: function( element, errorClass, validClass ) {
			$(element).addClass(errorClass).removeClass(validClass);
		},
		unhighlight: function( element, errorClass, validClass ) {
			$(element).removeClass(errorClass).addClass(validClass);
		}
	},

	// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
	setDefaults: function(settings) {
		$.extend( $.validator.defaults, settings );
	},

	messages: {
		required: "This field is required.",
		remote: "Please fix this field.",
		email: "Please enter a valid email address.",
		url: "Please enter a valid URL.",
		date: "Please enter a valid date.",
		dateISO: "Please enter a valid date (ISO).",
		number: "Please enter a valid number.",
		digits: "Please enter only digits.",
		creditcard: "Please enter a valid credit card number.",
		equalTo: "Please enter the same value again.",
		accept: "Please enter a value with a valid extension.",
		maxlength: $.validator.format("Please enter no more than {0} characters."),
		minlength: $.validator.format("Please enter at least {0} characters."),
		rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
		range: $.validator.format("Please enter a value between {0} and {1}."),
		max: $.validator.format("Please enter a value less than or equal to {0}."),
		min: $.validator.format("Please enter a value greater than or equal to {0}."),
		nr_answer: $.validator.format("Invalid character. Use numbers (0-9), a decimal (.) only once or a hyphen (-) at the start")
	},
	
	autoCreateRanges: false,
	
	prototype: {
		
		init: function() {
			this.labelContainer = $(this.settings.errorLabelContainer);
			this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
			this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
			this.submitted = {};
			this.valueCache = {};
			this.pendingRequest = 0;
			this.pending = {};
			this.invalid = {};
			this.reset();
			
			var groups = (this.groups = {});
			$.each(this.settings.groups, function(key, value) {
				$.each(value.split(/\s/), function(index, name) {
					groups[name] = key;
				});
			});
			var rules = this.settings.rules;
			$.each(rules, function(key, value) {
				rules[key] = $.validator.normalizeRule(value);
			});
			
			function delegate(event) {
				var validator = $.data(this[0].form, "validator");
				validator.settings["on" + event.type] && validator.settings["on" + event.type].call(validator, this[0] );
			}
			$(this.currentForm)
				.delegate("focusin focusout keyup", ":text, :password, :file, select, textarea", delegate)
				.delegate("click", ":radio, :checkbox, select, option", delegate);

			if (this.settings.invalidHandler)
				$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/form
		form: function() {
			this.checkForm();
			$.extend(this.submitted, this.errorMap);
			this.invalid = $.extend({}, this.errorMap);
			if (!this.valid())
				$(this.currentForm).triggerHandler("invalid-form", [this]);
			this.showErrors();
			return this.valid();
		},
		
		checkForm: function() {
			this.prepareForm();
			for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
				this.check( elements[i] );
			}
			return this.valid(); 
		},
		
		// http://docs.jquery.com/Plugins/Validation/Validator/element
		element: function( element ) {
			element = this.clean( element );
			this.lastElement = element;
			this.prepareElement( element );
			this.currentElements = $(element);
			var result = this.check( element );
			if ( result ) {
				delete this.invalid[element.name];
			} else {
				this.invalid[element.name] = true;
			}
			if ( !this.numberOfInvalids() ) {
				// Hide error containers on last error
				this.toHide = this.toHide.add( this.containers );
			}
			this.showErrors();
			return result;
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
		showErrors: function(errors) {
			if(errors) {
				// add items to error list and map
				$.extend( this.errorMap, errors );
				this.errorList = [];
				for ( var name in errors ) {
					this.errorList.push({
						message: errors[name],
						element: this.findByName(name)[0]
					});
				}
				// remove items from success list
				this.successList = $.grep( this.successList, function(element) {
					return !(element.name in errors);
				});
			}
			this.settings.showErrors
				? this.settings.showErrors.call( this, this.errorMap, this.errorList )
				: this.defaultShowErrors();
		},
		
		// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
		resetForm: function() {
			if ( $.fn.resetForm )
				$( this.currentForm ).resetForm();
			this.submitted = {};
			this.prepareForm();
			this.hideErrors();
			this.elements().removeClass( this.settings.errorClass );
		},
		
		numberOfInvalids: function() {
			return this.objectLength(this.invalid);
		},
		
		objectLength: function( obj ) {
			var count = 0;
			for ( var i in obj )
				count++;
			return count;
		},
		
		hideErrors: function() {
			this.addWrapper( this.toHide ).hide();
		},
		
		valid: function() {
			return this.size() == 0;
		},
		
		size: function() {
			return this.errorList.length;
		},
		
		focusInvalid: function() {
			if( this.settings.focusInvalid ) {
				try {
					$(this.findLastActive() || this.errorList.length && this.errorList[0].element || []).filter(":visible").focus();
				} catch(e) {
					// ignore IE throwing errors when focusing hidden elements
				}
			}
		},
		
		findLastActive: function() {
			var lastActive = this.lastActive;
			return lastActive && $.grep(this.errorList, function(n) {
				return n.element.name == lastActive.name;
			}).length == 1 && lastActive;
		},
		
		elements: function() {
			var validator = this,
				rulesCache = {};
			
			// select all valid inputs inside the form (no submit or reset buttons)
			// workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved
			return $([]).add(this.currentForm.elements)
			.filter(":input")
			.not(":submit, :reset, :image, [disabled]")
			.not( this.settings.ignore )
			.filter(function() {
				!this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
			
				// select only the first element for each name, and only those with rules specified
				if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
					return false;
				
				rulesCache[this.name] = true;
				return true;
			});
		},
		
		clean: function( selector ) {
			return $( selector )[0];
		},
		
		errors: function() {
			return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
		},
		
		reset: function() {
			this.successList = [];
			this.errorList = [];
			this.errorMap = {};
			this.toShow = $([]);
			this.toHide = $([]);
			this.currentElements = $([]);
		},
		
		prepareForm: function() {
			this.reset();
			this.toHide = this.errors().add( this.containers );
		},
		
		prepareElement: function( element ) {
			this.reset();
			this.toHide = this.errorsFor(element);
		},
	
		check: function( element ) {
			element = this.clean( element );
			
			// if radio/checkbox, validate first element in group instead
			if (this.checkable(element)) {
				element = this.findByName( element.name )[0];
			}
			
			var rules = $(element).rules();
			var dependencyMismatch = false;
			for( method in rules ) {
				var rule = { method: method, parameters: rules[method] };
				try {
					var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
					
					// if a method indicates that the field is optional and therefore valid,
					// don't mark it as valid when there are no other rules
					if ( result == "dependency-mismatch" ) {
						dependencyMismatch = true;
						continue;
					}
					dependencyMismatch = false;
					
					if ( result == "pending" ) {
						this.toHide = this.toHide.not( this.errorsFor(element) );
						return;
					}
					
					if( !result ) {
						this.formatAndAdd( element, rule );
						return false;
					}
				} catch(e) {
					this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
						 + ", check the '" + rule.method + "' method", e);
					throw e;
				}
			}
			if (dependencyMismatch)
				return;
			if ( this.objectLength(rules) )
				this.successList.push(element);
			return true;
		},
		
		// return the custom message for the given element and validation method
		// specified in the element's "messages" metadata
		customMetaMessage: function(element, method) {
			if (!$.metadata)
				return;
			
			var meta = this.settings.meta
				? $(element).metadata()[this.settings.meta]
				: $(element).metadata();
			
			return meta && meta.messages && meta.messages[method];
		},
		
		// return the custom message for the given element name and validation method
		customMessage: function( name, method ) {
			var m = this.settings.messages[name];
			return m && (m.constructor == String
				? m
				: m[method]);
		},
		
		// return the first defined argument, allowing empty strings
		findDefined: function() {
			for(var i = 0; i < arguments.length; i++) {
				if (arguments[i] !== undefined)
					return arguments[i];
			}
			return undefined;
		},
		
		defaultMessage: function( element, method) {
			return this.findDefined(
				this.customMessage( element.name, method ),
				this.customMetaMessage( element, method ),
				// title is never undefined, so handle empty string as undefined
				!this.settings.ignoreTitle && element.title || undefined,
				$.validator.messages[method],
				"<strong>Warning: No message defined for " + element.name + "</strong>"
			);
		},
		
		formatAndAdd: function( element, rule ) {
			var message = this.defaultMessage( element, rule.method ),
				theregex = /\$?\{(\d+)\}/g;
			if ( typeof message == "function" ) {
				message = message.call(this, rule.parameters, element);
			} else if (theregex.test(message)) {
				message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
			}			
			this.errorList.push({
				message: message,
				element: element
			});
			
			this.errorMap[element.name] = message;
			this.submitted[element.name] = message;
		},
		
		addWrapper: function(toToggle) {
			if ( this.settings.wrapper )
				toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
			return toToggle;
		},
		
		defaultShowErrors: function() {
			for ( var i = 0; this.errorList[i]; i++ ) {
				var error = this.errorList[i];
				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
				this.showLabel( error.element, error.message );
			}
			if( this.errorList.length ) {
				this.toShow = this.toShow.add( this.containers );
			}
			if (this.settings.success) {
				for ( var i = 0; this.successList[i]; i++ ) {
					this.showLabel( this.successList[i] );
				}
			}
			if (this.settings.unhighlight) {
				for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
					this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
				}
			}
			this.toHide = this.toHide.not( this.toShow );
			this.hideErrors();
			this.addWrapper( this.toShow ).show();
		},
		
		validElements: function() {
			return this.currentElements.not(this.invalidElements());
		},
		
		invalidElements: function() {
			return $(this.errorList).map(function() {
				return this.element;
			});
		},
		
		showLabel: function(element, message) {
			var label = this.errorsFor( element );
			if ( label.length ) {
				// refresh error/success class
				label.removeClass().addClass( this.settings.errorClass );
			
				// check if we have a generated label, replace the message then
				label.attr("generated") && label.html(message);
			} else {
				// create label
				label = $("<" + this.settings.errorElement + "/>")
					.attr({"for":  this.idOrName(element), generated: true})
					.addClass(this.settings.errorClass)
					.html(message || "");
				if ( this.settings.wrapper ) {
					// make sure the element is visible, even in IE
					// actually showing the wrapped element is handled elsewhere
					label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
				}
				if ( !this.labelContainer.append(label).length )
					this.settings.errorPlacement
						? this.settings.errorPlacement(label, $(element) )
						: label.insertAfter(element);
			}
			if ( !message && this.settings.success ) {
				label.text("");
				typeof this.settings.success == "string"
					? label.addClass( this.settings.success )
					: this.settings.success( label );
			}
			this.toShow = this.toShow.add(label);
		},
		
		errorsFor: function(element) {
			var name = this.idOrName(element);
    		return this.errors().filter(function() {
				return $(this).attr('for') == name
			});
		},
		
		idOrName: function(element) {
			return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
		},

		checkable: function( element ) {
			return /radio|checkbox/i.test(element.type);
		},
		
		findByName: function( name ) {
			// select by name and filter by form for performance over form.find("[name=...]")
			var form = this.currentForm;
			return $(document.getElementsByName(name)).map(function(index, element) {
				return element.form == form && element.name == name && element  || null;
			});
		},
		
		getLength: function(value, element) {
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				return $("option:selected", element).length;
			case 'input':
				if( this.checkable( element) )
					return this.findByName(element.name).filter(':checked').length;
			}
			return value.length;
		},
	
		depend: function(param, element) {
			return this.dependTypes[typeof param]
				? this.dependTypes[typeof param](param, element)
				: true;
		},
	
		dependTypes: {
			"boolean": function(param, element) {
				return param;
			},
			"string": function(param, element) {
				return !!$(param, element.form).length;
			},
			"function": function(param, element) {
				return param(element);
			}
		},
		
		optional: function(element) {
			return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
		},
		
		startRequest: function(element) {
			if (!this.pending[element.name]) {
				this.pendingRequest++;
				this.pending[element.name] = true;
			}
		},
		
		stopRequest: function(element, valid) {
			this.pendingRequest--;
			// sometimes synchronization fails, make sure pendingRequest is never < 0
			if (this.pendingRequest < 0)
				this.pendingRequest = 0;
			delete this.pending[element.name];
			if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
				$(this.currentForm).submit();
				this.formSubmitted = false;
			} else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
				$(this.currentForm).triggerHandler("invalid-form", [this]);
				this.formSubmitted = false;
			}
		},
		
		previousValue: function(element) {
			return $.data(element, "previousValue") || $.data(element, "previousValue", {
				old: null,
				valid: true,
				message: this.defaultMessage( element, "remote" )
			});
		}
		
	},
	
	classRuleSettings: {
		required: {required: true},
		email: {email: true},
		nr_answer: {nr_answer: true},
		url: {url: true},
		date: {date: true},
		dateISO: {dateISO: true},
		dateDE: {dateDE: true},
		number: {number: true},
		numberDE: {numberDE: true},
		digits: {digits: true},
		creditcard: {creditcard: true}
	},
	
	addClassRules: function(className, rules) {
		className.constructor == String ?
			this.classRuleSettings[className] = rules :
			$.extend(this.classRuleSettings, className);
	},
	
	classRules: function(element) {
		var rules = {};
		var classes = $(element).attr('class');
		classes && $.each(classes.split(' '), function() {
			if (this in $.validator.classRuleSettings) {
				$.extend(rules, $.validator.classRuleSettings[this]);
			}
		});
		return rules;
	},
	
	attributeRules: function(element) {
		var rules = {};
		var $element = $(element);
		
		for (method in $.validator.methods) {
			var value = $element.attr(method);
			if (value) {
				rules[method] = value;
			}
		}
		
		// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
		if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
			delete rules.maxlength;
		}
		
		return rules;
	},
	
	metadataRules: function(element) {
		if (!$.metadata) return {};
		
		var meta = $.data(element.form, 'validator').settings.meta;
		return meta ?
			$(element).metadata()[meta] :
			$(element).metadata();
	},
	
	staticRules: function(element) {
		var rules = {};
		var validator = $.data(element.form, 'validator');
		if (validator.settings.rules) {
			rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
		}
		return rules;
	},
	
	normalizeRules: function(rules, element) {
		// handle dependency check
		$.each(rules, function(prop, val) {
			// ignore rule when param is explicitly false, eg. required:false
			if (val === false) {
				delete rules[prop];
				return;
			}
			if (val.param || val.depends) {
				var keepRule = true;
				switch (typeof val.depends) {
					case "string":
						keepRule = !!$(val.depends, element.form).length;
						break;
					case "function":
						keepRule = val.depends.call(element, element);
						break;
				}
				if (keepRule) {
					rules[prop] = val.param !== undefined ? val.param : true;
				} else {
					delete rules[prop];
				}
			}
		});
		
		// evaluate parameters
		$.each(rules, function(rule, parameter) {
			rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
		});
		
		// clean number parameters
		$.each(['minlength', 'maxlength', 'min', 'max'], function() {
			if (rules[this]) {
				rules[this] = Number(rules[this]);
			}
		});
		$.each(['rangelength', 'range'], function() {
			if (rules[this]) {
				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
			}
		});
		
		if ($.validator.autoCreateRanges) {
			// auto-create ranges
			if (rules.min && rules.max) {
				rules.range = [rules.min, rules.max];
				delete rules.min;
				delete rules.max;
			}
			if (rules.minlength && rules.maxlength) {
				rules.rangelength = [rules.minlength, rules.maxlength];
				delete rules.minlength;
				delete rules.maxlength;
			}
		}
		
		// To support custom messages in metadata ignore rule methods titled "messages"
		if (rules.messages) {
			delete rules.messages
		}
		
		return rules;
	},
	
	// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
	normalizeRule: function(data) {
		if( typeof data == "string" ) {
			var transformed = {};
			$.each(data.split(/\s/), function() {
				transformed[this] = true;
			});
			data = transformed;
		}
		return data;
	},
	
	// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
	addMethod: function(name, method, message) {
		$.validator.methods[name] = method;
		$.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
		if (method.length < 3) {
			$.validator.addClassRules(name, $.validator.normalizeRule(name));
		}
	},

	methods: {

		// http://docs.jquery.com/Plugins/Validation/Methods/required
		required: function(value, element, param) {
			// check if dependency is met
			if ( !this.depend(param, element) )
				return "dependency-mismatch";
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				// could be an array for select-multiple or a string, both are fine this way
				var val = $(element).val();
				return val && val.length > 0;
			case 'input':
				if ( this.checkable(element) )
					return this.getLength(value, element) > 0;
			default:
				return $.trim(value).length > 0;
			}
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/remote
		remote: function(value, element, param) {
			if ( this.optional(element) )
				return "dependency-mismatch";
			
			var previous = this.previousValue(element);
			if (!this.settings.messages[element.name] )
				this.settings.messages[element.name] = {};
			previous.originalMessage = this.settings.messages[element.name].remote;
			this.settings.messages[element.name].remote = previous.message;
			
			param = typeof param == "string" && {url:param} || param; 
			
			if ( previous.old !== value ) {
				previous.old = value;
				var validator = this;
				this.startRequest(element);
				var data = {};
				data[element.name] = value;
				$.ajax($.extend(true, {
					url: param,
					mode: "abort",
					port: "validate" + element.name,
					dataType: "json",
					data: data,
					success: function(response) {
						validator.settings.messages[element.name].remote = previous.originalMessage;
						var valid = response === true;
						if ( valid ) {
							var submitted = validator.formSubmitted;
							validator.prepareElement(element);
							validator.formSubmitted = submitted;
							validator.successList.push(element);
							validator.showErrors();
						} else {
							var errors = {};
							var message = (previous.message = response || validator.defaultMessage( element, "remote" ));
							errors[element.name] = $.isFunction(message) ? message(value) : message;
							validator.showErrors(errors);
						}
						previous.valid = valid;
						validator.stopRequest(element, valid);
					}
				}, param));
				return "pending";
			} else if( this.pending[element.name] ) {
				return "pending";
			}
			return previous.valid;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/minlength
		minlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) >= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
		maxlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) <= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
		rangelength: function(value, element, param) {
			var length = this.getLength($.trim(value), element);
			return this.optional(element) || ( length >= param[0] && length <= param[1] );
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/min
		min: function( value, element, param ) {
			return this.optional(element) || value >= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/max
		max: function( value, element, param ) {
			return this.optional(element) || value <= param;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/range
		range: function( value, element, param ) {
			return this.optional(element) || ( value >= param[0] && value <= param[1] );
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/email
		email: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
			return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
		},
		
		nr_answer: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
			//return !((/(\$|,|%|[a-zA-Z]|(?!^-))/.test(value)) || value.split(/\.|-/).length-1 > 1);
			return ((/(^-{0,1}\d*\.{0,1}\d*$)/).test(value))
			
		},
		
	
		// http://docs.jquery.com/Plugins/Validation/Methods/url
		url: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
			return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
		},
        
		// http://docs.jquery.com/Plugins/Validation/Methods/date
		date: function(value, element) {
			return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
		dateISO: function(value, element) {
			return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/number
		number: function(value, element) {
			return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
		},
	
		// http://docs.jquery.com/Plugins/Validation/Methods/digits
		digits: function(value, element) {
			return this.optional(element) || /^\d+$/.test(value);
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
		// based on http://en.wikipedia.org/wiki/Luhn
		creditcard: function(value, element) {
			if ( this.optional(element) )
				return "dependency-mismatch";
			// accept only digits and dashes
			if (/[^0-9-]+/.test(value))
				return false;
			var nCheck = 0,
				nDigit = 0,
				bEven = false;

			value = value.replace(/\D/g, "");

			for (var n = value.length - 1; n >= 0; n--) {
				var cDigit = value.charAt(n);
				var nDigit = parseInt(cDigit, 10);
				if (bEven) {
					if ((nDigit *= 2) > 9)
						nDigit -= 9;
				}
				nCheck += nDigit;
				bEven = !bEven;
			}

			return (nCheck % 10) == 0;
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/accept
		accept: function(value, element, param) {
			param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
			return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i")); 
		},
		
		// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
		equalTo: function(value, element, param) {
			// bind to the blur event of the target in order to revalidate whenever the target field is updated
			// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
			var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
				$(element).valid();
			});
			return value == target.val();
		}
		
	}
	
});

// deprecated, use $.validator.format instead
$.format = $.validator.format;

})(jQuery);

// ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() 
;(function($) {
	var ajax = $.ajax;
	var pendingRequests = {};
	$.ajax = function(settings) {
		// create settings for compatibility with ajaxSetup
		settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings));
		var port = settings.port;
		if (settings.mode == "abort") {
			if ( pendingRequests[port] ) {
				pendingRequests[port].abort();
			}
			return (pendingRequests[port] = ajax.apply(this, arguments));
		}
		return ajax.apply(this, arguments);
	};
})(jQuery);

// provides cross-browser focusin and focusout events
// IE has native support, in other browsers, use event caputuring (neither bubbles)

// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target 

// provides triggerEvent(type: String, target: Element) to trigger delegated events
;(function($) {
	$.each({
		focus: 'focusin',
		blur: 'focusout'	
	}, function( original, fix ){
		$.event.special[fix] = {
			setup:function() {
				if ( $.browser.msie ) return false;
				this.addEventListener( original, $.event.special[fix].handler, true );
			},
			teardown:function() {
				if ( $.browser.msie ) return false;
				this.removeEventListener( original,
				$.event.special[fix].handler, true );
			},
			handler: function(e) {
				arguments[0] = $.event.fix(e);
				arguments[0].type = fix;
				return $.event.handle.apply(this, arguments);
			}
		};
	});
	$.extend($.fn, {
		delegate: function(type, delegate, handler) {
			return this.bind(type, function(event) {
				var target = $(event.target);
				if (target.is(delegate)) {
					return handler.apply(target, arguments);
				}
			});
		},
		triggerEvent: function(type, target) {
			return this.triggerHandler(type, [$.event.fix({ type: type, target: target })]);
		}
	})
})(jQuery);


/*
 * Metadata - jQuery plugin for parsing metadata from elements
 *
 * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $
 *
 */

/**
 * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
 * in the JSON will become a property of the element itself.
 *
 * There are three supported types of metadata storage:
 *
 *   attr:  Inside an attribute. The name parameter indicates *which* attribute.
 *          
 *   class: Inside the class attribute, wrapped in curly braces: { }
 *   
 *   elem:  Inside a child element (e.g. a script tag). The
 *          name parameter indicates *which* element.
 *          
 * The metadata for an element is loaded the first time the element is accessed via jQuery.
 *
 * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
 * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
 * 
 * @name $.metadata.setType
 *
 * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("class")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from the class attribute
 * 
 * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
 * @before $.metadata.setType("attr", "data")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a "data" attribute
 * 
 * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
 * @before $.metadata.setType("elem", "script")
 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
 * @desc Reads metadata from a nested script element
 * 
 * @param String type The encoding type
 * @param String name The name of the attribute to be used to get metadata (optional)
 * @cat Plugins/Metadata
 * @descr Sets the type of encoding to be used when loading metadata for the first time
 * @type undefined
 * @see metadata()
 */

(function($) {

$.extend({
	metadata : {
		defaults : {
			type: 'class',
			name: 'metadata',
			cre: /({.*})/,
			single: 'metadata'
		},
		setType: function( type, name ){
			this.defaults.type = type;
			this.defaults.name = name;
		},
		get: function( elem, opts ){
			var settings = $.extend({},this.defaults,opts);
			// check for empty string in single property
			if ( !settings.single.length ) settings.single = 'metadata';
			
			var data = $.data(elem, settings.single);
			// returned cached data if it already exists
			if ( data ) return data;
			
			data = "{}";
			
			if ( settings.type == "class" ) {
				var m = settings.cre.exec( elem.className );
				if ( m )
					data = m[1];
			} else if ( settings.type == "elem" ) {
				if( !elem.getElementsByTagName )
					return undefined;
				var e = elem.getElementsByTagName(settings.name);
				if ( e.length )
					data = $.trim(e[0].innerHTML);
			} else if ( elem.getAttribute != undefined ) {
				var attr = elem.getAttribute( settings.name );
				if ( attr )
					data = attr;
			}
			
			if ( data.indexOf( '{' ) <0 )
			data = "{" + data + "}";
			
			data = eval("(" + data + ")");
			
			$.data( elem, settings.single, data );
			return data;
		}
	}
});

/**
 * Returns the metadata object for the first member of the jQuery object.
 *
 * @name metadata
 * @descr Returns element's metadata object
 * @param Object opts An object contianing settings to override the defaults
 * @type jQuery
 * @cat Plugins/Metadata
 */
$.fn.metadata = function( opts ){
	return $.metadata.get( this[0], opts );
};

})(jQuery);

jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};

/*
 * Lightweight RTE - jQuery Plugin, version 1.2
 * Copyright (c) 2009 Andrey Gayvoronsky - http://www.gayvoronsky.com
 */
jQuery.fn.rte = function(options, editors) {
	if(!editors || editors.constructor != Array)
		editors = new Array();
		
	$(this).each(function(i) {
		var id = (this.id) ? this.id : editors.length;
		editors[id] = new lwRTE (this, options || {});
	});
	
	return editors;
}

var lwRTE_resizer = function(textarea) {
	this.drag = false;
	this.rte_zone = $(textarea).parents('.rte-zone');
}

lwRTE_resizer.mousedown = function(resizer, e) {
	resizer.drag = true;
	resizer.event = (typeof(e) == "undefined") ? window.event : e;
	resizer.rte_obj = $(".rte-resizer", resizer.rte_zone).prev().eq(0);
	$('body', document).css('cursor', 'se-resize');
	return false;
}

lwRTE_resizer.mouseup = function(resizer, e) {
	resizer.drag = false;
	$('body', document).css('cursor', 'auto');
	return false;
}

lwRTE_resizer.mousemove = function(resizer, e) {
	if(resizer.drag) {
		e = (typeof(e) == "undefined") ? window.event : e;
		var w = Math.max(1, resizer.rte_zone.width() + e.screenX - resizer.event.screenX);
		var h = Math.max(1, resizer.rte_obj.height() + e.screenY - resizer.event.screenY);
		resizer.rte_zone.width(w);
		resizer.rte_obj.height(h);
		resizer.event = e;
	}
	return false;
}

var lwRTE = function (textarea, options) {
	this.css		= [];
	this.css_class	= options.frame_class || '';
	this.base_url	= options.base_url || '';
	this.width		= options.width || $(textarea).width() || '100%';
	//this.height		= options.height || $(textarea).height() || 350;
	this.iframe		= null;
	this.iframe_doc	= null;
	this.textarea	= null;
	this.event		= null;
	this.range		= null;
	this.toolbars	= {rte: '', html : ''};
	// this.controls	= {rte: {disable: {hint: 'Source editor'}}, html: {enable: {hint: 'Visual editor'}}};
	this.controls = {rte: {}, html: {}};

	$.extend(this.controls.rte, options.controls_rte || {});
	$.extend(this.controls.html, options.controls_html || {});
	$.extend(this.css, options.css || {});

	if(document.designMode || document.contentEditable) {
		$(textarea).wrap($('<div></div>').addClass('rte-zone').width(this.width));		
		// $('<div class="rte-resizer"><a href="#"></a></div>').insertAfter(textarea);
		// 
		// var resizer = new lwRTE_resizer(textarea);
		// 
		// $(".rte-resizer a", $(textarea).parents('.rte-zone')).mousedown(function(e) {
		// 	$(document).mousemove(function(e) {
		// 		return lwRTE_resizer.mousemove(resizer, e);
		// 	});
		// 
		// 	$(document).mouseup(function(e) {
		// 		return lwRTE_resizer.mouseup(resizer, e)
		// 	});
		// 
		// 	return lwRTE_resizer.mousedown(resizer, e);
		// });

		this.textarea	= textarea;
		this.enable_design_mode();
	}
}

lwRTE.prototype.editor_cmd = function(command, args) {
	this.iframe.contentWindow.focus();
	try {
		this.iframe_doc.execCommand(command, false, args);
	} catch(e) {
	}
	this.iframe.contentWindow.focus();
}

lwRTE.prototype.get_toolbar = function() {
	var editor = (this.iframe) ? $(this.iframe) : $(this.textarea);
	return (editor.prev().hasClass('rte-toolbar')) ? editor.prev() : null;
}

lwRTE.prototype.activate_toolbar = function(editor, tb) {
	var old_tb = this.get_toolbar();

	if(old_tb)
		old_tb.remove();

	$(editor).before($(tb).clone(true));
}
	
lwRTE.prototype.enable_design_mode = function() {
	var self = this;

	// need to be created this way
	self.iframe	= document.createElement("iframe");
	self.iframe.frameBorder = 0;
	self.iframe.frameMargin = 0;
	self.iframe.framePadding = 0;
	self.iframe.width = '100%';
	self.iframe.height = self.height || '100%';
	self.iframe.src	= "javascript:void(0);";

	if($(self.textarea).attr('class'))
		self.iframe.className = $(self.textarea).attr('class');

	if($(self.textarea).attr('id'))
		self.iframe.id = $(self.textarea).attr('id');

	if($(self.textarea).attr('name'))
		self.iframe.title = $(self.textarea).attr('name');

	var content	= $(self.textarea).val();

	$(self.textarea).hide().after(self.iframe).remove();
	self.textarea	= null;
	
	var css = '';
	
	for(var i in self.css)
		css += "<link type='text/css' rel='stylesheet' href='" + self.css[i] + "' />";

	var base = (self.base_url) ? "<base href='" + self.base_url + "' />" : '';
	var style = (self.css_class) ? "class='" + self.css_class + "'" : '';

	// Mozilla need this to display caret
	if($.trim(content) == '' && $.browser.mozilla)
		content	= '<br>';

	var doc = "<html><head>" + base + css + "</head><body " + style + " style='padding:5px'>" + content + "</body></html>";

	self.iframe_doc	= self.iframe.contentWindow.document;

	try {
		self.iframe_doc.designMode = 'on';
	} catch ( e ) {
		// Will fail on Gecko if the editor is placed in an hidden container element
		// The design mode will be set ones the editor is focused
		$(self.iframe_doc).focus(function() { self.iframe_doc.designMode(); } );
	}

	self.iframe_doc.open();
	self.iframe_doc.write(doc);
	self.iframe_doc.close();

	if(!self.toolbars.rte)
		self.toolbars.rte	= self.create_toolbar(self.controls.rte);

	self.activate_toolbar(self.iframe, self.toolbars.rte);

	$(self.iframe).parents('form').submit( 
		function() { self.disable_design_mode(true); }
	);

	$(self.iframe_doc).mouseup(function(event) { 
		if(self.iframe_doc.selection)
			self.range = self.iframe_doc.selection.createRange();  //store to restore later(IE fix)

		self.set_selected_controls( (event.target) ? event.target : event.srcElement, self.controls.rte); 
	});

	$(self.iframe_doc).blur(function(event){ 
		if(self.iframe_doc.selection) 
			self.range = self.iframe_doc.selection.createRange(); // same fix for IE as above
	});

	$(self.iframe_doc).keyup(function(event) { self.set_selected_controls( self.get_selected_element(), self.controls.rte); });

	// Mozilla CSS styling off
	if(!$.browser.msie)
		self.editor_cmd('styleWithCSS', false);
}
    
lwRTE.prototype.disable_design_mode = function(submit) {
	var self = this;

	self.textarea = (submit) ? $('<input type="hidden" />').get(0) : $('<textarea></textarea>').width('100%').height(self.height).get(0);

	if(self.iframe.className)
		self.textarea.className = self.iframe.className;

	if(self.iframe.id)
		self.textarea.id = self.iframe.id;
		
	if(self.iframe.title)
		self.textarea.name = self.iframe.title;
	
	$(self.textarea).val($('body', self.iframe_doc).html());
	$(self.iframe).before(self.textarea);

	if(!self.toolbars.html)
		self.toolbars.html	= self.create_toolbar(self.controls.html);

	if(submit != true) {
		$(self.iframe_doc).remove(); //fix 'permission denied' bug in IE7 (jquery cache)
		$(self.iframe).remove();
		self.iframe = self.iframe_doc = null;
		self.activate_toolbar(self.textarea, self.toolbars.html);
	}
}
    
lwRTE.prototype.toolbar_click = function(obj, control) {
	var fn = control.exec;
	var args = control.args || [];
	var is_select = (obj.tagName.toUpperCase() == 'SELECT');
	
	$('.rte-panel', this.get_toolbar()).remove();

	if(fn) {
		if(is_select)
			args.push(obj);

		try {
			fn.apply(this, args);
		} catch(e) {

		}
	} else if(this.iframe && control.command) {
		if(is_select) {
			args = obj.options[obj.selectedIndex].value;

			if(args.length <= 0)
				return;
		}

		this.editor_cmd(control.command, args);
	}
}
	
lwRTE.prototype.create_toolbar = function(controls) {
	var self = this;
	var tb = $("<div></div>").addClass('rte-toolbar').width('100%').append($("<ul></ul>")).append($("<div></div>").addClass('clear'));
	var obj, li;
	
	for (var key in controls){
		if(controls[key].separator) {
			li = $("<li></li>").addClass('separator');
		} else {
			if(controls[key].init) {
				try {
					controls[key].init.apply(controls[key], [this]);
				} catch(e) {
				}
			}
			
			if(controls[key].select) {
				obj = $(controls[key].select)
					.change( function(e) {
						self.event = e;
						self.toolbar_click(this, controls[this.className]); 
						return false;
					});
			} else {
				obj = $("<a href='#'></a>")
					.attr('title', (controls[key].hint) ? controls[key].hint : key)
					.attr('rel', key)
					.click( function(e) {
						self.event = e;
						self.toolbar_click(this, controls[this.rel]); 
						return false;
					})
			}

			li = $("<li></li>").append(obj.addClass(key));
		}

		$("ul",tb).append(li);
	}

	$('.enable', tb).click(function() {
		self.enable_design_mode();
		return false; 
	});

	$('.disable', tb).click(function() {
		self.disable_design_mode();
		return false; 
	});

	return tb.get(0);
}

lwRTE.prototype.create_panel = function(title, width) {
	var self = this;
	var tb = self.get_toolbar();

	if(!tb)
		return false;

	$('.rte-panel', tb).remove();
	var drag, event;
	var left = self.event.pageX;
	var top = self.event.pageY;
	
	var panel	= $('<div></div>').hide().addClass('rte-panel').css({left: left, top: top});
	$('<div></div>')
		.addClass('rte-panel-title')
		.html(title)
		.append($("<a class='close' href='#'>X</a>")
		.click( function() { panel.remove(); return false; }))
		.mousedown( function() { drag = true; return false; })
		.mouseup( function() { drag = false; return false; })
		.mousemove( 
			function(e) {
				if(drag && event) {
					left -= event.pageX - e.pageX;
					top -=  event.pageY - e.pageY;
					panel.css( {left: left, top: top} ); 
				}

				event = e;
				return false;
			} 
		)
		.appendTo(panel);

	if(width)
		panel.width(width);

	tb.append(panel);
	return panel;
}

lwRTE.prototype.get_content = function() {
	return (this.iframe) ? $('body', this.iframe_doc).html() : $(this.textarea).val();
}

lwRTE.prototype.set_content = function(content) {
	(this.iframe) ? $('body', this.iframe_doc).html(content) : $(this.textarea).val(content);
}

lwRTE.prototype.set_selected_controls = function(node, controls) {
	var toolbar = this.get_toolbar();

	if(!toolbar)
		return false;
		
	var key, i_node, obj, control, tag, i, value;

	try {
		for (key in controls) {
			control = controls[key];
			obj = $('.' + key, toolbar);

			obj.removeClass('active');

			if(!control.tags)
				continue;

			i_node = node;
			do {
				if(i_node.nodeType != 1)
					continue;

				tag	= i_node.nodeName.toLowerCase();
				if($.inArray(tag, control.tags) < 0 )
					continue;

				if(control.select) {
					obj = obj.get(0);
					if(obj.tagName.toUpperCase() == 'SELECT') {
						obj.selectedIndex = 0;

						for(i = 0; i < obj.options.length; i++) {
							value = obj.options[i].value;
							if(value && ((control.tag_cmp && control.tag_cmp(i_node, value)) || tag == value)) {
								obj.selectedIndex = i;
								break;
							}
						}
					}
				} else
					obj.addClass('active');
			}  while(i_node = i_node.parentNode)
		}
	} catch(e) {
	}
	
	return true;
}

lwRTE.prototype.get_selected_element = function () {
	var node, selection, range;
	var iframe_win	= this.iframe.contentWindow;
	
	if (iframe_win.getSelection) {
		try {
			selection = iframe_win.getSelection();
			range = selection.getRangeAt(0);
			node = range.commonAncestorContainer;
		} catch(e){
			return false;
		}
	} else {
		try {
			selection = iframe_win.document.selection;
			range = selection.createRange();
			node = range.parentElement();
		} catch (e) {
			return false;
		}
	}

	return node;
}

lwRTE.prototype.get_selection_range = function() {
	var rng	= null;
	var iframe_window = this.iframe.contentWindow;
	this.iframe.focus();
	
	if(iframe_window.getSelection) {
		rng = iframe_window.getSelection().getRangeAt(0);
		if($.browser.opera) { //v9.63 tested only
			var s = rng.startContainer;
			if(s.nodeType === Node.TEXT_NODE)
				rng.setStartBefore(s.parentNode);
		}
	} else {
		this.range.select(); //Restore selection, if IE lost focus.
		rng = this.iframe_doc.selection.createRange();
	}

	return rng;
}

lwRTE.prototype.get_selected_text = function() {
	var iframe_win = this.iframe.contentWindow;

	if(iframe_win.getSelection)	
		return iframe_win.getSelection().toString();

	this.range.select(); //Restore selection, if IE lost focus.
	return iframe_win.document.selection.createRange().text;
};

lwRTE.prototype.get_selected_html = function() {
	var html = null;
	var iframe_window = this.iframe.contentWindow;
	var rng	= this.get_selection_range();

	if(rng) {
		if(iframe_window.getSelection) {
			var e = document.createElement('div');
			e.appendChild(rng.cloneContents());
			html = e.innerHTML;		
		} else {
			html = rng.htmlText;
		}
	}

	return html;
};
	
lwRTE.prototype.selection_replace_with = function(html) {
	var rng	= this.get_selection_range();
	var iframe_window = this.iframe.contentWindow;

	if(!rng)
		return;
	
	this.editor_cmd('removeFormat'); // we must remove formating or we will get empty format tags!

	if(iframe_window.getSelection) {
		rng.deleteContents();
		rng.insertNode(rng.createContextualFragment(html));
		this.editor_cmd('delete');
	} else {
		this.editor_cmd('delete');
		rng.pasteHTML(html);
	}
}

/*
 * Lightweight RTE - jQuery Plugin, v1.2
 * Basic Toolbars
 * Copyright (c) 2009 Andrey Gayvoronsky - http://www.gayvoronsky.com
 */
var rte_tag		= '-rte-tmp-tag-';

var	rte_toolbar = {
	save      : {exec: submit_form},
	s1				: {separator: true},
	bold			: {command: 'bold', tags:['b', 'strong']},
	italic			: {command: 'italic', tags:['i', 'em']},
	strikeThrough	: {command: 'strikethrough', tags: ['s', 'strike'] },
	underline		: {command: 'underline', tags: ['u']},
	s2				: {separator: true },
	justifyLeft   	: {command: 'justifyleft'},
	justifyCenter	: {command: 'justifycenter'},
	justifyRight	: {command: 'justifyright'},
	justifyFull		: {command: 'justifyfull'},
	s3				: {separator : true},
	subscript		: {command: 'subscript', tags: ['sub']},
	superscript		: {command: 'superscript', tags: ['sup']},
	s4				: {separator : true },
	orderedList		: {command: 'insertorderedlist', tags: ['ol'] },
	unorderedList	: {command: 'insertunorderedlist', tags: ['ul'] },
	color			: {exec: lwrte_color}
};

/** submit the form **/
function submit_form(){
  var content = this.get_content();
	var _method = $('input[name="_method"]', '#lesson_note');
	var method = _method.length > 0 ? _method.val() : 'POST';	
	// console.log(method);
  var self = this;	
	$.ajax({
		type: method,
		url: $('#lesson_note').attr('action'),
		data: { 
			'note[content]': content,
			'note[title]': $("#note_title").val()
		},
    dataType: "json",
    beforeSend: function(xhr) {
			xhr.setRequestHeader("Accept", "text/javascript");
		},
		success: function(data) {	
			if (data['note']){
				$("#note_" + data['note']).replaceWith(data['content']);
				$('#note-editor').html('');
			} else {
				Boxy.get('#lesson_note').hide();
			}
			
			Message.notice(data['message']);
			var ref = $('.note-indicator.' + data['ref_id']);
			if (!ref.length) {
				ref = $(document.createElement('a')).attr({
					'class' : 'note-indicator ' +  data['ref_id'],
					'href' : '/notes?lesson_id=' + data['lesson_id'] + '&ref_id=' + data['ref_id']
				}).insertBefore('#' + data['ref_id']);
				ref.html('0');
			}
			var ref_count = parseInt(ref.text()) + 1;
			ref.text(ref_count);
		},
		error : function(XMLHttpRequest, textStatus, errorThrown) {
			Message.error(XMLHttpRequest.responseText);
		}
	});
}

/*** tag compare callbacks ***/
function lwrte_block_compare(node, tag) {
	tag = tag.replace(/<([^>]*)>/, '$1');
	return (tag.toLowerCase() == node.nodeName.toLowerCase());
}

/*** init callbacks ***/
function lwrte_style_init(rte) {
	var self = this;
	self.select = '<select><option value="">- no css -</option></select>';

	// load CSS info. javascript only issue is not working correctly, that's why ajax-php :(
	if(rte.css.length) {	
		$.ajax({
			url: "styles.php", 
			type: "POST",
			data: { css: rte.css[rte.css.length - 1] }, 
			async: false,
			success: function(data) {
				var list = data.split(',');
				var select = "";

				for(var name in list)
					select += '<option value="' + list[name] + '">' + list[name] + '</option>';
	
				self.select = '<select><option value="">- css -</option>' + select + '</select>';
			}});
	}
}

/*** exec callbacks ***/
function lwrte_style(args) {
	if(args) {
		try {
			var css = args.options[args.selectedIndex].value
			var self = this;
			var html = self.get_selected_text();
			html = '<span class="' + css + '">' + html + '</span>';
			self.selection_replace_with(html);
			args.selectedIndex = 0;
		} catch(e) {
		}
	}
}

function lwrte_color(){
	var self = this;
	
	var panel = self.create_panel('Set color for text', 385);
	var mouse_down = false;
	var mouse_over = false;
	panel.append('\
<div class="colorpicker1"><div class="rgb" id="rgb"></div></div>\
<div class="colorpicker1"><div class="gray" id="gray"></div></div>\
<div class="colorpicker2">\
	<div class="palette" id="palette"></div>\
	<div class="preview" id="preview"></div>\
	<div class="color" id="color"></div>\
</div>\
<div class="clear"></div>\
<p class="submit"><button id="ok">Ok</button><button id="cancel">Cancel</button></p>'
).show().css({top:'5px',left:'375px'});

	var preview = $('#preview', panel);
	var color = $("#color", panel);
	var palette = $("#palette", panel);
	var colors = [
		'#660000', '#990000', '#cc0000', '#ff0000', '#333333',
		'#006600', '#009900', '#00cc00', '#00ff00', '#666666',
		'#000066', '#000099', '#0000cc', '#0000ff', '#999999',
		'#909000', '#900090', '#009090', '#ffffff', '#cccccc',
		'#ffff00', '#ff00ff', '#00ffff', '#000000', '#eeeeee'
	];
			
	for(var i = 0; i < colors.length; i++)
		$("<div></div>").addClass("item").css('background', colors[i]).appendTo(palette);
			
	var height = $('#rgb').height();
	var part_width = $('#rgb').width() / 6;

	$('#rgb,#gray,#palette', panel)
		.mousedown( function(e) {mouse_down = true; return false; } )
		.mouseup( function(e) {mouse_down = false; return false; } )
		.mouseout( function(e) {mouse_over = false; return false; } )
		.mouseover( function(e) {mouse_over = true; return false; } );

	$('#rgb').mousemove( function(e) { if(mouse_down && mouse_over) compute_color(this, true, false, false, e); return false;} );
	$('#gray').mousemove( function(e) { if(mouse_down && mouse_over) compute_color(this, false, true, false, e); return false;} );
	$('#palette').mousemove( function(e) { if(mouse_down && mouse_over) compute_color(this, false, false, true, e); return false;} );
	$('#rgb').click( function(e) { compute_color(this, true, false, false, e); return false;} );
	$('#gray').click( function(e) { compute_color(this, false, true, false, e); return false;} );
	$('#palette').click( function(e) { compute_color(this, false, false, true, e); return false;} );

	$('#cancel', panel).click( function() { panel.remove(); return false; } );
	$('#ok', panel).click( 
		function() {
			var value = color.html();

			if(value.length > 0 && value.charAt(0) =='#') {
				if(self.iframe_doc.selection) //IE fix for lost focus
					self.range.select();

				self.editor_cmd('foreColor', value);
			}
					
			panel.remove(); 
			return false;
		}
	);

	function to_hex(n) {
		var s = "0123456789abcdef";
		return s.charAt(Math.floor(n / 16)) + s.charAt(n % 16);
	}			

	function get_abs_pos(element) {
		var r = { x: element.offsetLeft, y: element.offsetTop };

		if (element.offsetParent) {
			var tmp = get_abs_pos(element.offsetParent);
			r.x += tmp.x;
			r.y += tmp.y;
		}

		return r;
	};
			
	function get_xy(obj, event) {
		var x, y;
		event = event || window.event;
		var el = event.target || event.srcElement;

		// use absolute coordinates
		var pos = get_abs_pos(obj);

		// subtract distance to middle
		x = event.pageX  - pos.x;
		y = event.pageY - pos.y;

		return { x: x, y: y };
	}
			
	function compute_color(obj, is_rgb, is_gray, is_palette, e) {
		var r, g, b, c;

		var mouse = get_xy(obj, e);
		var x = mouse.x;
		var y = mouse.y;

		if(is_rgb) {
			r = (x >= 0)*(x < part_width)*255 + (x >= part_width)*(x < 2*part_width)*(2*255 - x * 255 / part_width) + (x >= 4*part_width)*(x < 5*part_width)*(-4*255 + x * 255 / part_width) + (x >= 5*part_width)*(x < 6*part_width)*255;
			g = (x >= 0)*(x < part_width)*(x * 255 / part_width) + (x >= part_width)*(x < 3*part_width)*255	+ (x >= 3*part_width)*(x < 4*part_width)*(4*255 - x * 255 / part_width);
			b = (x >= 2*part_width)*(x < 3*part_width)*(-2*255 + x * 255 / part_width) + (x >= 3*part_width)*(x < 5*part_width)*255 + (x >= 5*part_width)*(x < 6*part_width)*(6*255 - x * 255 / part_width);

			var k = (height - y) / height;

			r = 128 + (r - 128) * k;
			g = 128 + (g - 128) * k;
			b = 128 + (b - 128) * k;
		} else if (is_gray) {
			r = g = b = (height - y) * 1.7;
		} else if(is_palette) {
			x = Math.floor(x / 10);
			y = Math.floor(y / 10);
			c = colors[x + y * 5];
		}

		if(!is_palette)
			c = '#' + to_hex(r) + to_hex(g) + to_hex(b);

		preview.css('background', c);
		color.html(c);
	}
}

function lwrte_image() {
	var self = this;
	var panel = self.create_panel('Insert image', 385);
	panel.append('\
<p><label>URL</label><input type="text" id="url" size="30" value=""><button id="file">Upload</button><button id="view">View</button></p>\
<div class="clear"></div>\
<p class="submit"><button id="ok">Ok</button><button id="cancel">Cancel</button></p>'
).show();

	var url = $('#url', panel);
	var upload = $('#file', panel).upload( {
		autoSubmit: false,
		action: 'uploader.php',
		onSelect: function() {
			var file = this.filename();
			var ext = (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
			if(!(ext && /^(jpg|png|jpeg|gif)$/.test(ext))){
				alert('Invalid file extension');
				return;
			}

			this.submit();
		},
		onComplete: function(response) { 
			if(response.length <= 0)
				return;

			response	= eval("(" + response + ")");
			if(response.error && response.error.length > 0)
				alert(response.error);
			else
				url.val((response.file && response.file.length > 0) ? response.file : '');
		}
	});

	$('#view', panel).click( function() {
			(url.val().length >0 ) ? window.open(url.val()) : alert("Enter URL of image to view");
			return false;
		}
	);
			
	$('#cancel', panel).click( function() { panel.remove(); return false;} );
	$('#ok', panel).click( 
		function() {
			var file = url.val();
			self.editor_cmd('insertImage', file);
			panel.remove(); 
			return false;
		}
	)
}

function lwrte_unformat() {
	this.editor_cmd('removeFormat');
	this.editor_cmd('unlink');
}

function lwrte_clear() {
	if(confirm('Clear Document?')) 
		this.set_content('');
}

function lwrte_cleanup_word() {
	this.set_content(cleanup_word(this.get_content(), true, true, true)); 
	
	function cleanup_word(s, bIgnoreFont, bRemoveStyles, bCleanWordKeepsStructure) {
		s = s.replace(/<o:p>\s*<\/o:p>/g, '') ;
		s = s.replace(/<o:p>[\s\S]*?<\/o:p>/g, '&nbsp;') ;

		// Remove mso-xxx styles.
		s = s.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '' ) ;

		// Remove margin styles.
		s = s.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '' ) ;
		s = s.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"" ) ;

		s = s.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '' ) ;
		s = s.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"" ) ;

		s = s.replace( /\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"" ) ;

		s = s.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"" ) ;

		s = s.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" ) ;

		s = s.replace( /\s*tab-stops:[^;"]*;?/gi, '' ) ;
		s = s.replace( /\s*tab-stops:[^"]*/gi, '' ) ;

		// Remove FONT face attributes.
		if (bIgnoreFont) {
			s = s.replace( /\s*face="[^"]*"/gi, '' ) ;
			s = s.replace( /\s*face=[^ >]*/gi, '' ) ;

			s = s.replace( /\s*FONT-FAMILY:[^;"]*;?/gi, '' ) ;
		}

		// Remove Class attributes
		s = s.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3") ;

		// Remove styles.
		if (bRemoveStyles)
			s = s.replace( /<(\w[^>]*) style="([^\"]*)"([^>]*)/gi, "<$1$3" ) ;

		// Remove style, meta and link tags
		s = s.replace( /<STYLE[^>]*>[\s\S]*?<\/STYLE[^>]*>/gi, '' ) ;
		s = s.replace( /<(?:META|LINK)[^>]*>\s*/gi, '' ) ;

		// Remove empty styles.
		s =  s.replace( /\s*style="\s*"/gi, '' ) ;

		s = s.replace( /<SPAN\s*[^>]*>\s*&nbsp;\s*<\/SPAN>/gi, '&nbsp;' ) ;

		s = s.replace( /<SPAN\s*[^>]*><\/SPAN>/gi, '' ) ;

		// Remove Lang attributes
		s = s.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3") ;

		s = s.replace( /<SPAN\s*>([\s\S]*?)<\/SPAN>/gi, '$1' ) ;

		s = s.replace( /<FONT\s*>([\s\S]*?)<\/FONT>/gi, '$1' ) ;

		// Remove XML elements and declarations
		s = s.replace(/<\\?\?xml[^>]*>/gi, '' ) ;

		// Remove w: tags with contents.
		s = s.replace( /<w:[^>]*>[\s\S]*?<\/w:[^>]*>/gi, '' ) ;

		// Remove Tags with XML namespace declarations: <o:p><\/o:p>
		s = s.replace(/<\/?\w+:[^>]*>/gi, '' ) ;

		// Remove comments [SF BUG-1481861].
		s = s.replace(/<\!--[\s\S]*?-->/g, '' ) ;

		s = s.replace( /<(U|I|STRIKE)>&nbsp;<\/\1>/g, '&nbsp;' ) ;

		s = s.replace( /<H\d>\s*<\/H\d>/gi, '' ) ;

		// Remove "display:none" tags.
		s = s.replace( /<(\w+)[^>]*\sstyle="[^"]*DISPLAY\s?:\s?none[\s\S]*?<\/\1>/ig, '' ) ;

		// Remove language tags
		s = s.replace( /<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3") ;

		// Remove onmouseover and onmouseout events (from MS Word comments effect)
		s = s.replace( /<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3") ;
		s = s.replace( /<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3") ;

		if (bCleanWordKeepsStructure) {
			// The original <Hn> tag send from Word is something like this: <Hn style="margin-top:0px;margin-bottom:0px">
			s = s.replace( /<H(\d)([^>]*)>/gi, '<h$1>' ) ;

			// Word likes to insert extra <font> tags, when using MSIE. (Wierd).
			s = s.replace( /<(H\d)><FONT[^>]*>([\s\S]*?)<\/FONT><\/\1>/gi, '<$1>$2<\/$1>' );
			s = s.replace( /<(H\d)><EM>([\s\S]*?)<\/EM><\/\1>/gi, '<$1>$2<\/$1>' );
		} else {
			s = s.replace( /<H1([^>]*)>/gi, '<div$1><b><font size="6">' ) ;
			s = s.replace( /<H2([^>]*)>/gi, '<div$1><b><font size="5">' ) ;
			s = s.replace( /<H3([^>]*)>/gi, '<div$1><b><font size="4">' ) ;
			s = s.replace( /<H4([^>]*)>/gi, '<div$1><b><font size="3">' ) ;
			s = s.replace( /<H5([^>]*)>/gi, '<div$1><b><font size="2">' ) ;
			s = s.replace( /<H6([^>]*)>/gi, '<div$1><b><font size="1">' ) ;

			s = s.replace( /<\/H\d>/gi, '<\/font><\/b><\/div>' ) ;

			// Transform <P> to <DIV>
			var re = new RegExp( '(<P)([^>]*>[\\s\\S]*?)(<\/P>)', 'gi' ) ;	// Different because of a IE 5.0 error
			s = s.replace( re, '<div$2<\/div>' ) ;

			// Remove empty tags (three times, just to be sure).
			// This also removes any empty anchor
			s = s.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
			s = s.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
			s = s.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
		}

		return s;
	}
}

function lwrte_link() {
	var self = this;
	var panel = self.create_panel("Create link / Attach file", 385);

	panel.append('\
<p><label>URL</label><input type="text" id="url" size="30" value=""><button id="file">Attach File</button><button id="view">View</button></p>\
<div class="clear"></div>\
<p><label>Title</label><input type="text" id="title" size="30" value=""><label>Target</label><select id="target"><option value="">default</option><option value="_blank">new</option></select></p>\
<div class="clear"></div>\
<p class="submit"><button id="ok">Ok</button><button id="cancel">Cancel</button></p>'
).show();

	$('#cancel', panel).click( function() { panel.remove(); return false; } );

	var url = $('#url', panel);
	var upload = $('#file', panel).upload( {
		autoSubmit: true,
		action: 'uploader.php',
		onComplete: function(response) { 
			if(response.length <= 0)
				return;

			response	= eval("(" + response + ")");

			if(response.error && response.error.length > 0)
				alert(response.error);
			else
				url.val((response.file && response.file.length > 0) ? response.file : '');
		}
	});

	$('#view', panel).click( function() {
		(url.val().length >0 ) ? window.open(url.val()) : alert("Enter URL to view");
		return false;
	}
	);

	$('#ok', panel).click( 
		function() {
			var url = $('#url', panel).val();
			var target = $('#target', panel).val();
			var title = $('#title', panel).val();

			if(self.get_selected_text().length <= 0) {
				alert('Select the text you wish to link!');
				return false;
			}

			panel.remove(); 

			if(url.length <= 0)
				return false;

			self.editor_cmd('unlink');

			// we wanna well-formed linkage (<p>,<h1> and other block types can't be inside of link due to WC3)
			self.editor_cmd('createLink', rte_tag);
			var tmp = $('<span></span>').append(self.get_selected_html());

			if(target.length > 0)
				$('a[href*="' + rte_tag + '"]', tmp).attr('target', target);

			if(title.length > 0)
				$('a[href*="' + rte_tag + '"]', tmp).attr('title', title);

			$('a[href*="' + rte_tag + '"]', tmp).attr('href', url);
				
			self.selection_replace_with(tmp.html());
			return false;
		}
	)
}


// jQuery Alert Dialogs Plugin
//
// Version 1.1
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 14 May 2009
//
// Visit http://abeautifulsite.net/notebook/87 for more information
//
// Usage:
//		jAlert( message, [title, callback] )
//		jConfirm( message, [title, callback] )
//		jPrompt( message, [value, title, callback] )
// 
// History:
//
//		1.00 - Released (29 December 2008)
//
//		1.01 - Fixed bug where unbinding would destroy all resize events
//
// License:
// 
// This plugin is dual-licensed under the GNU General Public License and the MIT License and
// is copyright 2008 A Beautiful Site, LLC. 
//
(function($) {
	
	$.alerts = {
		
		// These properties can be read/written by accessing $.alerts.propertyName from your scripts at any time
		
		verticalOffset: -75,                // vertical offset of the dialog from center screen, in pixels
		horizontalOffset: 0,                // horizontal offset of the dialog from center screen, in pixels/
		repositionOnResize: true,           // re-centers the dialog on window resize
		overlayOpacity: .01,                // transparency level of overlay
		overlayColor: '#FFF',               // base color of overlay
		draggable: true,                    // make the dialogs draggable (requires UI Draggables plugin)
		okButton: '&nbsp;OK&nbsp;',         // text for the OK button
		cancelButton: '&nbsp;Cancel&nbsp;', // text for the Cancel button
		dialogClass: null,                  // if specified, this class will be applied to all dialogs
		
		// Public methods
		
		alert: function(message, title, callback) {
			if( title == null ) title = 'Alert';
			$.alerts._show(title, message, null, 'alert', function(result) {
				if( callback ) callback(result);
			});
		},
		
		confirm: function(message, title, callback) {
			if( title == null ) title = 'Confirm';
			$.alerts._show(title, message, null, 'confirm', function(result) {
				if( callback ) callback(result);
			});
		},
			
		prompt: function(message, value, title, callback) {
			if( title == null ) title = 'Prompt';
			$.alerts._show(title, message, value, 'prompt', function(result) {
				if( callback ) callback(result);
			});
		},
		
		// Private methods
		
		_show: function(title, msg, value, type, callback) {
			
			$.alerts._hide();
			$.alerts._overlay('show');
			
			$("BODY").append(
			  '<div id="popup_container">' +
			    '<h1 id="popup_title"></h1>' +
			    '<div id="popup_content">' +
			      '<div id="popup_message"></div>' +
				'</div>' +
			  '</div>');
			
			if( $.alerts.dialogClass ) $("#popup_container").addClass($.alerts.dialogClass);
			
			// IE6 Fix
			var pos = ($.browser.msie && parseInt($.browser.version) <= 6 ) ? 'absolute' : 'fixed'; 
			
			$("#popup_container").css({
				position: pos,
				zIndex: 99999,
				padding: 0,
				margin: 0
			});
			
			$("#popup_title").text(title);
			$("#popup_content").addClass(type);
			$("#popup_message").text(msg);
			$("#popup_message").html( $("#popup_message").text().replace(/\n/g, '<br />') );
			
			$("#popup_container").css({
				minWidth: $("#popup_container").outerWidth(),
				maxWidth: $("#popup_container").outerWidth()
			});
			
			$.alerts._reposition();
			$.alerts._maintainPosition(true);
			
			switch( type ) {
				case 'alert':
					$("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /></div>');
					$("#popup_ok").click( function() {
						$.alerts._hide();
						callback(true);
					});
					$("#popup_ok").focus().keypress( function(e) {
						if( e.keyCode == 13 || e.keyCode == 27 ) $("#popup_ok").trigger('click');
					});
				break;
				case 'confirm':
					$("#popup_message").after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');
					$("#popup_ok").click( function() {
						$.alerts._hide();
						if( callback ) callback(true);
					});
					$("#popup_cancel").click( function() {
						$.alerts._hide();
						if( callback ) callback(false);
					});
					$("#popup_ok").focus();
					$("#popup_ok, #popup_cancel").keypress( function(e) {
						if( e.keyCode == 13 ) $("#popup_ok").trigger('click');
						if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');
					});
				break;
				case 'prompt':
					$("#popup_message").append('<br /><input type="text" size="30" id="popup_prompt" />').after('<div id="popup_panel"><input type="button" value="' + $.alerts.okButton + '" id="popup_ok" /> <input type="button" value="' + $.alerts.cancelButton + '" id="popup_cancel" /></div>');
					$("#popup_prompt").width( $("#popup_message").width() );
					$("#popup_ok").click( function() {
						var val = $("#popup_prompt").val();
						$.alerts._hide();
						if( callback ) callback( val );
					});
					$("#popup_cancel").click( function() {
						$.alerts._hide();
						if( callback ) callback( null );
					});
					$("#popup_prompt, #popup_ok, #popup_cancel").keypress( function(e) {
						if( e.keyCode == 13 ) $("#popup_ok").trigger('click');
						if( e.keyCode == 27 ) $("#popup_cancel").trigger('click');
					});
					if( value ) $("#popup_prompt").val(value);
					$("#popup_prompt").focus().select();
				break;
			}
			
			// Make draggable
			if( $.alerts.draggable ) {
				try {
					$("#popup_container").draggable({ handle: $("#popup_title") });
					$("#popup_title").css({ cursor: 'move' });
				} catch(e) { /* requires jQuery UI draggables */ }
			}
		},
		
		_hide: function() {
			$("#popup_container").remove();
			$.alerts._overlay('hide');
			$.alerts._maintainPosition(false);
		},
		
		_overlay: function(status) {
			switch( status ) {
				case 'show':
					$.alerts._overlay('hide');
					$("BODY").append('<div id="popup_overlay"></div>');
					$("#popup_overlay").css({
						position: 'absolute',
						zIndex: 99998,
						top: '0px',
						left: '0px',
						width: '100%',
						height: $(document).height(),
						background: $.alerts.overlayColor,
						opacity: $.alerts.overlayOpacity
					});
				break;
				case 'hide':
					$("#popup_overlay").remove();
				break;
			}
		},
		
		_reposition: function() {
			var top = (($(window).height() / 2) - ($("#popup_container").outerHeight() / 2)) + $.alerts.verticalOffset;
			var left = (($(window).width() / 2) - ($("#popup_container").outerWidth() / 2)) + $.alerts.horizontalOffset;
			if( top < 0 ) top = 0;
			if( left < 0 ) left = 0;
			
			// IE6 fix
			if( $.browser.msie && parseInt($.browser.version) <= 6 ) top = top + $(window).scrollTop();
			
			$("#popup_container").css({
				top: top + 'px',
				left: left + 'px'
			});
			$("#popup_overlay").height( $(document).height() );
		},
		
		_maintainPosition: function(status) {
			if( $.alerts.repositionOnResize ) {
				switch(status) {
					case true:
						$(window).bind('resize', $.alerts._reposition);
					break;
					case false:
						$(window).unbind('resize', $.alerts._reposition);
					break;
				}
			}
		}
		
	}
	
	// Shortuct functions
	jAlert = function(message, title, callback) {
		$.alerts.alert(message, title, callback);
	}
	
	jConfirm = function(message, title, callback) {
		$.alerts.confirm(message, title, callback);
	};
		
	jPrompt = function(message, value, title, callback) {
		$.alerts.prompt(message, value, title, callback);
	};
	
})(jQuery);

/**
 * jQuery.ScrollTo
 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
 * Dual licensed under MIT and GPL.
 * Date: 5/25/2009
 *
 * @projectDescription Easy element scrolling using jQuery.
 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
 * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
 *
 * @author Ariel Flesler
 * @version 1.4.2
 *
 * @id jQuery.scrollTo
 * @id jQuery.fn.scrollTo
 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
 *	  The different options for target are:
 *		- A number position (will be applied to all axes).
 *		- A string position ('44', '100px', '+=90', etc ) will be applied to all axes
 *		- A jQuery/DOM element ( logically, child of the element to scroll )
 *		- A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
 *		- A hash { top:x, left:y }, x and y can be any kind of number/string like above.
*		- A percentage of the container's dimension/s, for example: 50% to go to the middle.
 *		- The string 'max' for go-to-end. 
 * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
 * @param {Object,Function} settings Optional set of settings or the onAfter callback.
 *	 @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
 *	 @option {Number} duration The OVERALL length of the animation.
 *	 @option {String} easing The easing method for the animation.
 *	 @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
 *	 @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
 *	 @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
 *	 @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
 *	 @option {Function} onAfter Function to be called after the scrolling ends. 
 *	 @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
 * @return {jQuery} Returns the same jQuery object, for chaining.
 *
 * @desc Scroll to a fixed position
 * @example $('div').scrollTo( 340 );
 *
 * @desc Scroll relatively to the actual position
 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
 *
 * @dec Scroll using a selector (relative to the scrolled element)
 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
 *
 * @ Scroll to a DOM element (same for jQuery object)
 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
 *			$('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
 *				alert('scrolled!!');																   
 *			}});
 *
 * @desc Scroll on both axes, to different values
 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
 */
;(function( $ ){
	
	var $scrollTo = $.scrollTo = function( target, duration, settings ){
		$(window).scrollTo( target, duration, settings );
	};

	$scrollTo.defaults = {
		axis:'xy',
		duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1
	};

	// Returns the element that needs to be animated to scroll the window.
	// Kept for backwards compatibility (specially for localScroll & serialScroll)
	$scrollTo.window = function( scope ){
		return $(window)._scrollable();
	};

	// Hack, hack, hack :)
	// Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
	$.fn._scrollable = function(){
		return this.map(function(){
			var elem = this,
				isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1;

				if( !isWin )
					return elem;

			var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem;
			
			return $.browser.safari || doc.compatMode == 'BackCompat' ?
				doc.body : 
				doc.documentElement;
		});
	};

	$.fn.scrollTo = function( target, duration, settings ){
		if( typeof duration == 'object' ){
			settings = duration;
			duration = 0;
		}
		if( typeof settings == 'function' )
			settings = { onAfter:settings };
			
		if( target == 'max' )
			target = 9e9;
			
		settings = $.extend( {}, $scrollTo.defaults, settings );
		// Speed is still recognized for backwards compatibility
		duration = duration || settings.speed || settings.duration;
		// Make sure the settings are given right
		settings.queue = settings.queue && settings.axis.length > 1;
		
		if( settings.queue )
			// Let's keep the overall duration
			duration /= 2;
		settings.offset = both( settings.offset );
		settings.over = both( settings.over );

		return this._scrollable().each(function(){
			var elem = this,
				$elem = $(elem),
				targ = target, toff, attr = {},
				win = $elem.is('html,body');

			switch( typeof targ ){
				// A number will pass the regex
				case 'number':
				case 'string':
					if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){
						targ = both( targ );
						// We are done
						break;
					}
					// Relative selector, no break!
					targ = $(targ,this);
				case 'object':
					// DOMElement / jQuery
					if( targ.is || targ.style )
						// Get the real position of the target 
						toff = (targ = $(targ)).offset();
			}
			$.each( settings.axis.split(''), function( i, axis ){
				var Pos	= axis == 'x' ? 'Left' : 'Top',
					pos = Pos.toLowerCase(),
					key = 'scroll' + Pos,
					old = elem[key],
					max = $scrollTo.max(elem, axis);

				if( toff ){// jQuery / DOMElement
					attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] );

					// If it's a dom element, reduce the margin
					if( settings.margin ){
						attr[key] -= parseInt(targ.css('margin'+Pos)) || 0;
						attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0;
					}
					
					attr[key] += settings.offset[pos] || 0;
					
					if( settings.over[pos] )
						// Scroll to a fraction of its width/height
						attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos];
				}else{ 
					var val = targ[pos];
					// Handle percentage values
					attr[key] = val.slice && val.slice(-1) == '%' ? 
						parseFloat(val) / 100 * max
						: val;
				}

				// Number or 'number'
				if( /^\d+$/.test(attr[key]) )
					// Check the limits
					attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max );

				// Queueing axes
				if( !i && settings.queue ){
					// Don't waste time animating, if there's no need.
					if( old != attr[key] )
						// Intermediate animation
						animate( settings.onAfterFirst );
					// Don't animate this axis again in the next iteration.
					delete attr[key];
				}
			});

			animate( settings.onAfter );			

			function animate( callback ){
				$elem.animate( attr, duration, settings.easing, callback && function(){
					callback.call(this, target, settings);
				});
			};

		}).end();
	};
	
	// Max scrolling position, works on quirks mode
	// It only fails (not too badly) on IE, quirks mode.
	$scrollTo.max = function( elem, axis ){
		var Dim = axis == 'x' ? 'Width' : 'Height',
			scroll = 'scroll'+Dim;
		
		if( !$(elem).is('html,body') )
			return elem[scroll] - $(elem)[Dim.toLowerCase()]();
		
		var size = 'client' + Dim,
			html = elem.ownerDocument.documentElement,
			body = elem.ownerDocument.body;

		return Math.max( html[scroll], body[scroll] ) 
			 - Math.min( html[size]  , body[size]   );
			
	};

	function both( val ){
		return typeof val == 'object' ? val : { top:val, left:val };
	};

})( jQuery );

/*
* jQuery Simply Countable plugin
* Provides a character counter for any text input or textarea
* 
* @version  0.2
* @homepage http://github.com/aaronrussell/jquery-simply-countable/
* @author   Aaron Russell (http://www.aaronrussell.co.uk)
*
* Copyright (c) 2009 Aaron Russell (aaron@gc4.co.uk)
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*/

(function($){

  $.fn.simplyCountable = function(options){
    
    options = $.extend({
      counter: '#counter',
      countType: 'characters',
      maxCount: 140,
      countDirection: 'down',
      safeClass: 'safe',
      overClass: 'over'
    }, options);
    
    var countable = this;
    
    var countCheck = function(){
           
      if (options.countType === 'words'){
        var count = options.maxCount - countable.val().split(/[\s]+/).length;
        if (countable.val() === '') count += 1;
      }
      else var count = options.maxCount - countable.val().length;
      
      if (!$(options.counter).hasClass(options.safeClass) && !$(options.counter).hasClass(options.overClass)){
        if (count < 0) $(options.counter).addClass(options.overClass);
        else $(options.counter).addClass(options.safeClass);
      }
      else if (count < 0 && $(options.counter).hasClass(options.safeClass)){
        $(options.counter).removeClass(options.safeClass).addClass(options.overClass);
      }
      else if (count >= 0 && $(options.counter).hasClass(options.overClass)){
        $(options.counter).removeClass(options.overClass).addClass(options.safeClass);
      }
      
      if (options.countDirection === 'up'){
        count = count - (count*2) + options.maxCount;
      }
      
      $(options.counter).text(count);
    };
    countCheck();
    
    countable.keyup(countCheck);
    
  };

})(jQuery);

/**
* @author Remy Sharp
* @url http://remysharp.com/2007/01/25/jquery-tutorial-text-box-hints/
*/

(function ($) {

$.fn.hint = function (blurClass) {
  if (!blurClass) { 
    blurClass = 'blur';
  }
    
  return this.each(function () {
    // get jQuery version of 'this'
    var $input = $(this),
    
    // capture the rest of the variable to allow for reuse
      title = $input.attr('title'),
      $form = $(this.form),
      $win = $(window);

    function remove() {
      if ($input.val() === title && $input.hasClass(blurClass)) {
        $input.val('').removeClass(blurClass);
      }
    }

    // only apply logic if the element has the attribute
    if (title) { 
      // on blur, set value to title attr if text is blank
      $input.blur(function () {
        if (this.value === '') {
          $input.val(title).addClass(blurClass);
        }
      }).focus(remove).blur(); // now change all inputs to title
      
      // clear the pre-defined text when form is submitted
      $form.submit(remove);
      $win.unload(remove); // handles Firefox's autocomplete
    }
  });
};

})(jQuery);

/**
 * Boxy 0.1.4 - Facebook-style dialog, with frills
 *
 * (c) 2008 Jason Frame
 * Licensed under the MIT License (LICENSE)
 */
 
/*
 * jQuery plugin
 *
 * Options:
 *   message: confirmation message for form submit hook (default: "Please confirm:")
 * 
 * Any other options - e.g. 'clone' - will be passed onto the boxy constructor (or
 * Boxy.load for AJAX operations)
 */
jQuery.fn.boxy = function(options) {
    options = options || {};
    return this.each(function() {      
        var node = this.nodeName.toLowerCase(), self = this;
        if (node == 'a') {
            jQuery(this).click(function() {
                var active = Boxy.linkedTo(this),
                    href = this.getAttribute('href'),
                    localOptions = jQuery.extend({actuator: this, title: this.title}, options);
                    
                if (active) {
                    active.show();
                } else if (href.indexOf('#') >= 0) {
                    var content = jQuery(href.substr(href.indexOf('#'))),
                        newContent = content.clone(true);
                    content.remove();
                    localOptions.unloadOnHide = false;
                    new Boxy(newContent, localOptions);
                } else { // fall back to AJAX; could do with a same-origin check
                    if (!localOptions.cache) localOptions.unloadOnHide = true;
                    Boxy.load(this.href, localOptions);
                }
                
                return false;
            });
        } else if (node == 'form') {
            jQuery(this).bind('submit.boxy', function() {
                Boxy.confirm(options.message || 'Please confirm:', function() {
                    jQuery(self).unbind('submit.boxy').submit();
                });
                return false;
            });
        }
    });
};

//
// Boxy Class

function Boxy(element, options) {
    
    this.boxy = jQuery(Boxy.WRAPPER);
    jQuery.data(this.boxy[0], 'boxy', this);
    
    this.visible = false;
    this.options = jQuery.extend({}, Boxy.DEFAULTS, options || {});
    
    if (this.options.modal) {
        this.options = jQuery.extend(this.options, {center: true, draggable: false});
    }
    
    // options.actuator == DOM element that opened this boxy
    // association will be automatically deleted when this boxy is remove()d
    if (this.options.actuator) {
        jQuery.data(this.options.actuator, 'active.boxy', this);
    }
    
    this.setContent(element || "<div></div>");
    this._setupTitleBar();
    
    this.boxy.css('display', 'none').appendTo(document.body);
    this.toTop();

    if (this.options.fixed) {
        if (jQuery.browser.msie && jQuery.browser.version < 7) {
            this.options.fixed = false; // IE6 doesn't support fixed positioning
        } else {
            this.boxy.addClass('fixed');
        }
    }
    
    if (this.options.center && Boxy._u(this.options.x, this.options.y)) {
        this.center();
    } else {
        this.moveTo(
            Boxy._u(this.options.x) ? this.options.x : Boxy.DEFAULT_X,
            Boxy._u(this.options.y) ? this.options.y : Boxy.DEFAULT_Y
        );
    }
    
    if (this.options.show) this.show();

};

Boxy.EF = function() {};

jQuery.extend(Boxy, {
    
    WRAPPER:    "<table cellspacing='0' cellpadding='0' border='0' class='boxy-wrapper'>" +
                "<tr><td class='top-left'></td><td class='top'></td><td class='top-right'></td></tr>" +
                "<tr><td class='left'></td><td class='boxy-inner'></td><td class='right'></td></tr>" +
                "<tr><td class='bottom-left'></td><td class='bottom'></td><td class='bottom-right'></td></tr>" +
                "</table>",
    
    DEFAULTS: {
        title:                  null,           // titlebar text. titlebar will not be visible if not set.
        closeable:              true,           // display close link in titlebar?
        draggable:              true,           // can this dialog be dragged?
        clone:                  false,          // clone content prior to insertion into dialog?
        actuator:               null,           // element which opened this dialog
        center:                 true,           // center dialog in viewport?
        show:                   true,           // show dialog immediately?
        modal:                  false,          // make dialog modal?
        fixed:                  true,           // use fixed positioning, if supported? absolute positioning used otherwise
        closeText:              '[close]',      // text to use for default close link
        unloadOnHide:           false,          // should this dialog be removed from the DOM after being hidden?
        clickToFront:           false,          // bring dialog to foreground on any click (not just titlebar)?
        behaviours:             Boxy.EF,        // function used to apply behaviours to all content embedded in dialog.
        afterDrop:              Boxy.EF,        // callback fired after dialog is dropped. executes in context of Boxy instance.
        afterShow:              Boxy.EF,        // callback fired after dialog becomes visible. executes in context of Boxy instance.
        afterHide:              Boxy.EF,        // callback fired after dialog is hidden. executed in context of Boxy instance.
        beforeUnload:           Boxy.EF         // callback fired after dialog is unloaded. executed in context of Boxy instance.
    },
    
    DEFAULT_X:          50,
    DEFAULT_Y:          50,
    zIndex:             1337,
    dragConfigured:     false, // only set up one drag handler for all boxys
    resizeConfigured:   false,
    dragging:           null,
    
    // load a URL and display in boxy
    // url - url to load
    // options keys (any not listed below are passed to boxy constructor)
    //   type: HTTP method, default: GET
    //   cache: cache retrieved content? default: false
    //   filter: jQuery selector used to filter remote content
    load: function(url, options) {
        
        options = options || {};
        
        var ajax = {
            url: url, type: 'GET', cache: false, success: function(html) {
                html = jQuery(html);
                if (options.filter) html = jQuery(options.filter, html);
                new Boxy(html, options);
            }
        };
        
        jQuery.each(['type', 'cache'], function() {
            if (this in options) {
                ajax[this] = options[this];
                delete options[this];
            }
        });
        
        jQuery.ajax(ajax);
        
    },
    
    // allows you to get a handle to the containing boxy instance of any element
    // e.g. <a href='#' onclick='alert(Boxy.get(this));'>inspect!</a>.
    // this returns the actual instance of the boxy 'class', not just a DOM element.
    // Boxy.get(this).hide() would be valid, for instance.
    get: function(ele) {
        var p = jQuery(ele).parents('.boxy-wrapper');
        return p.length ? jQuery.data(p[0], 'boxy') : null;
    },
    
    // returns the boxy instance which has been linked to a given element via the
    // 'actuator' constructor option.
    linkedTo: function(ele) {
        return jQuery.data(ele, 'active.boxy');
    },
    
    // displays an alert box with a given message, calling optional callback
    // after dismissal.
    alert: function(message, callback, options) {
        return Boxy.ask(message, ['OK'], callback, options);
    },
    
    // displays an alert box with a given message, calling after callback iff
    // user selects OK.
    confirm: function(message, after, options) {
        return Boxy.ask(message, ['OK', 'Cancel'], function(response) {
            if (response == 'OK') after();
        }, options);
    },
    
    // asks a question with multiple responses presented as buttons
    // selected item is returned to a callback method.
    // answers may be either an array or a hash. if it's an array, the
    // the callback will received the selected value. if it's a hash,
    // you'll get the corresponding key.
    ask: function(question, answers, callback, options) {
        
        options = jQuery.extend({modal: true, closeable: false},
                                options || {},
                                {show: true, unloadOnHide: true});
        
        var body = jQuery('<div></div>').append(jQuery('<div class="question"></div>').html(question));
        
        // ick
        var map = {}, answerStrings = [];
        if (answers instanceof Array) {
            for (var i = 0; i < answers.length; i++) {
                map[answers[i]] = answers[i];
                answerStrings.push(answers[i]);
            }
        } else {
            for (var k in answers) {
                map[answers[k]] = k;
                answerStrings.push(answers[k]);
            }
        }
        
        var buttons = jQuery('<form class="answers"></form>');
        buttons.html(jQuery.map(answerStrings, function(v) {
            return "<input type='button' value='" + v + "' />";
        }).join(' '));
        
        jQuery('input[type=button]', buttons).click(function() {
            var clicked = this;
            Boxy.get(this).hide(function() {
                if (callback) callback(map[clicked.value]);
            });
        });
        
        body.append(buttons);
        
        new Boxy(body, options);
        
    },
    
    // returns true if a modal boxy is visible, false otherwise
    isModalVisible: function() {
        return jQuery('.boxy-modal-blackout').length > 0;
    },
    
    _u: function() {
        for (var i = 0; i < arguments.length; i++)
            if (typeof arguments[i] != 'undefined') return false;
        return true;
    },
    
    _handleResize: function(evt) {
        var d = jQuery(document);
        jQuery('.boxy-modal-blackout').css('display', 'none').css({
            width: d.width(), height: d.height()
        }).css('display', 'block');
    },
    
    _handleDrag: function(evt) {
        var d;
        if (d = Boxy.dragging) {
            d[0].boxy.css({left: evt.pageX - d[1], top: evt.pageY - d[2]});
        }
    },
    
    _nextZ: function() {
        return Boxy.zIndex++;
    },
    
    _viewport: function() {
        var d = document.documentElement, b = document.body, w = window;
        return jQuery.extend(
            jQuery.browser.msie ?
                { left: b.scrollLeft || d.scrollLeft, top: b.scrollTop || d.scrollTop } :
                { left: w.pageXOffset, top: w.pageYOffset },
            !Boxy._u(w.innerWidth) ?
                { width: w.innerWidth, height: w.innerHeight } :
                (!Boxy._u(d) && !Boxy._u(d.clientWidth) && d.clientWidth != 0 ?
                    { width: d.clientWidth, height: d.clientHeight } :
                    { width: b.clientWidth, height: b.clientHeight }) );
    }

});

Boxy.prototype = {
    
    // Returns the size of this boxy instance without displaying it.
    // Do not use this method if boxy is already visible, use getSize() instead.
    estimateSize: function() {
        this.boxy.css({visibility: 'hidden', display: 'block'});
        var dims = this.getSize();
        this.boxy.css('display', 'none').css('visibility', 'visible');
        return dims;
    },
                
    // Returns the dimensions of the entire boxy dialog as [width,height]
    getSize: function() {
        return [this.boxy.width(), this.boxy.height()];
    },
    
    // Returns the dimensions of the content region as [width,height]
    getContentSize: function() {
        var c = this.getContent();
        return [c.width(), c.height()];
    },
    
    // Returns the position of this dialog as [x,y]
    getPosition: function() {
        var b = this.boxy[0];
        return [b.offsetLeft, b.offsetTop];
    },
    
    // Returns the center point of this dialog as [x,y]
    getCenter: function() {
        var p = this.getPosition();
        var s = this.getSize();
        return [Math.floor(p[0] + s[0] / 2), Math.floor(p[1] + s[1] / 2)];
    },
                
    // Returns a jQuery object wrapping the inner boxy region.
    // Not much reason to use this, you're probably more interested in getContent()
    getInner: function() {
        return jQuery('.boxy-inner', this.boxy);
    },
    
    // Returns a jQuery object wrapping the boxy content region.
    // This is the user-editable content area (i.e. excludes titlebar)
    getContent: function() {
        return jQuery('.boxy-content', this.boxy);
    },
    
    // Replace dialog content
    setContent: function(newContent) {
        newContent = jQuery(newContent).css({display: 'block'}).addClass('boxy-content');
        if (this.options.clone) newContent = newContent.clone(true);
        this.getContent().remove();
        this.getInner().append(newContent);
        this._setupDefaultBehaviours(newContent);
        this.options.behaviours.call(this, newContent);
        return this;
    },
    
    // Move this dialog to some position, funnily enough
    moveTo: function(x, y) {
        this.moveToX(x).moveToY(y);        
        return this;
    },
    
    // Move this dialog (x-coord only)
    moveToX: function(x) {
        if (typeof x == 'number') this.boxy.css({left: x});
        else this.centerX();
        return this;
    },
    
    // Move this dialog (y-coord only)
    moveToY: function(y) {
        if (typeof y == 'number') this.boxy.css({top: y});
        else this.centerY();
        return this;
    },
    
    // Move this dialog so that it is centered at (x,y)
    centerAt: function(x, y) {
        var s = this[this.visible ? 'getSize' : 'estimateSize']();
        if (typeof x == 'number') this.moveToX(x - s[0] / 2);
        if (typeof y == 'number') this.moveToY(y - s[1] / 2);
        return this;
    },
    
    centerAtX: function(x) {
        return this.centerAt(x, null);
    },
    
    centerAtY: function(y) {
        return this.centerAt(null, y);
    },
    
    // Center this dialog in the viewport
    // axis is optional, can be 'x', 'y'.
    center: function(axis) {
        var v = Boxy._viewport();
        var o = this.options.fixed ? [0, 0] : [v.left, v.top];
        if (!axis || axis == 'x') this.centerAt(o[0] + v.width / 2, null);
        if (!axis || axis == 'y') this.centerAt(null, o[1] + v.height / 2);
        return this;
    },
    
    // Center this dialog in the viewport (x-coord only)
    centerX: function() {
        return this.center('x');
    },
    
    // Center this dialog in the viewport (y-coord only)
    centerY: function() {
        return this.center('y');
    },
    
    // Resize the content region to a specific size
    resize: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        this.boxy.css({left: bounds[0], top: bounds[1]});
        this.getContent().css({width: bounds[2], height: bounds[3]});
        if (after) after(this);
        return this;
    },
    
    // Tween the content region to a specific size
    tween: function(width, height, after) {
        if (!this.visible) return;
        var bounds = this._getBoundsForResize(width, height);
        var self = this;
        this.boxy.stop().animate({left: bounds[0], top: bounds[1]});
        this.getContent().stop().animate({width: bounds[2], height: bounds[3]}, function() {
            if (after) after(self);
        });
        return this;
    },
    
    // Returns true if this dialog is visible, false otherwise
    isVisible: function() {
        return this.visible;    
    },
    
    // Make this boxy instance visible
    show: function() {
        if (this.visible) return;
        if (this.options.modal) {
            var self = this;
            if (!Boxy.resizeConfigured) {
                Boxy.resizeConfigured = true;
                jQuery(window).resize(function() { Boxy._handleResize(); });
            }
            this.modalBlackout = jQuery('<div class="boxy-modal-blackout"></div>')
                .css({zIndex: Boxy._nextZ(),
                      opacity: 0.7,
                      width: jQuery(document).width(),
                      height: jQuery(document).height()})
                .appendTo(document.body);
            this.toTop();
            if (this.options.closeable) {
                jQuery(document.body).bind('keypress.boxy', function(evt) {
                    var key = evt.which || evt.keyCode;
                    if (key == 27) {
                        self.hide();
                        jQuery(document.body).unbind('keypress.boxy');
                    }
                });
            }
        }
        this.boxy.stop().show();
        this.visible = true;
        this._fire('afterShow');
        return this;
    },
    
    // Hide this boxy instance
    hide: function(after) {
        if (!this.visible) return;
        var self = this;
        if (this.options.modal) {
            jQuery(document.body).unbind('keypress.boxy');
            this.modalBlackout.animate({opacity: 0}, function() {
                jQuery(this).remove();
            });
        }
        this.boxy.stop().animate({opacity: 0}, 300, function() {
            self.boxy.css({display: 'none'});
            self.visible = false;
            self._fire('afterHide');
            if (after) after(self);
            if (self.options.unloadOnHide) self.unload();
        });
        return this;
    },
    
    toggle: function() {
        this[this.visible ? 'hide' : 'show']();
        return this;
    },
    
    hideAndUnload: function(after) {
        this.options.unloadOnHide = true;
        this.hide(after);
        return this;
    },
    
    unload: function() {
        this._fire('beforeUnload');
        this.boxy.remove();
        if (this.options.actuator) {
            jQuery.data(this.options.actuator, 'active.boxy', false);
        }
    },
    
    // Move this dialog box above all other boxy instances
    toTop: function() {
        this.boxy.css({zIndex: Boxy._nextZ()});
        return this;
    },
    
    // Returns the title of this dialog
    getTitle: function() {
        return jQuery('> .title-bar h2', this.getInner()).html();
    },
    
    // Sets the title of this dialog
    setTitle: function(t) {
        jQuery('> .title-bar h2', this.getInner()).html(t);
        return this;
    },
    
    //
    // Don't touch these privates
    
    _getBoundsForResize: function(width, height) {
        var csize = this.getContentSize();
        var delta = [width - csize[0], height - csize[1]];
        var p = this.getPosition();
        return [Math.max(p[0] - delta[0] / 2, 0),
                Math.max(p[1] - delta[1] / 2, 0), width, height];
    },
    
    _setupTitleBar: function() {
        if (this.options.title) {
            var self = this;
            var tb = jQuery("<div class='title-bar'></div>").html("<h2>" + this.options.title + "</h2>");
            if (this.options.closeable) {
                tb.append(jQuery("<a href='#' class='close'></a>").html(this.options.closeText));
            }
            if (this.options.draggable) {
                tb[0].onselectstart = function() { return false; }
                tb[0].unselectable = 'on';
                tb[0].style.MozUserSelect = 'none';
                if (!Boxy.dragConfigured) {
                    jQuery(document).mousemove(Boxy._handleDrag);
                    Boxy.dragConfigured = true;
                }
                tb.mousedown(function(evt) {
                    self.toTop();
                    Boxy.dragging = [self, evt.pageX - self.boxy[0].offsetLeft, evt.pageY - self.boxy[0].offsetTop];
                    jQuery(this).addClass('dragging');
                }).mouseup(function() {
                    jQuery(this).removeClass('dragging');
                    Boxy.dragging = null;
                    self._fire('afterDrop');
                });
            }
            this.getInner().prepend(tb);
            this._setupDefaultBehaviours(tb);
        }
    },
    
    _setupDefaultBehaviours: function(root) {
        var self = this;
        if (this.options.clickToFront) {
            root.click(function() { self.toTop(); });
        }
        jQuery('.close', root).click(function() {
            self.hide();
            return false;
        }).mousedown(function(evt) { evt.stopPropagation(); });
    },
    
    _fire: function(event) {
        this.options[event].call(this);
    }
    
};



$('.blockHead .regionState').live('click', function(e) {
    e.preventDefault();
    $('.regionList').slideToggle('slow');
});

$('#hide_Rules>.hideBtn').live('click', function(e) {
    e.preventDefault();
    $('.testRules').slideToggle('slow');
});


$('.contentDiv').livequery(function(){
    var arr = new Array();
    $('.contentDiv').children().each(function(i, o){
        arr.push($(o).outerWidth());
    });
    var cont = Math.max.apply(Math, arr);
	
    if(cont >= 370) {
        $('.optionA,.optionB,.optionC,.optionD,.ansA,.ansB,.ansC,.ansD').css({
            'width':'auto',
            'float':'none'
        });
    };
});

$(document).ready(function() {
	

    $('#toggleTitle>h3').live('click', function(e){
        e.preventDefault();
        $(this).parent().siblings('div').slideToggle('slow');
    });
	
	
    $('.featureRequest_toogler').live('click',function(e){
        e.preventDefault();
        $('.feature_window').show();
    });
    $('.feature_closer').live('click',function(e){
        e.preventDefault();
        $(this).parent().hide();
    });
	
    // Toggle display of elements
    jQuery.fn.showHide = function(){
        var el = $(this);
        var label1 = arguments[0];
        var label2 = arguments[1];
        var cookie_name = arguments[2];
	    
        var targetElement = el.parent().prev();
        el.live('click', function(e){
            e.preventDefault();
            targetElement.slideToggle('slow', function(){
                var disp = targetElement.css('display'); //this variable holds the css property of jQuery wrapper.
                if(disp == 'none'){
                    el.children().html(label1);
                    if(cookie_name) {
                        $.cookie(cookie_name, 'hidden');
                    }
                }
                else{
                    el.children().html(label2);
                    if(cookie_name) {
                        $.cookie(cookie_name, 'visible');
                    }
                }
            });
        });
	    
        return el;
    };

    $('.hideBtn').live('click', function(){
        $(this).toggleClass("showBtn");
    });

    // SOLARO Says
    $('.solaroBtnHide').showHide('Show','Hide');

    // Account page
    $('#hide_accounts .hideBtn').showHide('Show','Hide');

    // Topic Browser
    $('#hide_topics .hideBtn').showHide('Show','Hide', 'nav_state');

    // Rewards Page hide and Show
    $('#hideTopicsPanelReward .hideBtn').showHide('Show','Hide');
	
    // Parent--> Children Hide and Show functionality
    $('#child_information_hide .hideBtn').showHide('Show Child Profile','Hide Child Profile');

    //$('#lnk_toggle_completed_goals').hide();
        
    // Widgets minimise function
    $('.minMax').live('click', function(e){
            
            
        e.preventDefault();
        $(this).parent().siblings('div').slideToggle('slow');
    });
	
    $('.minMax').live('click', function(){
        $(this).toggleClass("maximise");
    });
	
    // Scroll Top Effect
    $.fn.scrollToTop = function() {
        $(this).each(function() {
            var $this = $(this);
		
            $this.hide();
		
            if($(window).scrollTop()!="0") {
                $this.fadeIn("slow");
            }
		
            $(window).scroll(function() {
                if($(window).scrollTop()=="0") {
                    $this.fadeOut("slow");
                } else {
                    $this.fadeIn("slow");
                }
            });
        });
	    
        $(this).click(function(e) {
            $("html, body").animate({
                scrollTop:0
            }, "slow");
            e.preventDefault();
        });
    }
	
    // bind scrollTop to these elements
    $("#toTop, .toTop").scrollToTop();
	
    // Navigation Colors
    $('.red').addGlow({
        textColor: '#f9c72e',
        haloColor: '#f9c72e',
        radius: 5
    });
	
    // Global settings for the tooltip
    $.extend($.tooltip.defaults, {
		track: false,
		delay: 0,
		showURL: false,
		showBody: " - ",
		fixPNG: true,
		extraClass: "pretty fancy",
		opacity: 1
	});
	$(".toolTipInfo:not(.disabled)").livequery(function(){
		$(this).tooltip();
	});
	
	
    // registrationForm
    $("#studentTab").click(function(e){
        e.preventDefault();
        $(this).addClass("selectedTab");
        $("#parentTab").removeClass("selectedTab");
        $(".parentReg").slideUp("slow");
        $(".studentReg").slideDown("slow");
    });

    $("#parentTab").click(function(e){
        e.preventDefault();
        $(this).addClass("selectedTab");
        $("#studentTab").removeClass("selectedTab");
        $(".studentReg").slideUp("slow");
        $(".parentReg").slideDown("slow");
    });

    //forum
    $('.postedReply+.postedComments>.forArrow').livequery(function(){
        $(this).show();
    });
    $('.postedComments+.postedComments2>.forArrow').livequery(function(){
        $(this).show();
    });
	
});




// NEW TEST PAGE---USED TO CHANGE DEFAULT RADIO BUTTON//
var checkboxHeight = "25";
var radioHeight = "40";
var selectWidth = "190";
/* No need to change anything after this */
document.write('<style type="text/css">input.styled { display: none; } select.styled { position: relative; width: ' + selectWidth + 'px; opacity: 0; filter: alpha(opacity=0); z-index: 5; }</style>');
var Custom = {
    init: function() {
        var inputs = document.getElementsByTagName("input"), span = Array(), textnode, option, active;
        for(a = 0; a < inputs.length; a++) {
            if(inputs[a].type == "radio" && inputs[a].className == "styled") {
                // alert(inputs[a].checked);
                span[a] = document.createElement("span");
                span[a].className = inputs[a].type;
                span[a].id = "dSpan"+a;
                if (inputs[a].checked == true)
                {
			
                    span[a].style.backgroundPosition = "0 -80px";
                    inputs[a].parentNode.style.border = "1px solid #000";
                }

                if(inputs[a].checked == true) {
                    if(inputs[a].type == "checkbox") {
                        position = "0 -" + (radioHeight*2) + "px";
                        span[a].style.backgroundPosition = position;
                    }
                }




                inputs[a].parentNode.insertBefore(span[a], inputs[a]);
                inputs[a].onchange = Custom.clear;
                span[a].parentNode.onmousedown = Custom.pushed;
                span[a].parentNode.onmouseup = Custom.check;
                document.onmouseup = Custom.clear;



            }
        }
    },
    pushed: function() {
        element = this.getElementsByTagName('input')[0];
        sp = this.getElementsByTagName('span')[1];

        if(element.checked == true && element.type == "checkbox") {
            sp.style.backgroundPosition = "0 -" + checkboxHeight*3 + "px";
        } else if(element.checked == true && element.type == "radio") {
            sp.style.backgroundPosition = "0 -" + radioHeight*1 + "px";
        }
        else if(element.checked != true && element.type == "checkbox") {
            sp.style.backgroundPosition = "0 -" + checkboxHeight + "px";
        }
        else  {
            sp.style.backgroundPosition = "0 -" + radioHeight + "px";
        }
    },


    check: function() {
        element = this.getElementsByTagName('input')[0];
        sp = this.getElementsByTagName('span')[1];
        if(element.checked == true && element.type == "checkbox") {
            sp.style.backgroundPosition = "0 0";
            element.checked = false;
        } else {
            if(element.type == "checkbox") {
                sp.style.backgroundPosition = "0 -" + checkboxHeight*2 + "px";
            } else {
                sp.style.backgroundPosition = "0 -" + radioHeight*2 + "px";
                group = element.name;
                inputs = document.getElementsByTagName("input");
                for(a = 0; a < inputs.length; a++) {
                    if(inputs[a].name == group && inputs[a] != element) {
                        inputs[a].previousSibling.style.backgroundPosition = "0 0";
                    }
                }
            }
            element.checked = true;
        }
    },

    clear: function() {
        inputs = document.getElementsByTagName("input");
        for(var b = 0; b < inputs.length; b++) {

            if(inputs[b].type == "checkbox" && inputs[b].checked == true && inputs[b].className == "styled") {
                inputs[b].previousSibling.style.backgroundPosition = "0 -" + checkboxHeight*2 + "px";
            } else if(inputs[b].type == "checkbox" && inputs[b].className == "styled") {
                inputs[b].previousSibling.style.backgroundPosition = "0 0";
            } else if(inputs[b].type == "radio" && inputs[b].checked == true && inputs[b].className == "styled") {
                inputs[b].previousSibling.style.backgroundPosition = "0 -" + radioHeight*2 + "px";
                inputs[b].parentNode.style.border = "1px solid #000";
            } else if(inputs[b].type == "radio" && inputs[b].className == "styled") {
                inputs[b].previousSibling.style.backgroundPosition = "0 0";
                inputs[b].parentNode.style.border = "1px solid #BDE5EE";
            }
        }
    },
    choose: function() {
        option = this.getElementsByTagName("option");
        for(d = 0; d < option.length; d++) {
            if(option[d].selected == true) {
                document.getElementById("select" + this.name).childNodes[0].nodeValue = option[d].childNodes[0].nodeValue;
            }
        }
    }
}
window.onload = Custom.init;
//END OF NEW TEST PAGE //
function show_hide_completed_goals(){
    if($("div.leftWidgetHolder #lnk_toggle_completed_goals").css('display') == 'inline')
        $('div.leftWidgetHolder #lnk_toggle_completed_goals').hide();
    else
        $('div.leftWidgetHolder #lnk_toggle_completed_goals').show();
}

// jQuery ajax requests should land in format.js block.
//----------------------------------------------------------------------------
$.ajaxSetup({
    'beforeSend': function(xhr) {
        xhr.setRequestHeader("Accept", "");
        xhr.setRequestHeader("Accept", "text/javascript")
    }
});

// Sends authenticity token with ajax request if required.
//----------------------------------------------------------------------------
$(document).ajaxSend(function(event, request, settings) {
    if (typeof(AUTH_TOKEN) == "undefined") return;
    if (settings.type == 'GET') return; // Don't add anything to a get request let IE turn it into a POST.
    // settings.data is a serialized string like "foo=bar&baz=boink" (or null)
    settings.data = settings.data || "";
    if (settings.data.indexOf("authenticity_token") < 0) {
        settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
    }
});

// Global ajax activity indicators.
//----------------------------------------------------------------------------
$(document).ajaxStart(function(){
    $('#progress').animate({top: 0});
}).ajaxStop(function(){
    $('#progress').animate({top: '-30px'});
});

$(function(){
    // Hide the notification panel on click.
    //----------------------------------------------------------------------------
    $('#notifications').click(function(){
        $(this).animate({
            "top" : "-60"
        })
    });

    // Equivalent of rails link_to_remote helper.
    //----------------------------------------------------------------------------
    $("a.remote, div.remote a").livequery('click', function() {
        var type = 'GET';
        var send_request = true;

        if($(this).hasClass('update')) {
            type = 'PUT';
        }else if($(this).hasClass('delete')) {
            type = 'DELETE';
            ref = $(this).attr('rel');
            if($(this).hasClass('confirm')) {
                if(confirm('Are you sure you want to delete this' + ' ' + ref + '?') == false) {
                    send_request = false;
                }
            }
        }
        else if($(this).hasClass('view_child_test_report')) {
            $(".row_selected").removeClass("row_selected");
            $(this).parent().parent().addClass("row_selected");
        }

        if(send_request == true) {
            $.ajax({
                url: $(this).attr('href'),
                type: type,
                dataType: "script",
                data: {'_' : String(new Date().getTime())}, // To fix 'Error occurred while parsing request parameters' while using Google Chrome
                beforeSend: function(xhr) {
                    xhr.setRequestHeader("Accept", "text/javascript");
                }
            });
        }

        return false;
    });

    // Equivalent of rails link_to_remote helper.
    // Only to send requests using http delete.
    //----------------------------------------------------------------------------
    $("a.remoteDelete").livequery('click', function() {
        $.ajax({
            url: $(this).attr('href'),
            dataType: "script",
            type: "DELETE",
            data: {'_' : String(new Date().getTime())}, // To fix 'Error occurred while parsing request parameters' while using Google Chrome
            beforeSend: function(xhr) {
                xhr.setRequestHeader("Accept", "text/javascript");
            }
        });
        return false;
    });

    // To validate forms client-side and submit through ajax.
    //----------------------------------------------------------------------------
    $('form').livequery(function(){
        $(this).validate({
            meta: "validate",
            submitHandler : function(form) {
                var f = $(form);
                if (f.hasClass('deactivatePost')) {
                    f.find("input[type='submit']").attr('disabled', true);
                }
                if (f.hasClass('remote')) {
                    f.ajaxSubmit({
                        dataType: 'script'
                    });
                } else if(f.hasClass('facebook_remote')) {
                    f.ajaxSubmit({
                        dataType: 'script',
                        beforeSubmit: showSpinner,
                        success: hideSpinner
                    });
                } else {
                    form.submit();
                }
            },
            errorPlacement: function(error, element) {
                if (element.attr("id") == "date_of_birth" )
                    error.insertAfter($(".calendars-trigger")[0]);
                else
                    error.insertAfter(element);
            }

        });
    });

    $('#admin_system_message').livequery(function(){
        $(this).simplyCountable({
            counter: '#counter',
            countable: 'characters',
            maxCount: 150,
            countDirection: 'down',
            safeClass: 'safe',
            overClass: 'over'
        });
    });

    $('#flash_card_question_content').livequery(function(){
        $(function(){
            // find all the input elements with title attributes
            $('#flash_card_question_content').hint();
        });
        $(this).simplyCountable({
            counter: '#counter',
            countable: 'characters',
            maxCount: 255,
            countDirection: 'down',
            safeClass: 'safe',
            overClass: 'over'
        });
    });


    // Display a hint in text fields.
    //----------------------------------------------------------------------------
    $('input.hint').livequery(function(){
        $(this).hint();
    });

    function showSpinner() {
        $('#facebook_form_spinner').show();
    }


    function hideSpinner() {
        $('#facebook_form_spinner').hide();
    }

});

// Calendar widget in 'mysolaro' page.
//----------------------------------------------------------------------------
var Calendar = function() {
    return {
        init: function(url) {
            $.get(url, null, function(data){
                $('#daily-activity-widget').html(data);
            });
        }
    };
}();

$("#system_messge").click(function () {
    $("#system_message_widget").effect("highlight", {}, 3000);
});

$(document).ready(function(){
    // Show popup for discussion
    jQuery('a.PopDiscussDetail').popProfileDiscussion();
});

// Remove duplicates from Array
// http://www.martienus.com/code/javascript-remove-duplicates-from-array.html
//----------------------------------------------------------------------------
Array.prototype.unique = function () {
    var r = new Array();
    o:for(var i = 0, n = this.length; i < n; i++)
    {
        for(var x = 0, y = r.length; x < y; x++)
        {
            if(r[x]==this[i])
            {
                continue o;
            }
        }
        r[r.length] = this[i];
    }
    return r;
}

// Namespace for storing all utility functions
//----------------------------------------------------------------------------
if (typeof Util == 'undefined') {
    Util = {};
}

Util = {
    // Get the selected text - cross browser compatible
    //----------------------------------------------------------------------------
    getSelectedText : function() {
        if (window.getSelection) {
            return window.getSelection();
        }
        else if (document.getSelection) {
            return document.getSelection();
        }
        else {
            var selectedText = document.selection ? document.selection.createRange() : null;
            if (selectedText.text) {
                return selectedText.text;
            }
            return null;
        }
        return null;
    },

    // Get the selected html - cross browser compatible
    //----------------------------------------------------------------------------
    getSelectionHTML : function() {
        var userSelection;
        try {
            if (window.getSelection) {
                // W3C Ranges
                userSelection = window.getSelection();

                // Get the range:
                if (userSelection.getRangeAt)
                    var range = userSelection.getRangeAt(0);
                else {
                    var range = document.createRange();
                    range.setStart(userSelection.anchorNode, userSelection.anchorOffset);
                    range.setEnd(userSelection.focusNode, userSelection.focusOffset);
                }
                // And the HTML:
                var clonedSelection = range.cloneContents();
                var div = document.createElement('div');
                div.appendChild(clonedSelection);
                return div.innerHTML;
            } else if (document.selection) {
                // Explorer selection, return the HTML
                userSelection = document.selection.createRange();
                return userSelection.htmlText;
            } else {
                return '';
            }

        } catch(e) {}
    },

    clearSelection : function(){
        if (window.getSelection) {
            // Mozilla
            var selection = window.getSelection();
            if (selection.rangeCount > 0) {
                window.getSelection().removeAllRanges();
            }
        } else if (document.selection) {
            // Internet Explorer
            document.selection.empty();
        }
    }
}

jQuery.validator.setDefaults({
    onkeyup: function(element) {
        if (element.id == 'user_screen_name') {
            return false;
        }
        else if (element.name in this.submitted || element == this.lastElement) {
            this.element(element);
        }
    }
});

Tax = function() {
    return{
        calculate : function() {
            country = $("#billing_address_country").val();

            $.get("set_total_amount", {country_name: country, no_of_children: $(".child_to_be_selected:checked").size()},
                    function(data) {
                        eval(data);
                    }

                    );
            if (country == "Canada")
            {

                $("#gst_value").show();

            }
            else
            {
                $("#gst_value").hide();
            }
        }
    };
}();
jQuery(document).ready(function() {


    jQuery.validator.addMethod("screen_name_format", function(screen_name) {
        $('#screen-name-availability').html("")
        return (screen_name.match(/^[a-zA-Z0-9\.\-_@]*$/) != null);

    }, "only letters, numbers, and .-_@ please.");

    jQuery.validator.addMethod("screen_name_availability", function(screen_name) {
        is_available = $.ajax({

            url: '/users/check_screen_name_availability',
            data:'screen_name=' + screen_name,
            async:false,
            dataType:"text",
            success : function(response) {
                if (response == "true")
                {
                    $('#screen-name-availability').html(screen_name + " <strong style='color:#358F63;'>is available.</strong>").show();
                }
                else
                {

                    $('#screen-name-availability').html(screen_name + " <strong style='color:#FF5D4A;'>is not available.</strong>").show();
                }
                return response;
            }
        });
        return is_available.responseText == "true";
    }, "");


});


$('#billing_address_country').change(function() {
    Tax.calculate();

});
$($(".child_to_be_selected")).live('click', function() {
    Tax.calculate();

});


$('#user_user_type_student').live('click', function() {
    $(".studentReg").css("background", "transparent url(/images/solaroTeens/theme1/global/studentsPic.png) no-repeat scroll right bottom")
});
$('#user_user_type_parent').live('click', function() {
    $(".studentReg").css("background", "transparent url(/images/solaroTeens/theme1/global/parentsPic.png) no-repeat scroll right bottom")
});


/* app/views/practice_questions/_multiple_choice.html.haml */

$(function() {
    $('a.choice').click(function(e) {
        e.preventDefault();
        $.ajax({
            type: 'GET',
            url: $(this).attr('href'),
            dataType: "script"
        });

        $('#practice-question-solution-body').show();
    });
});


/* app/views/practice_questions/_short_answer.html.haml */

$(function() {
    $('#check_short_answer_practice_questions_form').submit(function(e) {
        e.preventDefault();
        $.ajax({
            type: 'POST',
            url : $(this).attr('action'),
            data : $(this).serialize(),
            dataType: "script"
        });

        $('#practice-question-solution-body').show();
    });
});

/* app/views/student_profiles/edit.html.haml  */

$('#profile_country_id').live('change', function() {
    if ($(this).val().length > 0) {
        $.get('/profiles/update_state', $('#profile_country_id').fieldSerialize(), function(data, textStatus) {
            $('#state-list').html(data);
        });
    }
});

$('#product_key_value').live('change', function() {

    $('#product_key').val(this.value);

});

Reward = function() {

    function add(gadget) {
        var data = {};
        var url = '/gadgets/' + gadget;
        $.ajax({
            type  : 'PUT',
            url    : url,
            data : data,
            dataType: 'script'
        });
    }

    return {
        addToGadget: function(event, ui) {
            source = $(this);
            target = $(ui.item).parent();
            gadget = ui.item.attr('id')

            if (target.attr('id') == "dropGadgetsToCart") {
                $('#dropGadgetsToCart').removeClass("emptyCart");
                $('#dropGadgetsToCart').addClass("fullCart");

                add(gadget);
            }
        }
    };

}();

Message = function() {
    return {
        notice : function(text) {
            $('#message').html(text);

            this.showNotice();
            this.hideNotice();
        },

        error : function(text) {
            $('#message').html(text);

            this.showErrorNotice();
            this.hideNotice();
        },

        showNotice : function() {
            $('#notifications #notificationIcon').removeClass("errorNotificationIcon");
            $('#notifications #notificationIcon').addClass("notificationIcon");
            $('#notifications').animate({
                "top" : "0"
            });
        },

        showErrorNotice : function() {
            $('#notifications #notificationIcon').removeClass("notificationIcon");
            $('#notifications #notificationIcon').addClass("errorNotificationIcon");
            $('#notifications').animate({
                "top" : "0"
            });
        },

        hideNotice : function() {
            var msg = $('#message').html();

            if (msg.length > 0) {
                setTimeout(function() {
                    $('#notifications').animate({
                        "top" : "-60"
                    });
                }, 10000);
            }
        }
    };
}();

NotePad = function() {
    return {
        toggle : function() {
            var flap_left = $(this).offset()['left'];
            var pad_left = $('#notePad').offset()['left'];

            Discussion.hide();
            Cloze.hide();
            Practice.hide();

            if (pad_left < 0) {
                $('#notePad').animate({
                    left: 0
                }, 'slow');
            } else {
                Discussion.show();
                Cloze.show();
                Practice.show();
                $('#notePad').animate({
                    left: -flap_left
                }, 'slow');
            }
        },

        reset : function() {
            var flap_left = $('.flap').offset()['left'];
            var flap_width = $('.flap').width();

            $('#notePad').animate({
                left: -flap_left - flap_width
            }, 'fast', null, function() {
                $('#notePad').css('visibility', 'visible').animate({
                    left: -flap_left
                }, 'slow');
            });
        },

        show : function() {
            $('#notePad').fadeIn('slow');
        },

        hide : function() {
            $('#notePad').fadeOut('slow');
        }
    };
}();

$(function() {
    $('.flap').live('click', NotePad.toggle);
    NotePad.reset();
});


Discussion = function() {
    return {
        toggle : function() {
            var flap_left = $(this).offset()['left'];
            var pad_left = $('#discussionPad').offset()['left'];
            if (pad_left < 0) {

                Cloze.hide();
                NotePad.hide();
                Practice.hide();

                $('.discussionFlap').animate({
                    top: '30px'
                }, 'slow', null, function() {
                    $('#discussionPad').animate({
                        left: 0
                    }, 'slow');
                });
            } else {
                $('#discussionPad').animate({
                    left: -flap_left
                }, 'slow', null, function() {
                    $('.discussionFlap').animate({
                        top: '130px'
                    }, 'slow');
                    Cloze.show();
                    NotePad.show();
                    Practice.show();
                });
            }
        },

        reset : function() {
            var flap_left = $('.discussionFlap').offset()['left'];
            var flap_width = $('.discussionFlap').width();

            $('#discussionPad').animate({
                left: -flap_left - flap_width
            }, 'fast', null, function() {
                $('#discussionPad').css('visibility', 'visible').animate({
                    left: -flap_left
                }, 'slow');
            });
        },

        show : function() {
            $('#discussionPad').fadeIn('slow');
        },

        hide : function() {
            $('#discussionPad').fadeOut('slow');
        }
    };
}();

$(function() {
    $('.discussionFlap').live('click', Discussion.toggle);
    Discussion.reset();
});


jQuery.fn.toggleWithAjax = function() {
    this.click(function() {
        jQuery.post(jQuery(this).attr('href'), '_method=put', null, "script");
        return false;
    })
};

jQuery.fn.popProfileDiscussion = function() {
    this.click(function() {
        window.open(jQuery(this).attr('href'), '_blank', 'scrollbars=yes,height=550,width=1000,status=no,toolbar=no,menubar=no,location=yes')
        return false
    });
}

jQuery.fn.drawReportGraph = function() {
    if (!!document.createElement('canvas').getContext) {
      var canvas = document.getElementById("canvas_graph");
      if (canvas) {
        jQuery('.get_flash').remove();
        jQuery.getJSON(jQuery(canvas).attr('rel'), null,
        function(my_json) {
          my_json.colors = ['#00AF64','#36D792','#133AAC','#476BD6']
            my_json.title = 'Assesement Report'
            var course_canvas = new RGraph.Bar('canvas_graph', my_json.values);
            course_canvas.Set('chart.text.size', 7);
            course_canvas.Set('chart.background.grid.vlines', false);
            course_canvas.Set('chart.hmargin', my_json.hmargin);
            course_canvas.Set('chart.tooltips', my_json.tooltips);
            course_canvas.Set('chart.title', my_json.title);

            course_canvas.Set('chart.ylabels', true);
            course_canvas.Set('chart.grouping', 'stacked');
            course_canvas.Set('chart.gutter', 30);
            course_canvas.Set('chart.variant', '3d');
            course_canvas.Set('chart.labels', my_json.labels);
            course_canvas.Set('chart.colors', my_json.colors);
            course_canvas.Set('chart.strokecolor', 'rgba(0,0,0,0)');
            course_canvas.Set('chart.background.barcolor1', '#DDE3D5');
            course_canvas.Set('chart.background.barcolor2', '#DDE3D5');
            course_canvas.Draw();
        });
      }
    } else {
      var strURL = jQuery('#flash_graph').attr('rel');
      var height = jQuery('#flash_graph').attr('height');
      var width  = jQuery('#flash_graph').attr('width');
      if (strURL) {
        jQuery('#graph_info').remove();
        jQuery('#canvas_graph').remove();
          var chart = new FusionCharts("/Charts/FCF_StackedColumn3D.swf", "ChartId", width, height);
          strURL = escape(strURL);
          chart.setDataURL(strURL);
          chart.setTransparent(true);
          chart.render("flash_graph");
        }
   }
}

jQuery(document).ready(function() {

    jQuery().drawReportGraph();
    jQuery('.update_profile_preferences').toggleWithAjax();

    $('.my_boxy_cq').boxy({
        modal: true,
        closeable: true,
        title: '<span class="clozePanelHeader">Check your understanding</span>',
        unloadOnHide: false,
        afterShow: function() {
            $(".solutionSection").css('visibility', 'hidden');
            Cloze.setupDragDrop();
            $('body').css('overflow', 'hidden');
            $('.boxy-modal-blackout').css('width', '100%');

            jQuery('.boxy-wrapper a.close').unbind('click');
            jQuery(document.body).unbind('keypress.boxy');
            jQuery('.close').click(function() {
                if ($('#tryAgain').length || $('#scoreCloze').length)
                {
                    confirm_text = "All of your answers will be reset if you leave this page. Are you sure, you want to leave? "
                    Boxy.confirm(confirm_text, function() {
                        window.location.reload();

                    }, {
                        modal: false
                    });
                } else {
                    window.location.reload();
                }
                return false;
            });
        }
    });

    jQuery('#search_child_id').change(function() {
        if (parseInt(jQuery(this).val()) == 0) {
            jQuery("#search_user_ids").val(jQuery("#search_user_ids").attr('rel'))
        } else {
            jQuery("#search_user_ids").val(jQuery(this).val())
        }

    })

});


Invitation = function() {
    return {
        onclick : function() {
            $('.people-invited').live('click', function(e) {
                $('#invited_people_list').slideToggle('slow');
            });
        }
    };
}();


$('.students_invited').live('click', function(e) {
    $('#invited_by_students_list').slideToggle('slow');
});


$('.invited_user_link').live('click', function(e) {
    $('#notification_' + this.id.substr(10)).slideToggle('slow');
    //     $().slideToggle('slow');
});


// This written for validate the date_of_birth
$.validator.addMethod("fullDate", function() {
    //if all values are selected
    var year = $("#user_dob_1i").val();
    var month = $("#user_dob_2i").val();
    var day = $("#user_dob_3i").val();
    if (day != "" && month != "" && year != "") {
        return true;
    } else {
        return false;
    }
}, 'Please select a day, month, and year');

$.validator.addMethod("require_custom_question", function(value,element) {
    if (element.title == value)
    {
        return false;
    }
    else
    {
        return true;
    }

}, 'Create Customized Question.');

$.validator.addMethod("notEqualTo", function(value,element,params) {
   if (value  == $(params).html())
   {
   	return false;
   }
   else
   {
	return true;
   }
  

}, 'You cannot change email to the current email.');

var billing_check = $('input[type=checkbox][class*=update_billing_address]');
if (billing_check.length > 0 && !billing_check.checked) {
    $('.billing_details').attr('readonly', true);
}
$('.update_billing_address').live('click', function(e) {
    $('.billing_details').attr('readonly', !this.checked)
});

$('#boxy-close a').live('click', function(e) {   
		Boxy.get('#topic_flash_cards').hide();
});


jQuery(".child_info:input:checkbox").live('click', function(e) {
    $('#invalid_children').hide();
});


jQuery('.submit_frm').live('click', function(e) {
    if (jQuery(".child_info:input:checkbox:checked").length == 0)
    {
        $('#invalid_children').show("slow");
        return false;

    } else {
        jQuery("#order_student_profile_ids").attr('value', jQuery(".child_info:input:checkbox:checked").map(function() {
            return $(this).val();
        }).get().join(","));
    }

    if (jQuery("#payment_details").validate().form()) {
        months = jQuery("#order_subscription_period_in_months").val() - 1
        var message = "By proceeding you are agreeing to charge the amount summarized "
                + "on the previous page to the credit card account you provided."
                + "These charges may include one time and/or monthly payments.\n\n"
                + "Do you want to proceed?"

        if (confirm(message)) {
            var message = jQuery('#progressing').html();
            jQuery.blockUI({
                message: message,
                css: {
                    border: 'none',
                    padding: '15px',
                    width: 600,
                    backgroundColor: '#000',
                    '-webkit-border-radius': '10px',
                    '-moz-border-radius': '10px',
                    opacity: .7,
                    color: '#fff'
                }
            });
            
            jQuery("#payment_details").submit();
        }
    }
});

// This written for validate the date_of_birth 
$.validator.addMethod("fullDate", function() {
    //if all values are selected
	var year = $("#user_dob_1i").val();
	var month = $("#user_dob_2i").val();
	var day = $("#user_dob_3i").val();
    if(day != "" && month != "" && year != "") {
      return true;
    } else {
      return false;
    }
  }, 'Please select a day, month, and year');

function callMeOften()
{
    $.ajax({
        method: 'get',
        url : '/active',
        dataType : 'text',
        success: function (text) {
            if (text == 'false') window.location.href = '/timeout';
            else {
                setTimeout(callMeOften, 5 * 1000);
            }
        }
    });

}




var Navigation = function(){
	var mode = 'lessons';

	return {
		switchMode : function() {
			$('#course-list a.courseLink').queryString('href', {mode: mode});
			
			$('.courseTab li a').removeClass('select');

			if(mode == 'passages') {
				$('#passages-tab a').addClass('select');
			} else {
				$('#lessons-tab a').addClass('select');
			}
		},
			
		onCourseClick : function(e) {
			$('#course-list a.courseLink').removeClass('selected');
			$(this).addClass('selected');
		},
		
		onCourseTabClick : function(e) {
			target = $(this);
			mode = target.text().toLowerCase();
		},
		
		onGenreClick : function(e) {
			var target = $(this).attr('href');
	    $('#genreList a').removeClass('selected');
	    $('.passageGroup').hide();
			$('#selectGenreMessage').hide();
			
			if ($(target).length > 0) { 
				$(target).show();
				$('#noPassages').hide();
			} else {
				$('#noPassages').show();
			}

	    $(this).addClass('selected');
		}
	};
}();

$('#course-list a.courseLink').livequery('click', Navigation.onCourseClick);
$('.courseTab li a.active').livequery('click', Navigation.onCourseTabClick);
// $('#genreList a').livequery('click', Navigation.onGenreClick);

// var Report = {
// 	var graphical;
// 	
// 	return {
// 		init : function() {
// 			graphical = null;
// 		}
// 	};
// }();


$(function() {
	$('a.remoteStudentReport').live('click', function(event) {
		// var graphical = null;
		var e = $('#report-heading').next().contents('a.hidden');
		var hidden_el = e.attr('id');
		var mode;
		var reportType = $('#report_type').attr('value');
		
		if(hidden_el == 'graphical-reports') {
			mode = 'graphical';
		} else if(hidden_el == 'textual-reports') {
			mode = 'textual';
		} else {
			mode = null;
		}

	  $.ajax({
		      type: 'GET',
		      url: this.href,
			    dataType: "script",
			    data: {mode: mode, report_type: reportType},
			    beforeSend: function(xhr) {
			      xhr.setRequestHeader("Accept", "text/javascript");
			    },
			    success: function(data) {
				    //console.log(mode);				    
			    }
	  });
	  return false;
	});
	
	$('a#graphical-reports').live('click', function() {
		$('#report_type').hide();
		$(this).removeClass('visible').addClass('hidden').prev().removeClass('hidden').addClass('visible');
		var e = $('#report-details .courseCont ul'); 
    e.hide().next().show();
	  e.parent().next().hide();
	  return false;
	});
	
	$('a#textual-reports').live('click', function() {
		$('#report_type').show();
		$(this).removeClass('visible').addClass('hidden').next().removeClass('hidden').addClass('visible');
		var e = $('#report-details .courseCont ul'); 
    e.show().next().hide();
	  e.parent().next().show();
	  return false;
	});  
	
	// showTextualView = function() {
	//   var e = $('#report-details .courseCont ul'); 
	//     e.show().next().hide();
	//   e.parent().next().show();
	// };
	// 
	// showGraphicalView = function() {
	//   var e = $('#report-details .courseCont ul');
	//   e.hide().next().show();
	//   e.parent().next().hide();
	// };
	
	$('#report_type').livequery('change', function(event) {
		var val = $(this).val();
		if( val == 'dr') {
			$('#report-details ul li').show();
			$('#report-details ul .toggle').show();
		}
		else if(val == 'sr') {
			$('#report-details ul li').show();
			$('#report-details ul .toggle').hide();
		}
		else if(val == 'plr') {
			$('#report-details ul li').hide();
			$('.problematic').parents('li').show();
		}
	});
})

$('#Add_new_lesson').live('click', function(e) {
     $('#lesson_forum_question_new').slideToggle("slow");
     e.preventDefault();
});
/*---------- aded for new discussion ------------*/
$('#Add_new_lesson_discussion').live('click', function(e) {
     $('#lesson_forum_question_new').slideToggle("slow");
     e.preventDefault();
});

$('#Add_new_topic_discussion').live('click', function(e) {
     $('#topic_forum_question_new').slideToggle("slow");
     e.preventDefault();
});

$('.discussionCancel').live('click', function(e){	
  $(this).parent().parent().parent('form').resetForm();
  $('#errorExplanation').hide();	
  $(this).parent().parent().parent().parent().slideToggle('slow');
  	e.preventDefault();
});

$('#forum_question_link a').live('click', function(e){	
	$('#forum_question_link a').removeClass('select');
   	$(this).addClass('select');
  	e.preventDefault();
});



/*------------ end ----------------------------*/
$('#post_less_question_close').live('click', function(e) {
     $('#new_lesson_question_form').resetForm();
     e.preventDefault();
});

$('#Add_new').live('click', function(e) {
     $('#topic_forum_answer_new').slideToggle("slow");
     e.preventDefault();
});

$('#close_reply').live('click', function(e) {
     $('#new_forum_answers_form').resetForm();
     $('#topic_forum_answer_new').slideToggle("slow");         
     e.preventDefault();
});    

$('.comment_link').live('click', function(e){
  // $(this).parent().parent().siblings('div[id^=Addcomment_]').slideToggle('slow');
	$(this).parent().parent().parent().parent().siblings('div[id^=Addcomment_]').slideToggle('slow');
  e.preventDefault();
});

$('.commentoncomment').live('click', function(e){
  $(this).parent().siblings('.mBottomx2').slideToggle('slow');
  e.preventDefault();
});

$('.Quotecomment').live('click', function(e){
  //$(this).parent().siblings('.mBottomx2').slideToggle('slow');
  $(this).parent().parent().parent().parent().siblings('div[id^=comment_on_comment_new]').slideToggle('slow');	
  e.preventDefault();
});

$('#Add_new_topic').live('click', function(e) {
     $('#topic_forum_question_new').slideToggle('slow');
     e.preventDefault();
});

$('#post_topi_question_close').live('click', function(e) {
     $('#new_topic_question_form').resetForm();
     e.preventDefault();
});

// TO DO:  CLEAN UP THE CODE
$("#button1").live("click", function(){ 
    document.getElementById("test_started").value = false;
    if ($("#button1").attr('disabled') != true)
    {	
        jConfirm('Do you want to complete the Quiz?', 'Score Quiz?', function(r) {
            if (r)
            {
                $('#button_value').attr("value", "score");
                $('#exam_form').submit();
            }
            else
            {
                document.getElementById("test_started").value = true;
                return false;
            }
        });
    }
});

$("#button2").live("click",function(){	
    var input_vals = '';
    $("#nr-answer-value input[type='text']").each(function(){
        input_vals += this['value'];
    });
    $('#given-answer').val(input_vals);
    $('#button_value').attr("value", "next");
    $('#reload-form').val('0');
    $('#exam_form').submit();
});

$("#button3").live("click",function(){	
    if(!$("input[name='distractor']:checked").val() && $("input[name='distractor']").length) {
        jAlert('Please select an answer', 'No answer selected');
        return false;
    }
    if(document.getElementById("nr_answer") !=  null  && document.getElementById("nr_answer").value == "" && $("#nr_answer").length) {
        jAlert('Please enter an answer', 'No answer entered');
        return false;
    } 
    if(document.getElementById("nr_comma1") !=  null  && document.getElementById("nr_comma1").value == "" && $("#nr_comma1").length) {
        jAlert('Please enter a valid answer', 'No valid answer entered');
        return false;
    }  
	
	if($("#nr_decimal0") !=  null  && $("#nr_decimal0").val() == "" && $("#nr_decimal0").length) {
        jAlert('Please enter a valid answer', 'No valid answer entered');
        return false;
    }

    if(document.getElementById("nr_decimal1") !=  null  && document.getElementById("nr_decimal1").value == "" && $("#nr_decimal1").length) {
        jAlert('Please enter a valid answer', 'No valid answer entered');
        return false;
    }
    if(document.getElementById("nr_decimal2") !=  null  && document.getElementById("nr_decimal2").value == "" && $("#nr_decimal2").length) {
        jAlert('Please enter a valid answer', 'No valid answer entered');
        return false;
    } 
	
	var input_vals = '';
    $("#nr-answer-value input[type='text']").each(function(){
		if(this['value'] == '') this['value'] = 0;
        input_vals += this['value'];
    });
    $('#given-answer').val(input_vals);
    $('#button_value').attr("value", "guess");
    $('#reload-form').val('1');
    $('#exam_form').submit();
});

$("#button4,nr_score_button").live("click",function(){
    var input_vals = '';
    $("#nr-answer-value input[type='text']").each(function(){
        input_vals += this['value'];
    });
    $('#given-answer').val(input_vals);
    $('#button_value').attr("value", "previous");
    $('#reload-form').val('0');
    $('#exam_form').submit();
});

$("div.multipleChoice").live("click",function() {
    $('#exam_form').submit();                                 
}); 

$("#nr_score_button").live("click",function() {
    var nr_response = '';
	var flag = false;
	var ans = '';
    $("#nr-answer-value input[type='text'], .nrSeperator").each(function() {
	  if($(this).is('span')){
		if (this.textContent == '$') flag = true;
		ans = this.textContent
	  }else{
		if(this['value'] == '') this['value'] = 0;
		ans = (this['value'].split(/\./).length == 2 && flag)  ? Number(this['value']).toFixed(2) : this['value']
	  }
	  nr_response += ans;
	
    });
    $('#given-answer').val(nr_response);
    $('#reload-form').val('0');
    $('#exam_form').submit();
});

$("#score_topic_exam").live("click", function(){ 
    document.getElementById("test_started").value = false;
    if ($("#score_topic_exam").attr('disabled') != true)
    {
	
        jConfirm('Do you want to complete the Test?', 'Score Test?', function(r) {
            if (r)
            {
                $('#button_value').attr("value", "score");
                $('#exam_form').submit();
            }
            else
            {
                document.getElementById("test_started").value = true;
                return false;
            }
        });
    }
});


$("#end_topic_exam").live("click",function() {
    msg = jQuery(this).attr('rel');
    title = jQuery(this).val() + '?';
    document.getElementById("test_started").value = false;
    jConfirm(msg,title,function(r) {
        if(r) {
            $("#button_value").attr("value","end_early");
            $("#exam_form").submit();
        } else {
            document.getElementById("test_started").value = true;
            return false;
        }
    });
});

function preventClose(test_type)
{
    if($("#test_started").val() != "false") {
        $("#test_started").val("invalid");
        return("You are about to leave the " + test_type + ". The " + test_type + " can only be taken once" +
            ", and if you do not complete the " + test_type + " now,\nall unanswered questions will be marked as incorrect. ");
		
    }
}

function closeSession() {
    if($("#test_started").val() == "invalid") {
        window.location = "/courses";
    }
}

$("#nr-answer-value input[type='text']").livequery("keyup", function(){
	if ($(this).valid()) {
		$("#nr_score_button").show();
	} else {
		$("#nr_score_button").hide();
	}
});




Cloze = function() {
  var dragzone, dropzone, draggablesCount;

  function selectAnswer(item, target, ui) {
    var txt = $(item).text();

    if ($(target).children().length < 1) {
      item.appendTo(target).removeClass('whiteTint pAll5');
      ;

      $(target).next("input[type='hidden'].cloze_ans").val(txt);
    }
    else {
      // restore the text that was cleared from the hidden field in 'dragStart'
      $(item).parent().next("input[type='hidden'].cloze_ans").val(txt);
    }

    toggleSubmit();
  }

  function revertAnswer(item, target) {
    item.appendTo(target).addClass('whiteTint pAll5');

    item.parent().next("input[type='hidden'].cloze_ans").val('');

    toggleSubmit();
  }

  function dragStart(event, ui) {
    helper = $(ui.helper);
    helper.addClass('whiteTint pAll5');


    var target = $(event.target);
    target.parent().next("input[type='hidden'].cloze_ans").val('');
  }

  function toggleSubmit() {
    var inputs = $("input[type='hidden'].cloze_ans", '#cloze_question');
    var answers = $.map(inputs, function(n, i) {
      if ($(n).val().length > 0)
        return $(n).val();
    });

    if (answers.length == draggablesCount) {
      $('#scoreCloze').attr('disabled', false);
      $('#scoreCloze').removeClass('disabled');
    }
    else {
      $('#scoreCloze').attr('disabled', true);
      $('#scoreCloze').addClass('disabled');
    }
  }


  return {

    open_cloze_quiz : function(url) {
      $.get(url, null, function(data) {
        new Boxy(data, {
          modal: true,
          cache: false,
          closeable: true,
          title: '<span class="clozePanelHeader">Check your understanding</span>',
          unloadOnHide: false,
          afterShow: function() {
	    $(".solutionSection").css('visibility','hidden');
            Cloze.setupDragDrop();
            jQuery('.boxy-wrapper a.close').unbind('click');
            jQuery('.close').click(function() {
              if ($('#tryAgain').length || $('#scoreCloze').length)
              {
                confirm_text = "All of your answers will be reset if you leave this page. Are you sure, you want to leave? "
                Boxy.confirm(confirm_text, function() {
		    var oldUrl = window.location.href;
		    window.location = oldUrl.split("?")[0];

                }, {
                  modal: false
                });
              } else {
		  var oldUrl = window.location.href;
		  window.location = oldUrl.split("?")[0];
              }
              return false;
            });
          }
        });
      });
    },

    setupDragDrop : function() {
      dragzone = $('#cloze_answers'),dropzone = $('span.cloze-dropzone', '#cloze_question');
      draggablesCount = $('a.cloze').length;

      $('a.cloze', dragzone).livequery(function() {
        $(this).draggable({
          revert: 'invalid',
          helper: 'clone',
          cursor: 'move',
          start: dragStart
        });
      });

      $('a.cloze').live('click', function(e) {
        e.preventDefault();
      });

      dropzone.livequery(function() {
        $(this).droppable({
          accept: 'a.cloze',
          tolerance: 'touch',
          drop: function(event, ui) {
            selectAnswer(ui.draggable, event.target, ui, event);
          }
        });
      });

      dragzone.livequery(function() {
        $(this).droppable({
          accept: '#cloze_question a.cloze',
          activeClass: 'custom-state-active',
          drop: function(event, ui) {
            revertAnswer(ui.draggable, event.target);
          }
        });
      });

    },


    show: function() {
      $('#clozePad').fadeIn('slow');
    },

    hide: function() {
      $('#clozePad').fadeOut('slow');
    },

    reset: function() {
      var flap_left = $('#clozeFlap').offset()['left'];
      var flap_width = $('#clozeFlap').width();

      $('#clozePad').animate({
        left: -flap_left - flap_width
      }, 'fast', null, function() {
        $('#clozePad').css('visibility', 'visible').animate({
          left: -flap_left
        }, 'slow');
      });
    },

    toggle: function() {
      var flap_left = $(this).offset()['left'];
      var pad_left = $('#clozePad').offset()['left'];
      if (pad_left < 0) {
        Practice.hide();
        Discussion.hide();
        NotePad.hide();

        $('#clozeFlap').animate({
          top: '30px'
        }, 'slow', null, function() {
          $('#clozePad').animate({
            left: 0
          }, 'slow');
        });
      }
      else {
        $('#clozePad').animate({
          left: -flap_left
        }, 'slow', null, function() {
          $('#clozeFlap').animate({
            top: '230px'
          }, 'slow');

          Discussion.show();
          NotePad.show();
          Practice.show();
        });
      }
    }
  }
}();

$('#clozeFlap').live('click', Cloze.toggle);




Practice = function(){
	return {
		init : function(url) {
			$.get(url, null, function(data, textStatus){
				$('#sidebar').append(data);
				Practice.reset();
			});
		},
		
		show : function(){
			$('#practicePad').fadeIn('slow');
		},

		hide : function(){
			$('#practicePad').fadeOut('slow');
		},
		
		reset : function(){
			var flap_left = $('#practiceFlap').offset()['left'];
			var flap_width = $('#practiceFlap').width();

			$('#practicePad').animate({ 
				left: -flap_left -flap_width
			}, 'fast', null, function(){
				$('#practicePad').css('visibility', 'visible').animate({
					left: -flap_left
				}, 'slow');
			});
		},
		
		toggle : function(){
			var flap_left = $(this).offset()['left'];
			var pad_left = $('#practicePad').offset()['left'];
			if (pad_left < 0) {
				
				Discussion.hide();
				NotePad.hide();
				Cloze.hide();
					
				$('#practiceFlap').animate({
					top: '30px'
				},'slow', null, function(){
					$('#practicePad').animate({ 
						left: 0
					}, 'slow');
				});				
			} else {				
				$('#practicePad').animate({ 
					left: -flap_left
				}, 'slow', null, function(){
					$('#practiceFlap').animate({
                                        top: '330px'
					},'slow');
					Discussion.show();
					NotePad.show();
					Cloze.show();
				});
			}
		}
	};
}();

$('#practiceFlap').live('click', Practice.toggle);

$('.selected_practice_distractor').live("click", function(){
    $.ajax({
        type: 'GET',
        url: '/lessons/'+ this.id.split('_')[1]+'/practice_questions/'+ this.id.split('_')[2]+'/evaluate_answer',
        data: "distractor="+this.id.split('_')[3]+'&code='+jQuery(this).attr('code'),
        dataType: "script"
    });
    
});









var Comprehension = function(){
	return {
		init : function(url) {
			$.get(url, null, function(data, textStatus){
				$('#sidebar').append(data);
				Comprehension.reset();
			});
		},

		reset : function(){
			var flap_left = $('#comprehensionFlap').offset()['left'];
			var flap_width = $('#comprehensionFlap').width();

			$('#comprehensionPad').animate({ 
				left: -flap_left -flap_width
			}, 'fast', null, function(){
				$('#comprehensionPad').css('visibility', 'visible').animate({
					left: -flap_left
				}, 'slow');
			});
		},
		
		show : function(){
			$('#comprehensionPad').fadeIn('slow');
		},

		hide : function(){
			$('#comprehensionPad').fadeOut('slow');
		},
		
		toggle : function(){
			var flap_left = $(this).offset()['left'];
			var pad_left = $('#comprehensionPad').offset()['left'];
			if (pad_left < 0) {
				
				Discussion.hide();
				NotePad.hide();
				Cloze.hide();
				Practice.hide();
					
				$('#comprehensionFlap').animate({
					top: '30px'
				},'slow', null, function(){
					$('#comprehensionPad').animate({ 
						left: 0
					}, 'slow');
				});				
			} else {				
				$('#comprehensionPad').animate({ 
					left: -flap_left
				}, 'slow', null, function(){
					$('#comprehensionFlap').animate({
						top: '230px'
					},'slow');
					Discussion.show();
					NotePad.show();
					Cloze.show();
					Practice.show();
				});
			}
		}
	};
}();

$('#comprehensionFlap').live('click', Comprehension.toggle);

ELA = function(){
	return {
		// highlights text in a passage that is referred to by an embeded item in the question 
		highlightPassageReferences : function(){
			$('[class*=pHighlight_]', '#course_tab_panel_container').each(function() {
				$(this).removeClass(this.className);
				$(this).addClass('passageRef');
			});
			var questionTitles = $('.passageRef', '#passageQuestion').map(function() {
				return $(this).attr('title');
			}).get().unique();
			//$('.passageRef', '#course_tab_panel_container').removeClass(/pHighlight_\d/i);
			$.each(questionTitles, function(i, v) {
				var questionEl = $('.passageRef[title=' + v + ']', '#course_tab_panel_container');
				questionEl.addClass('pHighlight_'+i);
			});
		}
	};
}();

/*
 * Original code borrowed from -
 * selectlink 1.0
 * By Arshad Chummun (http://www.wydle.com)
 * Copyright (c) 2009 Arshad Chummun
 * Licensed under the MIT License: http://www.opensource.org/licenses/mit-license.php
 */
(function($) {

    $.fn.selectlink = function(options) {
        //default values
        $.fn.selectlink.defaults = {
            min : 3,
            max : 1000,
            text : '?',
            // url : '',
            target : '_blank'
        };

        // build main options
        var opts = $.extend({}, $.fn.selectlink.defaults, options);
        var start_ref;

        return this.each(function() {
            $this = $(this);

            $(this).mousedown(function(e) {
                var t = $(e.target);

                if (t.attr('id')) {
                    start_ref = t.attr('id');
                } else if (t.prev('[id]').attr('id')) {
                    start_ref = t.prev('[id]').attr('id');
                } else {
                    start_ref = t.closest('[id]').attr('id');
                }
            });

            $(this).mouseup(function(e) {
                var q = Util.getSelectionHTML();
                //check limit
                if (q && String(q).length > opts.min && String(q).length < opts.max) {

                    //remove any popText
                    $("#popText").remove();

                    if (!popText) {
                        var popText = $("<div>").attr({
                            id : 'popText',
                            'class': start_ref + ' popNote'
                        }).html(opts.text).css({
                            'position' : 'absolute',
                            'top' : e.pageY - 30,
                            'left' : e.pageX + 1
                        }).hide();
                    }

                    $('a.note').livequery(function() {
                        $(this).queryString({'ref_id' : start_ref});
                    });

					$('a.flash-card').livequery(function() {
                        $(this).queryString({'ref_id' : start_ref});
                    });

                    // insert the popText
                    $("body").append(popText).find('#popText').fadeIn("slow");

                    // hide popText on document click
                    $(document).mousedown(function() {
                        $("#popText").fadeOut("slow");
                    });
                }
				if(e.button == 2 || e.button == 3)
				{
					$("#popText").remove();
				}

            });

        });

    };

})(jQuery);

Note = function() {
    function setupModalBox() {
        $('#popText a.note, a.note-indicator').livequery(function(e) {
            $(this).boxy({
                modal: true,
                closeable: true,
                title: '<span class="notePanelHeader">Notes</span>',
                unloadOnHide: false
            });
        });

        $('a.iconnotes').livequery(function(e) {
            $(this).boxy({
                modal: true,
                closeable: true,
                title: '<span class="notePanelHeader">Notes</span>',
                unloadOnHide: false
            });
        });

        $('a.orphand_notes').livequery(function(e) {
            $(this).boxy({
                modal: true,
                closeable: true,
                title: '<span class="notePanelHeader">Notes</span>',
                unloadOnHide: false
            });
        });
    }

	
    function setupEditor() {
        $('textarea.rte1').livequery(function() {
            var v = Util.getSelectionHTML().replace(/<a.*class="(note|flashc)-indicator .*".*>\d+<\/a>/g, '');
            $(this).val(v);

            $(this).rte({
                controls_rte: rte_toolbar
            });
            $('.rte-zone').css('width', '100%');
        });

        $("#note_title").livequery(function(){
            if ($(this).val().length == 0) {
                var selection_val = Util.getSelectedText();
                if (selection_val != null){
                    $(this).val(selection_val.toString().substring(0, 60));
                }
                else{
                    $(this).val("");
                }
            }
            // clear the selection
            Util.clearSelection();
        });

    }

    return {
        initSelector : function(noteUrl, flashUrl, element) {
            setupEditor();
            setupModalBox();
            element.selectlink({
                min : 1,
                max : 5000,
                title : 'Selection for Note.',
                text : '<div class="innerPopNote"><a href="' + noteUrl + '" class="note">Add Note</a><span class="seperator"></span><a href="' + flashUrl + '" class="flash-card">Flash Card</a></div>'
            });
        },

        updateIndicatorVal : function(ref_id) {
            if (ref_id != '#no_ref_id') {
                var ref = $('.note-indicator.' + ref_id);
            } else {
                var ref = $('.count_orphand_notes');
            }

            var val = parseInt(ref.html());            
            if (val > 1) {
                ref.html(val - 1);
            } else {
                Boxy.get('#notes').hide();
                if (ref_id != '#no_ref_id'){
                    ref.remove();
                }else{
                    $('#no_ref_id').remove();
                }

            }
        }
    };
}();

function goToByScroll(id) {
    $('html,body').animate({scrollTop: $("#" + id).offset().top}, 'slow');
}


FlashCard = function(){
	function setupFlashCard() {
		$('a.flash-card, a.flashc-indicator').livequery(function(){
			$(this).boxy({
				modal: true,
        		closeable: true,
        		title: '<span>Flash Cards</span>',
		        unloadOnHide: false,
				afterShow: function() {
					$('.boxy-wrapper').addClass('flashCardWrapper');
				}
			});
		});
		
    $('a.orphand_flash_cards').livequery(function(e) {
    	$(this).boxy({
	      	modal: true,
	        closeable: true,
	        title: '<span>Flash Cards</span>',
	        unloadOnHide: false,
			afterShow: function() {
				$('.boxy-wrapper').addClass('flashCardWrapper');
			}
      	});
    });	
	}
	
	function setupFlashCardEditor() {
			$('textarea.flash_card_answer').livequery(function(){
				var v = Util.getSelectionHTML();
				
				if (typeof v != 'undefined' && v.length) {
					$(this).val(v.replace(/<a.*class="(note|flashc)-indicator .*".*>\d+<\/a>/g, ''));
				}
				
				$(this).rte();
				$('.rte-zone').css('width', '100%');
				$('.rte-toolbar').remove();
				// clear the selection
				Util.clearSelection();
			});
    }
	
	return {
		init : function() {
			setupFlashCard();
			setupFlashCardEditor();
		},
		
		updateFlashCardMarker: function(lesson_id, ref_id){
			var ref = $('.flashc-indicator.' + ref_id);
			if (!ref.length) {
				ref = $(document.createElement('a')).attr({
					'class' : 'flashc-indicator ' + ref_id,
					'href' : '/flash_cards?lesson_id=' + lesson_id  + '&ref_id=' + ref_id
				}).insertBefore('#' + ref_id);
				ref.html('0');
			}
			var ref_count = parseInt(ref.text()) + 1;
			ref.text(ref_count);
		}
	};
}();


$('input[type="radio"]', '#select_Flash_Card_Status').live('click',function() {
  $('#select_Flash_Card_Status').submit();
})


/*!
 * Modernizr JavaScript library 1.1
 * http://modernizr.com/
 *
 * Copyright (c) 2009 Faruk Ates - http://farukat.es/
 * Licensed under the MIT license.
 * http://modernizr.com/license/
 *
 * Featuring major contributions by
 * Paul Irish  - http://paulirish.com
 * Ben Alman   - http://benalman.com/
 */
/*
 * Modernizr is a script that will detect native CSS3 and HTML5 features
 * available in the current UA and provide an object containing all
 * features with a true/false value, depending on whether the UA has
 * native support for it or not.
 * 
 * In addition to that, Modernizr will add classes to the <html>
 * element of the page, one for each cutting-edge feature. If the UA
 * supports it, a class like "cssgradients" will be added. If not,
 * the class name will be "no-cssgradients". This allows for simple
 * if-conditionals in CSS styling, making it easily to have fine
 * control over the look and feel of your website.
 * 
 * @author    Faruk Ates
 * @copyright   (2009) Faruk Ates.
 *
 * @contributor   Paul Irish
 * @contributor   Ben Alman
 */

window.Modernizr = (function(window,doc){
    
    var version = '1.1',
    
    ret = {},
    

    /**
     * enableHTML5 is a private property for advanced use only. If enabled,
     * it will make Modernizr.init() run through a brief while() loop in
     * which it will create all HTML5 elements in the DOM to allow for
     * styling them in Internet Explorer, which does not recognize any
     * non-HTML4 elements unless created in the DOM this way.
     * 
     * enableHTML5 is ON by default.
     */
    enableHTML5 = true,
    
    /**
     * enableNoClasses is a private property that, when enabled, will
     * add classnames to the <html> element at all times, but prefixes
     * failed groups with "no-", e.g. "no-cssanimations".
     * This allows for very easy IF / ELSE style rules in your CSS. It
     * can be disabled if these "no-classes" are not needed or desired.
     * 
     * enableNoClasses is ON by default.
     */
    enableNoClasses = true,
    
    
    /**
     * fontfaceCheckDelay is the ms delay before the @font-face test is
     * checked a second time. This is neccessary because both Gecko and
     * WebKit do not load data: URI font data synchronously.
     *   https://bugzilla.mozilla.org/show_bug.cgi?id=512566
     * If you need to query for @font-face support, send a callback to: 
     *  Modernizr._fontfaceready(fn);
     * The callback is passed the boolean value of Modernizr.fontface
     */
    fontfaceCheckDelay = 100,
    
    
    docElement = doc.documentElement,

    /**
     * Create our "modernizr" element that we do most feature tests on.
     */
    m = doc.createElement( 'modernizr' ),
    m_style = m.style,

    /**
     * Create the input element for various Web Forms feature tests.
     */
    f = doc.createElement( 'input' ),
    
    // Reused strings.
    
    canvas = 'canvas',
    canvastext = 'canvastext',
    rgba = 'rgba',
    hsla = 'hsla',
    multiplebgs = 'multiplebgs',
    borderimage = 'borderimage',
    borderradius = 'borderradius',
    boxshadow = 'boxshadow',
    opacity = 'opacity',
    cssanimations = 'cssanimations',
    csscolumns = 'csscolumns',
    cssgradients = 'cssgradients',
    cssreflections = 'cssreflections',
    csstransforms = 'csstransforms',
    csstransforms3d = 'csstransforms3d',
    csstransitions = 'csstransitions',
    fontface = 'fontface',
    geolocation = 'geolocation',
    video = 'video',
    audio = 'audio',
    input = 'input',
    inputtypes = input + 'types',
    // inputtypes is an object of its own containing individual tests for
    // various new input types, such as search, range, datetime, etc.
    
    // SVG is not yet supported in Modernizr
    // svg = 'svg',
    
    background = 'background',
    backgroundColor = background + 'Color',
    canPlayType = 'canPlayType',
    localStorage = 'localstorage',
    sessionStorage = 'sessionstorage',
    webWorkers = 'webworkers',
    applicationCache = 'applicationcache',
    
    // list of property values to set for css tests. see ticket #21
    setProperties = ' -o- -moz- -ms- -webkit- '.split(' '),

    tests = {},
    inputs = {},
    attrs = {},
    
    elems,
    elem,
    i,
    feature,
    classes = [];
  
 
    /**
     * set_css applies given styles to the Modernizr DOM node.
     */
    function set_css( str ) {
        m_style.cssText = str;
    }

    /**
     * set_css_all extrapolates all vendor-specific css strings.
     */
    function set_css_all( str1, str2 ) {
        return set_css(setProperties.join(str1 + ';') + ( str2 || '' ));
    }

    /**
     * contains returns a boolean for if substr is found within str.
     */
    function contains( str, substr ) {
        return str.indexOf( substr ) !== -1;
    }

    /**
     * test_props is a generic CSS / DOM property test; if a browser supports
     *   a certain property, it won't return undefined for it.
     *   A supported CSS property returns empty string when its not yet set.
     */
    function test_props( props, callback ) {
        for ( var i in props ) {
            if ( m_style[ props[i] ] !== undefined && ( !callback || callback( props[i] ) ) ) {
                return true;
            }
        }
    }

    /**
     * test_props_all tests a list of DOM properties we want to check against.
     *   We specify literally ALL possible (known and/or likely) properties on 
     *   the element including the non-vendor prefixed one, for forward-
     *   compatibility.
     */
    function test_props_all( prop, callback ) {
        var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
        props = [
            prop,
            'webkit' + uc_prop,
            'Moz' + uc_prop,
            'moz' + uc_prop,
            'o' + uc_prop,
            'ms' + uc_prop
        ];

        return !!test_props( props, callback );
    }
    
    // Tests

    /**
     * Canvas tests in Modernizr 1.0 are still somewhat rudimentary. However,
     *   the added "canvastext" test allows for a slightly more reliable and
     *   usable setup.
     */
    tests[canvas] = function() {
        return !!doc.createElement( canvas ).getContext;
    };
    
    tests[canvastext] = function() {
        return !!(tests[canvas]() && typeof doc.createElement( canvas ).getContext('2d').fillText == 'function');
    };

    /**
     * geolocation tests for the new Geolocation API specification.
     *   This test is a standards compliant-only test; for more complete
     *   testing, including a Google Gears fallback, please see:
     *   http://code.google.com/p/geo-location-javascript/
     */
    tests[geolocation] = function() {
        return !!navigator.geolocation;
    };

    tests[rgba] = function() {
        // Set an rgba() color and check the returned value
        
        set_css( background + '-color:rgba(150,255,150,.5)' );
        
        return contains( m_style[backgroundColor], rgba );
    };
    
    tests[hsla] = function() {
        // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally
        
        set_css( background + '-color:hsla(120,40%,100%,.5)' );
        
        return contains( m_style[backgroundColor], rgba );
    };
    
    tests[multiplebgs] = function() {
        // Setting multiple images AND a color on the background shorthand property
        //  and then querying the style.background property value for the number of
        //  occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
        
        set_css( background + ':url(m.png),url(a.png),#f99 url(m.png)' );
        
        // If the UA supports multiple backgrounds, there should be three occurrences
        //  of the string "url(" in the return value for elem_style.background
        
        return /(url\s*\(.*?){3}/.test(m_style[background]);
    };
    
    
    // In testing support for a given CSS property, it's legit to test:
    //    elem.style[styleName] !== undefined
    // If the property is supported it will return an empty string,
    // if unsupported it will return undefined.
    // We'll take advantage of this quick test and skip setting a style 
    // on our modernizr element, but instead just testing undefined vs
    // empty string.
    // The legacy set_css_all calls will remain in the source 
    // (however, commented) in for clarity, yet functionally they are 
    // no longer needed.
    
    tests[borderimage] = function() {
        //  set_css_all( 'border-image:url(m.png) 1 1 stretch' );
        
        return test_props_all( 'borderImage' );
    };
    
    tests[borderradius] = function() {
        //  set_css_all( 'border-radius:10px' );

        return test_props_all( 'borderRadius', '', function( prop ) {
            return contains( prop, 'orderRadius' );
        });
    };
    
    tests[boxshadow] = function() {
        //  set_css_all( 'box-shadow:#000 1px 1px 3px' );
        
        return test_props_all( 'boxShadow' );
    };
    
    tests[opacity] = function() {
        // Browsers that actually have CSS Opacity implemented have done so
        //  according to spec, which means their return values are within the
        //  range of [0.0,1.0] - including the leading zero.
        
        set_css( 'opacity:.5' );
        
        return contains( m_style[opacity], '0.5' );
    };
    
    tests[cssanimations] = function() {
        //  set_css_all( 'animation:"animate" 2s ease 2', 'position:relative' );
        
        return test_props_all( 'animationName' );
    };
    
    tests[csscolumns] = function() {
        //  set_css_all( 'column-count:3' );
        
        return test_props_all( 'columnCount' );
    };
    
    tests[cssgradients] = function() {
        /**
         * For CSS Gradients syntax, please see:
         * http://webkit.org/blog/175/introducing-css-gradients/
         * https://developer.mozilla.org/en/CSS/-moz-linear-gradient
         * https://developer.mozilla.org/en/CSS/-moz-radial-gradient
         * http://dev.w3.org/csswg/css3-images/#gradients-
         */
        
        var str1 = background + '-image:',
            str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
            str3 = 'linear-gradient(left top,#9f9, white);';
        
        set_css(
                str1 + str2
            + str1 + '-webkit-' + str2
            + str1 + '-moz-' + str2
            + str1 + '-o-' + str2
            + str1 + '-ms-' + str2
            + str1 + str3
            + str1 + '-webkit-' + str3
            + str1 + '-moz-' + str3
            + str1 + '-o-' + str3
            + str1 + '-ms-' + str3
        );
        
        return contains( m_style.backgroundImage, 'gradient' );
    };
    
    tests[cssreflections] = function() {
        //  set_css_all( 'box-reflect:right 1px' );
        return test_props_all( 'boxReflect' );
    };
    
    tests[csstransforms] = function() {
        //  set_css_all( 'transform:rotate(3deg)' );
        
        return !!test_props([ 'transformProperty', 'webkitTransform', 'MozTransform', 'mozTransform', 'oTransform', 'msTransform' ]);
    };
    
    tests[csstransforms3d] = function() {
        //  set_css_all( 'perspective:500' );
        
        return !!test_props([ 'perspectiveProperty', 'webkitPerspective', 'MozPerspective', 'mozPerspective', 'oPerspective', 'msPerspective' ]);
    };
    
    tests[csstransitions] = function() {
        //  set_css_all( 'transition:all .5s linear' );
        
        return test_props_all( 'transitionProperty' );
    };



    // @font-face detection routine created by Paul Irish - paulirish.com
    // Merged into Modernizr with approval. Read more about Paul's work here:
    // http://paulirish.com/2009/font-face-feature-detection/  
    tests[fontface] = (function(){

        var fontret;
        if (!(!/*@cc_on@if(@_jscript_version>=5)!@end@*/0)) fontret = true;
  
        else {
      
          // Create variables for dedicated @font-face test
          var st  = doc.createElement('style'),
            spn = doc.createElement('span'),
            wid, nwid, isFakeBody = false, body = doc.body,
            callback, isCallbackCalled;
  
          // The following is a font-face + glyph definition for the . character:
          st.textContent = "@font-face{font-family:testfont;src:url('data:font/ttf;base64,AAEAAAAMAIAAAwBAT1MvMliohmwAAADMAAAAVmNtYXCp5qrBAAABJAAAANhjdnQgACICiAAAAfwAAAAEZ2FzcP//AAMAAAIAAAAACGdseWYv5OZoAAACCAAAANxoZWFk69bnvwAAAuQAAAA2aGhlYQUJAt8AAAMcAAAAJGhtdHgGDgC4AAADQAAAABRsb2NhAIQAwgAAA1QAAAAMbWF4cABVANgAAANgAAAAIG5hbWUgXduAAAADgAAABPVwb3N03NkzmgAACHgAAAA4AAECBAEsAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAACAAMDAAAAAAAAgAACbwAAAAoAAAAAAAAAAFBmRWQAAAAgqS8DM/8zAFwDMwDNAAAABQAAAAAAAAAAAAMAAAADAAAAHAABAAAAAABGAAMAAQAAAK4ABAAqAAAABgAEAAEAAgAuqQD//wAAAC6pAP///9ZXAwAAAAAAAAACAAAABgBoAAAAAAAvAAEAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEACoAAAAGAAQAAQACAC6pAP//AAAALqkA////1lcDAAAAAAAAAAIAAAAiAogAAAAB//8AAgACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAACAFUAAAIRAc0ADwAfAAATFRQWOwEyNj0BNCYrASIGARQGKwEiJj0BNDY7ATIWFX8aIvAiGhoi8CIaAZIoN/43KCg3/jcoAWD0JB4eJPQkHh7++EY2NkbVRjY2RgAAAAABAEH/+QCdAEEACQAANjQ2MzIWFAYjIkEeEA8fHw8QDxwWFhwWAAAAAQAAAAIAAIuYbWpfDzz1AAsEAAAAAADFn9IuAAAAAMWf0i797/8zA4gDMwAAAAgAAgAAAAAAAAABAAADM/8zAFwDx/3v/98DiAABAAAAAAAAAAAAAAAAAAAABQF2ACIAAAAAAVUAAAJmAFUA3QBBAAAAKgAqACoAWgBuAAEAAAAFAFAABwBUAAQAAgAAAAEAAQAAAEAALgADAAMAAAAQAMYAAQAAAAAAAACLAAAAAQAAAAAAAQAhAIsAAQAAAAAAAgAFAKwAAQAAAAAAAwBDALEAAQAAAAAABAAnAPQAAQAAAAAABQAKARsAAQAAAAAABgAmASUAAQAAAAAADgAaAUsAAwABBAkAAAEWAWUAAwABBAkAAQBCAnsAAwABBAkAAgAKAr0AAwABBAkAAwCGAscAAwABBAkABABOA00AAwABBAkABQAUA5sAAwABBAkABgBMA68AAwABBAkADgA0A/tDb3B5cmlnaHQgMjAwOSBieSBEYW5pZWwgSm9obnNvbi4gIFJlbGVhc2VkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UuIEtheWFoIExpIGdseXBocyBhcmUgcmVsZWFzZWQgdW5kZXIgdGhlIEdQTCB2ZXJzaW9uIDMuYmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhTGlnaHRiYWVjMmE5MmJmZmU1MDMyIC0gc3Vic2V0IG9mIEZvbnRGb3JnZSAyLjAgOiBKdXJhIExpZ2h0IDogMjMtMS0yMDA5YmFlYzJhOTJiZmZlNTAzMiAtIHN1YnNldCBvZiBKdXJhIExpZ2h0VmVyc2lvbiAyIGJhZWMyYTkyYmZmZTUwMzIgLSBzdWJzZXQgb2YgSnVyYUxpZ2h0aHR0cDovL3NjcmlwdHMuc2lsLm9yZy9PRkwAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMAA5ACAAYgB5ACAARABhAG4AaQBlAGwAIABKAG8AaABuAHMAbwBuAC4AIAAgAFIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAAdABlAHIAbQBzACAAbwBmACAAdABoAGUAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUALgAgAEsAYQB5AGEAaAAgAEwAaQAgAGcAbAB5AHAAaABzACAAYQByAGUAIAByAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAEcAUABMACAAdgBlAHIAcwBpAG8AbgAgADMALgBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQBMAGkAZwBoAHQAYgBhAGUAYwAyAGEAOQAyAGIAZgBmAGUANQAwADMAMgAgAC0AIABzAHUAYgBzAGUAdAAgAG8AZgAgAEYAbwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAASgB1AHIAYQAgAEwAaQBnAGgAdAAgADoAIAAyADMALQAxAC0AMgAwADAAOQBiAGEAZQBjADIAYQA5ADIAYgBmAGYAZQA1ADAAMwAyACAALQAgAHMAdQBiAHMAZQB0ACAAbwBmACAASgB1AHIAYQAgAEwAaQBnAGgAdABWAGUAcgBzAGkAbwBuACAAMgAgAGIAYQBlAGMAMgBhADkAMgBiAGYAZgBlADUAMAAzADIAIAAtACAAcwB1AGIAcwBlAHQAIABvAGYAIABKAHUAcgBhAEwAaQBnAGgAdABoAHQAdABwADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAAAAAAgAAAAAAAP+BADMAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAQACAQIAEQt6ZXJva2F5YWhsaQ==')}";
          doc.getElementsByTagName('head')[0].appendChild(st);
      
      
          spn.setAttribute('style','font:99px _,serif;position:absolute;visibility:hidden'); 
      
          if  (!body){
            body = docElement.appendChild(doc.createElement(fontface));
            isFakeBody = true;
          } 
      
          // the data-uri'd font only has the . glyph; which is 3 pixels wide.
          spn.innerHTML = '........';
          spn.id        = 'fonttest';
      
          body.appendChild(spn);
          wid = spn.offsetWidth;
          spn.style.font = '99px testfont,_,serif';
      
          // needed for the CSSFontFaceRule false positives (ff3, chrome, op9)
          fontret = wid !== spn.offsetWidth;
      
          var delayedCheck = function(){
            fontret = ret[fontface] = wid !== spn.offsetWidth;
            docElement.className = docElement.className.replace(/(no-)?font.*?\b/,'') + (fontret ? ' ' : ' no-') + fontface;
            
            callback && (isCallbackCalled = true) && callback(fontret);
            isFakeBody && setTimeout(function(){body.parentNode.removeChild(body)}, 50);
          }

          setTimeout(delayedCheck,fontfaceCheckDelay);
        }

        // allow for a callback
        ret._fontfaceready = function(fn){
          (isCallbackCalled || fontret) ? fn(fontret) : (callback = fn);
        }
      
        return function(){ return fontret || wid !== spn.offsetWidth; };
    
    })();
    

    
    // These tests evaluate support of the video/audio elements, as well as
    // testing what types of content they support.
    //
    // we're using the Boolean constructor here, so that we can extend the value
    // e.g.  Modernizr.video     // true
    //       Modernizr.video.ogg // 'probably'
    //
    // codec values from : http://www.w3.org/TR/html5/video.html#the-source-element
    //                     http://www.ietf.org/rfc/rfc4281.txt
    
    tests[video] = function() {
        var elem = doc.createElement(video),
            bool = !!elem[canPlayType];
        
        if (bool){  
            bool      = new Boolean(bool);  
            bool.ogg  = elem[canPlayType]('video/ogg; codecs="theora, vorbis"');
            bool.h264 = elem[canPlayType]('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
        }
        return bool;
    };
    
    tests[audio] = function() {
        var elem = doc.createElement(audio),
            bool = !!elem[canPlayType];
        
        if (bool){  
            bool      = new Boolean(bool);  
            bool.ogg  = elem[canPlayType]('audio/ogg; codecs="vorbis"');
            bool.mp3  = elem[canPlayType]('audio/mpeg3;');
            
            // mimetypes accepted: 
            //   https://developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
            //   http://developer.apple.com/safari/library/documentation/appleapplications/reference/SafariWebContent/CreatingContentforSafarioniPhone/CreatingContentforSafarioniPhone.html#//apple_ref/doc/uid/TP40006482-SW7
            bool.wav  = elem[canPlayType]('audio/wav; codecs="1"');
            bool.m4a  = elem[canPlayType]('audio/x-m4a;');
        }
        return bool;
    };

    // both localStorage and sessionStorage are
    // tested in this method because otherwise Firefox will
    //   throw an error: https://bugzilla.mozilla.org/show_bug.cgi?id=365772
    // if cookies are disabled
    tests[localStorage] = function() {
        return 'localStorage' in window;
    };

    tests[sessionStorage] = function() {
        return 'sessionStorage' in window;
    };

    tests[webWorkers] = function () {
        return !!window.Worker;
    };

    tests[applicationCache] =  function() {
        return !!window.applicationCache;
    };
 

    // Run through all tests and detect their support in the current UA.
    for ( feature in tests ) {
        if ( tests.hasOwnProperty( feature ) ) {
            classes.push( ( !( ret[ feature ] = tests[ feature ]() ) && enableNoClasses ? 'no-' : '' ) + feature );
        }
    }

    /**
     * Addtest allows the user to define their own feature tests
     * the result will be added onto the Modernizr object,
     * as well as an appropriate className set on the html element
     * 
     * @param feature - String naming the feature
     * @param test - Function returning true if feature is supported, false if not
     */
    ret.addTest = function (feature, test) {
      if (this.hasOwnProperty( feature )) {
        // warn that feature test is already present
      } 
      test = !!(test());
      docElement.className += ' ' + (!test && enableNoClasses ? 'no-' : '') + feature; 
      ret[ feature ] = test;
    };
    
    // Run through HTML5's new input attributes to see if the UA understands any.
    // We're using f which is the <input> element created early on
    // Mike Taylr has created a comprehensive resource for testing these attributes
    //   when applied to all input types: 
    //   http://miketaylr.com/code/input-type-attr.html
    // spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
    ret[input] = (function(props) {
        for ( var i in props ) {
            attrs[ props[i] ] = !!(props[i] in f);
        }
        return attrs;
    })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));

    // Run through HTML5's new input types to see if the UA understands any.
    //   This is put behind the tests runloop because it doesn't return a
    //   true/false like all the other tests; instead, it returns an object
    //   containing each input type with its corresponding true/false value 
    ret[inputtypes] = (function(props) {
        for ( var i in props ) {
            f.setAttribute('type', props[i]);
            inputs[ props[i] ] = !!( f.type !== 'text');
        }
        return inputs;
    })('search tel url email datetime date month week time datetime-local number range color'.split(' '));


    /**
     * Reset m.style.cssText to nothing to reduce memory footprint.
     */
    set_css( '' );
    m = f = null;

    // Enable HTML 5 elements for styling in IE:
    if ( enableHTML5 && !(!/*@cc_on!@*/0) ) {
        elems = 'abbr article aside audio canvas datalist details eventsource figure footer header hgroup mark menu meter nav output progress section time video'.split(' ');

        i = elems.length+1;
        while ( --i ) {
            elem = doc.createElement( elems[i] );
        }
        elem = null;
    }

    // Assign private properties to the return object with prefix
    ret._enableHTML5     = enableHTML5;
    ret._enableNoClasses = enableNoClasses;
    ret._version         = version;

    // Remove "no-js" class from <html> element, if it exists:
    (function(H,C){H[C]=H[C].replace(/\bno-js\b/,'js')})(docElement,'className');

    // Add the new classes to the <html> element.
    docElement.className += ' ' + classes.join( ' ' );
    
    return ret;

})(this,this.document);


// THIS FILE HAS BEEN MINIFIED

if(typeof(RGraph) == 'undefined') RGraph = {};RGraph.Registry = {};RGraph.Registry.store = []
RGraph.background = {};RGraph.objects = [];RGraph.getScale = function (max)
{
if(max == 0){
return ['0.2', '0.4', '0.6', '0.8', '1.0'];}
var original_max = max;if(max <= 1){
if(max > 0.5){
return [0.2,0.4,0.6,0.8, Number(1).toFixed(1)];} else if(max >= 0.1){
return [0.1,0.2,0.3,0.4,0.5];} else {
var tmp = max;var exp = 0;while (tmp < 1.01){
exp += 1;tmp *= 10;}
var ret = ['2e-' + exp, '4e-' + exp, '6e-' + exp, '8e-' + exp, '10e-' + exp];if(max <= ('5e-' + exp)){
ret = ['1e-' + exp, '2e-' + exp, '3e-' + exp, '4e-' + exp, '5e-' + exp];}
return ret;}
}
if(String(max).indexOf('.') > 0){
max = String(max).replace(/\.\d+$/, '');}
var interval = Math.pow(10, Number(String(Number(max)).length - 1));var topValue = interval;while (topValue < max){
topValue += (interval / 2);}
if(Number(original_max) > Number(topValue)){
topValue += (interval / 2);}
if(max < 10){
topValue = (Number(original_max) <= 5 ? 5 : 10);}
return [topValue * (1/5), topValue * (2/5), topValue * (3/5), topValue * (4/5), topValue];}
RGraph.array_max = function (arr)
{
var max = null;for (var i=0; i<arr.length; ++i){
max = (max ? Math.max(max, arguments[1] ? Math.abs(arr[i]) : arr[i]) : arr[i]);}
return max;}
RGraph.array_sum = function (arr)
{
if(typeof(arr) == 'number'){
return arr;}
var i, sum;var len = arr.length;for(i=0,sum=0;i<len;sum+=arr[i++]);return sum;}
RGraph.degrees2Radians = function (degrees)
{
return degrees * (Math.PI / 180);}
RGraph.lineByAngle = function (context, x, y, angle, length)
{
context.arc(x, y, length, angle, angle, false);context.lineTo(x, y);context.arc(x, y, length, angle, angle, false);}
RGraph.Text = function (context, font, size, x, y, text)
{
if(document.all){
y += 2;}
context.font = (arguments[11] ? 'Bold ': '') + size + 'pt ' + font;var i;var origX = x;var origY = y;var originalFillStyle = context.fillStyle;if(typeof(arguments[6]) == null) arguments[6] = 'bottom';if(typeof(arguments[7]) == null) arguments[7] = 'left';if(typeof(arguments[8]) == null) arguments[8] = null;if(typeof(arguments[9]) == null) arguments[9] = 0;if(navigator.userAgent.indexOf('Opera') != -1){
context.canvas.__rgraph_valign__ = arguments[6];context.canvas.__rgraph_halign__ = arguments[7];}
context.save();context.canvas.__rgraph_originalx__ = x;context.canvas.__rgraph_originaly__ = y;context.translate(x, y)
x = 0;y = 0;if(arguments[9]){
context.rotate(arguments[9] / 57.3);}
if(arguments[6]){
var vAlign = arguments[6];if(vAlign == 'center'){
context.translate(0, size / 2);} else if(vAlign == 'top'){
context.translate(0, size);}
}
if(arguments[7]){
var hAlign = arguments[7];var width = context.measureText(text).width;if(hAlign){
if(hAlign == 'center'){
context.translate(-1 * (width / 2), 0)
} else if(hAlign == 'right'){
context.translate(-1 * width, 0)
}
}
}
if(arguments[10]){
var offset = 3;var ieOffset = document.all ? 2 : 0;var width = context.measureText(text).width
context.strokeStyle = 'gray';context.fillStyle = arguments[10];context.fillRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset));context.strokeRect(x - offset, y - size - offset - ieOffset, width + (2 * offset), size + (2 * offset));}
context.fillStyle = originalFillStyle;context.save();context.fillText(text,0,0);if(arguments[8]){
var width = context.measureText(text).width;context.translate(x, y);context.strokeRect(0, 0, width, 0 - size);context.fillRect(
arguments[7] == 'left' ? 0 : (arguments[7] == 'center' ? width / 2 : width ) - 2,
arguments[6] == 'bottom' ? 0 : (arguments[6] == 'center' ? (0 - size) / 2 : 0 - size) - 2,
4,
4
);}
context.restore();context.restore();}
RGraph.Clear = function (canvas)
{
var width = canvas.width;canvas.width = width;if(arguments[1]){
context = canvas.getContext('2d');context.beginPath();context.fillStyle = String(arguments[1]);context.fillRect(0,0,width,canvas.height);context.fill();}
RGraph.ClearAnnotations(canvas.id);}
RGraph.DrawTitle = function (canvas, text, gutter)
{
var obj = canvas.__object__;var context = canvas.getContext('2d');var size = arguments[4] ? arguments[4] : 12;var centerx = (arguments[3] ? arguments[3] : canvas.width / 2);var keypos = obj.Get('chart.key.position');var vpos = gutter / 2;if(obj.type == 'bar' && obj.Get('chart.variant') == '3d'){
keypos = 'gutter';}
context.beginPath();context.fillStyle = obj.Get('chart.text.color') ? obj.Get('chart.text.color') : 'black';if(keypos && keypos != 'gutter'){
var vCenter = 'center';} else if(!keypos){
var vCenter = 'center';} else {
var vCenter = 'bottom';}
if(typeof(obj.Get('chart.title.vpos')) == 'number'){
vpos = obj.Get('chart.title.vpos') * gutter;}
if(typeof(obj.Get('chart.title.color') != null)){
var oldColor = context.fillStyle
context.fillStyle = obj.Get('chart.title.color');}
var font = obj.Get('chart.text.font');RGraph.Text(context, font, size, centerx, vpos, text, vCenter, 'center', null, null, null, true);context.fillStyle = oldColor;}
RGraph.getMouseXY = function (event)
{
var obj = (document.all ? event.srcElement : event.target);var e = event;var x;var y;if(typeof(e.offsetX) == 'number' && typeof(e.offsetY) == 'number'){
x = e.offsetX;y = e.offsetY;} else {
x = 0;y = 0;while (obj != document.body){
x += obj.offsetLeft;y += obj.offsetTop;obj = obj.offsetParent;}
x = e.pageX - x;y = e.pageY - y;}
return [x, y];}
RGraph.getCanvasXY = function (canvas)
{
var x = 0;var y = 0;var obj = canvas;while (obj != document.body){
x += obj.offsetLeft;y += obj.offsetTop;obj = obj.offsetParent;}
return [x, y];}
RGraph.Tooltip = function (canvas, text, x, y)
{
var timers = RGraph.Registry.Get('chart.tooltip.timers');if(timers && timers.length){
for (i=0; i<timers.length; ++i){
clearTimeout(timers[i]);}
}
RGraph.Registry.Set('chart.tooltip.timers', []);RGraph.HideContext();RGraph.Redraw(canvas.id);if(RGraph.Registry.Get('chart.tooltip')){
RGraph.Registry.Get('chart.tooltip').style.display = 'none';RGraph.Registry.Set('chart.tooltip', null);}
RGraph.HideContext();var result = /^id:(.*)/.exec(text);if(result){
text = document.getElementById(result[1]).innerHTML;}
var obj = document.createElement('DIV');obj.className = 'RGraph_tooltip';obj.style.display = 'none';obj.style.position = 'absolute';obj.style.left = 0;obj.style.top = 0;obj.style.backgroundColor = '#ffe';obj.style.color = 'black';if(!document.all) obj.style.border = '1px solid rgba(0,0,0,0)';obj.style.visibility = 'visible';obj.style.paddingLeft = '3px';obj.style.paddingRight = '3px';obj.style.fontFamily = 'Tahoma';obj.style.fontSize = '10pt';obj.style.zIndex = 3;obj.style.borderRadius = '5px';obj.style.MozBorderRadius = '5px';obj.style.WebkitBorderRadius = '5px';obj.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';obj.style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';obj.style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';obj.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)';obj.style.opacity = 0;obj.style.overflow = 'hidden';obj.innerHTML = text;obj.__text__ = text;obj.style.display = 'inline';document.body.appendChild(obj);var width = obj.offsetWidth;var height = obj.offsetHeight;obj.style.width = width + 'px';if( (x + width) > document.body.offsetWidth ){
x = x - width - 7;y = y;var placementLeft = true;obj.style.left = x + 'px';obj.style.top = y + 'px';} else {
x += 5;obj.style.left = x + 'px';obj.style.top = (y - height) + 'px';}
var effect = canvas.__object__.Get('chart.tooltip.effect');if(effect == 'expand'){
obj.style.left = (x + (width / 2)) + 'px';obj.style.top = (y - (height / 2)) + 'px';leftDelta = (width / 2) / 10;topDelta = (height / 2) / 10;obj.style.width = 0;obj.style.height = 0;obj.style.boxShadow = '';obj.style.MozBoxShadow = '';obj.style.WebkitBoxShadow = '';obj.style.borderRadius = 0;obj.style.MozBorderRadius = 0;obj.style.WebkitBorderRadius = 0;obj.style.opacity = 1;RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 25));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 50));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 75));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 100));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 125));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 150));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 175));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 200));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 225));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.left = (parseInt(RGraph.Registry.Get('chart.tooltip').style.left) - leftDelta) + 'px' }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 25));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 50));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 75));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 100));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 125));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 150));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 175));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 200));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 225));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.top = (parseInt(RGraph.Registry.Get('chart.tooltip').style.top) - topDelta) + 'px' }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.1) + "px'; }", 25));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.2) + "px'; }", 50));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.3) + "px'; }", 75));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.4) + "px'; }", 100));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.5) + "px'; }", 125));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.6) + "px'; }", 150));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.7) + "px'; }", 175));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.8) + "px'; }", 200));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + (width * 0.9) + "px'; }", 225));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.width = '" + width + "px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.1) + "px'; }", 25));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.2) + "px'; }", 50));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.3) + "px'; }", 75));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.4) + "px'; }", 100));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.5) + "px'; }", 125));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.6) + "px'; }", 150));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.7) + "px'; }", 175));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.8) + "px'; }", 200));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + (height * 0.9) + "px'; }", 225));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.height = '" + height + "px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').innerHTML = RGraph.Registry.Get('chart.tooltip').__text__; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.borderRadius = '5px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.MozBorderRadius = '5px'; }", 250));RGraph.Registry.Get('chart.tooltip.timers').push(setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.WebkitBorderRadius = '5px'; }", 250));} else if(placementLeft && effect == 'fade'){
obj.style.top = (y - height) + 'px';} else if(effect != 'fade' && effect != 'expand'){
alert('[COMMON] Unknown tooltip effect: ' + effect);}
setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.1; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 25);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.2; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 50);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.3; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 75);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.4; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 100);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.5; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 125);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.6; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.2)'; }", 150);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.7; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.4)'; }", 175);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.8; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.6)'; }", 200);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 0.9; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgba(96,96,96,0.8)'; }", 225);setTimeout("if(RGraph.Registry.Get('chart.tooltip')){ RGraph.Registry.Get('chart.tooltip').style.opacity = 1; RGraph.Registry.Get('chart.tooltip').style.border = '1px solid rgb(96,96,96'; }", 250);document.body.onmousedown = function (event)
{
var tooltip = RGraph.Registry.Get('chart.tooltip');if(tooltip){
tooltip.style.display = 'none';tooltip.style.visibility = 'hidden';RGraph.Registry.Set('chart.tooltip', null);RGraph.Redraw();}
}
var links = obj.getElementsByTagName('a');for (var i=0; i<links.length; ++i){
links[i].onmousedown = function (event)
{
location.href = this.href;event.cancelBubble = true;event.stopPropagation();return false;}
}
window.onresize = function ()
{
var tooltip = RGraph.Registry.Get('chart.tooltip');if(tooltip){
tooltip.style.display = 'none';
tooltip.style.visibility = 'hidden';RGraph.Registry.Set('chart.tooltip', null);RGraph.Clear(canvas);canvas.__object__.Draw();}
}
RGraph.Registry.Set('chart.tooltip', obj);}
RGraph.Register = function (obj)
{
var key = obj.id + '_' + obj.type;RGraph.objects[key] = obj;}
RGraph.Redraw = function ()
{
for (i in RGraph.objects){
if(typeof(i) == 'string' && typeof(RGraph.objects[i]) == 'object')  {
if(!arguments[0] || arguments[0] != RGraph.objects[i].id){
RGraph.Clear(RGraph.objects[i].canvas);RGraph.objects[i].Draw();}
}
}
}
RGraph.pr = function (obj)
{
var str = '';var indent = (arguments[2] ? arguments[2] : '');switch (typeof(obj)){
case 'number':
if(indent == ''){
str+= 'Number: '
}
str += String(obj);break;case 'string':
if(indent == ''){
str+= 'String (' + obj.length + '):'
}
str += '"' + String(obj) + '"';break;case 'object':
if(obj == null){
str += 'null';break;}
str += 'Object \n' + indent + '(\n';for (i=0; i<obj.length; ++i){
str += indent + '  ' + i + ' => ' + RGraph.pr(obj[i], true, indent + '\t') + '\n';}
var str = str + indent + ')';break;case 'function':
str += obj;break;case 'boolean':
str += 'Boolean: ' + (obj ? 'true' : 'false');break;}
if(arguments[1]){
return str;} else {
alert(str);}
}
RGraph.Registry.Set = function (name, value)
{
RGraph.Registry.store[name] = value;return value;}
RGraph.Registry.Get = function (name)
{
return RGraph.Registry.store[name];}
RGraph.background.Draw = function (obj)
{
var canvas = obj.canvas;var context = obj.context;var height = 0;var gutter = obj.Get('chart.gutter');var variant = obj.Get('chart.variant');if(variant == '3d'){
context.save();context.translate(10, -5);}
obj.context.beginPath();context.fillStyle = obj.Get('chart.background.barcolor1');height = (obj.canvas.height - obj.Get('chart.gutter'));for (var i=gutter; i < height ; i+=80){
obj.context.fillRect(gutter, i, obj.canvas.width - (gutter * 2), Math.min(40, obj.canvas.height - gutter - i) );}
context.fillStyle = obj.Get('chart.background.barcolor2');height = (obj.canvas.height - gutter);for (var i= (40 + gutter); i < height; i+=80){
obj.context.fillRect(gutter, i, obj.canvas.width - (gutter * 2), i + 40 > (obj.canvas.height - gutter) ? obj.canvas.height - (gutter + i) : 40);}
context.stroke();if(obj.Get('chart.background.grid')){
context.beginPath();context.lineWidth = obj.Get('chart.background.grid.width') ? obj.Get('chart.background.grid.width') : 1;context.strokeStyle = obj.Get('chart.background.grid.color');if(obj.Get('chart.background.grid.hlines')){
height = (canvas.height - gutter)
for (y=gutter; y < height; y+=obj.Get('chart.background.grid.hsize')){
context.moveTo(gutter, y);context.lineTo(canvas.width - gutter, y);}
}
if(obj.Get('chart.background.grid.vlines')){
var width = (canvas.width - gutter)
for (x=gutter; x<=width; x+=obj.Get('chart.background.grid.vsize')){
context.moveTo(x, gutter);context.lineTo(x, obj.canvas.height - gutter);}
}
if(obj.Get('chart.background.grid.border')){
context.strokeStyle = obj.Get('chart.background.grid.color');context.strokeRect(gutter, gutter, canvas.width - (2 * gutter), canvas.height - (2 * gutter));}
}
context.stroke();if(variant == '3d'){
context.restore();}
if( typeof(obj.Get('chart.title')) == 'string'){
if(obj.type == 'gantt'){
gutter /= 2;}
RGraph.DrawTitle(canvas, obj.Get('chart.title'), gutter, null, obj.Get('chart.text.size') + 2);}
context.stroke();}
RGraph.GetDays = function (obj)
{
var year = obj.getFullYear();var days = obj.getDate();var month = obj.getMonth();if(month == 0) return days;if(month >= 1) days += 31;
if(month >= 2) days += 28;if(year >= 2008 && year % 4 == 0) days += 1;if(month >= 3) days += 31;if(month >= 4) days += 30;if(month >= 5) days += 31;if(month >= 6) days += 30;if(month >= 7) days += 31;if(month >= 8) days += 31;if(month >= 9) days += 30;if(month >= 10) days += 31;if(month >= 11) days += 30;return days;}
RGraph.DrawKey = function (obj, key, colors)
{
var canvas = obj.canvas;var context = obj.context;context.lineWidth = 1;context.beginPath();var keypos = obj.Get('chart.key.position');var textsize = obj.Get('chart.text.size');var gutter = obj.Get('chart.gutter');if(keypos && keypos == 'gutter'){
var length = 0;var key = obj.Get('chart.key');if(obj.type == 'pie' && obj.Get('chart.align') == 'left'){
var centerx = obj.radius + obj.Get('chart.gutter');} else if(obj.type == 'pie' && obj.Get('chart.align') == 'right'){
var centerx = obj.canvas.width - obj.radius - obj.Get('chart.gutter');} else {
var centerx = canvas.width / 2;}
context.font = obj.Get('chart.text.size') + 'pt ' + obj.Get('chart.text.font');for (i=0; i<key.length; ++i){
length += context.measureText(key[i]).width;length += 20;length += 10;}
var start = centerx - (length / 2);for (i=0; i<key.length; ++i){
start += 10;context.fillStyle = colors[i];context.fillRect(start + 9, gutter - 5 - textsize, textsize, textsize + 1);context.stroke();context.fill();context.fillStyle = obj.Get('chart.text.color');RGraph.Text(context, obj.Get('chart.text.font'), textsize,
start + 25,
gutter - 6 - textsize,
key[i],
'top');context.fill();start += context.measureText(key[i]).width + 15;}
} else if(keypos && keypos == 'graph'){
context.font = textsize + 'pt ' + obj.Get('chart.text.font');var width = 0;for (i=0; i<key.length; ++i){
width = Math.max(width, context.measureText(key[i]).width);}
width += 32;if(obj.Get('chart.key.shadow')){
context.shadowColor = '#666';context.shadowBlur = 3;context.shadowOffsetX = 2;context.shadowOffsetY = 2;}
context.beginPath();context.fillStyle = obj.Get('chart.key.background');context.strokeStyle = 'black';var xpos = canvas.width - width - gutter;if(obj.Get('chart.yaxispos') == 'right'){
xpos -= (obj.canvas.width - (obj.Get('chart.gutter') * 2));xpos += width + 6;}
if(arguments[3] != false){
if(document.all && obj.Get('chart.key.shadow')){
context.beginPath();context.fillStyle = '#666';context.fillRect(xpos + 2, gutter + 5 + 2, width - 5, 5 + ( (textsize + 5) * key.length));context.fill();context.fillStyle = obj.Get('chart.key.background');}
context.strokeRect(xpos, gutter + 5, width - 5, 5 + ( (textsize + 5) * key.length));context.fillRect(xpos, gutter + 5, width - 5, 5 + ( (textsize + 5) * key.length) );}
context.shadowColor = 'rgba(0,0,0,0)';for (var i=key.length - 1; i>=0; i--){
var j = Number(i) + 1;context.fillStyle = colors[i];context.fillRect(xpos + 5, 5 + gutter + (5 * j) + (textsize * j) - (textsize), textsize, textsize);context.fill();context.stroke();context.fillStyle = obj.Get('chart.text.color');RGraph.Text(
context,
obj.Get('chart.text.font'),
textsize,
xpos + 21,
gutter + (5 * j) + (textsize * j) + 4,
key[i]
);}
} else {
alert('[COMMON] (' + obj.id + ') Unknown key position: ' + keypos);}
}
function pd(variable)
{
RGraph.pr(variable);}
RGraph.array_clone = function (obj)
{
if(obj == null || typeof(obj) != 'object'){
return obj;}
var temp = [];for(var i=0;i<obj.length; ++i){
temp[i] = RGraph.array_clone(obj[i]);}
return temp;}
RGraph.array_reverse = function (arr)
{
var newarr = [];for (var i=arr.length - 1; i>=0; i--){
newarr.push(arr[i]);}
return newarr;}
RGraph.number_format = function (num)
{
var i;var prepend = arguments[1] ? String(arguments[1]) : '';var append = arguments[2] ? String(arguments[2]) : '';var output = '';var decimal = '';RegExp.$1 = '';if(String(num).indexOf('e') > 0){
return String(prepend + String(num) + append);}
num = String(num);if(num.indexOf('.') > 0){
num = num.replace(/\.(.*)/, '');decimal = RegExp.$1;}
var seperator = ',';var foundPoint;for (i=(num.length - 1),j=0; i>=0; j++,i--){
var character = num.charAt(i);if( j % 3 == 0 && j != 0){
output += seperator;}
output += character;}
var rev = output;output = '';for (i=(rev.length - 1); i>=0; i--){
output += rev.charAt(i);}
output = output.replace(/^-,/, '-');if(decimal.length){
output =  output + '.' + decimal;decimal = '';RegExp.$1 = '';}
if(output.charAt(0) == '-'){
output *= -1;prepend = '-' + prepend;}
return prepend + output + append;}
RGraph.Contextmenu = function (canvas, menuitems, e)
{
e = RGraph.FixEventObject(e);if(RGraph.Registry.Get('chart.contextmenu')){
RGraph.HideContext();}
RGraph.HidePalette();canvas.__object__.Set('chart.mousedown', false)
var x = e.pageX;var y = e.pageY;var div = document.createElement('div');var bg = document.createElement('div');div.className = 'RGraph_contextmenu';div.__canvas__ = canvas;
div.style.position = 'absolute';div.style.left = 0;div.style.top = 0;div.style.border = '1px solid black';div.style.backgroundColor = 'white';div.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';div.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';div.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';div.style.WebkitBorderTopRightRadius = '5px';div.style.MozBorderRadiusTopright = '5px';div.style.borderTopRightRadius = '5px';div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)';div.style.opacity = 0;bg.className = 'RGraph_contextmenu_background';bg.style.position = 'absolute';bg.style.backgroundColor = '#ccc';bg.style.borderRight = '1px solid #aaa';bg.style.top = 0;bg.style.left = 0;bg.style.width = '18px';bg.style.height = '100%';bg.style.opacity = 0;div = document.body.appendChild(div);bg = div.appendChild(bg);for (i=0; i<menuitems.length; ++i){
var menuitem = document.createElement('div');menuitem.__canvas__ = canvas;menuitem.className = 'RGraph_contextmenu_item';if(menuitems[i]){
menuitem.style.padding = '2px 5px 2px 23px';menuitem.style.fontFamily = 'Arial';menuitem.style.fontSize = '10pt';menuitem.style.fontWeight = 'normal';menuitem.innerHTML = menuitems[i][0];if(menuitems[i][1]){
if(menuitem.addEventListener){
menuitem.addEventListener("mouseover", function (e){e.target.style.backgroundColor = '#eee'; e.target.style.cursor = 'pointer';}, false);menuitem.addEventListener("mouseout", function (e){e.target.style.backgroundColor = 'white'; e.target.style.cursor = 'default';}, false);} else  {
menuitem.attachEvent("onmouseover", function (){event.srcElement.style.backgroundColor = '#eee'; event.srcElement.style.cursor = 'hand'}, false);menuitem.attachEvent("onmouseout", function (){event.srcElement.style.backgroundColor = 'white'; event.srcElement.style.cursor = 'default';}, false);}
} else {
if(menuitem.addEventListener){
menuitem.addEventListener("mouseover", function (e){e.target.style.cursor = 'default';}, false);menuitem.addEventListener("mouseout", function (e){e.target.style.cursor = 'default';}, false);} else  {
menuitem.attachEvent("onmouseover", function (){event.srcElement.style.cursor = 'default'}, false);menuitem.attachEvent("onmouseout", function (){event.srcElement.style.cursor = 'default';}, false);}
}
} else {
menuitem.style.borderBottom = '1px solid #ddd';menuitem.style.marginLeft = '25px';}
if(i == 0){
menuitem.style.WebkitBorderTopRightRadius = '5px';menuitem.style.MozBorderRadiusTopright = '5px';menuitem.style.borderTopRightRadius = '5px';}
div.appendChild(menuitem);if(menuitems[i] && menuitems[i][1]){
if(document.all){
menuitem.attachEvent('onclick', menuitems[i][1]);menuitem.attachEvent('onclick', function (){RGraph.HideContext();});} else {
menuitem.addEventListener('click', menuitems[i][1], false);}
}
}
div.style.width = div.offsetWidth + 'px';if(document.all){
bg.style.height = (div.offsetHeight - 10) + 'px';}
if(x + div.offsetWidth > document.body.offsetWidth){
x -= div.offsetWidth;}
div.style.left = x + 'px';div.style.top = y + 'px';setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.2", 50);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.4", 100);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.6", 150);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.8", 200);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 1", 250);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.2", 50);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.4", 100);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.6", 150);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.8", 200);setTimeout("if(obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 1", 250);RGraph.Registry.Set('chart.contextmenu', div);RGraph.Registry.Set('chart.contextmenu.bg', bg);RGraph.Registry.Get('chart.contextmenu').oncontextmenu = function (){return false;};RGraph.Registry.Get('chart.contextmenu.bg').oncontextmenu = function (){return false;};if(navigator.userAgent.indexOf('Opera') == -1){
canvas.onclick = function ()
{
RGraph.HideContext();}
}
window.onclick = function (e)
{
RGraph.HideContext();RGraph.Redraw();if(e.target.onclick && e.button == 0){
e.target.onclick(e);}
}
window.onresize = function (){RGraph.HideContext();}
e.stopPropagation = true;e.cancelBubble = true;return false;}
RGraph.HideContext = function ()
{
var cm = RGraph.Registry.Get('chart.contextmenu');var cmbg = RGraph.Registry.Get('chart.contextmenu.bg');if(cm){
cm.style.visibility = 'hidden';cm.style.display = 'none';RGraph.Registry.Set('chart.contextmenu', null);cmbg.style.visibility = 'hidden';cmbg.style.display = 'none';RGraph.Registry.Set('chart.contextmenu.bg', null);}
}
RGraph.ShowContext = function (obj)
{
RGraph.HidePalette();if(obj.Get('chart.contextmenu') && obj.Get('chart.contextmenu').length){
var isOpera = navigator.userAgent.indexOf('Opera') >= 0;var isSafari = navigator.userAgent.indexOf('Safari') >= 0;var isChrome = navigator.userAgent.indexOf('Chrome') >= 0;var isMacFirefox = navigator.userAgent.indexOf('Firefox') > 0 && navigator.userAgent.indexOf('Mac') > 0;if(((!isOpera && !isSafari) || isChrome) && !isMacFirefox){
obj.canvas.oncontextmenu = function (e)
{
e = RGraph.FixEventObject(e);if(e.ctrlKey) return true;RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e);return false;}
} else {
obj.canvas.addEventListener('dblclick', function (e)
{
if(e.ctrlKey) return true;if(!RGraph.Registry.Get('chart.contextmenu')){
RGraph.Contextmenu(obj.canvas, obj.Get('chart.contextmenu'), e);}
}, false);}
}
}
RGraph.DrawBars = function (obj)
{
var hbars = obj.Get('chart.background.hbars');obj.context.beginPath();for (i=0; i<hbars.length; ++i){
if(hbars[i][1] == null){
hbars[i][1] = obj.max;} else if(hbars[i][0] + hbars[i][1] > obj.max){
hbars[i][1] = obj.max - hbars[i][0];}
if(Math.abs(hbars[i][1]) > obj.max){
hbars[i][1] = -1 * obj.max;}
if(Math.abs(hbars[i][0]) > obj.max){
hbars[i][0] = obj.max;}
if(hbars[i][0] + hbars[i][1] < (-1 * obj.max) ){
hbars[i][1] = -1 * (obj.max + hbars[i][0]);}
var ystart = (obj.grapharea - ((hbars[i][0] / obj.max) * obj.grapharea));var height = (Math.min(hbars[i][1], obj.max - hbars[i][0]) / obj.max) * obj.grapharea;if(obj.Get('chart.xaxispos') == 'center'){
ystart /= 2;height /= 2;}
ystart += obj.Get('chart.gutter')
var x = obj.Get('chart.gutter');var y = ystart - height;var w = obj.canvas.width - (2 * obj.Get('chart.gutter'));var h = height;if(navigator.userAgent.indexOf('Opera') != -1 && obj.Get('chart.xaxispos') == 'center' && h < 0){
h *= -1;y = y - h;}
obj.context.fillStyle = hbars[i][2];obj.context.fillRect(x, y, w, h);}
obj.context.fill();}
RGraph.DrawInGraphLabels = function (obj)
{
var canvas = obj.canvas;var context = obj.context;var labels = obj.Get('chart.labels.ingraph');RGraph.NoShadow(obj);if(labels && labels.length > 0){
for (var i=0; i<labels.length; ++i){
if(labels[i]){
var coords = obj.coords[i];if(coords && coords.length > 0){
var x = (obj.type == 'bar' ? coords[0] + (coords[2] / 2) : coords[0]);var y = (obj.type == 'bar' ? coords[1] + (coords[3] / 2) : coords[1]) - 5;context.beginPath();context.fillStyle = 'black';context.strokeStyle = '#666';if(obj.type == 'bar'){
if(obj.Get('chart.variant') == 'dot'){
context.moveTo(x, y - 15);context.lineTo(x, y - 25);} else if(obj.Get('chart.variant') == 'arrow'){
context.moveTo(x, y - 15);context.lineTo(x, y - 25);} else {
context.arc(x, y, 1, 0, 6.28, 0);context.moveTo(x, y);context.lineTo(x, y - 25);}
} else if(obj.type == 'line'){
context.moveTo(x, y - 5);context.lineTo(x, y - 25);context.moveTo(x, y);context.lineTo(x - 3, y - 7);context.lineTo(x + 3, y - 7);context.closePath();}
context.stroke();context.fill();var width = context.measureText(labels[i]).width;RGraph.Text(context, 'Verdana', obj.Get('chart.text.size'), x, y - 25, String(labels[i]), 'bottom', 'center', null, null, '#fff');context.fill();}
}
}
}
}
RGraph.FixEventObject = function (e)
{
if(document.all){
var e = event;e.pageX = (event.clientX + document.body.scrollLeft);e.pageY = (event.clientY + document.body.scrollTop);e.target = event.srcElement;if(!document.body.scrollTop && document.documentElement.scrollTop){
e.pageX += parseInt(document.documentElement.scrollLeft);e.pageY += parseInt(document.documentElement.scrollTop);}
}
if(typeof(e.offsetX) == 'undefined' && typeof(e.offsetY) == 'undefined'){
var coords = RGraph.getMouseXY(e);e.offsetX = coords[0];e.offsetY = coords[1];}
return e;}
RGraph.DrawCrosshairs = function (obj)
{
if(obj.Get('chart.crosshairs')){
var canvas = obj.canvas;var context = obj.context;if(obj.Get('chart.tooltips') && obj.Get('chart.tooltips').length > 0){
alert('[' + obj.type.toUpperCase() + '] Sorry - you cannot have crosshairs enabled with tooltips! Turning off crosshairs...');obj.Set('chart.crosshairs', false);return;}
canvas.onmousemove = function (e)
{
var e = RGraph.FixEventObject(e);var canvas = obj.canvas;var context = obj.context;var gutter = obj.Get('chart.gutter');var width = canvas.width;var height = canvas.height;var mouseCoords = RGraph.getMouseXY(e);var x = mouseCoords[0];var y = mouseCoords[1];RGraph.Clear(canvas);obj.Draw();if(   x > gutter
&& y > gutter
&& x < (width - gutter)
&& y < (height - gutter)
){
context.lineWidth = 1;context.beginPath();context.strokeStyle = obj.Get('chart.crosshairs.color');context.moveTo(x, gutter);context.lineTo(x, height - gutter);context.moveTo(gutter, y);context.lineTo(width - gutter, y);context.stroke();}
}
}
}
RGraph.Annotate = function (obj)
{
if(obj.Get('chart.annotatable')){
var canvas = obj.canvas;var context = obj.context;canvas.onmousedown = function (e)
{
if(e.button == 0){
e.target.__object__.Set('chart.mousedown', true);var context = e.target.__object__.canvas.getContext('2d');context.beginPath();var coords = RGraph.getMouseXY(e);var x = coords[0];var y = coords[1];RGraph.Registry.Set('annotate.actions', [obj.Get('chart.annotate.color')]);context.strokeStyle = obj.Get('chart.annotate.color');context.moveTo(x, y);context.lineWidth = 1;RGraph.Registry.Set('started.annotating', false);}
return false;}
window.onmouseup = function (e)
{
var tags = document.getElementsByTagName('canvas');for (var i=0; i<tags.length; ++i){
if(tags[i].__object__){
tags[i].__object__.Set('chart.mousedown', false);}
}
if(RGraph.Registry.Get('annotate.actions') && RGraph.Registry.Get('annotate.actions').length > 0 && window.localStorage){
var id = '__rgraph_annotations_' + e.target.id + '__';var annotations = window.localStorage[id] ? window.localStorage[id] + '|' : '';annotations += RGraph.Registry.Get('annotate.actions');window.localStorage[id] = annotations;}
RGraph.Registry.Set('annotate.actions', []);}
canvas.onmouseup = window.onmouseup;canvas.onmouseout = window.onmouseup;canvas.onmousemove = function (e)
{
var e = RGraph.FixEventObject(e);var obj = e.target.__object__;var coords = RGraph.getMouseXY(e);var x = coords[0];var y = coords[1];var gutter = obj.Get('chart.gutter');var width = canvas.width;var height = canvas.height;obj.context.lineWidth = 1;if(
x > gutter && x < (width - gutter)
&& y > gutter && y < (height - gutter)
){
canvas.style.cursor = 'crosshair';if(obj.Get('chart.mousedown')){
if(
x > gutter && x < (width - gutter)
&& y > gutter && y < (height - gutter)
){
if( (obj.type != 'hbar' && obj.type != 'gantt') || x > (3 * gutter)){
if(RGraph.Registry.Get('started.annotating') == false){
context.moveTo(x, y);RGraph.Registry.Set('started.annotating', true)
}
context.lineTo(x, y);RGraph.Registry.Set('annotate.actions', RGraph.Registry.Get('annotate.actions') + '|' + x + ',' + y);context.stroke();}
} else {
context.moveTo(x, y);}
}
} else {
canvas.style.cursor = 'default';}
}
if(!arguments[1]){
RGraph.ReplayAnnotations(obj);}
}
}
RGraph.Showpalette = function (e)
{
var isSafari = navigator.userAgent.indexOf('Safari') ? true : false;e = RGraph.FixEventObject(e);var canvas = e.target.parentNode.__canvas__;var context = canvas.getContext('2d');var obj = canvas.__object__;var div = document.createElement('DIV');var coords = RGraph.getMouseXY(e);div.__object__ = obj;div.className = 'RGraph_palette';div.style.position = 'absolute';div.style.backgroundColor = 'white';div.style.border = '1px solid black';div.style.left = 0;div.style.top = 0;div.style.padding = '3px';div.style.paddingBottom = 0;div.style.paddingRight = 0;div.style.opacity = 0;div.style.boxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';div.style.MozBoxShadow = 'rgba(96,96,96,0.5) 3px 3px 3px';div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#666666,direction=135)';var common_css = 'padding: 1px; display: inline; display: inline-block; width: 15px; height: 15px; margin-right: 3px; cursor: ' + (document.all ? 'hand;' : 'pointer; ');var common_mouseover = ' onmouseover="this.style.border = \'1px black solid\'; this.style.padding = 0"';var common_mouseout = ' onmouseout="this.style.border = 0; this.style.padding = \'1px\'" ';var safari_css = isSafari ? 'margin-bottom: 3px' : '';var str = '';var colors = ['red', 'blue', 'green', 'black', 'yellow', 'magenta', 'pink', 'cyan', 'purple', '#ddf', 'gray', '#36905c'];for (i=0; i<colors.length; ++i){
str = str + '<div ' + common_mouseover + common_mouseout + ' style="background-color: ' + colors[i] + '; ' + common_css + safari_css + '" onclick="this.parentNode.__object__.Set(\'chart.annotate.color\', this.style.backgroundColor); this.parentNode.style.display = \'none\'">&nbsp;</div>';if(i == 5){
str += '<br />';}
}
div.innerHTML = str;document.body.appendChild(div);div.style.width = (div.offsetWidth - 5) + 'px';div.style.height = (div.offsetHeight - 5) + 'px';div.style.left = Math.max(0, e.pageX - div.offsetWidth - 2) + 'px';div.style.top = (e.pageY - div.offsetHeight - 2) + 'px';RGraph.Registry.Set('palette', div);setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.2", 50);setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.4", 100);setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.6", 150);setTimeout("RGraph.Registry.Get('palette').style.opacity = 0.8", 200);setTimeout("RGraph.Registry.Get('palette').style.opacity = 1", 250);RGraph.HideContext();window.onclick = function ()
{
RGraph.HidePalette();}
e.cancelBubble = true;e.stopPropagation = true;}
RGraph.HidePalette = function ()
{
var div = RGraph.Registry.Get('palette');if(typeof(div) == 'object' && div){
div.style.visibility = 'hidden';div.style.display = 'none';RGraph.Registry.Set('palette', null);}
}
RGraph.ClearAnnotations = function (id)
{
if(window.localStorage && window.localStorage['__rgraph_annotations_' + id + '__'] && window.localStorage['__rgraph_annotations_' + id + '__'].length){
window.localStorage['__rgraph_annotations_' + id + '__'] = [];}
}
RGraph.ReplayAnnotations = function (obj)
{
if(!window.localStorage){
return;}
var context = obj.context;var annotations = window.localStorage['__rgraph_annotations_' + obj.id + '__'];var i, len, move, coords;context.lineWidth = 2;if(annotations && annotations.length){
annotations = annotations.split('|');} else {
return;}
for (i=0, len=annotations.length; i<len; ++i){
if(!annotations[i].match(/^[0-9]+,[0-9]+$/)){
context.stroke();context.beginPath();context.strokeStyle = annotations[i];move = true;continue;}
coords = annotations[i].split(',');if(move){
context.moveTo(coords[0], coords[1]);move = false;} else {
context.lineTo(coords[0], coords[1]);}
}
context.stroke();}
RGraph.rtrim = function (str)
{
return str.replace(/( |\n|\r|\t)+$/, '');}
RGraph.Draw3DAxes = function (obj)
{
var gutter = obj.Get('chart.gutter');var context = obj.context;var canvas = obj.canvas;context.strokeStyle = '#aaa';context.fillStyle = '#ddd';context.beginPath();context.moveTo(gutter, gutter);context.lineTo(gutter + 10, gutter - 5);context.lineTo(gutter + 10, canvas.height - gutter - 5);context.lineTo(gutter, canvas.height - gutter);context.closePath();context.stroke();context.fill();context.beginPath();context.moveTo(gutter, canvas.height - gutter);context.lineTo(gutter + 10, canvas.height - gutter - 5);context.lineTo(canvas.width - gutter + 10,  canvas.height - gutter - 5);context.lineTo(canvas.width - gutter, canvas.height - gutter);context.closePath();context.stroke();context.fill();}
RGraph.NoShadow = function (obj)
{
obj.context.shadowColor = 'rgba(0,0,0,0)';obj.context.shadowBlur = 0;obj.context.shadowOffsetX = 0;obj.context.shadowOffsetY = 0;}
RGraph.Zoom = function (e)
{
e = RGraph.FixEventObject(e);if(e.target.parentNode.__canvas__.__object__.Get('chart.zoom.mode') == 'window'){
return RGraph.ZoomWindow(e);}
var canvas = e.target.__canvas__;var context = canvas.getContext('2d');var obj = canvas.__object__;var tmp = canvas;var coords = RGraph.getCanvasXY(canvas);var factor = obj.Get('chart.zoom.factor') - 1;var x = coords[0];var y = coords[1];var img = document.createElement('img');img.style.border = '2px dashed gray';img.style.width = canvas.width + 'px';img.style.height = canvas.height + 'px';img.style.position = 'absolute';img.style.left = x + 'px';img.style.top = y + 'px';img.style.backgroundColor = 'white';img.style.opacity = obj.Get('chart.zoom.fade.in') ? 0 : 1;img.src = canvas.toDataURL();document.body.appendChild(img);
__zoomedimage__ = img;img.onclick = function (e)
{
e.cancelBubble = true;if(document.all) event.stopPropagation();return false;};
__HideZoomedCanvas__ = function ()
{
if(obj.Get('chart.zoom.fade.out')){
for (var i=10,j=1; i>=0; --i, ++j){
setTimeout("__zoomedimage__.style.opacity = " + String(i / 10), j * 50);}
}
setTimeout("__zoomedimage__.style.display = 'none'", obj.Get('chart.zoom.fade.out') ? 510 : 0);}
setTimeout(function (){window.onclick = __HideZoomedCanvas__;}, 1);var width = parseInt(canvas.width);var height = parseInt(canvas.height);var frames = obj.Get('chart.zoom.frames');var delay = obj.Get('chart.zoom.delay');if(obj.Get('chart.zoom.hdir') == 'center'){
for (var i=1; i<=frames; ++i){
var newWidth = width * factor * (i/frames) + width;var rightHandEdge = x + canvas.width;var newLeft = (x + (canvas.width / 2)) - (newWidth / 2);setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay);}
} else if(obj.Get('chart.zoom.hdir') == 'left'){
for (var i=1; i<=frames; ++i){
var newWidth = width * factor * (i/frames) + width;var rightHandEdge = x + canvas.width;var newLeft = rightHandEdge - newWidth;setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'; __zoomedimage__.style.left = '" + newLeft + "px'", i * delay);}
} else {
for (var i=1; i<=frames; ++i){
var newWidth = width * factor * (i/frames) + width;setTimeout("__zoomedimage__.style.width = '" + String(newWidth) + "px'", i * delay);}
}
if(obj.Get('chart.zoom.vdir') == 'up'){
for (var i=1; i<=frames; ++i){
var newHeight = (height * factor * (i/frames)) + height;var bottomEdge = y + canvas.height;var newTop = bottomEdge - newHeight;setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay);}
} else if(obj.Get('chart.zoom.vdir') == 'center'){
for (var i=1; i<=frames; ++i){
var newHeight = (height * factor * (i/frames)) + height;var bottomEdge = (y + (canvas.height / 2)) + (newHeight / 2);var newTop = bottomEdge - newHeight;setTimeout("__zoomedimage__.style.height = '" + String(newHeight) + "px'; __zoomedimage__.style.top = '" + newTop + "px'", i * delay);}
} else {
for (var i=1; i<=frames; ++i){
setTimeout("__zoomedimage__.style.height = '" + String(height * factor * (i/frames) + height) + "px'", i * delay);}
}
if(obj.Get('chart.zoom.fade.in')){
for (var i=1; i<=frames; ++i){
setTimeout("__zoomedimage__.style.opacity = " + String(i / frames), i * delay);}
}
if(obj.Get('chart.zoom.shadow')){
for (var i=1; i<=frames; ++i){
setTimeout("__zoomedimage__.style.boxShadow = 'rgba(0,0,0," + Number(i / frames) / 2 + ") 3px 3px 3px'", i * delay);setTimeout("__zoomedimage__.style.MozBoxShadow = 'rgba(0,0,0," + Number(i / frames) / 2 + ") 3px 3px 3px'", i * delay);setTimeout("__zoomedimage__.style.WebkitBoxShadow = 'rgba(0,0,0," + Number(i / frames) / 2 + ") 3px 3px 3px'", i * delay);}
}
}
RGraph.ZoomWindow = function (canvas)
{
canvas.onmousemove = function (e)
{
e = RGraph.FixEventObject(e);var obj = e.target.__object__;var canvas = obj.canvas;var context = obj.context;var coords = RGraph.getMouseXY(e);if(!RGraph.Registry.Get('chart.zoomed.div')){
var div = document.createElement('div');div.className = 'RGraph_zoom_window';div.style.width = obj.Get('chart.zoom.thumbnail.width') + 'px';div.style.height = obj.Get('chart.zoom.thumbnail.height') + 'px';div.style.border = '2px dashed gray';div.style.position = 'absolute';div.style.overflow = 'hidden';div.style.backgroundColor = 'white';div.style.left = '-1000px';div.style.top = '-1000px';div.style.borderRadius = '5px';div.style.MozBorderRadius = '5px';div.style.WebkitBorderRadius = '5px';if(obj.Get('chart.zoom.shadow')){
div.style.boxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';div.style.MozBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';div.style.WebkitBoxShadow = 'rgba(0,0,0,0.5) 3px 3px 3px';}
div.__object__ = obj;document.body.appendChild(div);var img = document.createElement('img');img.width = obj.canvas.width * obj.Get('chart.zoom.factor');img.height = obj.canvas.height * obj.Get('chart.zoom.factor');img.style.position = 'relative';img.style.backgroundColor = 'white';img.src = obj.canvas.toDataURL();div.appendChild(img);RGraph.Registry.Set('chart.zoomed.div', div);RGraph.Registry.Set('chart.zoomed.img', img);setTimeout("RGraph.Registry.Get('chart.zoomed.div').__object__.canvas.onmouseover()", 5);} else {
div = RGraph.Registry.Get('chart.zoomed.div');img = RGraph.Registry.Get('chart.zoomed.img');}
if(div && div.style.opacity < 1){
setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400);}
var c = RGraph.getCanvasXY(obj.canvas);var x = c[0];var y = c[1];var offset = 7;div.style.left = (e.pageX - obj.Get('chart.zoom.thumbnail.width') - offset) + 'px';div.style.top = (e.pageY -  obj.Get('chart.zoom.thumbnail.height') - offset) + 'px';var l = (obj.Get('chart.zoom.thumbnail.width') / 2) - (coords[0] * obj.Get('chart.zoom.factor'));var t = (obj.Get('chart.zoom.thumbnail.height') / 2) - (coords[1] * obj.Get('chart.zoom.factor'));img.style.left = (l + ((obj.Get('chart.zoom.thumbnail.width') / 2) * obj.Get('chart.zoom.factor'))) + 'px';img.style.top = (t + ((obj.Get('chart.zoom.thumbnail.height') / 2) * obj.Get('chart.zoom.factor'))) + 'px';}
canvas.onmouseover = function (e)
{
var div = RGraph.Registry.Get('chart.zoomed.div');if(!div) return;var obj = div.__object__;var targetWidth = obj.Get('chart.zoom.thumbnail.width');var targetHeight = obj.Get('chart.zoom.thumbnail.height');div.style.width = 0;div.style.height = 0;if(obj.Get('chart.zoom.fade.in')){
RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2;setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 100);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 200);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8", 300);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 400);} else {
setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 1", 1);}
setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (1/5) ) + "px'", 75);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (2/5) ) + "px'", 150);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (3/5) ) + "px'", 225);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (4/5) ) + "px'", 300);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.width = '" + (targetWidth * (5/5) ) + "px'", 325);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (1/5) ) + "px'", 75);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (2/5) ) + "px'", 150);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (3/5) ) + "px'", 225);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (4/5) ) + "px'", 300);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.height = '" + (targetHeight * (5/5) ) + "px'", 375);}
canvas.onmouseout = function (e)
{
if(RGraph.Registry.Get('chart.zoomed.div').__object__.Get('chart.zoom.fade.out')){
RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.8;setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.6", 100);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.4", 200);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0.2", 300);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.opacity = 0", 400);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 400);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 400);} else {
setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.left = '-400px'", 1);setTimeout("RGraph.Registry.Get('chart.zoomed.div').style.top = '-400px'", 1);}
}
}
RGraph.ShowZoomWindow = function (obj)
{
var gutter = obj.Get('chart.gutter');if(obj.Get('chart.zoom.mode') == 'thumbnail'){
RGraph.ZoomWindow(obj.canvas);}
}
RGraph.OldBrowserCompat = function (context)
{
if(!context.measureText){
context.measureText = function (text)
{
var textObj = document.createElement('DIV');textObj.innerHTML = text;textObj.style.backgroundColor = 'white';textObj.style.position = 'absolute';textObj.style.top = -100
textObj.style.left = 0;document.body.appendChild(textObj);var width = {width: textObj.offsetWidth};textObj.style.display = 'none';return width;}
}
if(!context.fillText){
context.fillText = function (text, targetX, targetY)
{
return false;}
}
}
RGraph.getSegment = function (e)
{
RGraph.FixEventObject(e);var obj = e.target.__object__;var canvas = obj.canvas;var context = obj.context;var mouseCoords = RGraph.getMouseXY(e);var x = mouseCoords[0] - obj.centerx;var y = mouseCoords[1] - obj.centery;var r = obj.radius;var theta = Math.atan(y / x);var hyp = y / Math.sin(theta);var angles = obj.angles;var ret = [];theta *= 57.3
if(obj.type == 'radar' || obj.type == 'donut'){
if(   (isNaN(hyp) && Math.abs(mouseCoords[0]) < (obj.centerx - r) )
|| (isNaN(hyp) && Math.abs(mouseCoords[0]) > (obj.centerx + r))
|| (!isNaN(hyp) && Math.abs(hyp) > r)){
return;} else if(obj.Get('chart.isdonut') && Math.abs(hyp) < (obj.radius / 2)){
return;}
}
if(x < 0 && y >= 0){
theta += 180;} else if(x < 0 && y < 0){
theta += 180;} else if(x > 0 && y < 0){
theta += 360;}
for (var i=0; i<angles.length; ++i){
if(theta >= angles[i][0] && theta < angles[i][1]){
hyp = Math.abs(hyp);if(obj.type == 'radar' && hyp > angles[i][2]){
return null;}
if(obj.type == 'pie' && hyp > obj.radius){
return null;}
if(obj.type == 'donut' && (hyp > obj.radius || hyp < obj.holewidth) ){
return null;}
ret[0] = obj.centerx;ret[1] = obj.centery;ret[2] = (obj.type == 'radar') ? angles[i][2] : obj.radius;ret[3] = angles[i][0];ret[4] = angles[i][1];return ret;}
}
return null;}
RGraph.Async = function (func)
{
return setTimeout(func, arguments[1] ? arguments[1] : 1);}
RGraph.random = function (min, max)
{
var dp = arguments[2] ? arguments[2] : 0;var r = Math.random();return Number((((max - min) * r) + min).toFixed(dp));}
RGraph.strokedCurvyRect = function (context, x, y, w, h)
{
var r = arguments[5] ? arguments[5] : 3;var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;var corner_br = (arguments[8] || arguments[8] == null) ? true : false;var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;context.beginPath();context.moveTo(x + (corner_tl ? r : 0), y);context.lineTo(x + w - (corner_tr ? r : 0), y);if(corner_tr){
context.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2, false);}
context.lineTo(x + w, y + h - (corner_br ? r : 0) );if(corner_br){
context.arc(x + w - r, y - r + h, r, Math.PI * 2, Math.PI * 0.5, false);}
context.lineTo(x + (corner_bl ? r : 0), y + h);if(corner_bl){
context.arc(x + r, y - r + h, r, Math.PI * 0.5, Math.PI, false);}
context.lineTo(x, y + (corner_tl ? r : 0) );if(corner_tl){
context.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5, false);}
context.stroke();}
RGraph.filledCurvyRect = function (context, x, y, w, h)
{
var r = arguments[5] ? arguments[5] : 3;var corner_tl = (arguments[6] || arguments[6] == null) ? true : false;var corner_tr = (arguments[7] || arguments[7] == null) ? true : false;var corner_br = (arguments[8] || arguments[8] == null) ? true : false;var corner_bl = (arguments[9] || arguments[9] == null) ? true : false;context.beginPath();if(corner_tl){
context.moveTo(x + r, y + r);context.arc(x + r, y + r, r, Math.PI, 1.5 * Math.PI, false);} else {
context.fillRect(x, y, r, r);}
if(corner_tr){
context.moveTo(x + w - r, y + r);context.arc(x + w - r, y + r, r, 1.5 * Math.PI, 0, false);} else {
context.moveTo(x + w - r, y);context.fillRect(x + w - r, y, r, r);}
if(corner_br){
context.moveTo(x + w - r, y + h - r);context.arc(x + w - r, y - r + h, r, 0, Math.PI / 2, false);} else {
context.moveTo(x + w - r, y + h - r);context.fillRect(x + w - r, y + h - r, r, r);}
if(corner_bl){
context.moveTo(x + r, y + h - r);context.arc(x + r, y - r + h, r, Math.PI / 2, Math.PI, false);} else {
context.moveTo(x, y + h - r);context.fillRect(x, y + h - r, r, r);}
context.fillRect(x + r, y, w - r - r, h);context.fillRect(x, y + r, w, h - r - r);context.fill();}
RGraph.Timer = function (label)
{
var d = new Date();console.log(label + ': ' + d.getSeconds() + '.' + d.getMilliseconds());}

// THIS FILE HAS BEEN MINIFIED

if(typeof(RGraph) == 'undefined') RGraph = {};RGraph.Bar = function (id, data)
{
this.id = id;this.canvas = document.getElementById(id);this.context = this.canvas.getContext ? this.canvas.getContext("2d") : null;this.canvas.__object__ = this;this.type = 'bar';this.max = 0;this.stackedOrGrouped = false;RGraph.OldBrowserCompat(this.context);this.properties = {
'chart.background.barcolor1':   'rgba(0,0,0,0)',
'chart.background.barcolor2':   'rgba(0,0,0,0)',
'chart.background.grid':        true,
'chart.background.grid.color':  '#ddd',
'chart.background.grid.width':  1,
'chart.background.grid.hsize':  20,
'chart.background.grid.vsize':  20,
'chart.background.grid.vlines': true,
'chart.background.grid.hlines': true,
'chart.background.grid.border': true,
'chart.ytickgap':               20,
'chart.smallyticks':            3,
'chart.largeyticks':            5,
'chart.hmargin':                5,
'chart.strokecolor':            '#666',
'chart.axis.color':             'black',
'chart.gutter':                 25,
'chart.labels':                 null,
'chart.labels.ingraph':         null,
'chart.labels.above':           false,
'chart.ylabels':                true,
'chart.ylabels.count':          5,
'chart.xaxispos':               'bottom',
'chart.yaxispos':               'left',
'chart.text.color':             'black',
'chart.text.size':              10,
'chart.text.angle':             0,
'chart.text.font':              'Verdana',
'chart.ymax':                   null,
'chart.title':                  '',
'chart.title.vpos':             null,
'chart.colors':                 ['rgb(0,0,255)', '#0f0', '#00f', '#ff0', '#0ff', '#0f0'],
'chart.grouping':               'grouped',
'chart.variant':                'bar',
'chart.shadow':                 false,
'chart.shadow.color':           '#666',
'chart.shadow.offsetx':         3,
'chart.shadow.offsety':         3,
'chart.shadow.blur':            3,
'chart.tooltips':               null,
'chart.tooltip.effect':         'fade',
'chart.background.hbars':       null,
'chart.key':                    [],
'chart.key.background':         'white',
'chart.key.position':           'graph',
'chart.key.shadow':             false,
'chart.contextmenu':            null,
'chart.line':                   null,
'chart.units.pre':              '',
'chart.units.post':             '',
'chart.scale.decimals':         0,
'chart.crosshairs':             false,
'chart.crosshairs.color':       '#333',
'chart.linewidth':              1,
'chart.annotatable':            false,
'chart.annotate.color':         'black',
'chart.zoom.factor':            1.5,
'chart.zoom.fade.in':           true,
'chart.zoom.fade.out':          true,
'chart.zoom.hdir':              'right',
'chart.zoom.vdir':              'down',
'chart.zoom.frames':            10,
'chart.zoom.delay':             50,
'chart.zoom.shadow':            true,
'chart.zoom.mode':              'canvas',
'chart.zoom.thumbnail.width':   75,
'chart.zoom.thumbnail.height':  75
}
if(!this.canvas){
alert('[BAR] No canvas support');return;}
if(typeof(RGraph) == 'undefined'){
alert('[BAR] Fatal error: The common library does not appear to have been included');}
for (i=0; i<data.length; ++i){
if(typeof(data[i]) == 'object'){
this.stackedOrGrouped = true;}
}
this.data = data;this.coords = [];}
RGraph.Bar.prototype.Set = function (name, value)
{
if(name == 'chart.labels.abovebar'){
name = 'chart.labels.above';}
this.properties[name.toLowerCase()] = value;}
RGraph.Bar.prototype.Get = function (name)
{
if(name == 'chart.labels.abovebar'){
name = 'chart.labels.above';}
return this.properties[name];}
RGraph.Bar.prototype.Draw = function ()
{
this.gutter = this.Get('chart.gutter');if(   (this.Get('chart.variant') == 'pyramid' || this.Get('chart.variant') == 'dot')
&& typeof(this.Get('chart.tooltips')) == 'object'
&& this.Get('chart.tooltips')
&& this.Get('chart.tooltips').length > 0){
alert('[BAR] (' + this.id + ') Sorry, tooltips are not supported with dot or pyramid charts');}
this.coords = [];this.max = 0;this.grapharea = this.canvas.height - ( (2 * this.gutter));this.halfgrapharea = this.grapharea / 2;this.halfTextHeight = this.Get('chart.text.size') / 2;RGraph.background.Draw(this);if(this.Get('chart.variant') == 'sketch'){
this.DrawAxes();this.Drawbars();} else {
this.Drawbars();this.DrawAxes();}
this.DrawLabels();if(this.Get('chart.key').length){
RGraph.DrawKey(this, this.Get('chart.key'), this.Get('chart.colors'));}
RGraph.ShowContext(this);var line = this.Get('chart.line');if(line){
if(line.original_data[0].length != this.data.length){
alert("[BAR] You're adding a line with a differing amount of data points to the bar chart - this is not permitted");}
if(this.Get('chart.xaxispos') != line.Get('chart.xaxispos')){
alert("[BAR] Using different X axis positions when combining the Bar and Line is not advised");}
line.Set('chart.gutter', this.Get('chart.gutter'));line.Set('chart.noaxes', true);line.Set('chart.background.barcolor1', 'rgba(0,0,0,0)');line.Set('chart.background.barcolor2', 'rgba(0,0,0,0)');line.Set('chart.background.grid', false);line.Set('chart.ylabels', false);line.Set('chart.hmargin', (this.canvas.width - (2 * this.gutter)) / (line.original_data[0].length * 2));if(this.Get('chart.ymax')){
line.Set('chart.ymax', this.Get('chart.ymax'));}
line.Draw();}
if(this.Get('chart.labels.ingraph')){
RGraph.DrawInGraphLabels(this);}
if(this.Get('chart.crosshairs')){
RGraph.DrawCrosshairs(this);}
if(this.Get('chart.annotatable')){
RGraph.Annotate(this);}
if(this.Get('chart.zoom.mode') == 'thumbnail'){
RGraph.ShowZoomWindow(this);}
}
RGraph.Bar.prototype.DrawAxes = function ()
{
var gutter = this.gutter;var xaxispos = this.Get('chart.xaxispos');var yaxispos = this.Get('chart.yaxispos');this.context.beginPath();this.context.strokeStyle = this.Get('chart.axis.color');this.context.lineWidth = 1;if(yaxispos == 'right'){
this.context.moveTo(this.canvas.width - gutter, gutter);this.context.lineTo(this.canvas.width - gutter, this.canvas.height - gutter);} else {
this.context.moveTo(gutter, gutter);this.context.lineTo(gutter, this.canvas.height - gutter);}
this.context.moveTo(gutter, (xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter));this.context.lineTo(this.canvas.width - gutter, xaxispos == 'center' ? this.canvas.height / 2 : this.canvas.height - gutter);var yTickGap = (this.canvas.height - (2 * gutter)) / 10;var xpos = yaxispos == 'left' ? gutter : this.canvas.width - gutter;for (y=gutter;xaxispos == 'center' ? y <= (this.canvas.height - gutter) : y < (this.canvas.height - gutter);y += yTickGap){
if(xaxispos == 'center' && y == (this.canvas.height / 2)) continue;this.context.moveTo(xpos, y);this.context.lineTo(xpos + (yaxispos == 'left' ? -3 : 3), y);}
xTickGap = (this.canvas.width - (2 * gutter) ) / this.data.length;yStart = this.canvas.height - gutter;yEnd = (this.canvas.height - gutter) + 3;if(xaxispos == 'center'){
yStart = (this.canvas.height / 2) + 3;yEnd = (this.canvas.height / 2) - 3;}
for (x=gutter + (yaxispos == 'left' ? xTickGap : 0); x<this.canvas.width - gutter + (yaxispos == 'left' ? 5 : 0); x+=xTickGap){
this.context.moveTo(x, yStart);this.context.lineTo(x, yEnd);}
this.context.stroke();}
RGraph.Bar.prototype.Drawbars = function ()
{
this.context.lineWidth = this.Get('chart.linewidth');this.context.strokeStyle = this.Get('chart.strokecolor');this.context.fillStyle = this.Get('chart.colors')[0];var prevX = 0;var prevY = 0;var gutter = this.gutter;var decimals = this.Get('chart.scale.decimals');if(this.Get('chart.ymax')){
this.max = this.Get('chart.ymax');this.scale = [
(this.max * (1/5)).toFixed(decimals),
(this.max * (2/5)).toFixed(decimals),
(this.max * (3/5)).toFixed(decimals),
(this.max * (4/5)).toFixed(decimals),
this.max.toFixed(decimals)
];} else {
for (i=0; i<this.data.length; ++i){
if(typeof(this.data[i]) == 'object'){
var value = this.Get('chart.grouping') == 'grouped' ? Number(RGraph.array_max(this.data[i], true)) : Number(RGraph.array_sum(this.data[i])) ;} else {
var value = Number(this.data[i]);}
this.max = Math.max(Math.abs(this.max), Math.abs(value));}
this.scale = RGraph.getScale(this.max);this.max = this.scale[4];if(this.Get('chart.scale.decimals')){
var decimals = this.Get('chart.scale.decimals');this.scale[0] = Number(this.scale[0]).toFixed(decimals);this.scale[1] = Number(this.scale[1]).toFixed(decimals);this.scale[2] = Number(this.scale[2]).toFixed(decimals);this.scale[3] = Number(this.scale[3]).toFixed(decimals);this.scale[4] = Number(this.scale[4]).toFixed(decimals);}
}
if(this.Get('chart.background.hbars') && this.Get('chart.background.hbars').length > 0){
RGraph.DrawBars(this);}
var variant = this.Get('chart.variant');if(variant == '3d'){
RGraph.Draw3DAxes(this);}
var xaxispos = this.Get('chart.xaxispos');var width = (this.canvas.width - (2 * gutter) ) / this.data.length;var orig_height = height;var hmargin = this.Get('chart.hmargin');var shadow = this.Get('chart.shadow');var shadowColor = this.Get('chart.shadow.color');var shadowBlur = this.Get('chart.shadow.blur');var shadowOffsetX = this.Get('chart.shadow.offsetx');var shadowOffsetY = this.Get('chart.shadow.offsety');var strokeStyle = this.Get('chart.strokecolor');var colors = this.Get('chart.colors');for (i=0; i<this.data.length; ++i){
var height = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - (2 * gutter) );if(xaxispos == 'center'){
height /= 2;}
var x = (i * width) + gutter;var y = xaxispos == 'center' ? (this.canvas.height / 2) - height : this.canvas.height - height - gutter;if(height < 0){
y += height;height = Math.abs(height);}
if(shadow){
this.context.shadowColor = shadowColor;this.context.shadowBlur = shadowBlur;this.context.shadowOffsetX = shadowOffsetX;this.context.shadowOffsetY = shadowOffsetY;}
this.context.beginPath();if(typeof(this.data[i]) == 'number'){
var barWidth = width - (2 * hmargin);this.context.strokeStyle = strokeStyle;this.context.fillStyle = colors[0];if(variant == 'sketch'){
this.context.lineCap = 'round';var sketchOffset = 3;this.context.beginPath();this.context.strokeStyle = colors[0];this.context.moveTo(x + hmargin + 2, y + height - 2);this.context.lineTo(x + hmargin , y - 2);this.context.moveTo(x + hmargin - 3, y + -2 + (this.data[i] < 0 ? height : 0));this.context.bezierCurveTo(x + ((hmargin + width) * 0.33),y + 5 + (this.data[i] < 0 ? height - 10: 0),x + ((hmargin + width) * 0.66),y + 5 + (this.data[i] < 0 ? height - 10 : 0),x + hmargin + width + -1, y + 0 + (this.data[i] < 0 ? height : 0));this.context.moveTo(x + hmargin + width - 2, y + -2);this.context.lineTo(x + hmargin + width - 3, y + height - 3);for (var r=0.2; r<=0.8; r+=0.2){
this.context.moveTo(x + hmargin + width + (r > 0.4 ? -1 : 3) - (r * width),y - 1);this.context.lineTo(x + hmargin + width - (r > 0.4 ? 1 : -1) - (r * width), y + height + (r == 0.2 ? 1 : -2));}
this.context.stroke();} else if(variant == 'bar' || variant == '3d' || variant == 'glass'){
this.coords[i] = [x, y, width, height];if(document.all && shadow){
this.DrawIEShadow([x + hmargin, y, barWidth, height]);}
if(variant == 'glass'){
RGraph.filledCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);RGraph.strokedCurvyRect(this.context, x + hmargin, y, barWidth, height, 3, this.data[i] > 0, this.data[i] > 0, this.data[i] < 0, this.data[i] < 0);} else {
this.context.strokeRect(x + hmargin, y, barWidth, height);this.context.fillRect(x + hmargin, y, barWidth, height);}
if(this.Get('chart.labels.above')){
if(shadow){
RGraph.NoShadow(this);}
var yPos = y - 3;if(this.data[i] < 0){
yPos += height + 6 + (this.Get('chart.text.size') - 4);}
this.context.fillStyle = this.Get('chart.text.color');RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') - 3, x + hmargin + (barWidth / 2), yPos, RGraph.number_format(this.data[i], this.Get('chart.units.pre'), this.Get('chart.units.post')), null, 'center');}
if(variant == '3d'){
var prevStrokeStyle = this.context.strokeStyle;var prevFillStyle = this.context.fillStyle;this.context.beginPath();this.context.moveTo(x + hmargin, y);this.context.lineTo(x + hmargin + 10, y - 5);this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);this.context.lineTo(x + hmargin + barWidth, y);this.context.closePath();this.context.stroke();this.context.fill();this.context.beginPath();this.context.moveTo(x + hmargin + barWidth, y);this.context.lineTo(x + hmargin + barWidth + 10, y - 5);this.context.lineTo(x + hmargin + barWidth + 10, y + height - 5);this.context.lineTo(x + hmargin + barWidth, y + height);this.context.closePath();this.context.stroke();
this.context.fill();this.context.beginPath();this.context.fillStyle = 'rgba(255,255,255,0.3)';this.context.moveTo(x + hmargin, y);this.context.lineTo(x + hmargin + 10, y - 5);this.context.lineTo(x + hmargin + 10 + barWidth, y - 5);this.context.lineTo(x + hmargin + barWidth, y);this.context.lineTo(x + hmargin, y);this.context.closePath();this.context.stroke();this.context.fill();this.context.beginPath();this.context.fillStyle = 'rgba(0,0,0,0.4)';this.context.moveTo(x + hmargin + barWidth, y);this.context.lineTo(x + hmargin + barWidth + 10, y - 5);this.context.lineTo(x + hmargin + barWidth + 10, y - 5 + height);this.context.lineTo(x + hmargin + barWidth, y + height);this.context.lineTo(x + hmargin + barWidth, y);this.context.closePath();this.context.stroke();this.context.fill();this.context.strokeStyle = prevStrokeStyle;this.context.fillStyle = prevFillStyle;} else if(variant == 'glass'){
var grad = this.context.createLinearGradient(
x + hmargin,
y,
x + hmargin + (barWidth / 2),
y
);grad.addColorStop(0, 'rgba(255,255,255,0.9)');grad.addColorStop(1, 'rgba(255,255,255,0.5)');this.context.beginPath();this.context.fillStyle = grad;this.context.fillRect(x + hmargin + 2,y + (this.data[i] > 0 ? 2 : 0),(barWidth / 2) - 2,height - 2);this.context.fill();}
} else if(variant == 'dot'){
this.context.beginPath();this.context.moveTo(x + (width / 2), y);this.context.lineTo(x + (width / 2), y + height);this.context.stroke();this.context.beginPath();this.context.fillStyle = this.Get('chart.colors')[i];this.context.arc(x + (width / 2), y + (this.data[i] > 0 ? 0 : height), 2, 0, 6.28, 0);this.context.fillStyle = this.Get('chart.colors')[0];this.context.stroke();this.context.fill();} else if(variant == 'pyramid'){
this.context.beginPath();var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.Get('chart.gutter')));this.context.moveTo(x + hmargin, startY);this.context.lineTo(
x + hmargin + (barWidth / 2),
y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0)
);this.context.lineTo(x + hmargin + barWidth, startY);this.context.closePath();this.context.stroke();this.context.fill();} else if(variant == 'arrow'){
var startY = (this.Get('chart.xaxispos') == 'center' ? (this.canvas.height / 2) : (this.canvas.height - this.gutter));this.context.lineWidth = this.Get('chart.linewidth') ? this.Get('chart.linewidth') : 1;this.context.lineCap = 'round';this.context.beginPath();this.context.moveTo(x + hmargin + (barWidth / 2), startY);this.context.lineTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));this.context.arc(x + hmargin + (barWidth / 2),
y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
5,
this.data[i] > 0 ? 0.78 : 5.6,
this.data[i] > 0 ? 0.79 : 5.48,
this.data[i] < 0);this.context.moveTo(x + hmargin + (barWidth / 2), y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0));this.context.arc(x + hmargin + (barWidth / 2),
y + (this.Get('chart.xaxispos') == 'center' && (this.data[i] < 0) ? height : 0),
5,
this.data[i] > 0 ? 2.355 : 4,
this.data[i] > 0 ? 2.4 : 3.925,
this.data[i] < 0);this.context.stroke();this.context.lineWidth = 1;} else {
alert('[BAR] Warning! Unknown chart.variant: ' + variant);}
this.coords.push([x, y, width, height]);} else if(typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'stacked'){
var barWidth = width - (2 * hmargin);var redrawCoords = [];var startY = 0;for (j=0; j<this.data[i].length; ++j){
if(xaxispos == 'center'){
alert("[BAR] It's fruitless having the X axis position at the center on a stacked bar chart.");return;}
if(this.data[i][j] < 0){
alert('[BAR] Negative values are not permitted with a stacked bar chart. Try a grouped one instead.');return;}
this.context.strokeStyle = strokeStyle
this.context.fillStyle = colors[j];var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.gutter) );if(xaxispos == 'center'){
height /= 2;}
var totalHeight = (RGraph.array_sum(this.data[i]) / this.max) * (this.canvas.height - hmargin - (2 * this.gutter));this.coords.push([x, y, width, height]);if(document.all && shadow){
this.DrawIEShadow([x + hmargin, y, width - (2 * hmargin), height + 1]);}
this.context.strokeRect(x + hmargin, y, width - (2 * hmargin), height);this.context.fillRect(x + hmargin, y, width - (2 * hmargin), height);if(j == 0){
var startY = y;var startX = x;}
if(shadow){
redrawCoords.push([x + hmargin, y, width - (2 * hmargin), height, colors[j]]);}
if(variant == '3d'){
var prevFillStyle = this.context.fillStyle;var prevStrokeStyle = this.context.strokeStyle;if(j == 0){
this.context.beginPath();this.context.moveTo(startX + hmargin, y);this.context.lineTo(startX + 10 + hmargin, y - 5);this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);this.context.lineTo(startX + barWidth + hmargin, y);this.context.closePath();this.context.fill();this.context.stroke();}
this.context.beginPath();this.context.moveTo(startX + barWidth + hmargin, y);this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);this.context.lineTo(startX + barWidth + hmargin , y + height);this.context.closePath();this.context.fill();this.context.stroke();if(j == 0){
this.context.fillStyle = 'rgba(255,255,255,0.3)';this.context.beginPath();this.context.moveTo(startX + hmargin, y);this.context.lineTo(startX + 10 + hmargin, y - 5);this.context.lineTo(startX + 10 + barWidth + hmargin, y - 5);this.context.lineTo(startX + barWidth + hmargin, y);this.context.closePath();this.context.fill();this.context.stroke();}
this.context.fillStyle = 'rgba(0,0,0,0.4)';this.context.beginPath();this.context.moveTo(startX + barWidth + hmargin, y);this.context.lineTo(startX + barWidth + hmargin + 10, y - 5);this.context.lineTo(startX + barWidth + + hmargin + 10, y - 5 + height);this.context.lineTo(startX + barWidth + hmargin , y + height);this.context.closePath();this.context.fill();this.context.stroke();this.context.strokeStyle = prevStrokeStyle;this.context.fillStyle = prevFillStyle;}
y += height;}
if(this.Get('chart.labels.above')){
RGraph.NoShadow(this);this.context.fillStyle = this.Get('chart.text.color');RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') - 3, startX + (barWidth / 2) + this.Get('chart.hmargin'), startY - (this.Get('chart.shadow') && this.Get('chart.shadow.offsety') < 0 ? 7 : 4), String(this.Get('chart.units.pre') + RGraph.array_sum(this.data[i]) + this.Get('chart.units.post')), null, 'center');if(shadow){
this.context.shadowColor = shadowColor;this.context.shadowBlur = shadowBlur;this.context.shadowOffsetX = shadowOffsetX;this.context.shadowOffsetY = shadowOffsetY;}
}
if(shadow){
RGraph.NoShadow(this);for (k=0; k<redrawCoords.length; ++k){
this.context.strokeStyle = strokeStyle;this.context.fillStyle = redrawCoords[k][4];this.context.strokeRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);this.context.fillRect(redrawCoords[k][0], redrawCoords[k][1], redrawCoords[k][2], redrawCoords[k][3]);this.context.stroke();this.context.fill();}
redrawCoords = [];}
} else if(typeof(this.data[i]) == 'object' && this.Get('chart.grouping') == 'grouped'){
for (j=0; j<this.data[i].length; ++j){
this.context.strokeStyle = strokeStyle;this.context.fillStyle = colors[j];var individualBarWidth = (width - (2 * hmargin)) / this.data[i].length;var height = (this.data[i][j] / this.max) * (this.canvas.height - (2 * this.gutter) );if(xaxispos == 'center'){
height /= 2;}
var startX = x + hmargin + (j * individualBarWidth);var startY = (xaxispos == 'bottom' ? this.canvas.height : (this.canvas.height / 2) + this.gutter) - this.gutter - height;if(height < 0){
startY += height;height = Math.abs(height);}
if(document.all && shadow){
this.DrawIEShadow([startX, startY, individualBarWidth, height]);}
this.context.strokeRect(startX, startY, individualBarWidth, height);this.context.fillRect(startX, startY, individualBarWidth, height);y += height;if(this.Get('chart.labels.above')){
this.context.strokeStyle = 'rgba(0,0,0,0)';if(shadow){
RGraph.NoShadow(this);}
var yPos = y - 3;if(this.data[i][j] < 0){
yPos += height + 6 + (this.Get('chart.text.size') - 4);}
this.context.fillStyle = this.Get('chart.text.color');RGraph.Text(this.context, this.Get('chart.text.font'), this.Get('chart.text.size') - 3, startX + (individualBarWidth / 2) , startY - 2, this.data[i][j], null, 'center');if(shadow){
this.context.shadowColor = shadowColor;this.context.shadowBlur = shadowBlur;this.context.shadowOffsetX = shadowOffsetX;this.context.shadowOffsetY = shadowOffsetY;}
}
if(variant == '3d'){
var prevFillStyle = this.context.fillStyle;var prevStrokeStyle = this.context.strokeStyle;this.context.beginPath();this.context.moveTo(startX, startY);this.context.lineTo(startX + 10, startY - 5);this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);this.context.lineTo(startX + individualBarWidth, startY);this.context.closePath();this.context.fill();this.context.stroke();this.context.beginPath();this.context.moveTo(startX + individualBarWidth, startY);this.context.lineTo(startX + individualBarWidth + 10, startY - 5);this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);this.context.lineTo(startX + individualBarWidth , startY + height);this.context.closePath();this.context.fill();this.context.stroke();this.context.fillStyle = 'rgba(255,255,255,0.3)';this.context.beginPath();this.context.moveTo(startX, startY);this.context.lineTo(startX + 10, startY - 5);this.context.lineTo(startX + 10 + individualBarWidth, startY - 5);this.context.lineTo(startX + individualBarWidth, startY);this.context.closePath();this.context.fill();this.context.stroke();this.context.fillStyle = 'rgba(0,0,0,0.4)';this.context.beginPath();this.context.moveTo(startX + individualBarWidth, startY);this.context.lineTo(startX + individualBarWidth + 10, startY - 5);this.context.lineTo(startX + individualBarWidth + 10, startY - 5 + height);this.context.lineTo(startX + individualBarWidth , startY + height);this.context.closePath();this.context.fill();this.context.stroke();this.context.strokeStyle = prevStrokeStyle;this.context.fillStyle = prevFillStyle;}
this.coords.push([startX - hmargin, startY, individualBarWidth + (2 * hmargin), height]);}
}
this.context.closePath();}
RGraph.NoShadow(this);if(this.Get('chart.tooltips')){
RGraph.Register(this);window.onclick = function ()
{
RGraph.Redraw();}
this.canvas.onmousemove = function (e)
{
e = RGraph.FixEventObject(e);var canvas = document.getElementById(this.id);var obj = canvas.__object__;var mouseCoords = RGraph.getMouseXY(e);for (var i=0; i<obj.coords.length; i++){
var mouseX = mouseCoords[0];var mouseY = mouseCoords[1];var left = obj.coords[i][0];var top = obj.coords[i][1];var width = obj.coords[i][2];var height = obj.coords[i][3];if(mouseX >= (left + 5  ) && mouseX <= (left + width - 5) && mouseY >= top && mouseY <= (top + height) ){
canvas.style.cursor = document.all ? 'hand' : 'pointer';return;}
canvas.style.cursor = 'default';}
}
this.canvas.onclick = function (e)
{
var e = RGraph.FixEventObject(e);if(e.button != 0) return;e = RGraph.FixEventObject(e);var canvas = document.getElementById(this.id);var obj = canvas.__object__;RGraph.Redraw();var mouseCoords = RGraph.getMouseXY(e);for (var i=0; i<obj.coords.length; i++){
var mouseX = mouseCoords[0];var mouseY = mouseCoords[1];var left = obj.coords[i][0];var top = obj.coords[i][1];var width = obj.coords[i][2];var height = obj.coords[i][3];if(mouseX >= (left + 5  ) && mouseX <= (left + width - 5) && mouseY >= top && mouseY <= (top + height) ){
obj.context.beginPath();obj.context.strokeStyle = 'black';obj.context.fillStyle = 'rgba(255,255,255,0.5)';obj.context.strokeRect(left + obj.Get('chart.hmargin'), top, width - (2 * obj.Get('chart.hmargin')), height);obj.context.fillRect(left + obj.Get('chart.hmargin'), top, width - (2 * obj.Get('chart.hmargin')), height);obj.context.stroke();obj.context.fill();if(obj.Get('chart.tooltips')[i]){
RGraph.Tooltip(canvas, obj.Get('chart.tooltips')[i], e.pageX, e.pageY);}
}
}
e.cancelBubble = true;e.stopPropagation();}
if(obj = RGraph.Registry.Get('chart.tooltip')){
obj.style.display = 'none';RGraph.Registry.Set('chart.tooltip', null)
}
} else {
this.canvas.onmousemove = null;this.canvas.onclick = null;}
}
RGraph.Bar.prototype.DrawLabels = function ()
{
var context = this.context;var gutter = this.gutter;var text_angle = this.Get('chart.text.angle');var text_size = this.Get('chart.text.size');var labels = this.Get('chart.labels');if(this.Get('chart.ylabels')){
this.Drawlabels_center();this.Drawlabels_bottom();}
if(typeof(labels) == 'object' && labels){
var yOffset = 13;var angle = 0;var halign = 'center';if(text_angle == 45 || text_angle == 90){
angle = -1 * text_angle;halign = 'right';yOffset -= 5;}
context.fillStyle = this.Get('chart.text.color');var barWidth = (this.canvas.width - (2 * gutter) ) / labels.length;xTickGap = (this.canvas.width - (2 * gutter)) / labels.length
var i=0;var font = this.Get('chart.text.font');for (x=gutter + (xTickGap / 2); x<=this.canvas.width - gutter; x+=xTickGap){
RGraph.Text(context, font,
text_size,
x,
(this.canvas.height - gutter) + yOffset,
String(labels[i++]),
null,
halign,
null,
angle);}
}
}
RGraph.Bar.prototype.Drawlabels_center = function ()
{
var font = this.Get('chart.text.font');var numYLabels = this.Get('chart.ylabels.count');this.context.fillStyle = this.Get('chart.text.color');if(this.Get('chart.xaxispos') == 'center'){
var interval = (this.grapharea * (1/10) );var text_size = this.Get('chart.text.size');var gutter = this.gutter;var units_pre = this.Get('chart.units.pre');var units_post = this.Get('chart.units.post');var context = this.context;var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';var xpos = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5;this.context.fillStyle = this.Get('chart.text.color');RGraph.Text(context, font, text_size, xpos,                gutter + this.halfTextHeight, RGraph.number_format(this.scale[4], units_pre, units_post), null, align);if(numYLabels >= 5){
RGraph.Text(context, font, text_size, xpos, (1*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[3], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (3*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[1], units_pre, units_post), null, align);}
if(numYLabels >= 3){
RGraph.Text(context, font, text_size, xpos, (4*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[0], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (2*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[2], units_pre, units_post), null, align);}
var interval = (this.grapharea) / 10;if(numYLabels >= 3){
RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (4 * interval), '-' + RGraph.number_format(this.scale[0], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (2 * interval), '-' + RGraph.number_format(this.scale[2], units_pre, units_post), null, align);}
if(numYLabels >= 5){
RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - (3 * interval), '-' + RGraph.number_format(this.scale[1], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (this.grapharea + gutter + this.halfTextHeight) - interval, '-' + RGraph.number_format(this.scale[3], units_pre, units_post), null, align);}
RGraph.Text(context, font, text_size, xpos,  this.grapharea + gutter + this.halfTextHeight, '-' + RGraph.number_format(this.scale[4], units_pre, units_post), null, align);}
}
RGraph.Bar.prototype.Drawlabels_bottom = function ()
{
this.context.beginPath();this.context.fillStyle = this.Get('chart.text.color');if(this.Get('chart.xaxispos') != 'center'){
var interval = (this.grapharea * (1/5) );var text_size = this.Get('chart.text.size');var units_pre = this.Get('chart.units.pre');var units_post = this.Get('chart.units.post');var gutter = this.gutter;var context = this.context;var align = this.Get('chart.yaxispos') == 'left' ? 'right' : 'left';var xpos = this.Get('chart.yaxispos') == 'left' ? gutter - 5 : this.canvas.width - gutter + 5;var font = this.Get('chart.text.font');var numYLabels = this.Get('chart.ylabels.count');RGraph.Text(context, font, text_size, xpos, gutter + this.halfTextHeight, RGraph.number_format(this.scale[4], units_pre, units_post), null, align);if(numYLabels >= 5){
RGraph.Text(context, font, text_size, xpos, (1*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[3], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (3*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[1], units_pre, units_post), null, align);}
if(numYLabels >= 3){
RGraph.Text(context, font, text_size, xpos, (2*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[2], units_pre, units_post), null, align);RGraph.Text(context, font, text_size, xpos, (4*interval) + gutter + this.halfTextHeight, RGraph.number_format(this.scale[0], units_pre, units_post), null, align);}
}
this.context.fill();this.context.stroke();}
RGraph.Bar.prototype.DrawIEShadow = function (coords)
{
var prevFillStyle = this.context.fillStyle;var offsetx = this.Get('chart.shadow.offsetx');var offsety = this.Get('chart.shadow.offsety');this.context.lineWidth = this.Get('chart.linewidth');this.context.fillStyle = this.Get('chart.shadow.color');this.context.beginPath();this.context.fillRect(coords[0] + offsetx, coords[1] + offsety, coords[2], coords[3]);this.context.fill();this.context.fillStyle = prevFillStyle;}

// This code has been taken from "http://www.quirksmode.org/js/detect.html". Please refer to this URL for any issues or furthur informations. 

var BrowserDetect = {
	init: function () {
		this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
		this.version = this.searchVersion(navigator.userAgent)
			|| this.searchVersion(navigator.appVersion)
			|| "an unknown version";
		this.OS = this.searchString(this.dataOS) || "an unknown OS";
	},
	searchString: function (data) {
		for (var i=0;i<data.length;i++)	{
			var dataString = data[i].string;
			var dataProp = data[i].prop;
			this.versionSearchString = data[i].versionSearch || data[i].identity;
			if (dataString) {
				if (dataString.indexOf(data[i].subString) != -1)
					return data[i].identity;
			}
			else if (dataProp)
				return data[i].identity;
		}
	},
	searchVersion: function (dataString) {
		var index = dataString.indexOf(this.versionSearchString);
		if (index == -1) return;
		return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
	},
	dataBrowser: [
		{
			string: navigator.userAgent,
			subString: "Chrome",
			identity: "Chrome"
		},
		{ 	string: navigator.userAgent,
			subString: "OmniWeb",
			versionSearch: "OmniWeb/",
			identity: "OmniWeb"
		},
		{
			string: navigator.vendor,
			subString: "Apple",
			identity: "Safari",
			versionSearch: "Version"
		},
		{
			prop: window.opera,
			identity: "Opera"
		},
		{
			string: navigator.vendor,
			subString: "iCab",
			identity: "iCab"
		},
		{
			string: navigator.vendor,
			subString: "KDE",
			identity: "Konqueror"
		},
		{
			string: navigator.userAgent,
			subString: "Firefox",
			identity: "Firefox"
		},
		{
			string: navigator.vendor,
			subString: "Camino",
			identity: "Camino"
		},
		{		// for newer Netscapes (6+)
			string: navigator.userAgent,
			subString: "Netscape",
			identity: "Netscape"
		},
		{
			string: navigator.userAgent,
			subString: "MSIE",
			identity: "Explorer",
			versionSearch: "MSIE"
		},
		{
			string: navigator.userAgent,
			subString: "Gecko",
			identity: "Mozilla",
			versionSearch: "rv"
		},
		{ 		// for older Netscapes (4-)
			string: navigator.userAgent,
			subString: "Mozilla",
			identity: "Netscape",
			versionSearch: "Mozilla"
		}
	],
	dataOS : [
		{
			string: navigator.platform,
			subString: "Win",
			identity: "Windows"
		},
		{
			string: navigator.platform,
			subString: "Mac",
			identity: "Mac"
		},
		{
			   string: navigator.userAgent,
			   subString: "iPhone",
			   identity: "iPhone/iPod"
	    },
		{
			string: navigator.platform,
			subString: "Linux",
			identity: "Linux"
		}
	]

};
BrowserDetect.init();

var old_browser = false;
if (((BrowserDetect.browser == "Firefox") && (BrowserDetect.version < '3')) || ((BrowserDetect.browser == "Safari") && (BrowserDetect.version < '3.2')) || 	((BrowserDetect.browser == "Explorer") && (BrowserDetect.version < '7'))) {
  old_browser = true;
}
// alert(BrowserDetect.browser);
if (BrowserDetect.OS == "Mac"){
  if ((BrowserDetect.browser == "Chrome") && (BrowserDetect.version < '5')) {
    old_browser = true;
  }
}

if (BrowserDetect.OS == "Windows"){
  if ((BrowserDetect.browser == "Chrome") && (BrowserDetect.version < '4')) {
	old_browser = true;
  }
}
  
if (old_browser == true) {
	$('#unsupportedBrowser').show();
	// $('#id_1').html('<a href="javascript:hide_browser_info();" class="close">close</a><p>Solaro.com has detected that you are using an older browser ( ' + BrowserDetect.browser + ' ' + BrowserDetect.version + ' )</p>' + '1) Download and install any updated browser that is supported by Solaro.com such as:&nbsp;&nbsp;<a href="http://www.microsoft.com/nz/windows/internet-explorer/default.aspx">IE8</a>,&nbsp;<a href="http://www.mozilla.com/en-US/firefox/personal.html">Firefox 3.x</a>,&nbsp;<a href="http://www.google.com/chrome">Google Chrome</a>,&nbsp;<a href="http://www.apple.com/safari/download/">Safari .</a> <br/>or <br />2) Ignore this message to login without upgrading, however some features of the site may not display correctly.');
	// }
	$('#id_1').html('<a href="javascript:hide_browser_info();" class="close">close</a><p>Solaro.com has detected that you are using an older browser ( ' + BrowserDetect.browser + ' ' + BrowserDetect.version + ' )</p>' + '1) Download and install any updated browser that is supported by Solaro.com such as<div id="browserIcon"><a href="http://www.microsoft.com/nz/windows/internet-explorer/default.aspx" class="ie">Internet Explorer 8</a><a href="http://www.mozilla.com/en-US/firefox/personal.html" class="firefox">Firefox 3.x</a><a href="http://www.google.com/chrome" class="chrome">Google Chrome</a><a href="http://www.apple.com/safari/download/" class="safari">Safari</a></div><br class="clear" />2) Ignore this message to login without upgrading, however some features of the site may not display correctly.');
}

function hide_browser_info() {
    $('#unsupportedBrowser').hide();
}


﻿/*!
 * jQuery blockUI plugin
 * Version 2.33 (29-MAR-2010)
 * @requires jQuery v1.2.3 or later
 *
 * Examples at: http://malsup.com/jquery/block/
 * Copyright (c) 2007-2008 M. Alsup
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Thanks to Amir-Hossein Sobhi for some excellent contributions!
 */

;(function($) {

if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
	alert('blockUI requires jQuery v1.2.3 or later!  You are using v' + $.fn.jquery);
	return;
}

$.fn._fadeIn = $.fn.fadeIn;

var noOp = function() {};

// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
// retarded userAgent strings on Vista)
var mode = document.documentMode || 0;
var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;

// global $ methods for blocking/unblocking the entire page
$.blockUI   = function(opts) { install(window, opts); };
$.unblockUI = function(opts) { remove(window, opts); };

// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
$.growlUI = function(title, message, timeout, onClose) {
	var $m = $('<div class="growlUI"></div>');
	if (title) $m.append('<h1>'+title+'</h1>');
	if (message) $m.append('<h2>'+message+'</h2>');
	if (timeout == undefined) timeout = 3000;
	$.blockUI({
		message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
		timeout: timeout, showOverlay: false,
		onUnblock: onClose, 
		css: $.blockUI.defaults.growlCSS
	});
};

// plugin method for blocking element content
$.fn.block = function(opts) {
	return this.unblock({ fadeOut: 0 }).each(function() {
		if ($.css(this,'position') == 'static')
			this.style.position = 'relative';
		if ($.browser.msie)
			this.style.zoom = 1; // force 'hasLayout'
		install(this, opts);
	});
};

// plugin method for unblocking element content
$.fn.unblock = function(opts) {
	return this.each(function() {
		remove(this, opts);
	});
};

$.blockUI.version = 2.33; // 2nd generation blocking at no extra cost!

// override these in your code to change the default behavior and style
$.blockUI.defaults = {
	// message displayed when blocking (use null for no message)
	message:  '<h1>Please wait...</h1>',

	title: null,	  // title string; only used when theme == true
	draggable: true,  // only used when theme == true (requires jquery-ui.js to be loaded)
	
	theme: false, // set to true to use with jQuery UI themes
	
	// styles for the message when blocking; if you wish to disable
	// these and use an external stylesheet then do this in your code:
	// $.blockUI.defaults.css = {};
	css: {
		padding:	0,
		margin:		0,
		width:		'30%',
		top:		'40%',
		left:		'35%',
		textAlign:	'center',
		color:		'#000',
		border:		'3px solid #aaa',
		backgroundColor:'#fff',
		cursor:		'wait'
	},
	
	// minimal style set used when themes are used
	themedCSS: {
		width:	'30%',
		top:	'40%',
		left:	'35%'
	},

	// styles for the overlay
	overlayCSS:  {
		backgroundColor: '#000',
		opacity:	  	 0.6,
		cursor:		  	 'wait'
	},

	// styles applied when using $.growlUI
	growlCSS: {
		width:  	'350px',
		top:		'10px',
		left:   	'',
		right:  	'10px',
		border: 	'none',
		padding:	'5px',
		opacity:	0.6,
		cursor: 	'default',
		color:		'#fff',
		backgroundColor: '#000',
		'-webkit-border-radius': '10px',
		'-moz-border-radius':	 '10px',
		'border-radius': 		 '10px'
	},
	
	// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
	// (hat tip to Jorge H. N. de Vasconcelos)
	iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',

	// force usage of iframe in non-IE browsers (handy for blocking applets)
	forceIframe: false,

	// z-index for the blocking overlay
	baseZ: 1000,

	// set these to true to have the message automatically centered
	centerX: true, // <-- only effects element blocking (page block controlled via css above)
	centerY: true,

	// allow body element to be stetched in ie6; this makes blocking look better
	// on "short" pages.  disable if you wish to prevent changes to the body height
	allowBodyStretch: true,

	// enable if you want key and mouse events to be disabled for content that is blocked
	bindEvents: true,

	// be default blockUI will supress tab navigation from leaving blocking content
	// (if bindEvents is true)
	constrainTabKey: true,

	// fadeIn time in millis; set to 0 to disable fadeIn on block
	fadeIn:  200,

	// fadeOut time in millis; set to 0 to disable fadeOut on unblock
	fadeOut:  400,

	// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
	timeout: 0,

	// disable if you don't want to show the overlay
	showOverlay: true,

	// if true, focus will be placed in the first available input field when
	// page blocking
	focusInput: true,

	// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
	applyPlatformOpacityRules: true,
	
	// callback method invoked when fadeIn has completed and blocking message is visible
	onBlock: null,

	// callback method invoked when unblocking has completed; the callback is
	// passed the element that has been unblocked (which is the window object for page
	// blocks) and the options that were passed to the unblock call:
	//	 onUnblock(element, options)
	onUnblock: null,

	// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
	quirksmodeOffsetHack: 4
};

// private data and functions follow...

var pageBlock = null;
var pageBlockEls = [];

function install(el, opts) {
	var full = (el == window);
	var msg = opts && opts.message !== undefined ? opts.message : undefined;
	opts = $.extend({}, $.blockUI.defaults, opts || {});
	opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
	var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
	var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
	msg = msg === undefined ? opts.message : msg;

	// remove the current block (if there is one)
	if (full && pageBlock)
		remove(window, {fadeOut:0});

	// if an existing element is being used as the blocking content then we capture
	// its current place in the DOM (and current display style) so we can restore
	// it when we unblock
	if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
		var node = msg.jquery ? msg[0] : msg;
		var data = {};
		$(el).data('blockUI.history', data);
		data.el = node;
		data.parent = node.parentNode;
		data.display = node.style.display;
		data.position = node.style.position;
		if (data.parent)
			data.parent.removeChild(node);
	}

	var z = opts.baseZ;

	// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
	// layer1 is the iframe layer which is used to supress bleed through of underlying content
	// layer2 is the overlay layer which has opacity and a wait cursor (by default)
	// layer3 is the message content that is displayed while blocking

	var lyr1 = ($.browser.msie || opts.forceIframe) 
		? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
		: $('<div class="blockUI" style="display:none"></div>');
	var lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
	
	var lyr3, s;
	if (opts.theme && full) {
		s = '<div class="blockUI blockMsg blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:fixed">' +
				'<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
				'<div class="ui-widget-content ui-dialog-content"></div>' +
			'</div>';
	}
	else if (opts.theme) {
		s = '<div class="blockUI blockMsg blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+z+';display:none;position:absolute">' +
				'<div class="ui-widget-header ui-dialog-titlebar blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
				'<div class="ui-widget-content ui-dialog-content"></div>' +
			'</div>';
	}
	else if (full) {
		s = '<div class="blockUI blockMsg blockPage" style="z-index:'+z+';display:none;position:fixed"></div>';
	}			
	else {
		s = '<div class="blockUI blockMsg blockElement" style="z-index:'+z+';display:none;position:absolute"></div>';
	}
	lyr3 = $(s);

	// if we have a message, style it
	if (msg) {
		if (opts.theme) {
			lyr3.css(themedCSS);
			lyr3.addClass('ui-widget-content');
		}
		else 
			lyr3.css(css);
	}

	// style the overlay
	if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))
		lyr2.css(opts.overlayCSS);
	lyr2.css('position', full ? 'fixed' : 'absolute');

	// make iframe layer transparent in IE
	if ($.browser.msie || opts.forceIframe)
		lyr1.css('opacity',0.0);

	//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
	var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
	$.each(layers, function() {
		this.appendTo($par);
	});
	
	if (opts.theme && opts.draggable && $.fn.draggable) {
		lyr3.draggable({
			handle: '.ui-dialog-titlebar',
			cancel: 'li'
		});
	}

	// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
	var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
	if (ie6 || expr) {
		// give body 100% height
		if (full && opts.allowBodyStretch && $.boxModel)
			$('html,body').css('height','100%');

		// fix ie6 issue when blocked element has a border width
		if ((ie6 || !$.boxModel) && !full) {
			var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
			var fixT = t ? '(0 - '+t+')' : 0;
			var fixL = l ? '(0 - '+l+')' : 0;
		}

		// simulate fixed position
		$.each([lyr1,lyr2,lyr3], function(i,o) {
			var s = o[0].style;
			s.position = 'absolute';
			if (i < 2) {
				full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
					 : s.setExpression('height','this.parentNode.offsetHeight + "px"');
				full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
					 : s.setExpression('width','this.parentNode.offsetWidth + "px"');
				if (fixL) s.setExpression('left', fixL);
				if (fixT) s.setExpression('top', fixT);
			}
			else if (opts.centerY) {
				if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
				s.marginTop = 0;
			}
			else if (!opts.centerY && full) {
				var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
				var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
				s.setExpression('top',expression);
			}
		});
	}

	// show the message
	if (msg) {
		if (opts.theme)
			lyr3.find('.ui-widget-content').append(msg);
		else
			lyr3.append(msg);
		if (msg.jquery || msg.nodeType)
			$(msg).show();
	}

	if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
		lyr1.show(); // opacity is zero
	if (opts.fadeIn) {
		var cb = opts.onBlock ? opts.onBlock : noOp;
		var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
		var cb2 = msg ? cb : noOp;
		if (opts.showOverlay)
			lyr2._fadeIn(opts.fadeIn, cb1);
		if (msg)
			lyr3._fadeIn(opts.fadeIn, cb2);
	}
	else {
		if (opts.showOverlay)
			lyr2.show();
		if (msg)
			lyr3.show();
		if (opts.onBlock)
			opts.onBlock();
	}

	// bind key and mouse events
	bind(1, el, opts);

	if (full) {
		pageBlock = lyr3[0];
		pageBlockEls = $(':input:enabled:visible',pageBlock);
		if (opts.focusInput)
			setTimeout(focus, 20);
	}
	else
		center(lyr3[0], opts.centerX, opts.centerY);

	if (opts.timeout) {
		// auto-unblock
		var to = setTimeout(function() {
			full ? $.unblockUI(opts) : $(el).unblock(opts);
		}, opts.timeout);
		$(el).data('blockUI.timeout', to);
	}
};

// remove the block
function remove(el, opts) {
	var full = (el == window);
	var $el = $(el);
	var data = $el.data('blockUI.history');
	var to = $el.data('blockUI.timeout');
	if (to) {
		clearTimeout(to);
		$el.removeData('blockUI.timeout');
	}
	opts = $.extend({}, $.blockUI.defaults, opts || {});
	bind(0, el, opts); // unbind events
	
	var els;
	if (full) // crazy selector to handle odd field errors in ie6/7
		els = $('body').children().filter('.blockUI').add('body > .blockUI');
	else
		els = $('.blockUI', el);

	if (full)
		pageBlock = pageBlockEls = null;

	if (opts.fadeOut) {
		els.fadeOut(opts.fadeOut);
		setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
	}
	else
		reset(els, data, opts, el);
};

// move blocking element back into the DOM where it started
function reset(els,data,opts,el) {
	els.each(function(i,o) {
		// remove via DOM calls so we don't lose event handlers
		if (this.parentNode)
			this.parentNode.removeChild(this);
	});

	if (data && data.el) {
		data.el.style.display = data.display;
		data.el.style.position = data.position;
		if (data.parent)
			data.parent.appendChild(data.el);
		$(el).removeData('blockUI.history');
	}

	if (typeof opts.onUnblock == 'function')
		opts.onUnblock(el,opts);
};

// bind/unbind the handler
function bind(b, el, opts) {
	var full = el == window, $el = $(el);

	// don't bother unbinding if there is nothing to unbind
	if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
		return;
	if (!full)
		$el.data('blockUI.isBlocked', b);

	// don't bind events when overlay is not in use or if bindEvents is false
	if (!opts.bindEvents || (b && !opts.showOverlay)) 
		return;

	// bind anchors and inputs for mouse and key events
	var events = 'mousedown mouseup keydown keypress';
	b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);

// former impl...
//	   var $e = $('a,:input');
//	   b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
};

// event handler to suppress keyboard/mouse events when blocking
function handler(e) {
	// allow tab navigation (conditionally)
	if (e.keyCode && e.keyCode == 9) {
		if (pageBlock && e.data.constrainTabKey) {
			var els = pageBlockEls;
			var fwd = !e.shiftKey && e.target == els[els.length-1];
			var back = e.shiftKey && e.target == els[0];
			if (fwd || back) {
				setTimeout(function(){focus(back)},10);
				return false;
			}
		}
	}
	// allow events within the message content
	if ($(e.target).parents('div.blockMsg').length > 0)
		return true;

	// allow events for content that is not being blocked
	return $(e.target).parents().children().filter('div.blockUI').length == 0;
};

function focus(back) {
	if (!pageBlockEls)
		return;
	var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
	if (e)
		e.focus();
};

function center(el, x, y) {
	var p = el.parentNode, s = el.style;
	var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
	var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
	if (x) s.left = l > 0 ? (l+'px') : '0';
	if (y) s.top  = t > 0 ? (t+'px') : '0';
};

function sz(el, p) {
	return parseInt($.css(el,p))||0;
};

})(jQuery);
