blob: 4851cfe43eaa1e90a6b6d172506d8d79fe1ac245 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2005-2026
* All rights reserved
*
* This file is part of GPAC / Scene Graph sub-project
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <gpac/internal/scenegraph_dev.h>
#ifndef GPAC_DISABLE_SVG
#include <gpac/events.h>
#include <gpac/nodes_svg.h>
#ifdef GPAC_HAS_QJS
#ifdef GPAC_CONFIG_ANDROID
#ifndef XP_UNIX
#define XP_UNIX
#endif
#endif
#include "qjs_common.h"
#define JSVAL_CHECK_STRING(_v) (JS_IsString(_v) || JS_IsNull(_v))
/*SVG uDOM classes*/
GF_JSClass svgElement;
GF_JSClass svgDocument;
GF_JSClass svg_globalClass;
GF_JSClass connectionClass;
GF_JSClass rgbClass;
GF_JSClass rectClass;
GF_JSClass pointClass;
GF_JSClass pathClass;
GF_JSClass matrixClass;
typedef struct
{
JSValue proto;
JSValue ctor;
} SVG_JSClass;
typedef struct __tag_svg_script_ctx
{
Bool (*script_execute)(struct __tag_scene_graph *sg, char *utf8_script, GF_DOM_Event *event);
Bool (*handler_execute)(GF_Node *n, GF_DOM_Event *event, GF_Node *observer, char *utf8_script);
u32 nb_scripts;
/*global script context for the scene*/
JSContext *js_ctx;
/*global object*/
JSValue global;
/*global event object - used to update the associated DOMEvent (JS private stack) when dispatching events*/
JSValue event;
Bool in_script;
Bool force_gc;
Bool use_strict;
/*SVG uDOM classes*/
SVG_JSClass svgElement;
SVG_JSClass svgDocument;
SVG_JSClass svg_globalClass;
SVG_JSClass connectionClass;
SVG_JSClass rgbClass;
SVG_JSClass rectClass;
SVG_JSClass pointClass;
SVG_JSClass pathClass;
SVG_JSClass matrixClass;
} GF_SVGJS;
void svg_mark_gc(struct __tag_svg_script_ctx *svg_js)
{
if (svg_js)
svg_js->force_gc = 1;
}
void svg_free_node_binding(struct __tag_svg_script_ctx *svg_js, GF_Node *node)
{
struct _node_js_binding *js_binding = node->sgprivate->interact->js_binding;
if (!JS_IsUndefined(js_binding->obj)) {
JS_SetOpaque(js_binding->obj, NULL);
JS_FreeValue(svg_js->js_ctx, js_binding->obj);
js_binding->obj = JS_UNDEFINED;
//unregister after destroying JS obj since this is a recursive call and we trigger the GC, we must make sure
//all JS opaque is NULL before destroying the node
gf_node_unregister(node, NULL);
}
if (svg_js->in_script)
svg_js->force_gc = 1;
else
gf_js_call_gc(svg_js->js_ctx);
}
GF_Err svg_exec_script(struct __tag_svg_script_ctx *svg_js, GF_SceneGraph *sg, const char *com)
{
Bool ret = sg->svg_js->script_execute(sg, (char *)com, NULL);
return (ret == GF_TRUE ? GF_OK : GF_BAD_PARAM);
}
void svgjs_handler_execute(struct __tag_svg_script_ctx *svg_js, GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer, const char *iri)
{
if (svg_js->handler_execute(hdl, event, observer, (char *) iri)) {
return;
}
GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[DOM Events] Error executing JavaScript event handler\n"));
}
static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer, char *utf8_script);
void dom_node_set_textContent(GF_Node *n, char *text);
JSValue dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only);
#ifdef GPAC_ENABLE_HTML5_MEDIA
void html_media_init_js_api(GF_SceneGraph *scene);
#endif
#define _ScriptMessage(_sg, _msg) {\
GF_JSAPIParam par; \
par.info.e = GF_SCRIPT_INFO; \
par.info.msg = _msg; \
_sg->script_action(_sg->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\
}
static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
{
if (scene->script_action)
return scene->script_action(scene->script_action_cbck, type, node, param);
return GF_FALSE;
}
static JSValue svg_new_path_object(JSContext *c, SVG_PathData *d);
/*note we are using float to avoid conversions fixed to/from JS native */
typedef struct
{
u32 r, g, b;
} rgbCI;
typedef struct
{
Float x, y, w, h;
/*if set, this is the svg.viewport uDOM object, its values are updated upon query*/
GF_SceneGraph *sg;
} rectCI;
typedef struct
{
Float x, y;
/*if set, this is the svg.currentTranslate uDOM object, its values are updated upon query*/
GF_SceneGraph *sg;
} pointCI;
typedef struct
{
Float x, y;
} ptCI;
typedef struct
{
u32 nb_coms;
u8 *tags;
ptCI *pts;
} pathCI;
static JSValue svg_nav_to_location(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
if ((argc!=1) || !sg)
return GF_JS_EXCEPTION(c);
par.uri.url = (char *) JS_ToCString(c, argv[0]);
par.uri.nb_params = 0;
if (par.uri.url) {
ScriptAction(sg, GF_JSAPI_OP_LOAD_URL, sg->RootNode, &par);
JS_FreeCString(c, par.uri.url);
}
return JS_UNDEFINED;
}
GF_Node *gf_sm_load_svg_from_string(GF_SceneGraph *sg, char *svg_str);
static JSValue svg_parse_xml(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_SceneGraph *sg;
GF_Node *node;
const char *str;
if ((argc<2) || !JS_IsObject(argv[1])) {
return js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
}
sg = dom_get_doc(c, argv[1]);
if (!sg) return js_throw_err(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
str = JS_ToCString(c, argv[0]);
if (!str) return JS_TRUE;
node = gf_sm_load_svg_from_string(sg, (char *) str);
JS_FreeCString(c, str);
return dom_element_construct(c, node);
}
static void svg_define_udom_exception(JSContext *c, JSValue global)
{
JSValue obj = JS_NewObject(c);
JS_SetPropertyStr(c, global, "GlobalException", obj);
#define DEFCONST(_name, _code)\
JS_SetPropertyStr(c, obj, _name, JS_NewInt32(c, _code));
DEFCONST("NOT_CONNECTED_ERR", 1)
DEFCONST("ENCODING_ERR", 2)
DEFCONST("DENIED_ERR", 3)
DEFCONST("UNKNOWN_ERR", 4)
obj = JS_NewObject(c);
JS_SetPropertyStr(c, global, "SVGException", obj);
DEFCONST("SVG_WRONG_TYPE_ERR", 0)
DEFCONST("SVG_INVALID_VALUE_ERR", 1)
DEFCONST("SVG_MATRIX_NOT_INVERTABLE", 2)
obj = JS_NewObject(c);
JS_SetPropertyStr(c, global, "SVGSVGElement", obj);
DEFCONST("NAV_AUTO", 1)
DEFCONST("NAV_NEXT", 2)
DEFCONST("NAV_PREV", 3)
DEFCONST("NAV_UP", 4)
DEFCONST("NAV_UP_RIGHT", 5)
DEFCONST("NAV_RIGHT", 6)
DEFCONST("NAV_DOWN_RIGHT", 7)
DEFCONST("NAV_DOWN", 8)
DEFCONST("NAV_DOWN_LEFT", 9)
DEFCONST("NAV_LEFT", 10)
DEFCONST("NAV_UP_LEFT", 11)
}
static JSValue global_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_SceneGraph *sg = JS_GetOpaque(obj, svg_globalClass.class_id);
if (!sg) return GF_JS_EXCEPTION(c);
switch (magic) {
/*namespaceURI*/
case 0:
return JS_NULL;
/*parent*/
case 1:
if (sg->parent_scene && sg->parent_scene->svg_js)
return JS_DupValue(c, sg->parent_scene->svg_js->global);
return JS_NULL;
default:
return JS_UNDEFINED;
}
}
/*TODO - try to be more precise...*/
static JSValue dom_imp_has_feature(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue ret = JS_FALSE;
if (argc) {
u32 len;
char sep;
char *fname = (char *) JS_ToCString(c, argv[0]);
if (!fname) return JS_TRUE;
while (fname[0] && strchr(" \t\n\r", fname[0])) fname++;
len = (u32) strlen(fname);
while (len && strchr(" \t\n\r", fname[len-1])) len--;
sep = fname[len];
fname[len] = 0;
if (!stricmp(fname, "xml")) ret = JS_TRUE;
else if (!stricmp(fname, "core")) ret = JS_TRUE;
else if (!stricmp(fname, "traversal")) ret = JS_TRUE;
else if (!stricmp(fname, "uievents")) ret = JS_TRUE;
else if (!stricmp(fname, "mouseevents")) ret = JS_TRUE;
else if (!stricmp(fname, "mutationevents")) ret = JS_TRUE;
else if (!stricmp(fname, "events")) ret = JS_TRUE;
fname[len] = sep;
JS_FreeCString(c, fname);
}
return ret;
}
static GF_Node *get_corresponding_use(GF_Node *n)
{
u32 i, count;
if (!n || !n->sgprivate->scenegraph->use_stack) return NULL;
/*find current node in the use stack - if found, return the use element*/
count = gf_list_count(n->sgprivate->scenegraph->use_stack);
for (i=count; i>0; i-=2) {
GF_Node *t = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-2);
if (t==n) {
GF_Node *use = (GF_Node *)gf_list_get(n->sgprivate->scenegraph->use_stack, i-1);
GF_Node *par_use = get_corresponding_use(use);
return par_use ? par_use : use;
}
}
/*otherwise recursively get up the tree*/
return get_corresponding_use(gf_node_get_parent(n, 0));
}
static JSValue svg_doc_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:/*global*/
return JS_GetGlobalObject(c);
}
return JS_UNDEFINED;
}
static JSValue svg_element_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n) return JS_TRUE;
switch (magic) {
case 0: /*id*/
{
const char *node_name = gf_node_get_name((GF_Node*)n);
if (node_name) {
return JS_NewString(c, node_name);
}
return JS_NULL;
}
case 5:/*currentScale*/
if (n->sgprivate->tag!=TAG_SVG_svg) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_SCALE, (GF_Node *)n, &par)) {
return JS_NewFloat64(c, FIX2FLT(par.val) );
}
return GF_JS_EXCEPTION(c);
case 6:/*currentRotate*/
if (n->sgprivate->tag!=TAG_SVG_svg) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_ROTATION, (GF_Node *)n, &par)) {
return JS_NewFloat64(c, FIX2FLT(par.val) );
}
return GF_JS_EXCEPTION(c);
case 7:/*currentTranslate*/
if (n->sgprivate->tag!=TAG_SVG_svg) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSLATE, (GF_Node *)n, &par)) {
JSValue r = JS_NewObjectClass(c, pointClass.class_id);
pointCI *rc = (pointCI *)gf_malloc(sizeof(pointCI));
rc->x = FIX2FLT(par.pt.x);
rc->y = FIX2FLT(par.pt.y);
rc->sg = n->sgprivate->scenegraph;
JS_SetOpaque(r, rc);
return r;
}
return GF_JS_EXCEPTION(c);
case 8:/*viewport*/
if (n->sgprivate->tag!=TAG_SVG_svg) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_VIEWPORT, (GF_Node *)n, &par)) {
JSValue r = JS_NewObjectClass(c, rectClass.class_id);
rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
rc->x = FIX2FLT(par.rc.x);
rc->y = FIX2FLT(par.rc.y);
rc->w = FIX2FLT(par.rc.width);
rc->h = FIX2FLT(par.rc.height);
rc->sg = n->sgprivate->scenegraph;
JS_SetOpaque(r, rc);
return r;
}
return GF_JS_EXCEPTION(c);
case 9:/*currentTime*/
return JS_NewFloat64(c, gf_node_get_scene_time((GF_Node *)n) );
case 10:/*isPaused*/
return JS_FALSE;
case 11:/*ownerSVGElement*/
while (1) {
GF_Node *n_par = gf_node_get_parent(n, 0);
if (!n_par) return JS_TRUE;
if (n_par->sgprivate->tag==TAG_SVG_svg) {
return dom_element_construct(c, n_par);
}
n = n_par;
}
return JS_NULL;
case 12:/*correspondingElement*/
/*if we can find a corresponding element for this node, then this is an SVGElementInstance*/
if (get_corresponding_use(n)) {
return dom_element_construct(c, n);
} else {
return dom_element_construct(c, NULL);
}
break;
case 13:/*correspondingUseElement*/
return dom_element_construct(c, get_corresponding_use(n));
default:
break;
}
return JS_UNDEFINED;
}
static JSValue svg_element_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
GF_JSAPIParam par;
Double d;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:/*id*/
if (JSVAL_CHECK_STRING(value)) {
const char *id = JS_ToCString(c, value);
if (id) {
GF_FieldInfo info;
u32 nid = gf_node_get_id(n);
if (!nid) nid = gf_sg_get_next_available_node_id(n->sgprivate->scenegraph);
gf_node_set_id(n, nid, id);
if (gf_node_get_attribute_by_tag(n, TAG_XML_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
*(DOM_String *)info.far_ptr = gf_strdup(id);
}
if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_id, GF_TRUE, GF_FALSE, &info)==GF_OK) {
if (*(DOM_String *)info.far_ptr) gf_free(*(DOM_String *)info.far_ptr);
*(DOM_String *)info.far_ptr = gf_strdup(id);
}
JS_FreeCString(c, id);
}
}
return JS_TRUE;
/*currentScale*/
case 5:
if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
return GF_JS_EXCEPTION(c);
par.val = FLT2FIX(d);
if (!par.val) {
return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
}
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCALE, (GF_Node *)n, &par)) {
return JS_TRUE;
}
return JS_FALSE;
/*currentRotate*/
case 6:
if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
return GF_JS_EXCEPTION(c);
par.val = FLT2FIX(d);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_ROTATION, (GF_Node *)n, &par)) {
return JS_TRUE;
}
return JS_FALSE;
/*currentTime*/
case 9:
if ((n->sgprivate->tag!=TAG_SVG_svg) || JS_ToFloat64(c, &d, value))
return GF_JS_EXCEPTION(c);
par.time = d;
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_TIME, (GF_Node *)n, &par)) {
return JS_TRUE;
}
return JS_FALSE;
default:
break;
}
return JS_UNDEFINED;
}
static GF_Node *svg_udom_smil_check_instance(JSContext *c, JSValue obj)
{
GF_Node *n = dom_get_element(c, obj);
if (!n) return NULL;
switch (n->sgprivate->tag) {
case TAG_SVG_animate:
case TAG_SVG_animateColor:
case TAG_SVG_animateMotion:
case TAG_SVG_animateTransform:
case TAG_SVG_animation:
case TAG_SVG_audio:
case TAG_SVG_video:
case TAG_SVG_set:
case TAG_LSR_updates:
/*not sure about this one...*/
case TAG_SVG_discard:
return n;
}
return NULL;
}
/*TODO*/
static JSValue svg_udom_smil_time_insert(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_end)
{
GF_FieldInfo info;
u32 i, count;
Double offset;
GF_List *times;
SMIL_Time *newtime;
GF_Node *n = svg_udom_smil_check_instance(c, obj);
if (!n) return JS_UNDEFINED;
if (is_end) {
info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->end;
} else {
info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->begin;
}
if (!info.far_ptr) {
return GF_JS_EXCEPTION(c);
}
times = *((GF_List **)info.far_ptr);
GF_SAFEALLOC(newtime, SMIL_Time);
if (!newtime) {
return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
}
newtime->type = GF_SMIL_TIME_EVENT_RESOLVED;
offset = 0;
if (argc)
JS_ToFloat64(c, &offset, argv[0]);
newtime->clock = gf_node_get_scene_time(n) + offset;
/*insert in sorted order*/
count = gf_list_count(times);
for (i=0; i<count; i++) {
SMIL_Time*t = (SMIL_Time*)gf_list_get(times, i);
if ( GF_SMIL_TIME_IS_CLOCK(t->type) ) {
if (t->clock > newtime->clock) break;
} else {
break;
}
}
gf_list_insert(times, newtime, i);
info.fieldType = SMIL_Times_datatype;
gf_node_changed(n, &info);
return JS_TRUE;
}
static JSValue svg_udom_smil_begin(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return svg_udom_smil_time_insert(c, obj, argc, argv, GF_FALSE);
}
static JSValue svg_udom_smil_end(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return svg_udom_smil_time_insert(c, obj, argc, argv, GF_TRUE);
}
/*TODO*/
static JSValue svg_udom_smil_pause(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 tag;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
tag = gf_node_get_tag(n);
if (gf_svg_is_animation_tag(tag)) {
/* pausing an animation element (set, animate ...) should pause the main time line ? */
gf_smil_timing_pause(n);
} else if (gf_svg_is_timing_tag(tag)) {
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
} else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL);
} else {
return JS_FALSE;
}
return JS_TRUE;
}
/*TODO*/
static JSValue svg_udom_smil_resume(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 tag;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
tag = gf_node_get_tag(n);
if (gf_svg_is_animation_tag(tag)) {
/* resuming an animation element (set, animate ...) should resume the main time line ? */
gf_smil_timing_resume(n);
} else if (gf_svg_is_timing_tag(tag)) {
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
} else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL);
} else {
return JS_FALSE;
}
return JS_TRUE;
}
static JSValue svg_udom_smil_restart(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 tag;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
tag = gf_node_get_tag(n);
if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESTART_SVG, n, NULL);
} else {
return JS_FALSE;
}
return JS_TRUE;
}
static JSValue svg_udom_smil_set_speed(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 tag;
Double speed = 1.0;
GF_Node *n = dom_get_element(c, obj);
if (!n || !argc || JS_ToFloat64(c, &speed, argv[0]) ) {
return GF_JS_EXCEPTION(c);
}
tag = gf_node_get_tag(n);
if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) {
GF_JSAPIParam par;
memset(&par, 0, sizeof(GF_JSAPIParam));
par.val = FLT2FIX(speed);
ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCENE_SPEED, n, &par);
} else {
return JS_TRUE;
}
return JS_UNDEFINED;
}
static JSValue svg_udom_get_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
char *attValue;
const char *name;
GF_Err e;
JSValue ret;
GF_FieldInfo info;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
//ns = NULL;
name = NULL;
if (! JSVAL_CHECK_STRING(argv[0]) ) return JS_TRUE;
if (argc==2) {
//ns = JS_ToCString(c, argv[0]);
name = JS_ToCString(c, argv[1]);
} else if (argc==1) {
name = JS_ToCString(c, argv[0]);
} else return GF_JS_EXCEPTION(c);
if (!name) {
//JS_FreeCString(c, ns);
return GF_JS_EXCEPTION(c);
}
if (!strcmp(name, "#text")) {
char *res = gf_dom_flatten_textContent(n);
ret = JS_NewString(c, res);
gf_free(res);
//JS_FreeCString(c, ns);
JS_FreeCString(c, name);
return ret;
}
e = gf_node_get_field_by_name(n, (char *) name, &info);
//JS_FreeCString(c, ns);
JS_FreeCString(c, name);
if (e!=GF_OK) return GF_JS_EXCEPTION(c);
switch (info.fieldType) {
/* inheritable floats */
case SVG_FontSize_datatype:
case SVG_Color_datatype:
case SVG_Paint_datatype:
/* inheritable float and unit */
case SVG_Length_datatype:
case SVG_Coordinate_datatype:
/*Number*/
case SVG_Number_datatype:
/*all string traits*/
case SVG_Boolean_datatype:
case SVG_FillRule_datatype:
case SVG_StrokeLineJoin_datatype:
case SVG_StrokeLineCap_datatype:
case SVG_FontStyle_datatype:
case SVG_FontWeight_datatype:
case SVG_FontVariant_datatype:
case SVG_TextAnchor_datatype:
case SVG_Display_datatype:
case SVG_Visibility_datatype:
case SVG_GradientUnit_datatype:
case SVG_PreserveAspectRatio_datatype:
case XML_Space_datatype:
case XMLEV_Propagate_datatype:
case XMLEV_DefaultAction_datatype:
case XMLEV_Phase_datatype:
case SMIL_SyncBehavior_datatype:
case SMIL_SyncTolerance_datatype:
case SMIL_AttributeType_datatype:
case SMIL_CalcMode_datatype:
case SMIL_Additive_datatype:
case SMIL_Accumulate_datatype:
case SMIL_Restart_datatype:
case SMIL_Fill_datatype:
case SVG_Overflow_datatype:
case SVG_ZoomAndPan_datatype:
case SVG_DisplayAlign_datatype:
case SVG_TextAlign_datatype:
case SVG_PointerEvents_datatype:
case SVG_RenderingHint_datatype:
case SVG_VectorEffect_datatype:
case SVG_PlaybackOrder_datatype:
case SVG_TimelineBegin_datatype:
/*end of string traits*/
/*DOM string traits*/
case SVG_FontFamily_datatype:
case XMLRI_datatype:
case DOM_String_datatype:
case SVG_ContentType_datatype:
case SVG_LanguageID_datatype:
case SVG_Focus_datatype:
case SVG_ClipPath_datatype:
case SVG_ID_datatype:
case SVG_GradientOffset_datatype:
/*end of DOM string traits*/
attValue = gf_svg_dump_attribute(n, &info);
ret = JS_NewString(c, attValue);
if (attValue) gf_free(attValue);
return ret;
#if 0
/*SVGT 1.2 default traits*/
case SMIL_KeyTimes_datatype:
case SMIL_KeyPoints_datatype:
case SMIL_KeySplines_datatype:
case SVG_Coordinates_datatype:
case SVG_StrokeDashArray_datatype:
case SVG_Points_datatype:
case SVG_Motion_datatype:
/*end SVGT 1.2 default traits*/
/*unimplemented/unknown/FIXME traits*/
case SMIL_SyncTolerance_datatype:
case SVG_TransformType_datatype:
case SVG_TransformList_datatype:
case SMIL_AnimateValue_datatype:
case SMIL_AnimateValues_datatype:
case SMIL_AttributeName_datatype:
case SMIL_Times_datatype:
case SMIL_Duration_datatype:
case SMIL_RepeatCount_datatype:
default:
/*end unimplemented/unknown/FIXME traits*/
return GF_JS_EXCEPTION(c);
#endif
}
return JS_NULL;
}
static JSValue svg_udom_get_float_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
if (!szName) return GF_JS_EXCEPTION(c);
e = gf_node_get_attribute_by_name(n, (char *) szName, 0, GF_TRUE, GF_TRUE, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
switch (info.fieldType) {
/* inheritable floats */
case SVG_Number_datatype:
case SVG_FontSize_datatype:
case SVG_Length_datatype:
case SVG_Coordinate_datatype:
{
SVG_Number *l = (SVG_Number *)info.far_ptr;
if (l->type==SVG_NUMBER_AUTO || l->type==SVG_NUMBER_INHERIT) return JS_TRUE;
return JS_NewFloat64(c, FIX2FLT(l->value));
}
case SVG_Clock_datatype:
return JS_NewFloat64(c, *(SVG_Clock*)info.far_ptr );
default:
return JS_NULL;
}
return JS_NULL;
}
static JSValue svg_udom_get_matrix_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_Transform_datatype) {
GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
if (!mx) return GF_JS_EXCEPTION(c);
JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
gf_mx2d_init(*mx);
gf_mx2d_copy(*mx, ((SVG_Transform*)info.far_ptr)->mat);
JS_SetOpaque(mO, mx);
return mO;
}
return JS_NULL;
}
static JSValue svg_udom_get_rect_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_ViewBox_datatype) {
JSValue newObj;
rectCI *rc;
SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
GF_SAFEALLOC(rc, rectCI);
if (!rc) {
return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
}
newObj = JS_NewObjectClass(c, rectClass.class_id);
rc->x = FIX2FLT(v->x);
rc->y = FIX2FLT(v->y);
rc->w = FIX2FLT(v->width);
rc->h = FIX2FLT(v->height);
JS_SetOpaque(newObj, rc);
return newObj;
}
return JS_NULL;
}
static JSValue svg_udom_get_path_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_PathData_datatype) {
return svg_new_path_object(c, (SVG_PathData *)info.far_ptr);
}
return JS_NULL;
}
static JSValue svg_udom_get_rgb_color_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
rgbCI *rgb;
GF_Err e;
JSValue newObj;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
switch (info.fieldType) {
case SVG_Color_datatype:
{
SVG_Color *col = (SVG_Color *)info.far_ptr;
if (col->type == SVG_COLOR_CURRENTCOLOR) return JS_UNDEFINED;
if (col->type == SVG_COLOR_INHERIT) return JS_UNDEFINED;
GF_SAFEALLOC(rgb, rgbCI);
if (!rgb) {
return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
}
newObj = JS_NewObjectClass(c, rgbClass.class_id);
rgb->r = (u8) (255*FIX2FLT(col->red)) ;
rgb->g = (u8) (255*FIX2FLT(col->green)) ;
rgb->b = (u8) (255*FIX2FLT(col->blue)) ;
JS_SetOpaque(newObj, rgb);
return newObj;
}
break;
case SVG_Paint_datatype:
{
SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
if ((1) || paint->type==SVG_PAINT_COLOR) {
GF_SAFEALLOC(rgb, rgbCI);
if (!rgb) {
return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
}
newObj = JS_NewObjectClass(c, rgbClass.class_id);
rgb->r = (u8) (255*FIX2FLT(paint->color.red) );
rgb->g = (u8) (255*FIX2FLT(paint->color.green) );
rgb->b = (u8) (255*FIX2FLT(paint->color.blue) );
JS_SetOpaque(newObj, rgb);
return newObj;
}
return JS_TRUE;
}
}
return JS_NULL;
}
static JSValue svg_udom_set_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *ns, *name, *val;
GF_Err e;
GF_FieldInfo info;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
val = ns = name = NULL;
if (!JSVAL_CHECK_STRING(argv[0])) return GF_JS_EXCEPTION(c);
if (argc==3) {
if (!JSVAL_CHECK_STRING(argv[1])) return GF_JS_EXCEPTION(c);
if (!JSVAL_CHECK_STRING(argv[2])) return GF_JS_EXCEPTION(c);
ns = JS_ToCString(c, argv[0]);
name = JS_ToCString(c, argv[1]);
val = JS_ToCString(c, argv[2]);
} else if (argc==2) {
name = JS_ToCString(c, argv[0]);
val = JS_ToCString(c, argv[1]);
} else {
return GF_JS_EXCEPTION(c);
}
if (!name) {
JS_FreeCString(c, ns);
JS_FreeCString(c, val);
return GF_JS_EXCEPTION(c);
}
if (!strcmp(name, "#text")) {
if (val) dom_node_set_textContent(n, (char *) val);
JS_FreeCString(c, ns);
JS_FreeCString(c, name);
JS_FreeCString(c, val);
return JS_UNDEFINED;
}
e = gf_node_get_field_by_name(n, (char *) name, &info);
JS_FreeCString(c, ns);
JS_FreeCString(c, name);
if (!val || (e!=GF_OK)) {
JS_FreeCString(c, val);
return GF_JS_EXCEPTION(c);
}
e = gf_svg_parse_attribute(n, &info, (char *) val, 0);
JS_FreeCString(c, val);
if (e) return js_throw_err(c, GF_DOM_EXC_INVALID_ACCESS_ERR);
dom_node_changed(n, GF_FALSE, &info);
return JS_UNDEFINED;
}
static JSValue svg_udom_set_float_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_FieldInfo info;
Double d;
GF_Err e;
const char *szName;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
if (!JS_IsNumber(argv[1]) || JS_ToFloat64(c, &d, argv[1])) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
switch (info.fieldType) {
/* inheritable floats */
case SVG_FontSize_datatype:
case SVG_Length_datatype:
case SVG_Coordinate_datatype:
case SVG_Number_datatype:
{
SVG_Number *l = (SVG_Number *)info.far_ptr;
l->type=SVG_NUMBER_VALUE;
l->value = FLT2FIX(d);
break;
}
case SVG_Numbers_datatype:
case SVG_Coordinates_datatype:
{
SVG_Number *val;
SVG_Coordinates *l = (SVG_Coordinates *)info.far_ptr;
while (gf_list_count(*l)) {
val = (SVG_Number *)gf_list_get(*l, 0);
gf_list_rem(*l, 0);
if (val) gf_free(val);
}
GF_SAFEALLOC(val, SVG_Coordinate);
if (!val) {
return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
}
val->type=SVG_NUMBER_VALUE;
val->value = FLT2FIX(d);
gf_list_add(*l, val);
break;
}
default:
return JS_FALSE;
}
dom_node_changed(n, GF_FALSE, &info);
return JS_TRUE;
}
static JSValue svg_udom_set_matrix_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
GF_Matrix2D *mx;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return JS_TRUE;
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
mx = JS_GetOpaque(argv[1], matrixClass.class_id);
if (!mx) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_Transform_datatype) {
gf_mx2d_copy(((SVG_Transform*)info.far_ptr)->mat, *mx);
dom_node_changed(n, GF_FALSE, NULL);
return JS_TRUE;
}
return JS_FALSE;
}
static JSValue svg_udom_set_rect_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *szName;
GF_FieldInfo info;
rectCI *rc;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return JS_TRUE;
if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
rc = JS_GetOpaque(argv[1], rectClass.class_id);
if (!rc) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_ViewBox_datatype) {
SVG_ViewBox *v = (SVG_ViewBox *)info.far_ptr;
v->x = FLT2FIX(rc->x);
v->y = FLT2FIX(rc->y);
v->width = FLT2FIX(rc->w);
v->height = FLT2FIX(rc->h);
dom_node_changed(n, GF_FALSE, NULL);
return JS_TRUE;
}
return JS_FALSE;
}
static JSValue svg_udom_set_path_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *path;
GF_FieldInfo info;
const char *szName;
GF_Err e;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
if (JS_IsNull(argv[1]) || !JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
path = JS_GetOpaque( argv[1], pathClass.class_id);
if (!path) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
if (info.fieldType==SVG_PathData_datatype) {
#if USE_GF_PATH
#else
u32 i;
u32 nb_pts;
SVG_PathData *d = (SVG_PathData *)info.far_ptr;
while (gf_list_count(d->commands)) {
u8 *t = gf_list_get(d->commands, 0);
gf_list_rem(d->commands, 0);
gf_free(t);
}
while (gf_list_count(d->points)) {
SVG_Point *t = gf_list_get(d->points, 0);
gf_list_rem(d->points, 0);
gf_free(t);
}
nb_pts = 0;
for (i=0; i<path->nb_coms; i++) {
u8 *t = gf_malloc(sizeof(u8));
*t = path->tags[i];
gf_list_add(d->commands, t);
switch (*t) {
case 0:
case 1:
nb_pts++;
break;
case 2:
nb_pts+=3;
break;
case 4:
nb_pts+=2;
break;
}
}
for (i=0; i<nb_pts; i++) {
SVG_Point *t = gf_malloc(sizeof(SVG_Point));
t->x = FLT2FIX(path->pts[i].x);
t->y = FLT2FIX(path->pts[i].y);
gf_list_add(d->points, t);
}
dom_node_changed(n, 0, NULL);
return JS_TRUE;
#endif
}
return JS_FALSE;
}
static JSValue svg_udom_set_rgb_color_trait(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_FieldInfo info;
rgbCI *rgb;
GF_Err e;
const char *szName;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (argc!=2) return GF_JS_EXCEPTION(c);
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(c);
if (!JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
rgb = JS_GetOpaque(argv[1], rgbClass.class_id);
if (!rgb) return GF_JS_EXCEPTION(c);
szName = JS_ToCString(c, argv[0]);
e = gf_node_get_field_by_name(n, (char *) szName, &info);
JS_FreeCString(c, szName);
if (e != GF_OK) return GF_JS_EXCEPTION(c);
switch (info.fieldType) {
case SVG_Color_datatype:
{
SVG_Color *col = (SVG_Color *)info.far_ptr;
col->type = SVG_COLOR_RGBCOLOR;
col->red = FLT2FIX(rgb->r / 255.0f);
col->green = FLT2FIX(rgb->g / 255.0f);
col->blue = FLT2FIX(rgb->b / 255.0f);
dom_node_changed(n, GF_FALSE, &info);
return JS_TRUE;
}
case SVG_Paint_datatype:
{
SVG_Paint *paint = (SVG_Paint *)info.far_ptr;
paint->type = SVG_PAINT_COLOR;
paint->color.type = SVG_COLOR_RGBCOLOR;
paint->color.red = FLT2FIX(rgb->r / 255.0f);
paint->color.green = FLT2FIX(rgb->g / 255.0f);
paint->color.blue = FLT2FIX(rgb->b / 255.0f);
dom_node_changed(n, GF_FALSE, &info);
return JS_TRUE;
}
}
return JS_FALSE;
}
static JSValue svg_get_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool get_screen)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
par.bbox.is_set = GF_FALSE;
if (ScriptAction(n->sgprivate->scenegraph, get_screen ? GF_JSAPI_OP_GET_SCREEN_BBOX : GF_JSAPI_OP_GET_LOCAL_BBOX, (GF_Node *)n, &par) ) {
if (par.bbox.is_set) {
rectCI *rc = (rectCI *)gf_malloc(sizeof(rectCI));
if (!rc) return GF_JS_EXCEPTION(c);
JSValue rO = JS_NewObjectClass(c, rectClass.class_id);
rc->sg = NULL;
rc->x = FIX2FLT(par.bbox.min_edge.x);
/*BBox is in 3D coord system style*/
rc->y = FIX2FLT(par.bbox.min_edge.y);
rc->w = FIX2FLT(par.bbox.max_edge.x - par.bbox.min_edge.x);
rc->h = FIX2FLT(par.bbox.max_edge.y - par.bbox.min_edge.y);
JS_SetOpaque(rO, rc);
return rO;
} else {
return JS_NULL;
}
return JS_TRUE;
}
return JS_FALSE;
}
static JSValue svg_udom_get_local_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return svg_get_bbox(c, obj, argc, argv, GF_FALSE);
}
static JSValue svg_udom_get_screen_bbox(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return svg_get_bbox(c, obj, argc, argv, GF_TRUE);
}
static JSValue svg_udom_get_screen_ctm(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_TRANSFORM, (GF_Node *)n, &par)) {
GF_Matrix2D *mx = (GF_Matrix2D *)gf_malloc(sizeof(GF_Matrix2D));
if (!mx) return GF_JS_EXCEPTION(c);
JSValue mO = JS_NewObjectClass(c, matrixClass.class_id);
gf_mx2d_from_mx(mx, &par.mx);
JS_SetOpaque(mO, mx);
return mO;
}
return GF_JS_EXCEPTION(c);
}
static JSValue svg_udom_create_matrix_components(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *mx;
JSValue mat;
Double v;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (argc!=6) return GF_JS_EXCEPTION(c);
GF_SAFEALLOC(mx, GF_Matrix2D)
if (!mx) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
JS_ToFloat64(c, &v, argv[0]);
mx->m[0] = FLT2FIX(v);
JS_ToFloat64(c, &v, argv[1]);
mx->m[3] = FLT2FIX(v);
JS_ToFloat64(c, &v, argv[2]);
mx->m[1] = FLT2FIX(v);
JS_ToFloat64(c, &v, argv[3]);
mx->m[4] = FLT2FIX(v);
JS_ToFloat64(c, &v, argv[4]);
mx->m[2] = FLT2FIX(v);
JS_ToFloat64(c, &v, argv[5]);
mx->m[5] = FLT2FIX(v);
mat = JS_NewObjectClass(c, matrixClass.class_id);
JS_SetOpaque(mat, mx);
return mat;
}
static JSValue svg_udom_create_rect(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
rectCI *rc;
JSValue r;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
GF_SAFEALLOC(rc, rectCI);
if (!rc) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
r = JS_NewObjectClass(c, rectClass.class_id);
JS_SetOpaque(r, rc);
return r;
}
static JSValue svg_udom_create_point(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pointCI *pt;
JSValue r;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
GF_SAFEALLOC(pt, pointCI);
if (!pt) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
r = JS_NewObjectClass(c, pointClass.class_id);
JS_SetOpaque(r, pt);
return r;
}
static JSValue svg_udom_create_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *path;
JSValue p;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
GF_SAFEALLOC(path, pathCI);
if (!path) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
p = JS_NewObjectClass(c, pathClass.class_id);
JS_SetOpaque(p, path);
return p;
}
static JSValue svg_udom_create_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
rgbCI *col;
JSValue p;
GF_Node *n = dom_get_element(c, obj);
if (!n|| (argc!=3)) return GF_JS_EXCEPTION(c);
GF_SAFEALLOC(col, rgbCI);
if (!col) return js_throw_err(c, GF_DOM_EXC_DATA_CLONE_ERR);
JS_ToInt32(c, &col->r, argv[0]);
JS_ToInt32(c, &col->g, argv[1]);
JS_ToInt32(c, &col->b, argv[2]);
p = JS_NewObjectClass(c, rgbClass.class_id);
JS_SetOpaque(p, col);
return p;
}
static JSValue svg_path_get_total_length(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double length = 0;
GF_FieldInfo info;
GF_Node *n = (GF_Node *)dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if (n->sgprivate->tag != TAG_SVG_path) return GF_JS_EXCEPTION(c);
gf_node_get_field_by_name(n, "d", &info);
if (info.fieldType == SVG_PathData_datatype) {
#if USE_GF_PATH
GF_Path *p = (GF_Path *)info.far_ptr;
GF_PathIterator *path_it = gf_path_iterator_new(p);
if (path_it) {
Fixed len = gf_path_iterator_get_length(path_it);
length = FIX2FLT(len);
gf_path_iterator_del(path_it);
}
#endif
}
return JS_NewFloat64(c, length);
}
static JSValue svg_udom_move_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
JS_ToInt32(c, &par.opt, argv[1]);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
return JS_TRUE;
return JS_FALSE;
}
static JSValue svg_udom_set_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
if ((argc!=1) || !JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
par.node = dom_get_element(c, argv[0]);
/*NOT IN THE GRAPH*/
if (!par.node || !par.node->sgprivate->num_instances) return GF_JS_EXCEPTION(c);
if (ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_FOCUS, (GF_Node *)n, &par))
return JS_TRUE;
return JS_TRUE;
}
static JSValue svg_udom_get_focus(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSAPIParam par;
GF_Node *n = dom_get_element(c, obj);
if (!n || argc) return GF_JS_EXCEPTION(c);
if (!ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_GET_FOCUS, (GF_Node *)n, &par))
return GF_JS_EXCEPTION(c);
if (par.node) {
return dom_element_construct(c, par.node);
}
return JS_NULL;
}
static JSValue svg_udom_get_time(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Node *n = dom_get_element(c, obj);
if (!n) return GF_JS_EXCEPTION(c);
return JS_NewFloat64(c, gf_node_get_scene_time(n) );
}
static JSValue svg_connection_create(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return js_throw_err(c, GF_DOM_EXC_NOT_SUPPORTED_ERR);
}
static void baseCI_finalize(JSRuntime *rt, JSValue obj)
{
JSClassID _classID;
void *data = JS_GetAnyOpaque(obj, &_classID);
if (data) gf_free(data);
}
static JSValue rgb_getProperty(JSContext *c, JSValueConst obj, int magic)
{
rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
if (!col) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0: return JS_NewInt32(c, col->r);
case 1: return JS_NewInt32(c, col->g);
case 2: return JS_NewInt32(c, col->b);
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue rgb_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
rgbCI *col = (rgbCI *) JS_GetOpaque(obj, rgbClass.class_id);
if (!col) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0: return JS_ToInt32(c, &col->r, value) ? GF_JS_EXCEPTION(c) : JS_TRUE;
case 1: return JS_ToInt32(c, &col->g, value) ? GF_JS_EXCEPTION(c) : JS_TRUE;
case 2: return JS_ToInt32(c, &col->b, value) ? GF_JS_EXCEPTION(c) : JS_TRUE;
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue rect_getProperty(JSContext *c, JSValueConst obj, int magic)
{
rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
if (!rc) return GF_JS_EXCEPTION(c);
if (rc->sg) {
GF_JSAPIParam par;
if (!ScriptAction(rc->sg, GF_JSAPI_OP_GET_VIEWPORT, rc->sg->RootNode, &par)) {
return GF_JS_EXCEPTION(c);
}
rc->x = FIX2FLT(par.rc.x);
rc->y = FIX2FLT(par.rc.y);
rc->w = FIX2FLT(par.rc.width);
rc->h = FIX2FLT(par.rc.height);
}
switch (magic) {
case 0: return JS_NewFloat64(c, rc->x);
case 1: return JS_NewFloat64(c, rc->y);
case 2: return JS_NewFloat64(c, rc->w);
case 3: return JS_NewFloat64(c, rc->h);
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue rect_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
rectCI *rc = (rectCI *) JS_GetOpaque(obj, rectClass.class_id);
if (!rc) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
rc->x = (Float) d;
return JS_TRUE;
case 1:
rc->y = (Float) d;
return JS_TRUE;
case 2:
rc->w = (Float) d;
return JS_TRUE;
case 3:
rc->h = (Float) d;
return JS_TRUE;
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue point_getProperty(JSContext *c, JSValueConst obj, int magic)
{
pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
if (!pt) return GF_JS_EXCEPTION(c);
if (pt->sg) {
GF_JSAPIParam par;
if (!ScriptAction(pt->sg, GF_JSAPI_OP_GET_TRANSLATE, pt->sg->RootNode, &par)) {
return GF_JS_EXCEPTION(c);
}
pt->x = FIX2FLT(par.pt.x);
pt->y = FIX2FLT(par.pt.y);
}
switch (magic) {
case 0: return JS_NewFloat64(c, pt->x);
case 1: return JS_NewFloat64(c, pt->y);
default: return GF_JS_EXCEPTION(c);
}
}
static JSValue point_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
pointCI *pt = (pointCI *) JS_GetOpaque(obj, pointClass.class_id);
if (!pt) return GF_JS_EXCEPTION(c);
Double d;
if (JS_ToFloat64(c, &d, value)) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
pt->x = (Float) d;
break;
case 1:
pt->y = (Float) d;
break;
default:
return GF_JS_EXCEPTION(c);
}
if (pt->sg) {
GF_JSAPIParam par;
par.pt.x = FLT2FIX(pt->x);
par.pt.y = FLT2FIX(pt->y);
ScriptAction(pt->sg, GF_JSAPI_OP_SET_TRANSLATE, pt->sg->RootNode, &par);
}
return JS_UNDEFINED;
}
static JSValue svg_new_path_object(JSContext *c, SVG_PathData *d)
{
#if USE_GF_PATH
return JS_NULL;
#else
JSValue obj;
pathCI *p;
GF_SAFEALLOC(p, pathCI);
if (!p) return GF_JS_EXCEPTION(c);
if (d) {
u32 i, count;
p->nb_coms = gf_list_count(d->commands);
p->tags = gf_malloc(sizeof(u8) * p->nb_coms);
for (i=0; i<p->nb_coms; i++) p->tags[i] = * (u8 *) gf_list_get(d->commands, i);
count = gf_list_count(d->points);
p->pts = gf_malloc(sizeof(pointCI) * count);
for (i=0; i<count; i++) {
GF_Point2D *pt = gf_list_get(d->commands, i);
p->pts[i].x = FIX2FLT(pt->x);
p->pts[i].y = FIX2FLT(pt->y);
}
}
obj = JS_NewObjectClass(c, pathClass.class_id);
JS_SetOpaque(obj, p);
return obj;
#endif
}
static void pathCI_finalize(JSRuntime *rt, JSValue obj)
{
pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (p) {
if (p->pts) gf_free(p->pts);
if (p->tags) gf_free(p->tags);
gf_free(p);
}
}
static JSValue path_getProperty(JSContext *c, JSValueConst obj, int magic)
{
pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0: return JS_NewInt32(c, p->nb_coms);
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue svg_path_get_segment(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 idx;
pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
if ((argc!=1) || JS_ToInt32(c, &idx, argv[0])) return GF_JS_EXCEPTION(c);
if (idx>=p->nb_coms) return JS_TRUE;
switch (p->tags[idx]) {
case 0: return JS_NewInt32(c, 77);/* Move To */
case 1: return JS_NewInt32(c, 76);/* Line To */
case 2:/* Curve To */
case 3:/* next Curve To */
return JS_NewInt32(c, 67);
case 4:/* Quad To */
case 5:/* next Quad To */
return JS_NewInt32(c, 81);
case 6:
return JS_NewInt32(c, 90);/* Close */
}
return GF_JS_EXCEPTION(c);
}
static JSValue svg_path_get_segment_param(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 i, idx, param_idx, pt_idx;
ptCI *pt;
pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
if ((argc!=2) || !JS_IsInteger(argv[0]) || !JS_IsInteger(argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &idx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &param_idx, argv[1])) return GF_JS_EXCEPTION(c);
if (idx>=p->nb_coms) return JS_TRUE;
pt_idx = 0;
for (i=0; i<idx; i++) {
switch (p->tags[i]) {
case 0:
pt_idx++;
break;
case 1:
pt_idx++;
break;
case 2:
pt_idx+=3;
break;
case 3:
pt_idx+=2;
break;
case 4:
pt_idx+=2;
break;
case 5:
pt_idx+=1;
break;
}
}
switch (p->tags[idx]) {
case 0:
case 1:
if (param_idx>1) return JS_TRUE;
pt = &p->pts[pt_idx];
return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
case 2:/* Curve To */
if (param_idx>5) return JS_TRUE;
pt = &p->pts[pt_idx + (param_idx/2) ];
return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
case 3:/* Next Curve To */
if (param_idx>5) return JS_TRUE;
if (param_idx<2) {
pt = &p->pts[pt_idx - 1];
return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
}
param_idx-=2;
pt = &p->pts[pt_idx + (param_idx/2)];
return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
case 4:/* Quad To */
if (param_idx>3) return JS_TRUE;
pt = &p->pts[pt_idx + (param_idx/2) ];
return JS_NewFloat64(c, (param_idx%2) ? pt->y : pt->x);
case 5:/* Next Quad To */
if (param_idx>3) return JS_TRUE;
if (param_idx<2) {
pt = &p->pts[pt_idx - 1];
return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
} else {
param_idx-=2;
pt = &p->pts[pt_idx];
return JS_NewFloat64(c, param_idx ? pt->y : pt->x);
}
return JS_TRUE;
/*spec is quite obscur here*/
case 6:
return JS_NewFloat64(c, 0);
}
return GF_JS_EXCEPTION(c);
}
static u32 svg_path_realloc_pts(pathCI *p, u32 nb_pts)
{
u32 i, orig_pts;
orig_pts = 0;
for (i=0; i<p->nb_coms; i++) {
switch (p->tags[i]) {
case 0:
orig_pts++;
break;
case 1:
orig_pts++;
break;
case 2:
orig_pts+=3;
break;
case 3:
orig_pts+=2;
break;
case 4:
orig_pts+=2;
break;
case 5:
orig_pts+=1;
break;
}
}
p->pts = (ptCI *)gf_realloc(p->pts, sizeof(ptCI)*(nb_pts+orig_pts));
return orig_pts;
}
static JSValue svg_path_move_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *p;
Double x, y;
u32 nb_pts;
p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p || (argc!=2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
nb_pts = svg_path_realloc_pts(p, 1);
p->pts[nb_pts].x = (Float) x;
p->pts[nb_pts].y = (Float) y;
p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
p->tags[p->nb_coms] = 0;
p->nb_coms++;
return JS_TRUE;
}
static JSValue svg_path_line_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *p;
Double x, y;
u32 nb_pts;
p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p || (argc!=2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
nb_pts = svg_path_realloc_pts(p, 1);
p->pts[nb_pts].x = (Float) x;
p->pts[nb_pts].y = (Float) y;
p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
p->tags[p->nb_coms] = 1;
p->nb_coms++;
return JS_TRUE;
}
static JSValue svg_path_quad_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *p;
Double x1, y1, x2, y2;
u32 nb_pts;
p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p || (argc!=4)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x1, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y1, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x2, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y2, argv[3])) return GF_JS_EXCEPTION(c);
nb_pts = svg_path_realloc_pts(p, 2);
p->pts[nb_pts].x = (Float) x1;
p->pts[nb_pts].y = (Float) y1;
p->pts[nb_pts+1].x = (Float) x2;
p->pts[nb_pts+1].y = (Float) y2;
p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
p->tags[p->nb_coms] = 4;
p->nb_coms++;
return JS_TRUE;
}
static JSValue svg_path_curve_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *p;
Double x1, y1, x2, y2, x, y;
u32 nb_pts;
p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p || (argc!=6)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x1, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y1, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x2, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y2, argv[3])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[4])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[5])) return GF_JS_EXCEPTION(c);
nb_pts = svg_path_realloc_pts(p, 3);
p->pts[nb_pts].x = (Float) x1;
p->pts[nb_pts].y = (Float) y1;
p->pts[nb_pts+1].x = (Float) x2;
p->pts[nb_pts+1].y = (Float) y2;
p->pts[nb_pts+2].x = (Float) x;
p->pts[nb_pts+2].y = (Float) y;
p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
p->tags[p->nb_coms] = 2;
p->nb_coms++;
return JS_TRUE;
}
static JSValue svg_path_close(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
pathCI *p = (pathCI *) JS_GetOpaque(obj, pathClass.class_id);
if (!p) return GF_JS_EXCEPTION(c);
p->tags = (u8 *)gf_realloc(p->tags, sizeof(u8)*(p->nb_coms+1) );
p->tags[p->nb_coms] = 6;
p->nb_coms++;
return JS_TRUE;
}
static JSValue matrix_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
default:
return GF_JS_EXCEPTION(c);
}
}
static JSValue matrix_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
Double d;
GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, value)) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
mx->m[0] = FLT2FIX(d);
return JS_UNDEFINED;
case 1:
mx->m[3] = FLT2FIX(d);
return JS_UNDEFINED;
case 2:
mx->m[1] = FLT2FIX(d);
return JS_UNDEFINED;
case 3:
mx->m[4] = FLT2FIX(d);
return JS_UNDEFINED;
case 4:
mx->m[2] = FLT2FIX(d);
return JS_UNDEFINED;
case 5:
mx->m[5] = FLT2FIX(d);
return JS_UNDEFINED;
default:
break;
}
return GF_JS_EXCEPTION(c);
}
static JSValue svg_mx2d_get_component(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 comp;
GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx || (argc!=1)) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &comp, argv[0])) return GF_JS_EXCEPTION(c);
switch (comp) {
case 0: return JS_NewFloat64(c, FIX2FLT(mx->m[0]));
case 1: return JS_NewFloat64(c, FIX2FLT(mx->m[3]));
case 2: return JS_NewFloat64(c, FIX2FLT(mx->m[1]));
case 3: return JS_NewFloat64(c, FIX2FLT(mx->m[4]));
case 4: return JS_NewFloat64(c, FIX2FLT(mx->m[2]));
case 5: return JS_NewFloat64(c, FIX2FLT(mx->m[5]));
}
return GF_JS_EXCEPTION(c);
}
static JSValue svg_mx2d_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *mx1, *mx2;
mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx1 || (argc!=1)) return GF_JS_EXCEPTION(c);
mx2 = (GF_Matrix2D *) JS_GetOpaque(argv[0], matrixClass.class_id);
if (!mx2) return GF_JS_EXCEPTION(c);
gf_mx2d_add_matrix(mx1, mx2);
return JS_DupValue(c, obj);
}
static JSValue svg_mx2d_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *mx = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx) return GF_JS_EXCEPTION(c);
gf_mx2d_inverse(mx);
return JS_DupValue(c, obj);
}
static JSValue svg_mx2d_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double x, y;
GF_Matrix2D *mx1, mx2;
mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx1 || (argc!=2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[0]) || JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
gf_mx2d_init(mx2);
mx2.m[2] = FLT2FIX(x);
mx2.m[5] = FLT2FIX(y);
gf_mx2d_pre_multiply(mx1, &mx2);
return JS_DupValue(c, obj);
}
static JSValue svg_mx2d_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double scale;
GF_Matrix2D *mx1, mx2;
mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx1 || (argc!=2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &scale, argv[0])) return GF_JS_EXCEPTION(c);
gf_mx2d_init(mx2);
mx2.m[0] = mx2.m[4] = FLT2FIX(scale);
gf_mx2d_pre_multiply(mx1, &mx2);
return JS_DupValue(c, obj);
}
static JSValue svg_mx2d_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double angle;
GF_Matrix2D *mx1, mx2;
mx1 = (GF_Matrix2D *) JS_GetOpaque(obj, matrixClass.class_id);
if (!mx1 || (argc!=1)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &angle, argv[0])) return GF_JS_EXCEPTION(c);
gf_mx2d_init(mx2);
gf_mx2d_add_rotation(&mx2, 0, 0, gf_mulfix(FLT2FIX(angle/180), GF_PI));
gf_mx2d_pre_multiply(mx1, &mx2);
return JS_DupValue(c, obj);
}
#ifdef GPAC_ENABLE_HTML5_MEDIA
void *html_get_element_class(GF_Node *n);
#endif
JSClassID svg_get_element_class(GF_Node *n)
{
if (!n) return 0;
if ((n->sgprivate->tag>=GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG)) {
#ifdef GPAC_ENABLE_HTML5_MEDIA
if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) {
gf_assert(0);
return html_get_element_class(n);
}
#endif
return svgElement.class_id;
}
return 0;
}
JSClassID svg_get_document_class(GF_SceneGraph *sg)
{
GF_Node *n = sg->RootNode;
if (!n) return 0;
if ((n->sgprivate->tag>=GF_NODE_RANGE_FIRST_SVG) && (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG))
return svgDocument.class_id;
return 0;
}
Bool is_svg_document_class(JSContext *c, JSValue obj)
{
void *ptr = JS_GetOpaque(obj, svgDocument.class_id);
if (ptr) return GF_TRUE;
return GF_FALSE;
}
Bool is_svg_element_class(JSContext *c, JSValue obj)
{
void *ptr = JS_GetOpaque(obj, svgElement.class_id);
if (ptr) return GF_TRUE;
return GF_FALSE;
}
#define SETUP_JSCLASS(_class, _name, _proto_funcs, _construct, _finalize, _proto_class_id) \
if (!_class.class_id) {\
JS_NewClassID(&(_class.class_id)); \
_class.class.class_name = _name; \
_class.class.finalizer = _finalize;\
JS_NewClass(jsrt, _class.class_id, &(_class.class));\
}\
scene->svg_js->_class.proto = JS_NewObjectClass(c, _proto_class_id ? _proto_class_id : _class.class_id);\
JS_SetPropertyFunctionList(c, scene->svg_js->_class.proto, _proto_funcs, countof(_proto_funcs));\
JS_SetClassProto(c, _class.class_id, scene->svg_js->_class.proto);\
if (_construct) {\
scene->svg_js->_class.ctor = JS_NewCFunction2(c, _construct, _name, 1, JS_CFUNC_constructor, 0);\
JS_SetPropertyStr(c, global, _name, scene->svg_js->_class.ctor);\
}\
static const JSCFunctionListEntry globalFuncs[] =
{
JS_CGETSET_MAGIC_DEF("connected", global_getProperty, NULL, 0),
JS_CGETSET_MAGIC_DEF("parent", global_getProperty, NULL, 1),
JS_CFUNC_DEF("createConnection", 0, svg_connection_create),
JS_CFUNC_DEF("gotoLocation", 1, svg_nav_to_location),
JS_CFUNC_DEF("alert", 0, js_print),
JS_CFUNC_DEF("print", 0, js_print),
JS_CFUNC_DEF("hasFeature", 2, dom_imp_has_feature),
JS_CFUNC_DEF("parseXML", 0, svg_parse_xml),
};
static const JSCFunctionListEntry documentFuncs[] =
{
JS_CGETSET_MAGIC_DEF("defaultView", svg_doc_getProperty, NULL, 0),
};
static const JSCFunctionListEntry svg_elementFuncs[] =
{
JS_CGETSET_MAGIC_DEF("id", svg_element_getProperty, svg_element_setProperty, 0),
/*svgSVGElement interface*/
JS_CGETSET_MAGIC_DEF("currentScale", svg_element_getProperty, svg_element_setProperty, 5),
JS_CGETSET_MAGIC_DEF("currentRotate", svg_element_getProperty, svg_element_setProperty, 6),
JS_CGETSET_MAGIC_DEF("currentTranslate", svg_element_getProperty, svg_element_setProperty, 7),
JS_CGETSET_MAGIC_DEF("viewport", svg_element_getProperty, NULL, 8),
JS_CGETSET_MAGIC_DEF("currentTime", svg_element_getProperty, svg_element_setProperty, 9),
/*timeControl interface*/
JS_CGETSET_MAGIC_DEF("isPaused", svg_element_getProperty, NULL, 10),
/*old SVG1.1 stuff*/
JS_CGETSET_MAGIC_DEF("ownerSVGElement", svg_element_getProperty, NULL, 11),
/*SVGElementInstance*/
JS_CGETSET_MAGIC_DEF("correspondingElement", svg_element_getProperty, NULL, 12),
JS_CGETSET_MAGIC_DEF("correspondingUseElement", svg_element_getProperty, NULL, 13),
/*trait access interface*/
JS_CFUNC_DEF("getTrait", 1, svg_udom_get_trait),
JS_CFUNC_DEF("getTraitNS", 2, svg_udom_get_trait),
JS_CFUNC_DEF("getFloatTrait", 1, svg_udom_get_float_trait),
JS_CFUNC_DEF("getMatrixTrait", 1, svg_udom_get_matrix_trait),
JS_CFUNC_DEF("getRectTrait", 1, svg_udom_get_rect_trait),
JS_CFUNC_DEF("getPathTrait", 1, svg_udom_get_path_trait),
JS_CFUNC_DEF("getRGBColorTrait", 1, svg_udom_get_rgb_color_trait),
/*FALLBACK TO BASE-VALUE FOR NOW - WILL NEED EITHER DOM TREE-CLONING OR A FAKE RENDER
PASS FOR EACH PRESENTATION VALUE ACCESS*/
JS_CFUNC_DEF("getPresentationTrait", 1, svg_udom_get_trait),
JS_CFUNC_DEF("getPresentationTraitNS", 2, svg_udom_get_trait),
JS_CFUNC_DEF("getFloatPresentationTrait", 1, svg_udom_get_float_trait),
JS_CFUNC_DEF("getMatrixPresentationTrait", 1, svg_udom_get_matrix_trait),
JS_CFUNC_DEF("getRectPresentationTrait", 1, svg_udom_get_rect_trait),
JS_CFUNC_DEF("getPathPresentationTrait", 1, svg_udom_get_path_trait),
JS_CFUNC_DEF("getRGBColorPresentationTrait", 1, svg_udom_get_rgb_color_trait),
JS_CFUNC_DEF("setTrait", 2, svg_udom_set_trait),
JS_CFUNC_DEF("setTraitNS", 3, svg_udom_set_trait),
JS_CFUNC_DEF("setFloatTrait", 2, svg_udom_set_float_trait),
JS_CFUNC_DEF("setMatrixTrait", 2, svg_udom_set_matrix_trait),
JS_CFUNC_DEF("setRectTrait", 2, svg_udom_set_rect_trait),
JS_CFUNC_DEF("setPathTrait", 2, svg_udom_set_path_trait),
JS_CFUNC_DEF("setRGBColorTrait", 2, svg_udom_set_rgb_color_trait),
/*locatable interface*/
JS_CFUNC_DEF("getBBox", 0, svg_udom_get_local_bbox),
JS_CFUNC_DEF("getScreenCTM", 0, svg_udom_get_screen_ctm),
JS_CFUNC_DEF("getScreenBBox", 0, svg_udom_get_screen_bbox),
/*svgSVGElement interface*/
JS_CFUNC_DEF("createSVGMatrixComponents", 0, svg_udom_create_matrix_components),
JS_CFUNC_DEF("createSVGRect", 0, svg_udom_create_rect),
JS_CFUNC_DEF("createSVGPath", 0, svg_udom_create_path),
JS_CFUNC_DEF("createSVGRGBColor", 0, svg_udom_create_color),
JS_CFUNC_DEF("createSVGPoint", 0, svg_udom_create_point),
JS_CFUNC_DEF("moveFocus", 0, svg_udom_move_focus),
JS_CFUNC_DEF("setFocus", 0, svg_udom_set_focus),
JS_CFUNC_DEF("getCurrentFocusedObject", 0, svg_udom_get_focus),
JS_CFUNC_DEF("getCurrentTime", 0, svg_udom_get_time),
/*timeControl interface*/
JS_CFUNC_DEF("beginElementAt", 1, svg_udom_smil_begin),
JS_CFUNC_DEF("beginElement", 0, svg_udom_smil_begin),
JS_CFUNC_DEF("endElementAt", 1, svg_udom_smil_end),
JS_CFUNC_DEF("endElement", 0, svg_udom_smil_end),
JS_CFUNC_DEF("pauseElement", 0, svg_udom_smil_pause),
JS_CFUNC_DEF("resumeElement", 0, svg_udom_smil_resume),
JS_CFUNC_DEF("restartElement", 0, svg_udom_smil_restart),
JS_CFUNC_DEF("setSpeed", 0, svg_udom_smil_set_speed),
JS_CFUNC_DEF("getTotalLength", 0, svg_path_get_total_length)
};
/*RGBColor class*/
static const JSCFunctionListEntry rgb_Funcs[] =
{
JS_CGETSET_MAGIC_DEF("red", rgb_getProperty, rgb_setProperty, 0),
JS_CGETSET_MAGIC_DEF("green", rgb_getProperty, rgb_setProperty, 1),
JS_CGETSET_MAGIC_DEF("blue", rgb_getProperty, rgb_setProperty, 2),
};
/*SVGRect class*/
static const JSCFunctionListEntry rect_Funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", rect_getProperty, rect_setProperty, 0),
JS_CGETSET_MAGIC_DEF("y", rect_getProperty, rect_setProperty, 1),
JS_CGETSET_MAGIC_DEF("width", rect_getProperty, rect_setProperty, 2),
JS_CGETSET_MAGIC_DEF("height", rect_getProperty, rect_setProperty, 3),
};
/*SVGPoint class*/
static const JSCFunctionListEntry point_Funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", point_getProperty, point_setProperty, 0),
JS_CGETSET_MAGIC_DEF("y", point_getProperty, point_setProperty, 1)
};
/*SVGMatrix class*/
static const JSCFunctionListEntry matrix_Funcs[] =
{
JS_CGETSET_MAGIC_DEF("a", matrix_getProperty, matrix_setProperty, 0),
JS_CGETSET_MAGIC_DEF("b", matrix_getProperty, matrix_setProperty, 1),
JS_CGETSET_MAGIC_DEF("c", matrix_getProperty, matrix_setProperty, 2),
JS_CGETSET_MAGIC_DEF("d", matrix_getProperty, matrix_setProperty, 3),
JS_CGETSET_MAGIC_DEF("e", matrix_getProperty, matrix_setProperty, 4),
JS_CGETSET_MAGIC_DEF("f", matrix_getProperty, matrix_setProperty, 5),
JS_CFUNC_DEF("getComponent", 1, svg_mx2d_get_component),
JS_CFUNC_DEF("mMultiply", 1, svg_mx2d_multiply),
JS_CFUNC_DEF("inverse", 0, svg_mx2d_inverse),
JS_CFUNC_DEF("mTranslate", 2, svg_mx2d_translate),
JS_CFUNC_DEF("mScale", 1, svg_mx2d_scale),
JS_CFUNC_DEF("mRotate", 1, svg_mx2d_rotate),
};
/*SVGPath class*/
static const JSCFunctionListEntry path_Funcs[] =
{
JS_CGETSET_MAGIC_DEF("numberOfSegments", path_getProperty, NULL, 0),
JS_CFUNC_DEF("getSegment", 1, svg_path_get_segment),
JS_CFUNC_DEF("getSegmentParam", 2, svg_path_get_segment_param),
JS_CFUNC_DEF("moveTo", 2, svg_path_move_to),
JS_CFUNC_DEF("lineTo", 2, svg_path_line_to),
JS_CFUNC_DEF("quadTo", 4, svg_path_quad_to),
JS_CFUNC_DEF("curveTo", 6, svg_path_curve_to),
JS_CFUNC_DEF("close", 0, svg_path_close),
};
JSClassID dom_js_get_element_proto(struct JSContext *c);
JSClassID dom_js_get_document_proto(JSContext *c);
/*defines a new global object "document" of type Document*/
void dom_js_define_document(struct JSContext *c, JSValue global, GF_SceneGraph *doc);
void domDocument_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func);
void domElement_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func);
static void svg_init_js_api(GF_SceneGraph *scene)
{
JSContext *c = scene->svg_js->js_ctx;
JSRuntime *jsrt = JS_GetRuntime(c);
JSValue global = JS_GetGlobalObject(scene->svg_js->js_ctx);
SETUP_JSCLASS(svg_globalClass, "Window", globalFuncs, NULL, NULL, 0);
scene->svg_js->global = JS_NewObjectClass(c, svg_globalClass.class_id);
JS_SetOpaque(scene->svg_js->global, scene);
JS_SetPropertyStr(c, global, "Window", scene->svg_js->global);
JS_SetPropertyStr(c, global, "alert", JS_NewCFunction(c, js_print, "alert", 1));
/*initialize DOM core */
dom_js_load(scene, scene->svg_js->js_ctx);
#ifdef GPAC_USE_DOWNLOADER
qjs_module_init_xhr_global(c, global);
#endif
svg_define_udom_exception(scene->svg_js->js_ctx, scene->svg_js->global);
JSValue console = JS_NewObject(c);
JS_SetPropertyStr(c, console, "log", JS_NewCFunction(c, js_print, "print", 1));
JS_SetPropertyStr(c, global, "console", console);
svgDocument.class.gc_mark = domDocument_gc_mark;
SETUP_JSCLASS(svgDocument, "SVGDocument", documentFuncs, NULL, dom_document_finalize, dom_js_get_document_proto(c));
svgElement.class.gc_mark = domElement_gc_mark;
SETUP_JSCLASS(svgElement, "SVGElement", svg_elementFuncs, NULL, dom_element_finalize, dom_js_get_element_proto(c));
SETUP_JSCLASS(rgbClass, "SVGRGBColor", rgb_Funcs, NULL, baseCI_finalize, 0);
SETUP_JSCLASS(rectClass, "SVGRect", rect_Funcs, NULL, baseCI_finalize, 0);
SETUP_JSCLASS(pointClass, "SVGPoint", point_Funcs, NULL, baseCI_finalize, 0);
SETUP_JSCLASS(matrixClass, "SVGMatrix", matrix_Funcs, NULL, baseCI_finalize, 0);
SETUP_JSCLASS(pathClass, "SVGPath", path_Funcs, NULL, pathCI_finalize, 0);
JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "MOVE_TO", JS_NewInt32(c, 77));
JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "LINE_TO", JS_NewInt32(c, 76));
JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CURVE_TO", JS_NewInt32(c, 67));
JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "QUAD_TO", JS_NewInt32(c, 81));
JS_SetPropertyStr(c, scene->svg_js->pathClass.proto, "CLOSE", JS_NewInt32(c, 90));
/*we have our own constructors*/
scene->get_element_class = svg_get_element_class;
scene->get_document_class = svg_get_document_class;
/*create document object*/
dom_js_define_document(scene->svg_js->js_ctx, global, scene);
/*create event object, and remember it*/
scene->svg_js->event = dom_js_define_event(scene->svg_js->js_ctx);
JS_FreeValue(c, global);
}
GF_DOM_Event *dom_get_evt_private(JSValue v);
JSContext *svg_script_get_context(GF_SceneGraph *sg)
{
return sg->svg_js ? sg->svg_js->js_ctx : NULL;
}
Bool svg_script_execute(GF_SceneGraph *sg, char *utf8_script, GF_DOM_Event *event)
{
char szFuncName[1024];
JSValue ret;
Bool ok=GF_TRUE;
GF_DOM_Event *prev_event = NULL;
char *sep = strchr(utf8_script, '(');
if (!sep) {
gf_strcpy(szFuncName, utf8_script);
gf_strcat(szFuncName, "(evt)");
utf8_script = szFuncName;
}
gf_js_lock(sg->svg_js->js_ctx, GF_TRUE);
prev_event = dom_get_evt_private(sg->svg_js->event);
JS_SetOpaque(sg->svg_js->event, event);
ret = JS_Eval(sg->svg_js->js_ctx, utf8_script, (u32) strlen(utf8_script), "inline script", sg->svg_js->use_strict ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL);
JS_SetOpaque(sg->svg_js->event, prev_event);
#if 0
//to check, what was the purpose of this ?
if (ret==JS_FALSE) {
char *sep = strchr(utf8_script, '(');
if (sep) {
sep[0] = 0;
ret = JS_LookupProperty(sg->svg_js->js_ctx, sg->svg_js->global, utf8_script, &rval);
sep[0] = '(';
}
}
#endif
if (JS_IsException(ret)) {
js_dump_error(sg->svg_js->js_ctx);
ok=GF_FALSE;
}
JS_FreeValue(sg->svg_js->js_ctx, ret);
if (sg->svg_js->force_gc) {
gf_js_call_gc(sg->svg_js->js_ctx);
sg->svg_js->force_gc = GF_FALSE;
}
js_std_loop(sg->svg_js->js_ctx);
gf_js_lock(sg->svg_js->js_ctx, GF_FALSE);
return ok;
}
#ifdef GPAC_ENABLE_HTML5_MEDIA
void html_media_js_api_del();
#endif
void gf_svg_script_context_del(GF_SVGJS *svg_js, GF_SceneGraph *scenegraph)
{
gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), scenegraph, NULL);
gf_js_delete_context(svg_js->js_ctx);
dom_js_unload();
#ifdef GPAC_ENABLE_HTML5_MEDIA
/* HTML */
html_media_js_api_del();
#endif
gf_free(svg_js);
scenegraph->svg_js = NULL;
}
static void svg_script_predestroy(GF_Node *n, void *eff, Bool is_destroy)
{
if (is_destroy) {
GF_SVGJS *svg_js = n->sgprivate->scenegraph->svg_js;
/*unregister script from parent scene (cf base_scenegraph::sg_reset) */
gf_list_del_item(n->sgprivate->scenegraph->scripts, n);
if (svg_js->nb_scripts) {
svg_js->nb_scripts--;
/*detach this script from our object cache*/
gf_sg_js_dom_pre_destroy(JS_GetRuntime(svg_js->js_ctx), n->sgprivate->scenegraph, n);
if (!svg_js->nb_scripts) {
gf_svg_script_context_del(svg_js, n->sgprivate->scenegraph);
}
}
}
}
GF_Err JSScript_CreateSVGContext(GF_SceneGraph *sg)
{
GF_SVGJS *svg_js;
if (sg->svg_js) {
/* the JS/SVG context is already created, no need to do anything */
return GF_OK;
}
GF_SAFEALLOC(svg_js, GF_SVGJS);
if (!svg_js) {
return GF_OUT_OF_MEM;
}
/*create new ecmascript context*/
svg_js->js_ctx = gf_js_create_context();
if (!svg_js->js_ctx) {
gf_free(svg_js);
return GF_SCRIPT_ERROR;
}
gf_js_lock(svg_js->js_ctx, GF_TRUE);
sg->svg_js = svg_js;
/*load SVG & DOM APIs*/
svg_init_js_api(sg);
#ifdef GPAC_ENABLE_HTML5_MEDIA
/* HTML */
html_media_init_js_api(sg);
#endif
svg_js->script_execute = svg_script_execute;
svg_js->handler_execute = svg_script_execute_handler;
gf_js_lock(svg_js->js_ctx, GF_FALSE);
return GF_OK;
}
GF_DOMText *svg_get_text_child(GF_Node *node)
{
GF_ChildNodeItem *child;
GF_DOMText *txt;
txt = NULL;
child = ((SVG_Element*)node)->children;
if (! child) return NULL;
while (child) {
txt = (GF_DOMText*)child->node;
if ((txt->sgprivate->tag==TAG_DOMText) && txt->textContent) return txt;
txt = NULL;
child = child->next;
}
return NULL;
}
Bool svg_js_load_script(GF_Node *script, char *file)
{
GF_Err e;
u8 *jsscript;
u32 fsize;
Bool success = GF_TRUE;
JSValue ret;
GF_SVGJS *svg_js;
u32 flags = JS_EVAL_TYPE_GLOBAL;
char *abs_url = NULL;
svg_js = script->sgprivate->scenegraph->svg_js;
if (!strnicmp(file, "file://", 7)) file += 7;
if (!gf_file_exists(file)) {
GF_JSAPIParam par;
GF_SceneGraph *scene = script->sgprivate->scenegraph;
par.uri.url = file;
if (scene->script_action && scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_RESOLVE_XLINK, script, &par))
abs_url = (char *) par.uri.url;
if (abs_url || !gf_file_exists(abs_url)) {
if (abs_url) gf_free(abs_url);
return GF_FALSE;
}
}
e = gf_file_load_data(abs_url ? abs_url : file, &jsscript, &fsize);
if (abs_url) gf_free(abs_url);
if (e!=GF_OK) return GF_FALSE;
/*for handler, only load code*/
if (script->sgprivate->tag==TAG_SVG_handler) {
GF_DOMText *txt = gf_dom_add_text_node(script, jsscript);
txt->type = GF_DOM_TEXT_INSERTED;
return GF_TRUE;
}
gf_js_lock(svg_js->js_ctx, GF_TRUE);
if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) jsscript, fsize )) {
flags = JS_EVAL_TYPE_MODULE;
svg_js->use_strict = GF_TRUE;
}
ret = JS_Eval(svg_js->js_ctx, jsscript, sizeof(char)*fsize, file, flags);
if (JS_IsException(ret)) {
js_dump_error(svg_js->js_ctx);
success=GF_FALSE;
}
JS_FreeValue(svg_js->js_ctx, ret);
if (svg_js->force_gc) {
gf_js_call_gc(svg_js->js_ctx);
svg_js->force_gc = GF_FALSE;
}
gf_js_lock(svg_js->js_ctx, GF_FALSE);
gf_dom_listener_process_add(script->sgprivate->scenegraph);
gf_free(jsscript);
return success;
}
#include <gpac/download.h>
#include <gpac/network.h>
void JSScript_LoadSVG(GF_Node *node)
{
GF_DOMText *txt;
GF_SVGJS *svg_js;
GF_FieldInfo href_info;
if (!node->sgprivate->scenegraph->svg_js) {
if (JSScript_CreateSVGContext(node->sgprivate->scenegraph) != GF_OK) return;
}
/*register script with parent scene (cf base_scenegraph::sg_reset) */
gf_list_add(node->sgprivate->scenegraph->scripts, node);
svg_js = node->sgprivate->scenegraph->svg_js;
if (!node->sgprivate->UserCallback) {
svg_js->nb_scripts++;
node->sgprivate->UserCallback = svg_script_predestroy;
}
/*if href download the script file*/
if (gf_node_get_attribute_by_tag(node, TAG_XLINK_ATT_href, GF_FALSE, GF_FALSE, &href_info) == GF_OK) {
GF_DownloadManager *dnld_man;
GF_JSAPIParam par;
char *url;
XMLRI *xmlri = (XMLRI *)href_info.far_ptr;
/* getting a download manager */
par.dnld_man = NULL;
ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
dnld_man = par.dnld_man;
/* resolve the uri of the script*/
par.uri.nb_params = 0;
par.uri.url = xmlri->string;
ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_RESOLVE_URI, node, &par);
url = (char *)par.uri.url;
/* if the file is local, we don't need to download it */
if (!strstr(url, "://") || !strnicmp(url, "file://", 7)) {
svg_js_load_script(node, url);
} else if (dnld_man) {
GF_Err e;
#ifdef GPAC_USE_DOWNLOADER
GF_Err vrml_svg_js_async_load(GF_DownloadManager *dnld_man, char *url, u32 type, GF_Node *script, JSValue *rval);
e = vrml_svg_js_async_load(dnld_man, url, 2, node, NULL);
#else
e = GF_NOT_SUPPORTED;
#endif
if (e) {
par.info.e = e;
par.info.msg = "Cannot fetch script";
ScriptAction(node->sgprivate->scenegraph, GF_JSAPI_OP_MESSAGE, NULL, &par);
}
}
gf_free(url);
}
/*for scripts only, execute*/
else if (node->sgprivate->tag == TAG_SVG_script) {
JSValue ret;
u32 txtlen;
u32 flags = JS_EVAL_TYPE_GLOBAL;
txt = svg_get_text_child(node);
if (!txt) return;
txtlen = (u32) strlen(txt->textContent);
if (!gf_opts_get_bool("core", "no-js-mods") && JS_DetectModule((const char *) txt, txtlen )) {
flags = JS_EVAL_TYPE_MODULE;
svg_js->use_strict = GF_TRUE;
}
ret = JS_Eval(svg_js->js_ctx, txt->textContent, (u32) strlen(txt->textContent), "inline_script", flags);
if (JS_IsException(ret)) {
js_dump_error(svg_js->js_ctx);
}
JS_FreeValue(svg_js->js_ctx, ret);
gf_dom_listener_process_add(node->sgprivate->scenegraph);
js_std_loop(svg_js->js_ctx);
}
}
#ifdef _DEBUG
//#define DUMP_DEF_AND_ROOT
#endif
#ifdef DUMP_DEF_AND_ROOT
void dump_root(const char *name, void *rp, void *data)
{
if (name[0]=='_') fprintf(stderr, "\t%s\n", name);
}
#endif
/* Executes JavaScript code in response to an event being triggered
The code to be executed (stored in a GF_DOMHandler struct) is either:
- text content not yet passed to the JS engine
- text contained in a node's text content not yet parsed (node)
- text, outside of a node, obtained by some external means (XHR, ...) - utf8_script
- already in the JS engine, in the form of:
- an anonymous function (js_fun_val)
- a named function (js_fun)
*/
static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer, char *utf8_script)
{
GF_DOMText *txt = NULL;
GF_SVGJS *svg_js;
JSValue __this;
JSValue ret;
Bool free_this = GF_FALSE;
u32 flags = JS_EVAL_TYPE_GLOBAL;
Bool success=GF_TRUE;
GF_DOM_Event *prev_event = NULL;
GF_DOMHandler *hdl = (GF_DOMHandler *)node;
/*LASeR hack for encoding scripts without handler - node is a listener in this case, not a handler*/
if (utf8_script) {
if (!node) return GF_FALSE;
hdl = NULL;
} else {
if (!hdl->js_data || (JS_IsUndefined(hdl->js_data->fun_val) && JS_IsUndefined(hdl->js_data->evt_listen_obj))) {
txt = svg_get_text_child(node);
if (!txt) return GF_FALSE;
}
/*not sure about this (cf test struct-use-205-t.svg)*/
//if (!node->sgprivate->parents) return GF_FALSE;
}
svg_js = node->sgprivate->scenegraph->svg_js;
#ifndef GPAC_DISABLE_LOG
if (gf_log_tool_level_on(GF_LOG_SCRIPT, GF_LOG_DEBUG)) {
char *content, *_content = NULL;
if (utf8_script) {
content = utf8_script;
} else if (!JS_IsUndefined(hdl->js_data->fun_val)) {
content = _content = (char *) JS_ToCString(svg_js->js_ctx, hdl->js_data->fun_val);
} else if (txt) {
content = txt->textContent;
} else {
content = "unknown";
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOM Events ] Executing script code from handler: %s\n", content) );
JS_FreeCString(svg_js->js_ctx, _content);
}
#endif
gf_js_lock(svg_js->js_ctx, GF_TRUE);
prev_event = dom_get_evt_private(svg_js->event);
/*break loops*/
if (prev_event && (prev_event->type==event->type) && (prev_event->target==event->target)) {
gf_js_lock(svg_js->js_ctx, GF_FALSE);
return GF_FALSE;
}
JS_SetOpaque(svg_js->event, event);
svg_js->in_script = GF_TRUE;
if (svg_js->use_strict)
flags = JS_EVAL_TYPE_MODULE;
/*if an observer is being specified, use it*/
if (hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->evt_listen_obj))
__this = hdl->js_data->evt_listen_obj;
/*compile the jsfun if any - 'this' is the associated observer*/
else if (observer) {
__this = dom_element_construct(svg_js->js_ctx, observer);
free_this = GF_TRUE;
} else {
__this = svg_js->global;
}
if (txt && hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->fun_val)) {
hdl->js_data->fun_val = JS_EvalThis(svg_js->js_ctx, __this, txt->textContent, strlen(txt->textContent), "handler", flags|JS_EVAL_FLAG_COMPILE_ONLY);
}
if (utf8_script) {
ret = JS_EvalThis(svg_js->js_ctx, __this, utf8_script, (u32) strlen(utf8_script), "inline script", flags);
}
else if (hdl && hdl->js_data
&& (!JS_IsUndefined(hdl->js_data->fun_val) || !JS_IsUndefined(hdl->js_data->evt_listen_obj) )
) {
JSValue evt;
JSValue argv[1];
evt = gf_dom_new_event(svg_js->js_ctx);
JS_SetOpaque(evt, event);
argv[0] = evt;
if (!JS_IsUndefined(hdl->js_data->fun_val) ) {
ret = JS_Call(svg_js->js_ctx, hdl->js_data->fun_val, __this, 1, argv);
} else {
JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, hdl->js_data->evt_listen_obj, "hanldeEvent");
ret = JS_Call(svg_js->js_ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
JS_FreeValue(svg_js->js_ctx, fun);
}
JS_FreeValue(svg_js->js_ctx, evt);
} else if (txt) {
JSValue fun = JS_GetPropertyStr(svg_js->js_ctx, svg_js->global, txt->textContent);
if (JS_IsUndefined(fun)) {
JSValue glob = JS_GetGlobalObject(svg_js->js_ctx);
fun = JS_GetPropertyStr(svg_js->js_ctx, glob, txt->textContent);
JS_FreeValue(svg_js->js_ctx, glob);
}
if (!JS_IsUndefined(fun)) {
ret = JS_NULL;
if (! svg_script_execute(node->sgprivate->scenegraph, txt->textContent, event)) {
success = GF_FALSE;
}
}
else {
ret = JS_EvalThis(svg_js->js_ctx, __this, txt->textContent, (u32) strlen(txt->textContent), "internal", flags);
}
JS_FreeValue(svg_js->js_ctx, fun);
} else {
ret = JS_NULL;
}
if (JS_IsException(ret)) {
js_dump_error(svg_js->js_ctx);
success = GF_FALSE;
}
JS_FreeValue(svg_js->js_ctx, ret);
if (free_this)
JS_FreeValue(svg_js->js_ctx, __this);
JS_SetOpaque(svg_js->event, prev_event);
if (txt && hdl && hdl->js_data) hdl->js_data->fun_val = JS_UNDEFINED;
while (svg_js->force_gc) {
svg_js->force_gc = GF_FALSE;
gf_js_call_gc(svg_js->js_ctx);
}
svg_js->in_script = GF_FALSE;
/*check any pending exception if outer-most event*/
if (!prev_event) {
js_std_loop(svg_js->js_ctx);
}
gf_js_lock(svg_js->js_ctx, GF_FALSE);
#ifdef DUMP_DEF_AND_ROOT
if ((event->type==GF_EVENT_CLICK) || (event->type==GF_EVENT_MOUSEOVER)) {
NodeIDedItem *reg_node;
fprintf(stderr, "Node registry\n");
reg_node = node->sgprivate->scenegraph->id_node;
while (reg_node) {
fprintf(stderr, "\t%s\n", reg_node->NodeName);
reg_node = reg_node->next;
}
fprintf(stderr, "\n\nNamed roots:\n");
JS_DumpNamedRoots(JS_GetRuntime(svg_js->js_ctx), dump_root, NULL);
}
#endif
if (!success) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("SVG: Invalid event handler script\n" ));
return GF_FALSE;
}
return GF_TRUE;
}
void svg_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_Node *observer)
{
JSValue evt;
JSValue argv[1];
JSValue __this;
JSValue ret;
Bool success=GF_TRUE;
GF_DOMHandler *hdl = (GF_DOMHandler *)node;
JSContext *ctx;
if (!hdl || !hdl->js_data || !hdl->js_data->ctx
|| (JS_IsUndefined(hdl->js_data->fun_val) && JS_IsUndefined(hdl->js_data->evt_listen_obj))
) {
return;
}
ctx = hdl->js_data->ctx;
/*if an observer is being specified, use it*/
if (hdl && hdl->js_data && !JS_IsUndefined(hdl->js_data->evt_listen_obj))
__this = hdl->js_data->evt_listen_obj;
else
return;
/*compile the jsfun if any - 'this' is the associated observer*/
// else
// __this = observer ? dom_element_construct(ctx, observer) : svg_js->global;
gf_js_lock(ctx, GF_TRUE);
evt = gf_dom_new_event(ctx);
JS_SetOpaque(evt, event);
argv[0] = evt;
if (!JS_IsUndefined(hdl->js_data->fun_val) ) {
ret = JS_Call(ctx, hdl->js_data->fun_val, __this, 1, argv);
} else {
JSValue fun = JS_GetPropertyStr(ctx, hdl->js_data->evt_listen_obj, "hanldeEvent");
ret = JS_Call(ctx, fun, hdl->js_data->evt_listen_obj, 1, argv);
JS_FreeValue(ctx, fun);
}
if (JS_IsException(ret)) {
js_dump_error(ctx);
success = GF_FALSE;
}
JS_FreeValue(ctx, ret);
JS_FreeValue(ctx, evt);
gf_js_lock(ctx, GF_FALSE);
if (!success) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("SVG: Invalid event handler script\n" ));
return;
}
}
#endif
#endif