var OFC = {

	ofcs: new Hash(),
	
	baseURL: '',
	
	defaultColors: [
			'#336699'				// data1
		,	'#339966'				// data2
		,	'#996633'				// data3
		,	'#ffcc00'				// data4
		,	'#ff00cc'				// data5
		,	'#cc00ff'				// data6
		,	'#009900'				// data7
		,	'#000099'				// data8
		,	'#990000'				// data9
	],
	
	defaultColorScheme: new Hash({
		background:	'#FFFFFF',
		text:		'#295E84',
		axis:		'#595441',
		grid:		'#B9B09D'
	}),

	getChart: function(id) {
		if (OFC.ofcs.keys().indexOf(id) == -1) {
			OFC.ofcs.set(id, new OFC.Chart(id));
		}
		return OFC.ofcs.get(id);
	},

	resetOFCs: function() {
		this.ofcs.each(function(pair) {
			// completely delete the ofc-object
			delete OFC.ofcs.unset(pair.key);
		}, this);
		this.ofcs = new Hash();
	},

	setChart: function(id, chart) {
		if (typeof this.ofcs.get(id) != 'undefined') {
			var ofi = OFC.ofcs.unset(id);
			delete ofi;
		}
		OFC.ofcs.set(id, chart);
		return true;
	},

	drawingComplete: function(id) {
		var ofi = OFC.getChart(id);
		ofi.onRedraw();
	},
	
	downloadingComplete: function(id, key) {
		var ofi = OFC.getChart(id);
		ofi.downloadHelper(key);
	},
	
	/**
	 * Finds the SWF-object. This is browserdependent
	 *
	 * @access private			this.dataList[i].addToOFC(this);

	 * @param {String} movieName name assigned to object or embed
	 * @return {Element}
	 */
	findSWF: function(movieName) {
		if (Prototype.Browser.IE) {
			return window[movieName];
		} else {
			return document[movieName];
		}
	}
};

OFC.Chart = Class.create({

	/*public static*/ supported: [
		/* bar charts */
		'bar', 'bar_3d', 'bar_fade', 'filled_bar', 'bar_glass', 'bar_sketch',
		/* line charts */
		'line', 'line_dot', 'line_hollow',
		/* area charts */
		'area_hollow',
		/* pie charts */
		'pie'
	],

	/**
	 * @constructor
	 * @param {String} id ID of object/embed-element
	 * @return void
	 */
	initialize: function(id, opts) {
		/* everytime set is called, redraw chart? */
		this.autoRedraw = false;

		/* Options */
		this.options = {
			width:				670
		,	height:				440
		,	flashURL:			OFC.baseURL +
								'actionscript/open-flash-chart.swf'
		,	sourceURL:			OFC.baseURL +
								'data-files/data-10.txt'
		,	extFlashURL:		OFC.baseURL +
								'js-ofc-library/extension/ofcext.swf'
		,	saveURL:			OFC.baseURL +
								'js-ofc-library/extension/saveJPEG.php'
		,	downloadURL:		OFC.baseURL +
								'js-ofc-library/extension/downloadJPEG.php'
		,	flashInstaller:		OFC.baseURL +
								'js-ofc-library/swfobject/expressInstall.swf'
		,	onRedraw:			null
		};
		if (typeof opts == 'object') {
			if (typeof opts.flashURL != 'undefined') {
				this.options.flashURL = opts.flashURL;
			}
			if (typeof opts.sourceURL != 'undefined') {
				this.options.sourceURL = opts.sourceURL;
			}
			if (typeof opts.extFlashURL != 'undefined') {
				this.options.extFlashURL = opts.extFlashURL;
			}
			if (typeof opts.saveURL != 'undefined') {
				this.options.saveURL = opts.saveURL;
			}
			if (typeof opts.flashInstaller != 'undefined') {
				this.options.flashInstaller = opts.flashInstaller;
			}
			if (typeof opts.onRedraw != 'undefined' && opts.onRedraw != null) {
				this.options.onRedraw = opts.onRedraw.bind(this);
			}
			if (typeof opts.width != 'undefined' && opts.width != null) {
				this.options.width = opts.width;
			}
			if (typeof opts.height != 'undefined' && opts.height != null) {
				this.options.height = opts.height;
			}
		}

		this.object = null;
		this.xlabels = [];
		this.dataList = [];
		
		this.colorScheme = OFC.defaultColorScheme.clone();
		
		this.id = id;
		OFC.setChart(id, this);

		/* Draw it! */
		if ($(id) != null) {
			swfobject.embedSWF(
				this.options.extFlashURL
			,	id
			,	this.options.width
			,	this.options.height
			,	'9.0.0'
			,	this.options.flashInstaller
			,	{
					// Flash-Vars
					data:			this.options.sourceURL
				,	stage_width:	this.options.width
				,	stage_height:	this.options.height
				,	saveURL:		this.options.saveURL
				,	swfURL:			this.options.flashURL
				,	externalHTMLID:	id // flash 9 wouldnt need this... and yet it needs it under linux
				}
			,	{
					// HTML-Opts
					bgcolor:	this.colorScheme.get('background')
				,	scale:		"noscale"
				,	salign:		"lt"
				,	wmode:		"transparent"
				,	quality:	"high"
				,	allowscriptaccess: "sameDomain"
				}
			,	{
					id:		id
				,	name:	id
				}
			);
		} else {
			alert('ID: ' + id + ' not found!');
		}
	},

	debug: function() {
		var liste = this.getAll();
		var m = "";
		liste.each(function(pair) {
			m += pair.key + " - " + pair.value + "\n";
		}, this);
		alert (m);
	},

	/****************************************************************
	 * Flash communication methods									*
	 ****************************************************************/

	/**
	 * initializes the object. it is nearly not usable without this
	 * function being called. it is also called everytime the chart
	 * changes.
	 *
	 * @return Array
	 */
	onRedraw: function() {
		if (this.object != null) {
			if (typeof this.options.onRedraw == 'function') {
				this.options.onRedraw();
			}
			return true;
		}

		this.object = OFC.findSWF(this.id);

		// load x-labels as they are the only permanent stored object
		this.xlabels = this.get('x_labels');

		// load data-objects
		this.loadDataObjects();

		// set default color-scheme
		// background-color
		var bc = this.get('bg_colour', false);
		if (bc != '') {
			this.colorScheme.set('background', '#'+bc.substr(-6));
		} else {
			//this.colorScheme.unset('background');
		}
		
		// text-color (try to get from title)
		var t = this.get('title');
		if (t.length > 0) {
			var tcr = /[^-]color:([#a-z0-9]+)/i;
			tcr.exec(t[1]);
			var tc = RegExp.$1;
			this.colorScheme.set('text', '#'+tc.substr(-6));
		} else {
			// try to get from x_label_style
			t = this.get('x_label_style');
			if (t.length > 0) {
				this.colorScheme.set('text', '#'+t[1].substr(-6));
			} else {
				//this.colorScheme.unset('text');
			}
		}
		
		// axis-color;
		var ac = this.get('x_axis_colour', false);
		if (ac != null && ac != '' && ac != '0') {
			this.colorScheme.set('axis', '#'+ac.substr(-6));
		}
		
		// grid-color;
		var gc = this.get('x_grid_colour', false);
		if (gc != '') {
			this.colorScheme.set('grid', '#'+gc.substr(-6));
		} else {
			//this.colorScheme.unset('grid');
		}
		
		if (typeof this.options.onRedraw == 'function') {
			this.options.onRedraw();
		}

		return true;
	},

	/**
	 * loads all the Dataobjects
	 *
	 * @return Array
	 */
	/*public Array[OFCData]*/ loadDataObjects: function() {
		var lvs = this.getAll();
		var datas = [];
		lvs.each(function(pair) {
			var ofcdataobj = null;
			var info = pair.key.split('_');
			var number = 0;
			if (info[info.length-1] == info[info.length-1] * 1) {
				number = info[info.length-1] * 1;
			}
			if (number > 0) {
				info.pop();
			}
			if (this.supported.indexOf(info.join('_')) == -1) {
				return;
			}
			switch (info[0]) {
				case 'filled':
				case 'bar':
					if (info.length > 1) {
						// solve inconsistency
						if (info[0] == 'filled') {
							ofcdataobj = new Bar('filled');
						} else {
							ofcdataobj = new Bar(info[1]);
						}
					} else {
						ofcdataobj = new Bar();
					}
				break;

				case 'pie':
					ofcdataobj = new Pie();
				break;

				case 'area':
					ofcdataobj = new Area('hollow');
				break;

				case 'line':
					if (info.length > 1) {
						ofcdataobj = new Line(info[1]);
					} else {
						ofcdataobj = new Line();
					}
				break;
			}
			
			// setting chart so that the child can find its parent
			ofcdataobj.ofc = this;
			
			// set number of OFC
			ofcdataobj.number = number;
			
			// set data-values
			var val = 'values';
			if (number > 0) {
				val += '_' + number;
			}
			ofcdataobj.values = this.get(val);
			
			// create from info found
			ofcdataobj.fromData(this.deFlash(pair.value));
			
			this.dataList.push(ofcdataobj);
		}, this);
		
		return this.dataList;
	},

	/**
	 * Adds a specified OFCData-object to the object list
	 *
	 * @param {OFCData} data
	 */
	addData: function(/*OFCData*/ data) {
		data.number = this.dataList.length + (this.dataList.length > 0 ? 1 : 0);
		this.dataList[this.dataList.length] = data;
	},

	/**
	 * Replaces a specified old OFCData-object in the object list with the new
	 * one. Use getData to fetch the old one.
	 *
	 * @param {OFCData} oldData
	 * @param {OFCData} newData
	 */
	replaceData: function(/*OFCData*/ oldData, /*OFCData*/ newData) {
		// find position in dataList
		var pos = 0;
		for (; pos < this.dataList.length; pos++) {
			if (this.dataList[pos].number == oldData.number) break;
		}
		
		// copy number
		newData.number = oldData.number;
		
		// delete old one
		delete this.dataList[pos];
		oldData.removeFromOFC(this);
		delete oldData;
		
		// save new one
		this.dataList[pos] = newData;
	},

	/**
	 * removes a specified OFCData-object from the object list
	 *
	 * @param {OFCData} position
	 */
	removeData: function(/*OFCData*/ data) {
		var pos = 0;
		for (; pos < this.dataList.length; pos++) {
			if (this.dataList[pos].equals(data)) break;
		}

		this.dataList[pos].removeFromOFC(this);
		this.dataList = $A(this.dataList).without(this.dataList[pos]);
		this.reOrder();
	},

	/**
	 * returns the specified OFCData-object
	 * if the object is not found null is returned
	 *
	 * @param {int} number Number of the data-set in ofc (0, 2, 3, 4...)
	 * @type {OFCData}
	 */
	getData: function(number) {
		for (var i = 0; i < this.dataList.length; i++) {
			if (this.dataList[i].number == number) return this.dataList[i];
		}
		return null;
	},

	/**
	 * resets the datalist - removes all OFCData-objects
	 * @param {Array} exceptions
	 */
	emptyDataList: function(exceptions) {
		for (var i = 0; i < this.dataList.length; i++) {
			if (typeof exceptions != 'undefined') {
				var cont = false;
				$A(exceptions).each(function(data) {
					if (this.dataList[i].equals(data)) {
						cont = true;
						throw $break;
					}
				}, this);
				if (cont) continue;
			}
			this.dataList[i].removeFromOFC(this);
			this.dataList = $A(this.dataList).without(this.dataList[i]);
		}
		this.reOrder();
	},

	/**
	 * returns an empty OFCData-object based on the defined name
	 * if the name is not supported null is returned
	 *
	 * @param {String} type
	 * @type {OFCData}
	 */
	getDataFromType: function(type) {
		if (this.supported.indexOf(type) == -1) return null;

		switch (type) {
			case 'bar':
				return new Bar();
				break;

			case 'bar_filled':
			case 'filled_bar':
				return new Bar('filled');
				break;

			case 'bar_fade':
				return new Bar('fade');
				break;

			case 'bar_glass':
				return new Bar('glass');
				break;

			case 'bar_sketch':
				return new Bar('sketch');
				break;

			case 'bar_3d':
				return new Bar('3d');
				break;

			case 'line':
				return new Line();
				break;

			case 'line_hollow':
				return new Line('hollow');
				break;

			case 'line_dot':
				return new Line('dot');
				break;

			case 'area_hollow':
				return new Area('hollow');
				break;

			case 'pie':
				return new Pie();
				break;
		}
	},

	/**
	 * Reorders the OFCData-objects (gives them new numbers)
	 */
	reOrder: function() {
		for (var i = 0; i < this.dataList.length; i++) {
			// fill gap in list!
			var n = i + (i > 0 ? 1 : 0);

			this.dataList[i].number = n;
			this.dataList[i].addToOFC(this);
		}
	},

	/**
	 * Returns all LoadVars-parameters set in the flashobject
	 *
	 * @return {Hash}
	 */
	getAll: function() {
		var vars = this.object.getAllLVVars().split('&');
		var lvs = new Hash();
		// walk through LoadVars in reverse order
		// no one knows why flash sends them in this order *rolleyes*
		// however order is important!
		for (var i = vars.length - 1; i >= 0; i--) {
			var val = vars[i].split('=');
			if (decodeURIComponent(val[0]).blank()) continue;
			lvs.set(decodeURIComponent(val[0]), decodeURIComponent(val[1]));
		}
		return lvs;
	},

	/**
	 * Sets a specified parameter of the LoadVars to the given data.
	 * Data can be String, int or Array. If its an Array it gets converted
	 * to a comma-separated string automatically
	 *
	 * @param {String} name name of parameter
	 * @param {mixed} value value of parameter
	 * @return void
	 */
	set:function(name,value) {
		if (value != null && typeof value == 'object') value = this.makeFlashing(value);
		this.object.setLVVar(name,value);
		if (this.autoRedraw) {
			this.redraw();
		}
	},
	
	/**
	 * Unsets a specified parameter in the LoadVars
	 * 
	 * @param {String} name name of parameter
	 * @return void
	 */
	unset:function(name) {
		this.set(name, null);
	},

	/**
	 * Returns the value of a specified LoadVar-parameter.
	 * Per default this function always returns an array. If the value is
	 * a comma-separated string it is splited. If you don't want this
	 * behaviour, set deFlash to false
	 *
	 * @param {String} name name of parameter
	 * [@param {bool} deFlash if set to true, it always returns]
	 * @return {mixed}
	 */
	get:function(name, deFlash) {
		if (typeof deFlash == 'undefined') deFlash = true;
		var str = this.object.getLVVar(name);
		if (typeof str == 'undefined' || str == 'undefined') {
			if (deFlash) return [];
			return '';
		}
		if (deFlash) {
			return this.deFlash(str);
		}
		return str;
	},

	/**
	 * Redraws the flash-object. This function should be called after all
	 * changes have been applied.
	 *
	 * @return void;
	 */
	redraw:function() {
		// take labels
		if (this.xlabels.length > 0) {
			this.set('x_labels', this.xlabels);
		}

		// reset all objects
		this.reOrder();

		// redraw
		this.object.redraw();
	},

	/**
	 * Joins the given array using , (comma) and returns it
	 *
	 * @param {Array} arr
	 * @return {String}
	 */
	makeFlashing: function(arr) {
		return arr.join(',');
	},

	/**
	 * Exlpodes the given string using , (comma) and returns it
	 *
	 * @param {String} str
	 * @return {Array}
	 */
	deFlash: function(str) {
		var arr = str.split(',');
		if (arr.length == 1 && arr[0] == '') return [];
		return arr;
	},

	/****************************************************************
	 * Wrapper methods												*
	 ***************************************************************/

	/**
	 * Defines the charts title
	 *
	 * @param {String} text Title's text
	 * @param {String} css Title's format
	 */
	/*public void*/ setTitle: function(text, css) {
		if (text == null || text.blank()) {
			this.unset('title');
			return;
		}
		if (typeof css == 'undefined' || css == null) {
			var title = this.get('title');
			if (title.length == 2) css = title[1];
			else css = '{font-size:20px,color:#000000}';
		}
		this.set('title', [
			text
		,	css
		]);
	},

	/**
	 * Defines the charts legend
	 * Unlike the PHP-Library there is only one setLegend-Method - use the first
	 * parameter to define which legend should be set
	 *
	 * @param {String} type can be y, x or y2
	 * @param {String} text Legends value
	 * @param {int} size Legends size
	 * @param {String} color Legends color
	 */
	/*public void*/ setLegend: function(type, text, size, color) {
		
		type += '_legend'
		var legend = this.get(type);
		var changed = false;
		
		var values = [];
		if (typeof text == 'undefined' || text == null) {
			if (legend.length > 0) text = legend[0];
			else text = null;
		} else {
			changed = changed
					|| legend.length == 0 && text.length > 0
					|| legend.length > 0 && legend[0] != text;
		}
		
		// remove it if text is empty
		if (text == null || text.blank()) {
			this.unset(type);
			return;
		}
		
		values.push(text);
		if (typeof size == 'undefined' || size == null) {
			if (legend.length > 1) size = legend[1];
			else size = 10;
		}
		if (typeof size == 'string') {
			var repl = /color:(\s*#[0-9a-zA-Z]{3,6})/g;
			if (typeof color == 'undefined') {
				color = this.colorScheme.get('text');
			}
			size = size.replace(repl, 'color:'+color);
			values.push(size);
			changed = changed || legend.length == 0 || legend.length > 1 && legend[1] != size;
		} else {
			values.push(size);
			changed = changed || legend.length == 0 || legend.length > 1 && legend[1] != size;
			if (typeof color == 'undefined' || color == null) {
				if (legend.length > 2) color = legend[2];
				else color = this.colorScheme.get('text');
			}
			values.push(color);
			changed = changed || legend.length == 0 || legend.length > 2 && legend[2] != color;
		}
		if (!changed) return;
		this.set(type, values);
	},

	/**
	 * Applies a given color-scheme to the chart. The first four params are
	 * easy to understand. The last one is an Array of colors, which is used
	 * for the different data-sets
	 *
	 * @param {String} bgcolor Background color
	 * @param {String} textcolor Text color
	 * @param {String} axiscolor Axis color
	 * @param {String} gridcolor Grid color
	 * @param {Array} datacolors List of colors used to color the bars/lines...
	 * @return void
	 */
	/*public void*/ setColorScheme: function(colors,datacolors) {
		if (typeof datacolors != 'object') return;
		
		// set inofc-colorscheme
		this.colorScheme = this.colorScheme.merge(colors);

		// background
		//if (this.get('bg_colour', false) != '') {
			this.set('bg_colour', this.colorScheme.get('background'));
		//}

		// axis
		//if (this.colorScheme.get('axis') != null) {
			this.setAxisColor(this.colorScheme.get('axis'));
		//}

		// grid
		//if (this.colorScheme.get('grid') != null) {
			this.setGridColor(this.colorScheme.get('grid'));
		//}
		
		// text
		var textcolor = this.colorScheme.get('text');
		
		// text - title
		var title = this.get('title');
		if (title.length > 0) {
			var css = '';
			if (title.length > 1) {
				css = title[1];
				var replacer = /color:(#[a-zA-Z0-9]{6})/g
				css = css.replace(replacer, 'color:'+textcolor);
			}
			this.setTitle(title[0], css);
		}

		// text - legends
		this.setLegend('x', null, null, textcolor);
		this.setLegend('y', null, null, textcolor);
		this.setLegend('y2', null, null, textcolor);

		// text - labels
		var ylabel = this.get('y_label_style');
		if (ylabel.length == 0 || ylabel.length > 0 && ylabel[0] != 'none') {
			if (ylabel.length > 0) {
				size = ylabel[0];
			} else {
				size = 10;
			}
			this.setYLabelStyle(size, textcolor);
		}
		
		var y2label = this.get('y2_label_style');
		if (y2label.length > 0 && y2label[0] != 'none') {
			this.setRightYLabelStyle(y2label[0], textcolor);
		}

		var xlabel = this.get('x_label_style');
		if (xlabel.length == 0 || xlabel.length > 0 && xlabel[0] != 'none') {
			this.setXLabelStyle(xlabel[0], textcolor, xlabel[2], xlabel[3], xlabel[4]);
		}

		var j = 0;
		for (var i = 0; i < this.dataList.length; i++) {
			// special treatment for pie
			this.dataList[i].changeColors(datacolors[j]);
			j++;
		}
	},

	/**
	 * Converts the the first given OFCData-object to the second (blank)
	 * OFCData-Object and returns the new one. Typical usage would be:
	 *
	 * var old = ofc.getData(0); // get first data-set
	 * var filled = ofc.convert(old, new Bar('filled'));
	 * ofc.redraw();
	 *
	 * @param {OFCData} oldData
	 * @param {OFCData} newData
	 * @type {OFCData}
	 * @return Returns the converted object
	 */
	convert: function(/*OFCData*/ oldData, /*OFCData*/ newData) {
		if (newData.getType() == 'pie' && this.dataList.length > 1) {
			var l = this.dataList.length-1;
			for (var i = l; i >= 0; i--) {
				if (this.dataList[i].equals(oldData)) continue;
				this.removeData(this.dataList[i]);
			}
		}
		
		// do the f**** transformation
		newData.convertFrom(oldData);
		
		// replace them!
		this.replaceData(oldData, newData);

		return newData;
	},

	/**
	 * Does the same as convert, but automatically with all OFCData-objects
	 *
	 * @param {OFCData} newData The new data type
	 */
	convertAll: function(/*OFCData*/ newData) {
		for (i = 0; i < this.dataList.length; i++) {
			var newDataClone = Object.clone(newData);
			this.convert(this.dataList[i], newDataClone);
		}
		// belive it or not, but thats it.
	},

	/**
	 * Defines the y label style
	 *
	 * @param {int} size Font size
	 * @param {String} color Font color
	 */
	/*public void*/ setYLabelStyle: function(size,color) {
		this.set('y_label_style', [
			size
		,	color
		]);
	},

	/**
	 * Defines the right y label style
	 *
	 * @param {int} size Font size
	 * @param {String} color Font color
	 */
	/*public void*/ setRightYLabelStyle: function(size,color) {
		this.set('y2_label_style', [
			size
		,	color
		]);
	},

	/**
	 * Defines the x label style
	 * orientation defaults to 0
	 * step defaults to -1
	 * grid defaults to the label-color
	 *
	 * @param {int} size Font size
	 * @param {String} color Font color
	 * [@param {int} orientation 0 = horizontal, 1 = vertical, 2 = 45 degrees]
	 * [@param {int} step every step a colored line is drawn in the grid]
	 * [@param {String} grid Grid color at step postion
	 */
	/*public void*/ setXLabelStyle: function(size,color,orientation,step,grid) {
		var values = [];
		values.push(size);
		values.push(color);
		if (typeof orientation != 'undefined') values.push(orientation)
		if (typeof step != 'undefined') values.push(step);
		if (typeof grid != 'undefined') values.push(grid);
		
		this.set('x_label_style', values);
	},

	/**
	 * Sets the y and x grid color at the same time
	 *
	 * @param {String} color
	 */
	/*public void*/ setGridColor: function(color) {
		//if (this.get('x_grid_colour', false) != '') {
			this.set('x_grid_colour', color);
			this.set('y_grid_colour', color);
		//}
	},

	/**
	 * Sets the y and x axis color at the same time
	 *
	 * @param {String} color
	 */
	/*public void*/ setAxisColor: function(color) {
		//if (this.get('x_axis_colour', false) != '') {
			this.set('x_axis_colour', color);
			this.set('y_axis_colour', color);
		//}
	},

	/**
	 * Sets the labels for the chart
	 *
	 * @param {Array} labels Array of Strings
	 */
	/*public void*/ setXLabels: function(labels) {
		this.xlabels = labels;
	},
	
	/**
	 * Sets the logo of the chart
	 * 
	 * @param {String} url - URL to logo (relative to page or absolute http)
	 * @param {String} xPosition - horizontal position: left, center or right
	 * @param {String} yPosition - vertical position: top, middle or bottom
	 * @return void
	 */
	/*public void*/ setLogo: function(url, xPosition, yPosition) {
		if (this.logo == null) {
			this.set('bg_image', url);
		}
		
		if (typeof xPosition != 'undefined' && (
			xPosition == 'left' ||
			xPosition == 'center' ||
			xPosition == 'right'
		)) {
			this.set('bg_image_x', xPosition);
		}
		
		if (typeof yPosition != 'undefined' && (
			yPosition == 'top' ||
			yPosition == 'middle' ||
			yPosition == 'bottom'
		)) {
			this.set('bg_image_y', yPosition);
		}
	},
	
	/**
	 * removes the logo from the chart
	 * 
	 * @return void
	 */
	/*public void*/ removeLogo: function() {
		this.unset('bg_image');
		this.unset('bg_image_x');
		this.unset('bg_image_y');
	},
	
	/************************************************
	 * Mighty Line Functions							*
	 ************************************************/
	/**
	 * Shows Mighty Line
	 *
	 */
	/*public void*/ showMightyLine: function()
	{
		this.set( 'show_mighty_line', 'true' );
	},
	/**
	 * Hides Mighty Line
	 *
	 */
	/*public void*/ hideMightyLine: function()
	{
		this.set( 'show_mighty_line', 'false' );
	},
	/**
	 * Checks if Mighty Line is visible
	 *
	 * @return {bool}
	 */
	/* public boolean*/ mightyLineVisible: function()
	{
		return ( this.get( 'show_mighty_line' ) == 'true' );
	},
	/**
	 * Sets Mighty Line
	 *
	 * @param {Float} Position of Line 0 to 1
	 */
	/*public void*/ setMightyLine: function( n )
	{
		this.object.setMightyLine( n );
	},

	/************************************************
	 * Download Functions							*
	 ************************************************/
	
	/**
	 * Invokes the upload of JPEG-data to the server
	 */
	download: function(callback) {
		this.object.doIt('', 'OFC.downloadingComplete');
	},
	
	downloadHelper: function(key) {
		window.location.href = this.options.downloadURL+'?tempkey='+key;
	}
});
