JSBool gf_sg_js_has_instance()

in src/scenegraph/vrml_smjs.c [408:1271]


JSBool gf_sg_js_has_instance(JSContext *c, JSHandleObject obj, JSMutableHandleValue __val, JSBool *vp)
#elif defined(USE_FFDEV_15)
JSBool gf_sg_js_has_instance(JSContext *c, JSHandleObject obj,const jsval *val, JSBool *vp)
#else
JSBool gf_sg_js_has_instance(JSContext *c, JSObject *obj,const jsval *val, JSBool *vp)
#endif
#else
JSBool gf_sg_js_has_instance(JSContext *c, JSObject *obj, jsval val, JSBool *vp)
#endif
{
#ifdef USE_FFDEV_18
	jsval       *val = __val._;
#endif
	*vp = JS_FALSE;
#if (JS_VERSION>=185)
	if (val && JSVAL_IS_OBJECT(*val)) {
		JSObject *p = JSVAL_TO_OBJECT(*val);
#else
	if (JSVAL_IS_OBJECT(val)) {
		JSObject *p = JSVAL_TO_OBJECT(val);
#endif
		JSClass *js_class = JS_GET_CLASS(c, obj);
		if (JS_InstanceOf(c, p, js_class, NULL) ) *vp = JS_TRUE;
	}
	return JS_TRUE;
}

#ifndef GPAC_DISABLE_SVG
/*SVG tags for script handling*/
#include <gpac/nodes_svg.h>

GF_Node *dom_get_element(JSContext *c, JSObject *obj);
#endif


JSBool gf_sg_script_to_node_field(struct JSContext *c, jsval v, GF_FieldInfo *field, GF_Node *owner, GF_JSField *parent);
jsval gf_sg_script_to_smjs_field(GF_ScriptPriv *priv, GF_FieldInfo *field, GF_Node *parent, Bool force_evaluate);

static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo *info, GF_Node *script);

Bool JSScriptFromFile(GF_Node *node, const char *opt_file, Bool no_complain, jsval *rval);

void gf_sg_js_call_gc(JSContext *c)
{
	gf_sg_lock_javascript(c, 1);
#ifdef 	USE_FFDEV_14
	JS_GC(js_rt->js_runtime);
#else
	JS_GC(c);
#endif
	gf_sg_lock_javascript(c, 0);
}

void do_js_gc(JSContext *c, GF_Node *node)
{
#ifdef FORCE_GC
	node->sgprivate->scenegraph->trigger_gc = GF_TRUE;
#endif

	if (node->sgprivate->scenegraph->trigger_gc) {
		node->sgprivate->scenegraph->trigger_gc = GF_FALSE;
		gf_sg_js_call_gc(c);
	}
}


#ifndef GPAC_DISABLE_VRML

/*MPEG4 & X3D tags (for node tables & script handling)*/
#include <gpac/nodes_mpeg4.h>
#include <gpac/nodes_x3d.h>

void SFColor_fromHSV(SFColor *col)
{
	Fixed f, q, t, p, hue, sat, val;
	u32 i;
	hue = col->red;
	sat = col->green;
	val = col->blue;
	if (sat==0) {
		col->red = col->green = col->blue = val;
		return;
	}
	if (hue == FIX_ONE) hue = 0;
	else hue *= 6;
	i = FIX2INT( gf_floor(hue) );
	f = hue-i;
	p = gf_mulfix(val, FIX_ONE - sat);
	q = gf_mulfix(val, FIX_ONE - gf_mulfix(sat,f));
	t = gf_mulfix(val, FIX_ONE - gf_mulfix(sat, FIX_ONE - f));
	switch (i) {
	case 0:
		col->red = val;
		col->green = t;
		col->blue = p;
		break;
	case 1:
		col->red = q;
		col->green = val;
		col->blue = p;
		break;
	case 2:
		col->red = p;
		col->green = val;
		col->blue = t;
		break;
	case 3:
		col->red = p;
		col->green = q;
		col->blue = val;
		break;
	case 4:
		col->red = t;
		col->green = p;
		col->blue = val;
		break;
	case 5:
		col->red = val;
		col->green = p;
		col->blue = q;
		break;
	}
}

void SFColor_toHSV(SFColor *col)
{
	Fixed h, s;
	Fixed _max = MAX(col->red, MAX(col->green, col->blue));
	Fixed _min = MIN(col->red, MAX(col->green, col->blue));

	s = (_max == 0) ? 0 : gf_divfix(_max - _min, _max);
	if (s != 0) {
		Fixed rl = gf_divfix(_max - col->red, _max - _min);
		Fixed gl = gf_divfix(_max - col->green, _max - _min);
		Fixed bl = gf_divfix(_max - col->blue, _max - _min);
		if (_max == col->red) {
			if (_min == col->green) h = 60*(5+bl);
			else h = 60*(1-gl);
		} else if (_max == col->green) {
			if (_min == col->blue) h = 60*(1+rl);
			else h = 60*(3-bl);
		} else {
			if (_min == col->red) h = 60*(3+gl);
			else h = 60*(5-rl);
		}
	} else {
		h = 0;
	}
	col->red = h;
	col->green = s;
	col->blue = _max;
}

static GFINLINE GF_JSField *NewJSField(JSContext *c)
{
	GF_JSField *ptr;
	GF_SAFEALLOC(ptr, GF_JSField);
	ptr->js_ctx = c;
	return ptr;
}

static GFINLINE M_Script *JS_GetScript(JSContext *c)
{
	return (M_Script *) JS_GetContextPrivate(c);
}
static GFINLINE GF_ScriptPriv *JS_GetScriptStack(JSContext *c)
{
	M_Script *script = (M_Script *) JS_GetContextPrivate(c);
	return script->sgprivate->UserPrivate;
}

static void script_error(JSContext *c, const char *msg, JSErrorReport *jserr)
{
	if (jserr->linebuf) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[JavaScript] Error: %s - line %d (%s) - file %s\n", msg, jserr->lineno, jserr->linebuf, jserr->filename));
	} else if (jserr->filename) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[JavaScript] Error: %s - line %d - file %s\n", msg, jserr->lineno, jserr->filename));
	} else {
		GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[JavaScript] Error: %s - line %d\n", msg, jserr->lineno));
	}
}

static JSBool SMJS_FUNCTION(JSPrint)
{
	SMJS_ARGS
	if (!argc) return JS_FALSE;

	if (JSVAL_IS_STRING(argv[0])) {
		char *str = SMJS_CHARS(c, argv[0]);
		_ScriptMessage(c, str);
		SMJS_FREE(c, str);
	}
	if (JSVAL_IS_INT(argv[0]) && (argc>1) && JSVAL_IS_STRING(argv[1]) ) {
		u32 level = JSVAL_TO_INT(argv[0]);
		char *str = SMJS_CHARS(c, argv[1]);
		if (level > GF_LOG_DEBUG) level = GF_LOG_DEBUG;
		if (str[0] == '[') {
			GF_LOG(level, GF_LOG_CONSOLE, ("%s\n", str));
		} else {
			GF_LOG(level, GF_LOG_CONSOLE, ("[JS] %s\n", str));
		}
		SMJS_FREE(c, str);
	}
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(getName)
{
	JSString *s = JS_NewStringCopyZ(c, "GPAC RichMediaEngine");
	if (!s) return JS_FALSE;
	SMJS_SET_RVAL( STRING_TO_JSVAL(s) );
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(getVersion)
{
	JSString *s = JS_NewStringCopyZ(c, GPAC_FULL_VERSION);
	if (!s) return JS_FALSE;
	SMJS_SET_RVAL( STRING_TO_JSVAL(s) );
	return JS_TRUE;
}
static JSBool SMJS_FUNCTION(getCurrentSpeed)
{
	GF_JSAPIParam par;
	GF_Node *node = JS_GetContextPrivate(c);
	par.time = 0;
	ScriptAction(c, NULL, GF_JSAPI_OP_GET_SPEED, node->sgprivate->scenegraph->RootNode, &par);
	SMJS_SET_RVAL(  JS_MAKE_DOUBLE( c, par.time) );
	return JS_TRUE;
}
static JSBool SMJS_FUNCTION(getCurrentFrameRate)
{
	GF_JSAPIParam par;
	GF_Node *node = JS_GetContextPrivate(c);
	par.time = 0;
	ScriptAction(c, NULL, GF_JSAPI_OP_GET_FPS, node->sgprivate->scenegraph->RootNode, &par);
	SMJS_SET_RVAL( JS_MAKE_DOUBLE( c, par.time) );
	return JS_TRUE;
}
static JSBool SMJS_FUNCTION(getWorldURL)
{
	GF_JSAPIParam par;
	GF_Node *node = JS_GetContextPrivate(c);
	par.uri.url = NULL;
	par.uri.nb_params = 0;
	if (ScriptAction(c, NULL, GF_JSAPI_OP_RESOLVE_URI, node->sgprivate->scenegraph->RootNode, &par)) {
		JSString *s = JS_NewStringCopyZ(c, par.uri.url);
		if (!s) return JS_FALSE;
		SMJS_SET_RVAL( STRING_TO_JSVAL(s) );
		gf_free(par.uri.url);
		return JS_TRUE;
	}
	return JS_FALSE;
}

static JSObject *node_get_binding(GF_ScriptPriv *priv, GF_Node *node, Bool is_constructor)
{
	JSObject *obj;
	GF_JSField *field;

	if (node->sgprivate->interact && node->sgprivate->interact->js_binding && node->sgprivate->interact->js_binding->node) {
		field = node->sgprivate->interact->js_binding->node;
		if (!field->is_rooted) {
			gf_js_add_root(priv->js_ctx, &field->obj, GF_JSGC_OBJECT);
			field->is_rooted=1;
		}
		return field->obj;
	}

	field = NewJSField(priv->js_ctx);
	field->field.fieldType = GF_SG_VRML_SFNODE;
	field->node = node;
	field->field.far_ptr = &field->node;


	node->sgprivate->flags |= GF_NODE_HAS_BINDING;
	gf_node_register(node, NULL);

	obj = JS_NewObject(priv->js_ctx, &js_rt->SFNodeClass._class, 0, 0);
	SMJS_SET_PRIVATE(priv->js_ctx, obj, field);

	field->obj = obj;
	gf_list_add(priv->js_cache, obj);

	/*remember the object*/
	if (!node->sgprivate->interact) GF_SAFEALLOC(node->sgprivate->interact, struct _node_interactive_ext);
	if (!node->sgprivate->interact->js_binding) {
		GF_SAFEALLOC(node->sgprivate->interact->js_binding, struct _node_js_binding);
		node->sgprivate->interact->js_binding->fields = gf_list_new();
	}
	node->sgprivate->flags |= GF_NODE_HAS_BINDING;
	node->sgprivate->interact->js_binding->node = field;
	if (!is_constructor) {
		field->is_rooted = 1;
		gf_js_add_root(priv->js_ctx, &field->obj, GF_JSGC_OBJECT);
	}
	return obj;
}

static JSBool SMJS_FUNCTION(getScript)
{
	GF_ScriptPriv *priv = JS_GetScriptStack(c);
	GF_Node *node = JS_GetContextPrivate(c);
	JSObject *an_obj = node_get_binding(priv, node, 0);

	if (an_obj) SMJS_SET_RVAL( OBJECT_TO_JSVAL(an_obj) );
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(loadScript)
{
	Bool no_complain = 0;
	char *url;
	GF_Node *node = JS_GetContextPrivate(c);
	SMJS_ARGS
	jsval aval;
	if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE;

	if ((argc>1) && JSVAL_IS_BOOLEAN(argv[1])) no_complain = (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ? 1 : 0;

	url = SMJS_CHARS(c, argv[0]);
	if (url) {
		JSScriptFromFile(node, url, no_complain, &aval);
		SMJS_SET_RVAL(aval);
	}
	SMJS_FREE(c, url);
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(getProto)
{
	JSObject *an_obj;
	GF_ScriptPriv *priv = JS_GetScriptStack(c);
	GF_Node *node = JS_GetContextPrivate(c);
	SMJS_SET_RVAL( JSVAL_NULL);
	if (!node->sgprivate->scenegraph->pOwningProto) {
		return JS_TRUE;
	}
	node = (GF_Node *) node->sgprivate->scenegraph->pOwningProto;

	an_obj = node_get_binding(priv, node, 0);
	if (an_obj) SMJS_SET_RVAL( OBJECT_TO_JSVAL(an_obj) );
	return JS_TRUE;
}

#ifndef GPAC_DISABLE_SVG
char *js_get_utf8(jsval val);
jsval dom_element_construct(JSContext *c, GF_Node *n);
#endif

static JSBool SMJS_FUNCTION(vrml_parse_xml)
{
#ifndef GPAC_DISABLE_SVG
	GF_SceneGraph *sg;
	GF_Node *node;
	char *str;
	GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
	SMJS_ARGS

	str = js_get_utf8(argv[0]);
	if (!str) return JS_TRUE;

	node = JS_GetContextPrivate(c);
	sg = node->sgprivate->scenegraph;

	node = gf_sm_load_svg_from_string(sg, str);
	gf_free(str);
	SMJS_SET_RVAL( dom_element_construct(c, node) );
#endif
	return JS_TRUE;
}


static JSBool SMJS_FUNCTION(getElementById)
{
	GF_Node *elt;
	JSObject *an_obj;
	char *name = NULL;
	u32 ID = 0;
	GF_ScriptPriv *priv = JS_GetScriptStack(c);
	GF_Node *sc = JS_GetContextPrivate(c);
	SMJS_ARGS
	if (JSVAL_IS_STRING(argv[0])) name = SMJS_CHARS(c, argv[0]);
	else if (JSVAL_IS_INT(argv[0])) ID = JSVAL_TO_INT(argv[0]);

	if (!ID && !name) return JS_FALSE;

	elt = NULL;
	if (ID) elt = gf_sg_find_node(sc->sgprivate->scenegraph, ID);
	else elt = gf_sg_find_node_by_name(sc->sgprivate->scenegraph, name);

	SMJS_FREE(c, name);
	if (!elt) return JS_TRUE;

	an_obj = node_get_binding(priv, elt, 0);
	if (an_obj) SMJS_SET_RVAL( OBJECT_TO_JSVAL(an_obj) );
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(replaceWorld)
{
	return JS_TRUE;
}

static void on_route_to_object(GF_Node *node, GF_Route *_r)
{
	jsval argv[2], rval;
	Double time;
	GF_FieldInfo t_info;
	GF_ScriptPriv *priv;
	JSObject *obj;
	GF_RouteToScript *r = (GF_RouteToScript *)_r;
	if (!node) return;
	priv = gf_node_get_private(node);
	if (!priv) return;

	if (!r->FromNode) {
		if (r->obj) {
//			gf_js_remove_root(priv->js_ctx, &r->obj, GF_JSGC_OBJECT);
			r->obj=NULL;
		}
		if ( ! JSVAL_IS_VOID(r->fun)) {
//			gf_js_remove_root(priv->js_ctx, &r->fun, GF_JSGC_OBJECT);
			r->fun=JSVAL_NULL;
		}
		return;
	}

	obj = (JSObject *) r->obj;
	if (!obj) obj = priv->js_obj;

	memset(&t_info, 0, sizeof(GF_FieldInfo));
	time = gf_node_get_scene_time(node);
	t_info.far_ptr = &time;
	t_info.fieldType = GF_SG_VRML_SFTIME;
	t_info.fieldIndex = -1;
	t_info.name = "timestamp";

	gf_sg_lock_javascript(priv->js_ctx, 1);

	argv[1] = gf_sg_script_to_smjs_field(priv, &t_info, node, 1);
	argv[0] = gf_sg_script_to_smjs_field(priv, &r->FromField, r->FromNode, 1);

	/*protect args*/
	if (JSVAL_IS_GCTHING(argv[0])) gf_js_add_root(priv->js_ctx, &argv[0], GF_JSGC_VAL);
	if (JSVAL_IS_GCTHING(argv[1])) gf_js_add_root(priv->js_ctx, &argv[1], GF_JSGC_VAL);

	JS_CallFunctionValue(priv->js_ctx, obj, (jsval) r->fun, 2, argv, &rval);

	/*release args*/
	if (JSVAL_IS_GCTHING(argv[0])) gf_js_remove_root(priv->js_ctx, &argv[0], GF_JSGC_VAL);
	if (JSVAL_IS_GCTHING(argv[1])) gf_js_remove_root(priv->js_ctx, &argv[1], GF_JSGC_VAL);

	/*check any pending exception if outer-most event*/
	if ( (JS_IsRunning(priv->js_ctx)==JS_FALSE) && JS_IsExceptionPending(priv->js_ctx)) {
		JS_ReportPendingException(priv->js_ctx);
		JS_ClearPendingException(priv->js_ctx);
	}

	gf_sg_lock_javascript(priv->js_ctx, 0);

	do_js_gc(priv->js_ctx, node);
}

static JSBool SMJS_FUNCTION(addRoute)
{
	GF_JSField *ptr;
	GF_Node *n1, *n2;
	char *f1, *f2;
	GF_FieldInfo info;
	u32 f_id1, f_id2;
	GF_Err e;
	SMJS_ARGS
	if (argc!=4) return JS_FALSE;

	if (!JSVAL_IS_OBJECT(argv[0]) || !GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE;

	ptr = (GF_JSField *) SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]));
	assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
	n1 = * ((GF_Node **)ptr->field.far_ptr);
	if (!n1) return JS_FALSE;
	n2 = NULL;

	if (!JSVAL_IS_STRING(argv[1])) return JS_FALSE;
	f1 = SMJS_CHARS(c, argv[1]);
	if (!f1) return JS_FALSE;
	if (!strnicmp(f1, "_field", 6)) {
		f_id1 = atoi(f1+6);
		e = gf_node_get_field(n1, f_id1, &info);
	} else {
		e = gf_node_get_field_by_name(n1, f1, &info);
		f_id1 = info.fieldIndex;
	}
	SMJS_FREE(c, f1);
	if (e != GF_OK) return JS_FALSE;


	if (!JSVAL_IS_OBJECT(argv[2])) return JS_FALSE;

	/*regular route*/
	if (GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[2]), &js_rt->SFNodeClass, NULL) && JSVAL_IS_STRING(argv[3]) ) {
		GF_Route *r;
		ptr = (GF_JSField *) SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[2]));
		assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
		n2 = * ((GF_Node **)ptr->field.far_ptr);
		if (!n2) return JS_FALSE;

		f2 = SMJS_CHARS(c, argv[3]);
		if (!f2) return JS_FALSE;

		if (!strnicmp(f2, "_field", 6)) {
			f_id2 = atoi(f2+6);
			e = gf_node_get_field(n2, f_id2, &info);
		} else {
			if ((n2->sgprivate->tag==TAG_MPEG4_Script)
#ifndef GPAC_DISABLE_X3D
			        || (n2->sgprivate->tag==TAG_X3D_Script)
#endif
			   ) {
				GF_FieldInfo src = info;
				if (gf_node_get_field_by_name(n2, f2, &info) != GF_OK) {
					gf_sg_script_field_new(n2, GF_SG_SCRIPT_TYPE_EVENT_IN, src.fieldType, f2);
				}
			}
			e = gf_node_get_field_by_name(n2, f2, &info);
			f_id2 = info.fieldIndex;
		}
		SMJS_FREE(c, f2);
		if (e != GF_OK) return JS_FALSE;

		r = gf_sg_route_new(n1->sgprivate->scenegraph, n1, f_id1, n2, f_id2);
		if (!r) return JS_FALSE;
	}
	/*route to object*/
	else {
		u32 i = 0;
		const char *fun_name;
		GF_RouteToScript *r = NULL;
		if (!JSVAL_IS_OBJECT(argv[3]) || !JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(argv[3])) ) return JS_FALSE;

		fun_name = JS_GetFunctionName( JS_ValueToFunction(c, argv[3] ) );
		if (fun_name && n1->sgprivate->interact && n1->sgprivate->interact->routes ) {
			while ( (r = (GF_RouteToScript*)gf_list_enum(n1->sgprivate->interact->routes, &i) )) {
				if ( (r->FromNode == n1)
				        && (r->FromField.fieldIndex == f_id1)
				        && (r->ToNode == (GF_Node*)JS_GetScript(c))
				        && !stricmp(r->ToField.name, fun_name)
				   )
					break;
			}
		}

		if ( !r ) {
			GF_SAFEALLOC(r, GF_RouteToScript)
			if (!r) return JS_FALSE;
			r->FromNode = n1;
			r->FromField.fieldIndex = f_id1;
			gf_node_get_field(r->FromNode, f_id1, &r->FromField);

			r->ToNode = (GF_Node*)JS_GetScript(c);
			r->ToField.fieldType = GF_SG_VRML_SCRIPT_FUNCTION;
			r->ToField.on_event_in = on_route_to_object;
			r->ToField.eventType = GF_SG_EVENT_IN;
			r->ToField.far_ptr = NULL;
			r->ToField.name = fun_name;

			r->obj = JSVAL_TO_OBJECT( argv[2] ) ;
//			gf_js_add_root(c, & r->obj, GF_JSGC_OBJECT);

			r->fun = argv[3];
//			gf_js_add_root(c, &r->fun, GF_JSGC_OBJECT);

			r->is_setup = 1;
			r->graph = n1->sgprivate->scenegraph;

			if (!n1->sgprivate->interact) GF_SAFEALLOC(n1->sgprivate->interact, struct _node_interactive_ext);
			if (!n1->sgprivate->interact->routes) n1->sgprivate->interact->routes = gf_list_new();
			gf_list_add(n1->sgprivate->interact->routes, r);
			gf_list_add(n1->sgprivate->scenegraph->Routes, r);
		}
	}

	return JS_TRUE;
}


static JSBool SMJS_FUNCTION(deleteRoute)
{
	GF_JSField *ptr;
	GF_Node *n1, *n2;
	char *f1, *f2;
	GF_FieldInfo info;
	GF_Route *r;
	GF_Err e;
	u32 f_id1, f_id2, i;
	SMJS_ARGS
	if (argc!=4) return JS_FALSE;

	if (!JSVAL_IS_OBJECT(argv[0]) || JSVAL_IS_NULL(argv[0]) || !GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE;

	if (JSVAL_IS_STRING(argv[1]) && JSVAL_IS_NULL(argv[2]) && JSVAL_IS_NULL(argv[3])) {
		ptr = (GF_JSField *) SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]));
		assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
		n1 = * ((GF_Node **)ptr->field.far_ptr);
		f1 = SMJS_CHARS(c, argv[1]);
		if (!strcmp(f1, "ALL")) {
			while (n1->sgprivate->interact && n1->sgprivate->interact->routes && gf_list_count(n1->sgprivate->interact->routes) ) {
				r = gf_list_get(n1->sgprivate->interact->routes, 0);
				gf_sg_route_del(r);
			}
		}
		SMJS_FREE(c, f1);
		return JS_TRUE;
	}

	if (!JSVAL_IS_OBJECT(argv[2]) || !GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[2]), &js_rt->SFNodeClass, NULL) ) return JS_FALSE;
	if (!JSVAL_IS_STRING(argv[1]) || !JSVAL_IS_STRING(argv[3])) return JS_FALSE;

	ptr = (GF_JSField *) SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]));
	assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
	n1 = * ((GF_Node **)ptr->field.far_ptr);
	ptr = (GF_JSField *) SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[2]));
	assert(ptr->field.fieldType==GF_SG_VRML_SFNODE);
	n2 = * ((GF_Node **)ptr->field.far_ptr);

	if (!n1 || !n2) return JS_FALSE;
	if (!n1->sgprivate->interact) return JS_TRUE;

	f1 = SMJS_CHARS(c, argv[1]);
	f2 = SMJS_CHARS(c, argv[3]);
	if (!f1 || !f2) {
		SMJS_FREE(c, f1);
		SMJS_FREE(c, f2);
		return JS_FALSE;
	}

	if (!strnicmp(f1, "_field", 6)) {
		f_id1 = atoi(f1+6);
		e = gf_node_get_field(n1, f_id1, &info);
	} else {
		e = gf_node_get_field_by_name(n1, f1, &info);
		f_id1 = info.fieldIndex;
	}
	SMJS_FREE(c, f1);
	if (e != GF_OK) return JS_FALSE;

	if (!strnicmp(f2, "_field", 6)) {
		f_id2 = atoi(f2+6);
		e = gf_node_get_field(n2, f_id2, &info);
	} else {
		e = gf_node_get_field_by_name(n2, f2, &info);
		f_id2 = info.fieldIndex;
	}
	SMJS_FREE(c, f2);
	if (e != GF_OK) return JS_FALSE;

	i=0;
	while ((r = gf_list_enum(n1->sgprivate->interact->routes, &i))) {
		if (r->FromField.fieldIndex != f_id1) continue;
		if (r->ToNode != n2) continue;
		if (r->ToField.fieldIndex != f_id2) continue;
		gf_sg_route_del(r);
		return JS_TRUE;
	}
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(loadURL)
{
	u32 i;
	GF_JSAPIParam par;
	jsval item;
	jsuint len;
	JSObject *p;
	GF_JSField *f;
	SMJS_ARGS
	M_Script *script = (M_Script *) JS_GetContextPrivate(c);

	if (argc < 1) return JS_FALSE;

	if (JSVAL_IS_STRING(argv[0])) {
		Bool res;
		par.uri.url = SMJS_CHARS(c, argv[0]);
		/*TODO add support for params*/
		par.uri.nb_params = 0;
		res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node *)script, &par);
		SMJS_FREE(c, par.uri.url);
		return res ? JS_TRUE : JS_FALSE;
	}
	if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE;

	JS_ValueToObject(c, argv[0], &p);

	f = (GF_JSField *) SMJS_GET_PRIVATE(c, p);
	if (!f || !f->js_list) return JS_FALSE;
	JS_GetArrayLength(c, f->js_list, &len);

	for (i=0; i<len; i++) {
		JS_GetElement(c, f->js_list, (jsint) i, &item);

		if (JSVAL_IS_STRING(item)) {
			Bool res;
			par.uri.url = SMJS_CHARS(c, item);
			/*TODO add support for params*/
			par.uri.nb_params = 0;
			res = ScriptAction(c, NULL, GF_JSAPI_OP_LOAD_URL, (GF_Node*)script, &par);
			SMJS_FREE(c, par.uri.url);
			if (res) return JS_TRUE;
		}
	}
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(setDescription)
{
	GF_JSAPIParam par;
	GF_Node *node = JS_GetContextPrivate(c);
	SMJS_ARGS
	if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_FALSE;
	par.uri.url = SMJS_CHARS(c, argv[0]);
	ScriptAction(c, NULL, GF_JSAPI_OP_SET_TITLE, node->sgprivate->scenegraph->RootNode, &par);
	SMJS_FREE(c, par.uri.url);
	return JS_TRUE;
}

static JSBool SMJS_FUNCTION(createVrmlFromString)
{
#ifndef GPAC_DISABLE_LOADER_BT
	GF_ScriptPriv *priv;
	GF_FieldInfo field;
	/*BT/VRML from string*/
	GF_List *gf_sm_load_bt_from_string(GF_SceneGraph *in_scene, char *node_str, Bool force_wrl);
	char *str;
	GF_List *nlist;
	SMJS_ARGS
	GF_Node *sc_node = JS_GetContextPrivate(c);
	if (argc < 1) return JS_FALSE;

	if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE;
	str = SMJS_CHARS(c, argv[0]);
	nlist = gf_sm_load_bt_from_string(sc_node->sgprivate->scenegraph, str, 1);
	SMJS_FREE(c, str);
	if (!nlist) return JS_FALSE;

	priv = JS_GetScriptStack(c);
	memset(&field, 0, sizeof(GF_FieldInfo));
	field.fieldType = GF_SG_VRML_MFNODE;
	field.far_ptr = &nlist;
	SMJS_SET_RVAL( gf_sg_script_to_smjs_field(priv, &field, NULL, 0) );

	/*don't forget to unregister all this stuff*/
	while (gf_list_count(nlist)) {
		GF_Node *n = gf_list_get(nlist, 0);
		gf_list_rem(nlist, 0);
		gf_node_unregister(n, NULL);
	}
	gf_list_del(nlist);
	return JS_TRUE;
#else
	return JS_FALSE;
#endif
}

void gf_node_event_out_proto(GF_Node *node, u32 FieldIndex);

void Script_FieldChanged(JSContext *c, GF_Node *parent, GF_JSField *parent_owner, GF_FieldInfo *field)
{
	GF_ScriptPriv *priv;
	u32 script_field;
	u32 i;
	GF_ScriptField *sf;


	if (!parent) {
		parent = parent_owner->owner;
		field = &parent_owner->field;
	}
	if (!parent) return;

	script_field = 0;
	if ((parent->sgprivate->tag == TAG_MPEG4_Script)
#ifndef GPAC_DISABLE_X3D
	        || (parent->sgprivate->tag == TAG_X3D_Script)
#endif
	   ) {
		script_field = 1;
		if ( (GF_Node *) JS_GetContextPrivate(c) == parent) script_field = 2;
	}

	if (script_field!=2) {
		if (field->on_event_in) field->on_event_in(parent, NULL);
		else if (script_field && (field->eventType==GF_SG_EVENT_IN) ) {
			gf_sg_script_event_in(parent, field);
			gf_node_changed_internal(parent, field, 0);
			return;
		}
		/*field has changed, set routes...*/
		if (parent->sgprivate->tag == TAG_ProtoNode) {
			GF_ProtoInstance *inst = (GF_ProtoInstance *)parent;
			gf_sg_proto_propagate_event(parent, field->fieldIndex, (GF_Node*)JS_GetScript(c));
			/* Node exposedField can also be routed to another field */
			gf_node_event_out_proto(parent, field->fieldIndex);

			//hardcoded protos be implemented in ways not inspecting the node_dirty propagation scheme (eg defining an SFNode in their interface, not linked with the graph).
			//in this case handle the node as a regular one
			if (inst->flags & GF_SG_PROTO_HARDCODED) {
				gf_node_changed_internal(parent, field, 0);
			}
		} else {
			gf_node_event_out(parent, field->fieldIndex);
			gf_node_changed_internal(parent, field, 0);
		}
		return;
	}
	/*otherwise mark field if eventOut*/
	if (parent_owner || parent) {
		priv = parent ? parent->sgprivate->UserPrivate : parent_owner->owner->sgprivate->UserPrivate;
		i=0;
		while ((sf = gf_list_enum(priv->fields, &i))) {
			if (sf->ALL_index == field->fieldIndex) {
				/*queue eventOut*/
				if (sf->eventType == GF_SG_EVENT_OUT) {
					sf->activate_event_out = 1;
				}
			}
		}
	}
}

SMJS_FUNC_PROP_SET( gf_sg_script_eventout_set_prop)

u32 i;
char *eventName;
GF_ScriptPriv *script;
GF_Node *n;
GF_ScriptField *sf;
GF_FieldInfo info;
jsval idval;
JSString *str;
JS_IdToValue(c, id, &idval);
if (! SMJS_ID_IS_STRING(id)) return JS_FALSE;
str = SMJS_ID_TO_STRING(id);
if (!str) return JS_FALSE;
/*avoids gcc warning*/
if (!obj) obj=NULL;

script = JS_GetScriptStack(c);
if (!script) return JS_FALSE;
n = (GF_Node *) JS_GetScript(c);

eventName = SMJS_CHARS_FROM_STRING(c, str);
i=0;
while ((sf = gf_list_enum(script->fields, &i))) {
	if (!stricmp(sf->name, eventName)) {
		gf_node_get_field(n, sf->ALL_index, &info);
		gf_sg_script_to_node_field(c, *vp, &info, n, NULL);
		sf->activate_event_out = 1;
		SMJS_FREE(c, eventName);
		return JS_TRUE;
	}
}
SMJS_FREE(c, eventName);
return JS_FALSE;
}