function Cloner(o)
{
	this.enabled = true;

	this.mode = Cloner.GRID_MODE;

	this.createProperty( "count", vec3.fromValues(10,1,1) );
	this.createProperty( "size", vec3.fromValues(100,100,100) );

	this.mesh = null;
	this.lod_mesh = null;
	this.material = null;

	this._instances_matrix = [];

	this._RI = new LS.RenderInstance( null, this );

	if(o)
		this.configure(o);
}
****************************

Script.prototype.onError = function(e)
{
	var scene = this._root.scene;
	if(!scene)
		return;

	e.script = this;
	e.node = this._root;

	console.warn("Script: blocking function on script due to error: " + e.method_name );
	this._blocked_functions.add( e.method_name );

	LEvent.trigger( this, "code_error",e);
	LEvent.trigger( scene, "code_error",e);
	LEvent.trigger( LS, "code_error",e);

	//conditional this?
	console.log("app finishing due to error in script, scene state is keep as it was during the error.");
	scene.finish();
}

//called from the editor?
Script.prototype.onCodeChange = function(code)
{
	this.processCode();
}

Script.prototype.getResources = function(res)
{
	var ctx = this.getContext();

	for(var i in ctx)
	{
		var value = ctx[i];
		var info = ctx.constructor[ "@" + i];
		if( !value || !info )
			continue;

		//for basic resource types
		if( LS.RESOURCE_TYPES[ info.type ] )
			res[ value ] = true;

		//for arrays
		if( info.type == LS.TYPES.ARRAY && value.length && info.data_type && info.data_type.constructor === String && LS.RESOURCE_TYPES[ (info.data_type).toLowerCase() ] )
		{
			for(var j = 0; j < value.length; ++j)
				res[ value[j] ] = true;
		}
	}

	if(ctx && ctx.onGetResources )
		ctx.onGetResources( res );
}

Script.prototype.onResourceRenamed = function( old_name, new_name, resource )
{
	var ctx = this.getContext();
	if(ctx && ctx.onResourceRenamed )
		ctx.onResourceRenamed( old_name, new_name, resource );
}

LS.registerComponent( Script );
LS.Script = Script;

//*****************

function ScriptFromFile(o)
{
	this.enabled = true;
	this._filename = "";
	this._name = "";

	this._script = new LScript();
	this._blocked_functions = new Set(); //used to block functions that has errors

	this._script.extra_methods = {
		getComponent: (function() { return this; }).bind(this),
		getLocator: function() { return this.getComponent().getLocator() + "/context"; },
		createProperty: LS.BaseComponent.prototype.createProperty,
		createAction: LS.BaseComponent.prototype.createAction,
		bind: LS.BaseComponent.prototype.bind,
		unbind: LS.BaseComponent.prototype.unbind,
		unbindAll: LS.BaseComponent.prototype.unbindAll
	};

	this._script.onerror = this.onError.bind(this);
	this._script.exported_functions = [];//this.constructor.exported_callbacks;
	this._last_error = null;

	if(o)
		this.configure(o);
}

ScriptFromFile.coding_help = Script.coding_help;

Object.defineProperty( ScriptFromFile.prototype, "filename", {
	set: function(v){ 
		if(v) //to avoid double slashes
			v = LS.ResourcesManager.cleanFullpath( v );
		this._filename = v;
		this.processCode();
	},
	get: function() { 
		return this._filename;
	},
	enumerable: true
});

Object.defineProperty( ScriptFromFile.prototype, "context", {
	set: function(v){ 
		console.error("ScriptFromFile: context cannot be assigned");
	},
	get: function() { 
		if(this._script)
				return this._script._context;
		return null;
	},
	enumerable: false //if it was enumerable it would be serialized
});

Object.defineProperty( ScriptFromFile.prototype, "name", {
	set: function(v){ 
		console.error("Script: name cannot be assigned, set the first line with //@name");
	},
	get: function() { 
		return this._name;
	},
	enumerable: false //if it was enumerable it would be serialized
});

ScriptFromFile.prototype.onAddedToScene = function( scene )
{
	//avoid to parse it again
	if(this._script && this._script._context && this._script._context._initialized )
	{
		if( this._script._context.onBind )
			this._script._context.onBind( scene );
		if( this._script._context.onAddedToScene )
			this._script._context.onAddedToScene( scene );

		return;
	}

	if( !this.constructor.catch_important_exceptions )
	{
		this.processCode();
		return;
	}

	//catch
	try
	{
		//careful, if the code saved had an error, do not block the flow of the configure or the rest will be lost
		this.processCode();
	}
	catch (err)
	{
		console.error(err);
	}
}

/**
Force to reevaluate the code (only for special situations like remove codes)
@method reload
@param {Function} [on_complete=null]