| /* | |
| * 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, ¶m_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 |