Source: form/file.js

/**
 File upload component<br>
 This components submits the file to an iframe. The url of this iframe is by default.<br>
 LUDOJS_CONFIG.fileupload.url. You can override it with remote.url config property.

 The file upload component should be implemented this way:

 1) File is uploaded to the server<br>
 2) You copy the file to a temporary area and save a reference to it in a database<br>
 3) You return the reference to the file, example id of database row(e.g. 1000)<br>
 4) The reference(1000) will be sent back from the server and saved as value of file upload component.<br>

 A PHP implementation of the PHP code of this can be obtained by contacting post[at]dhtmlgoodies.com.

 @namespace ludo.form
 @class ludo.form.File
 @augments ludo.form.Element
 
 @param {Object} config
 @param {String} config.labelButton Label for button, default: "Browse"
 @param {String} config.labelRemove Remove existing file label, default: "Remove"
 @param {Boolean} config.instantUpload  Upload instantly when selecting file. During upload the form component will be flagged
 as invalid, i.e. submit button will be disabled during file upload.
 @param {Number} config.buttonWidth Width of browse button in pixels.
 @param {String} config.accept Comma separated string of valid extensions, example : 'png,gif,bmp'
 @fires ludo.form.File#submit Fired after successful file upload. Arguments: 1) {Object} Json from server, 2) ludo.form.File
 @fires ludo.form.File#submitFail Fired after failed file upload, i.e. server responded with a JSON object where success was false({ "success": false }). Arguments: 1) {Object} Json from server, 2) ludo.form.File
 @fires ludo.form.File#fail Fired on other failures than submitFail, example: server error. Arguments: 1) String response from server, 2) ludo.form.File
 @example
	 ...
	 children:[{
		 type:'form.File', label:'Pgn File', name:'pgnfile', required:true, labelButton:'Find Pgn file', buttonWidth:100
	 }]
 	 ...
 is example of code used to add a file components as child of a component.

 When the file is uploaded to the server(happens instantly when instantUpload is set to true), the name
 of the file will be sent in POST variable ludo-file-upload-name. The actual file should be available in the
 FILES array sent to the server.

 Example of code sent to server:
 	{
		ludo-file-upload-name : '<name of file>',
		'name-of-file-upload-component' : 'pgnfile'
    }


 Example of PHP Code used to handle the file:

 @example

	 if(isset($_POST['ludo-file-upload-name'])){
		 header('Content-Type: text/html; charset=utf-8');
		 $uploadInfo = FileUpload::uploadFile($_FILES[$_POST['ludo-file-upload-name']]);

		 $data = array('success' => true, 'message' => '', 'data' => $uploadInfo);

		 die(utf8_encode(json_encode($data)));
	 }
 Response from server may look like this:

 @example
	 {
	 	success : true,
	 	value : '100'
	 }

 where success indicates if the upload was successful and value is a reference to the file. When the form with this
 file upload component is later submitted,

 */
ludo.form.File = new Class({
	Extends:ludo.form.Element,
	type:'form.File',

	inputTag:'input',
	inputType:'file',


	labelButton:'Browse',


	labelRemove:'Remove',

	labelDelete:'Delete',


	valueForDisplay:'',

	instantUpload:true,

	uploadInProgress:false,

	fileUpparseNewData:true,


	isFileUploadComponent:true,


	buttonWidth:80,

	accept:undefined,

    resource:'FileUpload',

	__construct:function (config) {
		this.parent(config);
        this.setConfigParams(config, ['resource','instantUpload','labelButton','labelRemove','labelDelete','buttonWidth']);
		if (config.accept) {
			this.accept = config.accept.toLowerCase().split(/,/g);
		}
		if (config.value) {
			this.valueForDisplay = config.value;
		}
		this.value = '';
	},

	__rendered:function () {
		this.parent();

		var cell = $('<div>');
		cell.width(this.buttonWidth);
		cell.css('textAlign', 'right');
		this.getBody().append(cell);
		cell.append(this.getFormEl());

		var btn = new ludo.form.Button({
			type:'form.Button',
			layout:{
				height:30,
				width:this.buttonWidth
			},
			value:this.labelButton,
			overflow:'hidden',
			renderTo:cell
		});

		var fe = this.getFormEl();
		fe.css({
			opacity:0,
			'-moz-opacity':0,
			height:'100%',
			'position':'absolute',
			'right':0,
			top:0,
			'z-index':100000,
			cursor:'pointer',
			filter:'alpha(opacity=0)'
		});

		fe.on('mouseover', btn.mouseOver.bind(btn));
		fe.on('mouseout', btn.mouseOut.bind(btn));
		fe.on('mousedown', btn.mouseDown.bind(btn));
		fe.on('mouseup', btn.mouseUp.bind(btn));
		fe.on('change', this.selectFile.bind(this));



		btn.getEl().append(fe);

		this.createIframe();
		this.createFormElementForComponent();

		if (this.valueForDisplay) {
			this.displayFileName();
		}
	},

	createFormElementForComponent:function () {
		var formEl = this.els.form = $('<form method="post action="' + this.getUploadUrl() + '" enctype="multipart/form-data" target="' + this.getIframeName() + '">');

		formEl.css({ margin:0, padding:0, border:0});
		this.getEl().append(formEl);
		formEl.append(this.getBody());

		this.addElToForm('ludo-file-upload-name',this.getName());
		this.addElToForm('request', this.getResource() + '/save');

	},

    getResource:function(){
        return this.resource || 'FileUpload';
    },

	addElToForm:function(name,value){
		var el =$('<input type="hidden" name="' + name + '">');
		el.val(value);
		this.els.form.append(el);
	},

	createIframe:function () {
		this.iframeName = this.getIframeName();
		var el = this.els.iframe = $('<iframe name="' + this.iframeName + '">');
		el.css({
			width:1, height:1,
			visibility:'hidden',
			position:'absolute'
		});
		this.getEl().append(el);
		el.on('load', this.onUpparseNewData.bind(this));

	},

	getIframeName:function () {
		return 'iframe-' + this.getId();
	},

	onUpparseNewData:function () {
		this.fileUpparseNewData = true;
		if (window.frames[this.iframeName].location.href.indexOf('http:') == -1) {
			return;
		}
		try {
			var json = JSON.parse(window.frames[this.iframeName].document.body.innerHTML);
			if (json.success) {
				this.value = json.response;
				this.fireEvent('submit', [json.response, this]);
			} else {
				this.fireEvent('submitfail', [json, this]);
			}
			this.fireEvent('valid', ['', this]);
		} catch (e) {
			var html = '';
			try {
				html = window.frames[this.iframeName].document.body.innerHTML;
			} catch (e) {
			}
			this.fireEvent('fail', [e, html, this]);

		}

		this.uploadInProgress = false;
		this.displayFileName();

        this.validate();
	},

	isValid:function () {
		if (this.required && !this.getValue() && !this.hasFileToUpload()) {
			return false;
		}
		if (!this.hasValidExtension())return false;
		return !this.uploadInProgress;
	},

	hasValidExtension:function () {
		if (!this.hasFileToUpload() || this.accept === undefined) {
			return true;
		}
		return this.accept.indexOf(this.getExtension()) >= 0;

	},

	getExtension:function () {
		var file = this._get();
		var tokens = file.split(/\./g);
        return tokens.pop().toLowerCase();
	},

	getUploadUrl:function () {
        var url = ludo.config.getFileUploadUrl() || this.getUrl();
        if (!url) {
            ludo.util.warn('No url defined for file upload. You can define it with the code ludo.config.setFileUploadUrl(url)');
        }
		return url;
	},

	selectFile:function () {
		this.value = this.valueForDisplay = this.getFormEl().get('value');
		this.fileUpparseNewData = false;
		this.displayFileName();
		this.setDirty();
		if (this.instantUpload) {
			this.upload();
		}

	},

	displayFileName:function () {
        var ci = this.els.cellInput;
		ci.html( '');
		ci.removeClass('ludo-input-file-name-new-file');
		ci.removeClass('ludo-input-file-name-initial');
		ci.removeClass('ludo-input-file-name-not-uploaded');
		if (this.valueForDisplay) {

            var span = ludo.dom.create({
                tag:'span',
                html : this.valueForDisplay + ' ',
                renderTo:ci
            });

			var deleteLink = new Element('a');
			deleteLink.addEvent('click', this.removeFile.bind(this));
			deleteLink.set('href', '#');
			var html = this.labelRemove;
			if (this.valueForDisplay == this.initialValue) {
				html = this.labelDelete;
				ci.addClass('ludo-input-file-name-initial');
			} else {
				ci.addClass('ludo-input-file-name-new-file');
			}
			if (!this.fileUpparseNewData) {
				ci.addClass('ludo-input-file-name-not-uploaded');
			}
			deleteLink.html( html);
			ci.append(deleteLink);
		}
	},
	resizeDOM:function () {
		/* No DOM resize necessary for this component */
	},
	upload:function () {
		if (!this.hasValidExtension()) {
			return;
		}
		this.fireEvent('invalid', ['', this]);
		this.uploadInProgress = true;
		this.els.form.submit();
	},

	getValue:function () {
		console.warn("Use of deprecated getValue");
		console.trace();
		return this.value;
	},


	_get:function(){
		return this.value;
	},
	/*
	 * setValue for file inputs is display only. File inputs are readonly
	 * @function setValue
	 * @param {Object} value
	 *
	 */
	setValue:function (value) {
		console.warn("Use of deprecated setValue");
		this.valueForDisplay = value;
		this.displayFileName();
		this.validate();
	},

	/**
	 * "set" is readonly for file inputs. It will update the displayed file name, not the file input it's self.
	 * Method without arguments returns the file input value
	 * @function val
	 * @param {Object} value
	 * @memberof ludo.form.File.prototype
	 */
	val:function(value){
		if(arguments.length == 0){
			return this._get();
		}

		this.valueForDisplay = value;
		this.displayFileName();
		this.validate();
	},

	commit:function () {
		this.initialValue = this.valueForDisplay;
		this.displayFileName();
	},

	removeFile:function () {
        this.valueForDisplay = this.valueForDisplay === this.initialValue ? '' : this.initialValue;
		this.value = '';
		this.displayFileName();
		this.validate();
		return false;
	},

	hasFileToUpload:function () {
		return !this.fileUpparseNewData;
	},

	blur:function () {

	},

    supportsInlineLabel:function(){
        return false;
    }
});