/* jsint.c
 * (c) 2002 Mikulas Patocka (Vrxni Ideolog), Petr 'Brain' Kulhavy
 * This file is a part of the Links program, relased under GPL.
 */

/*
 * Ve vsech upcallech plati, ze pokud dostanu ID nejakeho objektu, tak
 * javascript ma k tomu objektu pristupova prava. Jinymi slovy pristupova prava
 * se testuji v upcallech jen, aby se neco neproneslo vratnici ven. Dovnitr se
 * muze donaset vsechno, co si javascript donese, na to ma prava.
 *
 * Navic vsechny upcally dostanou pointer na f_data_c, kde bezi javascript,
 * takze se bude moci testovat, zda javascript nesaha na f_data_c, ke kteremu
 * nema pristupova prava.
 *
 *   Brain
 */

/* Uctovani pameti:
 * js_mem_alloc/js_mem_free se bude pouzivat na struktury fax_me_tender
 * dale se bude pouzivat take ve funkcich pro praci s cookies, protoze string
 * cookies v javascript_context se tez alokuje pomoci js_mem_alloc/js_mem_free.
 */

/*
   Retezce:

   vsechny retezce v ramci javascriptu jsou predavany v kodovani f_data->cp
   (tedy tak, jak prisly v dokumentu ze site)
 */


#include "links.h"

#ifdef JS

tcount jsint_execute_seq = 0;

#include "struct.h"
#include "ipret.h"
#include "builtin_keys.h"

/*
vypisuje to: jaky kod byl zarazen do fronty. jaky kod byl predan interpretu do js_execute_code. jaky kod byl vykonan a ukoncen intepretem jsint_done_execution
#define TRACE_EXECUTE
*/

struct js_request {
	list_entry_1st
	int onclick_submit;	/* >=0 (znamena cislo formulare) pokud tohle je request onclick handleru u submit tlacitka nebo onsubmit handleru */
	int onsubmit;		/* dtto pro submit handler */
	struct links_event ev;	/* event, ktery se ma poslat pri uspechu */
	int write_pos;	/* document.write position from END of document. -1 if document.write cannot be used */
	int wrote;	/* this request called document.write */
	int len;
	tcount seq;
	list_entry_last
	unsigned char code[1];
};


/* set_cookies bude parsovat takhle:
 * +....=............;...............................+   <- tohle je strinzik
 *      ^            ^
 * najdu 1. rovnase a za nim 1. strednik, najdu nasledujici rovnase a
 * nasledujici strednik, pokud to bude platne (&'='<&';') a 2. jmeno bude
 * "expires", tak to je furt jedna cookie -> poslu Mikulasovi.
 *
 * kdyz ne, tak je to jina susenka a poslu to 1. Mikulasovi
 *
 * pokud najdu ';' a za nim whitespace, za kterym neni rovnase, tak od toho
 * stredniku je to garbaz, kterou vratim do strinziku (fd->js->ctx->cookies)
 */

/* sets all cookies in fd>js->ctx->cookies */
/* final_flush means, that set_cookies was called from jsint_done_execution */






/* JESTLI V TYHLE FUNKCI BUDE NEJAKA BUGA, tak za to muze PerM, Clock a pan GNU,
 * ktery tady kolem rusili, ze jsem se nemohl soustredit. Takze se s
 * pripadnejma reklamacema obratte na ne!
 *
 *  Brain
 */

/* prototypes */
static void jsint_send_event(struct f_data_c *fd, struct links_event *ev);
static int jsint_create(struct f_data_c *);
static void jsint_done_execution(void *);
static int jsint_can_access(struct f_data_c *, struct f_data_c *);
static struct f_data_c *jsint_find_recursive(struct f_data_c *, long);  /* line 89 */
static void *jsint_find_object(struct f_data_c *, long);
static long *add_id(long *, int *, long );
static long *add_fd_id(long *, int *, long, long, unsigned char *);
static void add_all_recursive_in_fd(long **, int *, struct f_data_c *, struct f_data_c *);


void jsint_set_cookies(struct f_data_c *fd, int final_flush)
{
	unsigned char *str;
	unsigned char *next;
	unsigned char *eq1, *semic1, *eq2, *semic2;

	if(!(fd->js)||!(fd->js->ctx))internal_error("jsint_set_cookies called with NULL context.\n");
	if (!(fd->js->ctx->cookies)||!(fd->rq))return;  /* cookies string is empty, nothing to set */
	str=fd->js->ctx->cookies;

a_znova:
	eq1=strchr(str,'=');
	semic1=strchr(str,';');

	if (!*str||(!final_flush&&!semic1))  /* na konci neni strednik a skript jeste bezi, takze to musime vratit do stringu a vypadnout */
	{
		unsigned char *bla=NULL;
		if (*str)bla=stracpy1(str);
		js_mem_free(fd->js->ctx->cookies);
		fd->js->ctx->cookies=bla;
		return;
	}

	/* ted se v str bud vyskytuje strednik, nebo skript uz skoncil */

	if (semic1&&eq1>semic1)	/* '=' je za ';' takze to pred strednikem a strednik skipnem */
	{
		str=semic1+1;
		goto a_znova;
	}

	next=semic1?semic1+1:str+strlen(cast_const_char str);
	if (!eq1)	/* neni tam '=', takze to preskocime */
	{
		str=next;
		goto a_znova;
	}

	/* ted by to mela bejt regulerni susenka */

	next_par:
	eq2=NULL,semic2=NULL;
	if (semic1!=NULL)
	{
		eq2=strchr(semic1+1,'=');
		semic2=strchr(semic1+1,';');
	}

	if (eq2&&semic1&&(final_flush||semic2))
	{
		unsigned char *p=strstr(semic1+1,"expires");
		if (!p)p=strstr(semic1+1,"Expires");
		if (!p)p=strstr(semic1+1,"EXPIRES");
		if (!p)p=strstr(semic1+1,"domain");
		if (!p)p=strstr(semic1+1,"Domain");
		if (!p)p=strstr(semic1+1,"DOMAIN");
		if (!p)p=strstr(semic1+1,"path");
		if (!p)p=strstr(semic1+1,"Path");
		if (!p)p=strstr(semic1+1,"PATH");
		if (!p)p=strstr(semic1+1,"comment");
		if (!p)p=strstr(semic1+1,"Comment");
		if (!p)p=strstr(semic1+1,"COMMENT");
		if (!p)p=strstr(semic1+1,"max-age");
		if (!p)p=strstr(semic1+1,"Max-age");
		if (!p)p=strstr(semic1+1,"Max-Age");
		if (!p)p=strstr(semic1+1,"MAX-AGE");
		if (!p)p=strstr(semic1+1,"version");
		if (!p)p=strstr(semic1+1,"Version");
		if (!p)p=strstr(semic1+1,"VERSION");
		if (p&&p>semic1&&p<eq2)  /* za 1. prirazenim nasleduje "expires=", takze to je porad jedna susenka */
			{
				next=semic2?semic2+1:str+strlen(cast_const_char str);
				semic1=semic2;
				goto next_par;
			}
	}

	if (*next)next[-1]=0;

	for (;*str&&WHITECHAR(*str);str++); /* skip whitechars */

	/*debug("set_cookie: \"%s\"", str);*/
	set_cookie(fd->rq->url, str);

	str=next;
	goto a_znova;
}


int jsint_object_type(long to_je_on_Padre)
{
	return to_je_on_Padre&JS_OBJ_MASK;
}


static int jsint_create(struct f_data_c *fd)
{
	struct js_state *js;

	if (fd->js) internal_error("javascript state present");
	js = mem_calloc(sizeof(struct js_state));
	if (!(js->ctx = js_create_context(fd, ((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT))) {
		mem_free(js);
		return 1;
	}
	init_list(js->queue);
	fd->js = js;
	return 0;
}

void jsint_destroy(struct f_data_c *fd)
{
	struct js_state *js = fd->js;
	fd->script_t = 0;
	if (!js) return;
	fd->js = NULL;
	pr(js_destroy_context(js->ctx)) return;
	if (js->src) mem_free(js->src);
	if (js->active) mem_free(js->active);
	js_zaflaknuto_pameti -= js->newdata;
	free_list(struct js_request, js->queue);
	mem_free(js);
}

/* for <a href="javascript:..."> */
void javascript_func(struct session *ses, unsigned char *hlavne_ze_je_vecirek)
{
	unsigned char *code=get_url_data(hlavne_ze_je_vecirek);

	jsint_execute_code(current_frame(ses),code,strlen(cast_const_char code),-1,-1,-1, NULL);
}

static void jsint_send_event(struct f_data_c *fd, struct links_event *ev)
{
	if (!ev || !ev->b) return;
	send_event(fd->ses, ev);
}

/* executes or queues javascript code in frame:
	write_pos is number of bytes from the position where document.write should write to the end of document
	write_pos == -1 if it is not from <SCRIPT> statement and cannot use document.write
*/
/* data je cislo formulare pro onclick submit handler, jinak se nepouziva */
/* ev je udalost, ktera se ma znovu poslat, pokud bylo vraceno true */
void jsint_execute_code(struct f_data_c *fd, unsigned char *code, int len, int write_pos, int onclick_submit, int onsubmit, struct links_event *ev)
{
	struct js_request *r, *q;
	struct list_head *lq;

	for (;code&&len&&*code&&((*code)==' '||(*code)==9||(*code)==13||(*code)==10);code++,len--);

	/*
	FUJ !!!!
	if (!casecmp(code, cast_uchar "javascript:",strlen(cast_const_char "javascript:")))code+=strlen(cast_const_char "javascript:");
	*/

	if (len >= 11 && !casecmp(code, "javascript:", 11)) code += 11, len -= 11;

	if (!js_enable) {
		jsint_send_event(fd, ev);
		return;
	}

#ifdef TRACE_EXECUTE
	fprintf(stderr, "Submitted: ^^%.*s^^\n", len, code);
#endif

	if (!fd->js && jsint_create(fd)) {
		jsint_send_event(fd, ev);
		return;
	}
	if ((unsigned)len > MAXINT - sizeof(struct js_request)) overalloc();
	r = mem_calloc(sizeof(struct js_request) + len);
	r->seq = jsint_execute_seq++;
	r->write_pos = write_pos;
	r->len = len;
	r->onclick_submit = onclick_submit;
	r->onsubmit = onsubmit;
	memcpy(r->code, code, len);
	if (ev) memcpy(&r->ev, ev, sizeof(struct links_event));
	if (write_pos == -1) {
		add_to_list_end(fd->js->queue, r);
	} else {
		/* add it beyond all <SCRIPT> requests but before non-<SCRIPT> ones */
		foreach(struct js_request, q, lq, fd->js->queue) if (q->write_pos == -1) break;
		add_before_list_entry(lq, &r->list_entry);
	}
	jsint_run_queue(fd);
}

static void jsint_done_execution(void *fd_)
{
	struct f_data_c *fd = (struct f_data_c *)fd_;
	struct js_request *r, *to_exec;
	struct list_head *lr;
	struct js_state *js = fd->js;
	struct links_event ev = { 0, 0, 0, 0 };
	if (!js) {
		internal_error("no js in frame");
		return;
	}
	if (!js->active) {
		internal_error("jsint_done_execution: completion function called on inactive js");
		return;
	}

#ifdef TRACE_EXECUTE
	fprintf(stderr, "Done: ^^%.*s^^\n", js->active->len, js->active->code);
#endif

	/* accept all cookies set by the script */
	jsint_set_cookies(fd,1);

	/* js->active obsahuje request prave dobehnuteho skriptu */

	/* dobehl onclick_handler a nezaplatil (vratil false), budou se dit veci */
	if (js->active->ev.b && js->ctx->zaplatim)
		memcpy(&ev, &js->active->ev, sizeof(struct links_event));
	if (js->active->onclick_submit >=0 && !js->ctx->zaplatim)
	{
		/* pokud je handler od stejneho formulare, jako je defered, tak odlozeny skok znicime a zlikvidujem prislusny onsubmit handler z fronty */
		if (js->active->onclick_submit == fd->ses->defered_data)
		{
			foreach(struct js_request, r, lr,js->queue)
				/* to je onsubmit od naseho formulare, tak ho smazem */
				if (r->onsubmit == js->active->onclick_submit)
				{
					del_from_list(r);
					mem_free(r);
					break;	/* zadny dalsi onsubmit tohoto formulare uz nebude */
				}
			ses_destroy_defered_jump(fd->ses);
		}
	}

	if (js->active->write_pos == -1) mem_free(js->active), js->active = NULL;
	else {
		r = js->active; js->active = NULL;
		if (r->wrote) js->wrote = 1;
		jsint_scan_script_tags(fd);
		if (!f_is_finished(fd->f_data)) {
			fd->done = 0;
			fd->parsed_done = 0;
		}
		if (js->wrote && fd->script_t == -1) {
			fd->done = 0;
			fd->parsed_done = 0;
			fd_loaded(NULL, fd);
			js->wrote = 0;
		}
		mem_free(r);
	}

	to_exec = js->active;
	if (!to_exec && !list_empty(fd->js->queue)) to_exec = list_struct(fd->js->queue.next, struct js_request);
	if (fd->ses->defered_url && (!to_exec || (to_exec->seq > fd->ses->defered_seq && to_exec->write_pos == -1)))
	{
		unsigned char *url, *target;

		url=stracpy(fd->ses->defered_url);
		target=stracpy(fd->ses->defered_url);

		goto_url_f(fd->ses,NULL,url,target,fd->ses->defered_target_base,fd->ses->defered_data,0,0,0);
		mem_free(url);
		mem_free(target);
	}
	else
		jsint_run_queue(fd);
	jsint_send_event(fd, &ev);
}

void jsint_run_queue(struct f_data_c *fd)
{
	struct js_request *r;
	struct js_state *js = fd->js;

	if ((!fd->done && fd->f_data) || !js || js->active || list_empty(js->queue)) return;

	r = list_struct(js->queue.next, struct js_request);
	del_from_list(r);
	js->active = r;
#ifdef TRACE_EXECUTE
	fprintf(stderr, "Executing: ^^%.*s^^\n", r->len, r->code);

#endif
	pr(js_execute_code(js->ctx, r->code, r->len, jsint_done_execution)) {};
}

/* returns: 1 - source is modified by document.write
	    0 - original source
*/

int jsint_get_source(struct f_data_c *fd, unsigned char **start, size_t *len)
{
	struct js_state *js = fd->js;

	if (!js || !js->src) return 0;
	if (start) *start = js->src;
	if (len) *len = (size_t)js->srclen;
	return 1;
}

/*
 * tests if script running in frame "running" can access document in frame "accessed"
 * 0=no permission, 1=permission OK
 */
static int jsint_can_access(struct f_data_c *running, struct f_data_c *accessed)
{
	int a;
	unsigned char *h1, *h2;
	if (!running || !accessed || !running->rq || !accessed->rq) return 0;

	h1 = get_host_name(running->rq->url);
	h2 = get_host_name(accessed->rq->url);
	a = !casestrcmp(h1, h2);
	mem_free(h1);
	mem_free(h2);
	return a;
}


/* doc_id is real document id, whithout any type */
/* fd must be a valid pointer */
static struct f_data_c *jsint_find_recursive(struct f_data_c *fd, long doc_id)
{
	struct f_data_c *sub, *fdd;
	struct list_head *lsub;
	if (fd->id == doc_id) return fd;
	foreach(struct f_data_c, sub, lsub, fd->subframes) {
		if ((fdd = jsint_find_recursive(sub, doc_id))) return fdd;
	}
	return NULL;
}

/*
 *	This function finds document that has given ID
 */
struct f_data_c *jsint_find_document(long doc_id)
{
	struct f_data_c *fd;
	struct session *ses;
	struct list_head *lses;
	int type=jsint_object_type(doc_id);

	if (type!=JS_OBJ_T_DOCUMENT&&type!=JS_OBJ_T_FRAME)
		{unsigned char txt[256]; snprintf(txt,256,"jsint_find_document called with type=%d\n",type);internal_error(txt);}
	doc_id>>=JS_OBJ_MASK_SIZE;
	foreach(struct session, ses, lses, sessions) if ((fd = jsint_find_recursive(ses->screen, doc_id))) return fd;
	return NULL;
}

/* Document has just loaded. Scan for <SCRIPT> tags and execute each of them */

void jsint_scan_script_tags(struct f_data_c *fd)
{
	unsigned char *name, *attr;
	int namelen;
	unsigned char *val, *e, *ee;
	unsigned char *s, *ss, *eof;
	size_t len;
	unsigned char *start, *end;
	unsigned char *onload_code = NULL;
	int uv;
	int bs;

	if (!js_enable) return;
	if (!fd->rq || !fd->rq->ce || !fd->f_data) return;
	if (!jsint_get_source(fd, &ss, &len)) {
		if (get_file(fd->rq, &ss, &len)) return;
	}
	if (len > MAXINT) len = MAXINT;
	eof = ss + len;

	d_opt = &fd->f_data->opt;

	s = ss;
	se:
	while (s < eof && *s != '<') sp:s++;
	if (s >= eof || fd->script_t < 0)
	{
		if (onload_code && fd->script_t != -1)
		{
			jsint_execute_code(fd,onload_code,strlen(cast_const_char onload_code),-1,-1,-1, NULL);
		}
		fd->script_t = -1;
		goto ret;
	}
	if (eof - s >= 2 && (s[1] == '!' || s[1] == '?')) {
		s = skip_comment(s, eof);
		goto se;
	}
	if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
	if (!onload_code&&namelen==4&&!casecmp(name,"BODY",4))
	{
		onload_code=get_attr_val(attr,"onload");	/* if the element doesn't have onload attribute get_attr_val returns NULL */
		goto se;
	}

	if (!onload_code&&namelen==3&&!casecmp(name,"IMG",3))
	{
		onload_code=get_attr_val(attr,"onload");	/* if the element doesn't have onload attribute get_attr_val returns NULL */
		goto se;
	}

	if (namelen != 6 || casecmp(name, "SCRIPT", 6) || s - ss < fd->script_t) goto se;
	start = end = NULL;
	if ((val = get_attr_val(attr, "src"))) {
		unsigned char *url;
		if (fd->f_data->script_href_base && ((url = join_urls(fd->f_data->script_href_base, val)))) {
			size_t len;
			int code, version;
			struct additional_file *af = request_additional_file(fd->f_data, url);
			mem_free(url);
			mem_free(val);
			if (!af || !af->rq) goto se;
			if (af->rq->state >= 0) goto ret;
			if (!af->rq->ce) goto se;
			if (!get_http_code(af->rq->ce->head, &code, &version)) {
				if (code < 200 || code >= 300) goto se;
			}
			if (get_file(af->rq, &start, &len)) goto se;
			if (!len) goto se;
			if (len > MAXINT) len = MAXINT;
			end = start + len;
		} else {
			mem_free(val);
			goto se;
		}
	}
	e = s;
	uv = 0;
	bs = 0;
	while (e < eof && *e != '<') {
		es:
		if (!uv && (*e == '"' || *e == '\'')) uv = *e, bs = 0;
		else if (*e == '\\' && uv) bs = 1;
		else if (*e == uv && !bs) uv = 0;
		else bs = 0;
		e++;
	}
	if (eof - e >= 8) {
		if (/*uv ||*/ casecmp(e, "</SCRIPT", 8)) goto es;
	} else e = eof;
	ee = e;
	while (ee < eof && *ee != '>') ee++;
	if (ee < eof) ee++;
	fd->script_t = ee - ss;
	if (!start || !end) jsint_execute_code(fd, s, e - s, eof - ee,-1,-1, NULL);
	else jsint_execute_code(fd, start, end - start, eof - ee,-1,-1, NULL);
	ret:
	if (onload_code)mem_free(onload_code);

	d_opt = &dd_opt;
}


struct hopla_mladej
{
	struct form_control *fc;
	struct form_state *fs;
};


/* Returns pointer to the object with given ID in the document, or NULL when
 * there's no such object. Document must be a valid pointer.
 *
 * Pointer type depends on type of object, caller must know the type and
 * interpret the pointer in the right way.
 */

static void *jsint_find_object(struct f_data_c *document, long obj_id)
{
	int type=obj_id&JS_OBJ_MASK;
	int orig_obj_id=obj_id;
	obj_id>>=JS_OBJ_MASK_SIZE;

	switch (type)
	{
		/* form element
		 * obj_id can be from 0 up to number of form fields
		 * (document->vs->form_info_len may be actually lower if the fields were never
		 * touched)
		 * returns allocated struct hopla_mladej, you must free it after use
		 */
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
		{
			struct hopla_mladej *hopla;

			struct form_control *fc;
			struct list_head *lfc;
			int a=0;

			if (obj_id<0/*||obj_id>=n*/)return NULL;
			hopla=mem_alloc(sizeof(struct hopla_mladej));

			if (!(document->f_data)){mem_free(hopla);return NULL;};

			foreachback(struct form_control, fc, lfc, document->f_data->forms)
				if (fc->g_ctrl_num==obj_id){a=1;break;}
			if (!a){mem_free(hopla);return NULL;}

			hopla->fs=find_form_state(document, fc);
			hopla->fc=fc;
			return hopla;
		}

		/* link
		 * obj_id can be from 0 to (nlinks-1)
		 */
		case JS_OBJ_T_LINK:
		{
			struct link*l;
			int n;

			if (!(document->f_data))return NULL;

			l=document->f_data->links;
			n=document->f_data->nlinks;

			if (obj_id<0||obj_id>=n)return NULL;
			return l+obj_id;
		}

		/* form
		 * obj_id is form_num in struct form_control (f_data->forms)
		 */
		case JS_OBJ_T_FORM:
		{
			struct form_control *f;
			struct list_head *lf;

			if (!(document->f_data))return NULL;
			foreachback(struct form_control, f, lf, document->f_data->forms) if ((f->form_num)==obj_id)return f;
			return NULL;
		}

		/* anchors
		 * obj_id is position in list of all tags
		 */
		case JS_OBJ_T_ANCHOR:
		{
			struct tag *t;
			struct list_head *lt;
			int a=0;

			if (!(document->f_data))return NULL;
			foreach(struct tag, t, lt, document->f_data->tags)
			{
				if (obj_id==a)return t;
				a++;
			}
			return NULL;
		}
		break;

		/* this is document
		 * call jsint_find_document
		 * returned value is struct f_data_c
		 */
		case JS_OBJ_T_FRAME:
		case JS_OBJ_T_DOCUMENT:
		return jsint_find_document(orig_obj_id);

		/* image
		 * returned value is struct g_object_image *
		 */
		case JS_OBJ_T_IMAGE:
#ifdef G
		if (F) {
			struct g_object_image *gi;
			struct list_head *lgi;

			if (!document->f_data) return NULL;
			foreach(struct g_object_image, gi, lgi, document->f_data->images) {
				if (gi->id == obj_id) return gi;
			}
			return NULL;
		}else
#endif
		return NULL;

		default:
		internal_error("jsint_find_object: unknown type %d.",type);
		return NULL;  /* never called, but GCC likes it ;-) */
	}
}


static long *add_id(long *field,int *len,long id)
{
	long *p;
	int a;
	for (a=0;a<(*len);a++)	/* this object is already on the list */
		if (field[a]==id)return field;

	(*len)++;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	p=mem_realloc(field,(*len)*sizeof(long));

	p[(*len)-1]=id;
	return p;
}

long *add_fd_id(long *field,int *len,long fd,long id, unsigned char *name)
{
	long *p;
	int a;
	for (a=0;a<(*len);a+=3)	/* this object is already on the list */
		if (field[a]==fd&&field[a+1]==id)return field;


	(*len)+=3;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	p=mem_realloc(field,(*len)*sizeof(long));

	p[(*len)-3]=fd;
	p[(*len)-2]=id;
	p[(*len)-1]=(name&&(*name))?(long)stracpy(name):(long)NULL;
	return p;
}

static long js_upcall_get_frame_id(void *data);

/* finds all objects with name takhle_tomu_u_nas_nadavame
 * in fd and all it's subframes with rq==NULL
 * js_ctx is f_data_c of the accessing script
 */
static long *find_in_subframes(struct f_data_c *js_ctx, struct f_data_c *fd, long *pole_vole, int *n_items, unsigned char *takhle_tomu_u_nas_nadavame)
{
	struct f_data_c *ff;
	struct list_head *lff;
	struct form_control *f;
	struct list_head *lf;

	/* search frame */
	foreach(struct f_data_c, ff, lff, fd->subframes)
		if (ff->loc&&ff->loc->name&&!strcmp(cast_const_char ff->loc->name,cast_const_char takhle_tomu_u_nas_nadavame)&&jsint_can_access(js_ctx,ff))	/* to je on! */
			if (!(pole_vole=add_id(pole_vole,n_items,js_upcall_get_frame_id(ff))))return NULL;

	if (!(fd->f_data))goto a_je_po_ptakach;

#ifdef G
	if (F) {
		struct g_object_image *gi;
		struct list_head *lgi;
		/* search images */
		foreach(struct g_object_image, gi, lgi, fd->f_data->images) {
			if (gi->name&&!strcmp(cast_const_char gi->name, cast_const_char takhle_tomu_u_nas_nadavame))
				if (!(pole_vole=add_id(pole_vole,n_items,JS_OBJ_T_IMAGE+((gi->id)<<JS_OBJ_MASK_SIZE))))return NULL;
		}
	}
#endif
	/* search forms */
	foreachback(struct form_control, f, lf, fd->f_data->forms)
		if (f->form_name&&!strcmp(cast_const_char f->form_name,cast_const_char takhle_tomu_u_nas_nadavame))   /* tak tohle JE Jim Beam */
			if (!(pole_vole=add_id(pole_vole,n_items,((f->form_num)<<JS_OBJ_MASK_SIZE)+JS_OBJ_T_FORM)))return NULL;

	/* search form elements */
	foreachback(struct form_control, f, lf, fd->f_data->forms)
		if (f->name&&!strcmp(cast_const_char f->name,cast_const_char takhle_tomu_u_nas_nadavame))   /* tak tohle JE Jim Beam */
		{
			long tak_mu_to_ukaz=0;
			tak_mu_to_ukaz=(f->g_ctrl_num)<<JS_OBJ_MASK_SIZE;
			switch (f->type)
			{
				case FC_TEXT:		tak_mu_to_ukaz|=JS_OBJ_T_TEXT; break;
				case FC_PASSWORD:	tak_mu_to_ukaz|=JS_OBJ_T_PASSWORD; break;
				case FC_TEXTAREA:	tak_mu_to_ukaz|=JS_OBJ_T_TEXTAREA; break;
				case FC_CHECKBOX:	tak_mu_to_ukaz|=JS_OBJ_T_CHECKBOX; break;
				case FC_RADIO:		tak_mu_to_ukaz|=JS_OBJ_T_RADIO; break;
				case FC_IMAGE:
				case FC_SELECT:		tak_mu_to_ukaz|=JS_OBJ_T_SELECT; break;
				case FC_SUBMIT:		tak_mu_to_ukaz|=JS_OBJ_T_SUBMIT ; break;
				case FC_RESET:		tak_mu_to_ukaz|=JS_OBJ_T_RESET ; break;
				case FC_HIDDEN:		tak_mu_to_ukaz|=JS_OBJ_T_HIDDEN ; break;
				case FC_BUTTON:		tak_mu_to_ukaz|=JS_OBJ_T_BUTTON ; break;
				default: /* internal_error("Invalid form element type.\n"); */
				tak_mu_to_ukaz=0;break;
			}
			if (tak_mu_to_ukaz&&!(pole_vole=add_id(pole_vole,n_items,tak_mu_to_ukaz)))return NULL;
		}

a_je_po_ptakach:
	/* find in all rq==NULL */
	foreach(struct f_data_c, ff, lff, fd->subframes)
		if (!(ff->rq)) pole_vole=find_in_subframes(js_ctx,ff,pole_vole,n_items,takhle_tomu_u_nas_nadavame);


	return pole_vole;
}

/* resolves name of an object, returns field of all ID's with the name
 * obj_id is object in which we're searching
 * takhle_tomu_u_nas_nadavame is the searched name
 * context is identifier of the javascript context
 * n_items is number of returned items
 *
 * on error returns NULL
 */
long *jsint_resolve(void *context, long obj_id, char *takhle_tomu_u_nas_nadavame,int *n_items)
{
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)context;
	long *pole_vole;
	*n_items=0;

	if (!takhle_tomu_u_nas_nadavame||!(*takhle_tomu_u_nas_nadavame))return NULL;
	pole_vole=mem_alloc(sizeof(long));
	switch(jsint_object_type(obj_id))
	{
		/* searched object can be a frame, image, form or a form element */
		case JS_OBJ_T_DOCUMENT:
		case JS_OBJ_T_FRAME:
		fd=jsint_find_document(obj_id);
		if (!fd||!(jsint_can_access(js_ctx,fd)))break;

		pole_vole=find_in_subframes(js_ctx, fd, pole_vole, n_items, takhle_tomu_u_nas_nadavame);
		break;

		/* searched name can be a form element */
		case JS_OBJ_T_FORM:
		{
			struct form_control *fc=jsint_find_object(js_ctx,obj_id);
			struct form_control *f;
			struct list_head *lf;
			if (!fc){mem_free(pole_vole);return NULL;}

			if (!(js_ctx->f_data)){mem_free(pole_vole);return NULL;}
			foreachback(struct form_control, f, lf, js_ctx->f_data->forms)
			{
				if (f->form_num==fc->form_num)	/* this form */
					if (f->name&&!strcmp(cast_const_char f->name,cast_const_char takhle_tomu_u_nas_nadavame))   /* this IS Jim Beam */
					{
						long tak_mu_to_ukaz=0;
						tak_mu_to_ukaz=(f->g_ctrl_num)<<JS_OBJ_MASK_SIZE;
						switch (f->type)
						{
							case FC_TEXT:		tak_mu_to_ukaz|=JS_OBJ_T_TEXT; break;
							case FC_PASSWORD:	tak_mu_to_ukaz|=JS_OBJ_T_PASSWORD; break;
							case FC_TEXTAREA:	tak_mu_to_ukaz|=JS_OBJ_T_TEXTAREA; break;
							case FC_CHECKBOX:	tak_mu_to_ukaz|=JS_OBJ_T_CHECKBOX; break;
							case FC_RADIO:		tak_mu_to_ukaz|=JS_OBJ_T_RADIO; break;
							case FC_IMAGE:
							case FC_SELECT:		tak_mu_to_ukaz|=JS_OBJ_T_SELECT; break;
							case FC_SUBMIT:		tak_mu_to_ukaz|=JS_OBJ_T_SUBMIT ; break;
							case FC_RESET:		tak_mu_to_ukaz|=JS_OBJ_T_RESET ; break;
							case FC_HIDDEN:		tak_mu_to_ukaz|=JS_OBJ_T_HIDDEN ; break;
							case FC_BUTTON:		tak_mu_to_ukaz|=JS_OBJ_T_BUTTON ; break;
							default: tak_mu_to_ukaz=0;break;
							/* internal_error("Invalid form element type.\n"); */
						}
						if ((tak_mu_to_ukaz&JS_OBJ_MASK)&&!(pole_vole=add_id(pole_vole,n_items,tak_mu_to_ukaz)))return NULL;
					}
			}
		}
		break;
	}
	if (!pole_vole)return NULL;
	if (!(*n_items)){mem_free(pole_vole);pole_vole=NULL;}
	return pole_vole;
}

/*------------------------>>>>>>>> UPCALLS <<<<<<<<-------------------------*/


/* tyhle upcally se volaji ze select smycky:

	void js_upcall_confirm(void *data)
	void js_upcall_alert(void * data)
	void js_upcall_close_window(void *data)
	void js_upcall_get_string(void *data)
	void js_upcall_goto_url(void * data)
	void js_upcall_goto_history(void * data)
	void js_upcall_set_image_src(void* data)

V nich se musi volat js_spec_vykill_timer, aby se znicil timer, ktery upcall
zavolal.

Tyto upcally MUZOU dostavat f_data_c pointer primo, protoze kdyz ten f_data_c
umre a s nim i ten JS, tak se timery znicej --- tudiz se nic nestane.
*/


static void redraw_document(struct f_data_c *f)
{
	/*
	if (F) {
		f->xl = -1;
		f->yl = -1;
		draw_to_window(f->ses->win, draw_doc, f);
	}
	*/
	draw_fd(f);
}


/* returns ID of a document with the javascript */
long js_upcall_get_document_id(void *data)
{
	struct f_data_c *fd;
	if (!data)internal_error("js_upcall_get_document_id called with NULL pointer!");

	fd=(struct f_data_c*)data;
	return (((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT);
}


/* same as get_document_id, but returned type is FRAME */
static long js_upcall_get_frame_id(void *data)
{
	struct f_data_c *fd;
	if (!data)internal_error("js_upcall_get_document_id called with NULL pointer!");

	fd=(struct f_data_c*)data;
	return (((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_FRAME);
}


/* writes "len" bytes starting at "str" to document */
void js_upcall_document_write(void *p, unsigned char *str, int len)
{
	int pos;
	unsigned char *s;
	struct f_data_c *fd = p;
	struct js_state *js = fd->js;
	if (!js)return;
	if (!js->active) internal_error("js_upcall_document_write: no request active");
	if (js->active->write_pos == -1) return;
	if (js->active->write_pos < 0) internal_error("js_upcall_document_write: js->active trashed");
	if (!js->src) {
		size_t len;
		unsigned char *s;
		if (get_file(fd->rq, &s, &len)) return;
		if (len > MAXINT) return;
		js->src = memacpy(s, len);
		js->srclen = len;
	}
	if ((unsigned)js->srclen + (unsigned)len > MAXINT) overalloc();
	if ((unsigned)js->srclen + (unsigned)len < (unsigned)len) overalloc();
	s = mem_realloc(js->src, js->srclen + len);
	js->src = s;
	if ((pos = js->srclen - js->active->write_pos) < 0) pos = 0;
	memmove(s + pos + len, s + pos, js->srclen - pos);
	memcpy(s + pos, str, len);
	js->srclen += len;
	js->newdata += len;
	js_zaflaknuto_pameti += len;
	js->active->wrote = 1;
}


/* returns title of actual document (=document in the script context) */
/* when an error occurs, returns NULL */
/* returned string should be deallocated after use */
unsigned char *js_upcall_get_title(void *data)
{
	struct f_data_c *fd;
	unsigned char *title, *t;

	if (!data)internal_error("js_upcall_get_title called with NULL pointer!");
	fd=(struct f_data_c *)data;

	title=mem_alloc(MAX_STR_LEN);

	if (!(get_current_title(fd,title,MAX_STR_LEN))){mem_free(title);return NULL;}
	if (fd->f_data)
	{
		t = convert(fd->f_data->opt.cp, fd->f_data->cp, title, NULL);
		mem_free(title);
		title=t;
	}
	return title;
}


/* sets title of actual document (=document in the script context) */
/* string title will be deallocated after use */
void js_upcall_set_title(void *data, unsigned char *title)
{
	unsigned char *t;
	struct f_data_c *fd;
	int l=0;

	if (!data)internal_error("js_upcall_get_title called with NULL pointer!");
	fd=(struct f_data_c *)data;

	if (!title)return;

	if (!(fd->f_data)){mem_free(title);return;}
	if (fd->f_data->title)mem_free(fd->f_data->title);
	fd->f_data->title=init_str();
	fd->f_data->uncacheable=1;
	t = convert(fd->f_data->cp, fd->f_data->opt.cp, title, NULL);
	add_to_str(&(fd->f_data->title),&l,t);
	mem_free(t);

	mem_free(title);
	redraw_document(fd);
}


/* returns URL of actual document (=document in the script context) */
/* when an error occurs, returns NULL */
/* returned string should be deallocated after use */
unsigned char *js_upcall_get_location(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}
	return loc;
}


/* returns string containing last modification date */
/* or NULL when the date is not known or when an error occurs */
unsigned char *js_upcall_document_last_modified(void *data, long document_id)
{
	struct f_data_c *fd;
	struct f_data_c *document;
	unsigned char *retval;

	document=jsint_find_document(document_id);
	if (!data)internal_error("js_upcall_document_last_modified called with NULL pointer!");
	fd=(struct f_data_c *)data;

	if (!document)return NULL;  /* document not found */
	if (!jsint_can_access(fd, document))return NULL; /* you have no permissions to look at the document */

	if (!fd->rq||!fd->rq->ce)return NULL;
	retval=stracpy(fd->rq->ce->last_modified);

	return retval;
}


/* returns allocated string with user-agent */
unsigned char *js_upcall_get_useragent(void *data)
{
	/*struct f_data_c *fd;*/
	unsigned char *retval=init_str();
	int l=0;

	if (!data)internal_error("js_upcall_get_useragent called with NULL pointer!");
	/*fd=(struct f_data_c *)data;*/

	if (!http_options.header.fake_useragent||!(*http_options.header.fake_useragent)) {
		add_to_str(&retval, &l, "Links (" VERSION_STRING "; ");
		add_to_str(&retval, &l, system_name);
		add_to_str(&retval, &l, ")");
	}
	else {
		add_to_str(&retval, &l, http_options.header.fake_useragent);
	}

	return retval;
}


/* returns allocated string with browser name */
unsigned char *js_upcall_get_appname(void)
{
	if (!http_options.header.fake_useragent||!(*http_options.header.fake_useragent))
		return stracpy("Links");
	else
		return stracpy(http_options.header.fake_useragent);
}


/* returns allocated string with browser name */
unsigned char *js_upcall_get_appcodename(void)
{
	if (!http_options.header.fake_useragent||!(*http_options.header.fake_useragent))
		return stracpy("Links");
	else
		return stracpy(http_options.header.fake_useragent);
}


/* returns allocated string with browser version: "version_number (system_name)" */
unsigned char *js_upcall_get_appversion(void)
{
	unsigned char *str;
	int l=0;

	if (http_options.header.fake_useragent&&(*http_options.header.fake_useragent))return stracpy(http_options.header.fake_useragent);
	str=init_str();
	add_to_str(&str,&l,VERSION_STRING);
	add_to_str(&str,&l," (");
	add_to_str(&str,&l,system_name);
	add_to_str(&str,&l,")");
	return str;
}


/* returns allocated string with referrer */
unsigned char *js_upcall_get_referrer(void *data)
{
	struct f_data_c *fd;
	unsigned char *retval=init_str();
	unsigned char *loc;
	int l=0;

	if (!data)internal_error("js_upcall_get_referrer called with NULL pointer!");
	fd=(struct f_data_c *)data;

	switch (http_options.header.referer)
	{
		case REFERER_FAKE:
		add_to_str(&retval, &l, http_options.header.fake_referer);
		break;

		case REFERER_SAME_URL:
		loc=mem_alloc(MAX_STR_LEN);
		if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);break;}
		add_to_str(&retval, &l, loc);
		mem_free(loc);
		break;

		case REFERER_REAL:
		{
			unsigned char *post;

			if (!fd->rq||!(fd->rq->prev_url))break;   /* no referrer */
			post=strchr(fd->rq->prev_url, POST_CHAR);
			if (!post)add_to_str(&retval, &l, fd->rq->prev_url);
			else add_bytes_to_str(&retval, &l, fd->rq->prev_url, post - fd->rq->prev_url);
		}
		break;
	}

	return retval;
}

struct gimme_js_id
{
	long id; /* id of f_data_c */
	long js_id; /* unique id of javascript */
};

/* tady se netestuje js_id, protoze BFU to chce killnout, tak to proste killne */
/* aux function for all dialog upcalls */
static void js_kill_script_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js))return;
	js_downcall_game_over(fd->js->ctx);   /* call downcall */
}



/* aux function for js_upcall_confirm */
static void js_upcall_confirm_ok_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_true(fd->js->ctx);   /* call downcall */
}


/* aux function for js_upcall_confirm */
static void js_upcall_confirm_cancel_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_false(fd->js->ctx);   /* call downcall */
}


/* creates dialog with text s->string and buttons OK/Cancel */
/* s->string will be dealocated */
/* s will be dealocated too */
/* must be called from select loop */
void js_upcall_confirm(void *data)
{
	struct fax_me_tender_string *s=(struct fax_me_tender_string*)data;
	struct gimme_js_id* jsid;
	struct f_data_c *fd;
	struct terminal *term;
	unsigned char *txt;

	if (!s)internal_error("js_upcall_confirm called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd=(struct f_data_c*)(s->ident);
	term=fd->ses->term;

	if (!fd->js)return;
	jsid=mem_alloc(sizeof(struct gimme_js_id));

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	/* fill in jsid */
	jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
	jsid->js_id=fd->js->ctx->js_id;

	skip_nonprintable(s->string);
	if (fd->f_data)
	{
		txt=convert(fd->f_data->cp, fd->f_data->opt.cp, s->string, NULL);
	}
	else
		txt=stracpy(s->string);
	js_mem_free(s->string);
	msg_box(
		term,   /* terminal */
		getml(txt,jsid,NULL),   /* memory blocks to free */
		TEXT_(T_QUESTION),   /* title */
		AL_CENTER,   /* alignment */
		txt,MSG_BOX_END,   /* message */
		(void *)jsid,   /* data for button functions */
		3,   /* # of buttons */
		TEXT_(T_OK),js_upcall_confirm_ok_pressed,B_ENTER,  /* first button */
		TEXT_(T_CANCEL),js_upcall_confirm_cancel_pressed,B_ESC,  /* second button */
		TEXT_(T_KILL_SCRIPT), js_kill_script_pressed,NULL
	);

	js_mem_free(s);
}


/* aux function for js_upcall_alert */
static void js_upcall_alert_ok_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* gets struct fax_me_tender_string* */
/* creates dialog with title "Alert" and message got from struct fax_me_tender_string */
/* structure and the text are both deallocated */
/* must be called from select loop */
void js_upcall_alert(void * data)
{
	struct fax_me_tender_string *s=(struct fax_me_tender_string*)data;
	struct gimme_js_id* jsid;
	struct f_data_c *fd;
	struct terminal *term;
	unsigned char *txt;

	if (!s)internal_error("Alert called with NULL pointer.\n"); /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd=(struct f_data_c*)(s->ident);
	term=fd->ses->term;

	if (!fd->js) return;
	jsid=mem_alloc(sizeof(struct gimme_js_id));

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	/* fill in jsid */
	jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
	jsid->js_id=fd->js->ctx->js_id;

	skip_nonprintable(s->string);
	if (fd->f_data)
	{
		txt=convert(fd->f_data->cp, fd->f_data->opt.cp, s->string, NULL);
	}
	else
		txt=stracpy(s->string);
	js_mem_free(s->string);
	msg_box(
		term,   /* terminal */
		getml(txt,jsid,NULL),   /* memory blocks to free */
		TEXT_(T_ALERT),   /* title */
		AL_CENTER,   /* alignment */
		txt,MSG_BOX_END,   /* message */
		(void *)jsid,   /* data for button functions */
		2,   /* # of buttons */
		TEXT_(T_OK),js_upcall_alert_ok_pressed,B_ENTER|B_ESC,
		TEXT_(T_KILL_SCRIPT), js_kill_script_pressed,NULL
	);

	js_mem_free(s);
}


/* aux function for js_upcall_close_window */
/* tady se netestuje js_id, protoze BFU zmacklo, ze chce zavrit okno a v
 * nekterych pripadech by ho to nezavrelo (kdyby se testovalo) a to by vypadalo
 * blbe */
static void js_upcall_close_window_yes_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	really_exit_prog(fd->ses);
}


/* asks user if he really wants to close the window and calls really_exit_prog */
/* argument is struct fax_me_tender_nothing* */
/* must be called from select loop */
void js_upcall_close_window(void *data)
{
	struct fax_me_tender_nothing *s=(struct fax_me_tender_nothing*)data;
	struct f_data_c *fd;
	struct terminal *term;

	if (!s)internal_error("js_upcall_close_window called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd=(struct f_data_c*)(s->ident);
	if (!fd->js) return;
	term=fd->ses->term;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	if (js_manual_confirmation)
	{
		struct gimme_js_id* jsid;

		jsid=mem_alloc(sizeof(struct gimme_js_id));

		/* fill in jsid */
		jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
		jsid->js_id=fd->js->ctx->js_id;

		msg_box(
			term,   /* terminal */
			getml(jsid,NULL),   /* memory blocks to free */
			TEXT_(T_EXIT_LINKS),   /* title */
			AL_CENTER,   /* alignment */
			TEXT_(T_SCRIPT_TRYING_TO_CLOSE_WINDOW),MSG_BOX_END,   /* message */
			(void *)jsid,   /* data for button functions */
			2,   /* # of buttons */
			TEXT_(T_YES),js_upcall_close_window_yes_pressed,NULL,
			TEXT_(T_KILL_SCRIPT), js_kill_script_pressed,NULL
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		if (term->list_entry.next == term->list_entry.prev && are_there_downloads())
			query_exit(fd->ses);
		else
			really_exit_prog(fd->ses);
	}
}


/* returns parent window ID of the script */
long js_upcall_get_window_id(void *data)
{
	struct f_data_c *fd;
	if (!data)internal_error("js_upcall_get_window_id called with NULL pointer!");

	fd=(struct f_data_c*)data;
	return ((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_FRAME;
}



/* aux function for js_upcall_get_string */
static void js_upcall_get_string_ok_pressed(void *data, unsigned char *str)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_string(fd->js->ctx, stracpy(str));   /* call downcall */
}

static void js_upcall_get_string_kill_script_pressed(void *data, unsigned char *str)
{
	js_kill_script_pressed(data);
}

struct history js_get_string_history={0, {&js_get_string_history.items, &js_get_string_history.items}};


/* creates input field for string, with text s->string1, default response
 * s->string2 and buttons OK/Kill Script
 * s->string1 and s->string2 will be dealocated
 * s will be dealocated too
 * must be called from select loop */

void js_upcall_get_string(void *data)
{
	struct fax_me_tender_2_stringy *s=(struct fax_me_tender_2_stringy*)data;
	struct gimme_js_id* jsid;
	struct f_data_c *fd;
	struct terminal *term;
	unsigned char *str1,*str2;

	if (!s)internal_error("js_upcall_get_string called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd=(struct f_data_c*)(s->ident);
	term=fd->ses->term;

	if (!fd->js) return;
	jsid=mem_alloc(sizeof(struct gimme_js_id));

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	/* fill in jsid */
	jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
	jsid->js_id=fd->js->ctx->js_id;

	str1=stracpy(s->string1);
	str2=stracpy(s->string2);
	js_mem_free(s->string1);
	js_mem_free(s->string2);

	input_field(
		term,   /* terminal */
		getml(str1, str2,jsid,NULL),   /* mem to free */
		TEXT_(T_ENTER_STRING),  /* title */
		str1,   /* question */
		jsid,   /* data for functions */
		&js_get_string_history,   /* history */
		MAX_INPUT_URL_LEN,   /* string len */
		str2,  /* string to fill the dialog with */
		0,  /* min value */
		0,  /* max value */
		NULL,  /* check fn */
		2,  /* the number of buttons */
		TEXT_(T_OK),   /* ok button */
		js_upcall_get_string_ok_pressed,
		TEXT_(T_KILL_SCRIPT),  /* cancel button */
		js_upcall_get_string_kill_script_pressed
	);
	js_mem_free(s);
}


/* clears window with javascript */
/* must be called from select loop */
/* javascript must halt before calling this upcall */
void js_upcall_clear_window(void *data)
{
	/* context must be a valid pointer ! */
	/*struct f_data_c *fd=(struct f_data_c*)data;*/
	/* no jsint_destroy context or so here, it's called automatically from reinit_f_data_c */
	/*
	zatim jsem to zrusil ... tahle funkce musi byt volana pres timer, takhle je to uplne blbe a spadne to pri kazdem volani -- Mikulas

	to je <|>vina, v komentari je jasne napsano, ze tahle fce musi byt volana ze select loop, takze to nema co padat -- Brain

	no prave!! ze select smycky == z timeru. Z javascriptu to volat nemuzes, protoze to pod sebou ten kontext javascriptu smaze, a ten interpret spadne, protoze jeho kontext uz nebude existovat. -- Mikulas

	reinit_f_data_c(fd);
	*/
}


/* returns allocated string with window name */
unsigned char *js_upcall_get_window_name(void *data)
{
	/* context must be a valid pointer ! */
	struct f_data_c *fd=(struct f_data_c*)data;

	return fd->loc?stracpy(fd->loc->name):NULL;
}


/* returns allocated field of ID's of links in JS document
 * number of links is stored in len
 * if number of links is 0, returns NULL
 * on error returns NULL too
 */
long *js_upcall_get_links(void *data, long document_id, int *len)
{
	struct f_data_c *js_ctx=(struct f_data_c*)data;
	struct f_data_c *fd;
	/*struct link *l;*/
	int a;
	long *to_je_Ono;

	fd=jsint_find_document(document_id);
	if (!js_ctx)internal_error("js_upcall_get_links called with NULL context pointer\n");
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;
	if (!(fd->f_data))return NULL;
	*len=fd->f_data->nlinks;
	if (!(*len))return NULL;
	/*l=fd->f_data->links;*/
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	to_je_Ono=mem_alloc((*len)*sizeof(long));

	for (a=0;a<(*len);a++)
		/*to_je_Ono[a]=JS_OBJ_T_LINK+(((l+a)->num)<<JS_OBJ_MASK_SIZE);*/
		to_je_Ono[a]=JS_OBJ_T_LINK+(a<<JS_OBJ_MASK_SIZE);

	return to_je_Ono;
}


/* returns allocated string with TARGET of the link
 * if the link doesn't exist in the document, returns NULL
 */
unsigned char *js_upcall_get_link_target(void *data, long document_id, long link_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)data;
	struct f_data_c *fd;
	struct link *l;

	if (!js_ctx)internal_error("js_upcall_get_link_target called with NULL context pointer\n");
	if ((link_id&JS_OBJ_MASK)!=JS_OBJ_T_LINK)return NULL;   /* this isn't link */

	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	l=jsint_find_object(fd,link_id);
	if (!l)return NULL;

	return stracpy((l->target)?(l->target):(unsigned char *)(""));
}


/* returns allocated field of ID's of forms in JS document
 * number of forms is stored in len
 * if number of forms is 0, returns NULL
 * on error returns NULL too
 */
long *js_upcall_get_forms(void *data, long document_id, int *len)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc;
	struct list_head *lfc;
	long *to_je_Ono;
	long last=0;

	if (!js_ctx)internal_error("js_upcall_get_forms called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if (!(fd->f_data))return NULL;

	to_je_Ono=mem_alloc(sizeof(long));

	*len=0;

	foreachback(struct form_control, fc, lfc, fd->f_data->forms) {
		long *p;
		int a;

		if ((*len)&&(fc->form_num)==last)continue;
		for (a=0;a<(*len);a++)
			if ((to_je_Ono[a]>>JS_OBJ_MASK_SIZE)==(fc->form_num))goto already_have;  /* we already have this number */

		(*len)++;
		if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
		p=mem_realloc(to_je_Ono,(*len)*sizeof(long));
		to_je_Ono=p;
		to_je_Ono[(*len)-1]=JS_OBJ_T_FORM|((fc->form_num)<<JS_OBJ_MASK_SIZE);
		last=fc->form_num;
already_have:;
	}

	if (!(*len)){mem_free(to_je_Ono);to_je_Ono=NULL;}

	return to_je_Ono;
}


/* returns allocated string with the form action
 * when an error occurs, returns NULL
 */
unsigned char *js_upcall_get_form_action(void *data, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc;

	if (!js_ctx)internal_error("js_upcall_get_form_action called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return NULL;   /* this isn't form */

	fc=jsint_find_object(fd,form_id);
	if (!fc)return NULL;

	return stracpy(fc->action);
}



/* sets form action
 */
void js_upcall_set_form_action(void *context, long document_id, long form_id, unsigned char *action)
{
	struct f_data_c *js_ctx=(struct f_data_c *)context;
	struct f_data_c *fd;
	struct form_control *fc;

	if (!js_ctx) {
		internal_error("js_upcall_set_form_action called with NULL context pointer\n");
	}

	fd=jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx,fd)) {
		if (action) mem_free(action);
		return;
	}

	if ( (form_id&JS_OBJ_MASK) != JS_OBJ_T_FORM ) {
		if (action) mem_free(action);
		return;
	}

	fc=jsint_find_object(fd,form_id);
	if (!fc) {
		if (action) mem_free(action);
		return;
	}

	if (fc->action) {
		mem_free (fc->action);
		if ( fd->loc && fd->loc->url )
			fc->action=join_urls(fd->loc->url,action);
		else
			fc->action=stracpy(action);
		fd->f_data->uncacheable=1;
	}

	if (action) {
		mem_free(action);
	}
}


/* returns allocated string with the form target
 * when an error occurs, returns NULL
 */
unsigned char *js_upcall_get_form_target(void *data, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc;

	if (!js_ctx)internal_error("js_upcall_get_form_target called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return NULL;   /* this isn't form */

	fc=jsint_find_object(fd,form_id);
	if (!fc)return NULL;

	return stracpy(fc->target);
}



/* returns allocated string with the form method
 * when an error occurs, returns NULL
 */
unsigned char *js_upcall_get_form_method(void *data, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc;

	if (!js_ctx)internal_error("js_upcall_get_form_method called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return NULL;   /* this isn't form */

	fc=jsint_find_object(fd,form_id);
	if (!fc)return NULL;

	switch (fc->method)
	{
		case FM_GET:
		return stracpy("GET");

		case FM_POST:
		case FM_POST_MP:
		return stracpy("POST");

		default:
		internal_error("Invalid form method!\n");
		return NULL;  /* never called, but GCC likes it */
	}
}



/* returns allocated string with the form encoding (value of attribute enctype)
 * when an error occurs, returns NULL
 */
unsigned char *js_upcall_get_form_encoding(void *data, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc;

	if (!js_ctx)internal_error("js_upcall_get_form_encoding called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return NULL;   /* this isn't form */

	fc=jsint_find_object(fd,form_id);
	if (!fc)return NULL;

	switch (fc->method)
	{
		case FM_GET:
		case FM_POST:
		return stracpy("application/x-www-form-urlencoded");

		case FM_POST_MP:
		return stracpy("multipart/form-data");

		default:
		internal_error("Invalid form method!\n");
		return NULL;  /* never called, but GCC likes it */
	}
}


/* returns allocated string containing protocol from current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_protocol(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *p;
	int l;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	if (parse_url(loc, &l, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)){mem_free(loc);return NULL;}
	p=memacpy(loc,l+1);   /* l is pointing to the colon, but we want protocol with colon */
	mem_free(loc);
	return p;
}


/* returns allocated string containing port of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_port(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *p;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	p=get_port_str(loc);
	mem_free(loc);
	return p;
}


/* returns allocated string containing hostname of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_hostname(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *p;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	p=get_host_name(loc);
	mem_free(loc);
	return p;
}


/* returns allocated string containing hostname and port of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_host(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *p, *h;
	int l1,l2;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, &h, &l1, NULL, &l2, NULL, NULL, NULL)){mem_free(loc);return NULL;}
	p=memacpy(h,l1+l2);
	mem_free(loc);
	return p;
}


/* returns allocated string containing pathname of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_pathname(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *d, *p;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL)){mem_free(loc);return NULL;}
	if (!d){mem_free(loc);return NULL;}
	p=memacpy(d,strcspn(d,"?"));
	mem_free(loc);
	return p;
}


/* returns allocated string containing everything after ? in current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_search(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *d, *p;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL)){mem_free(loc);return NULL;}
	if (!d){mem_free(loc);return NULL;}
	p=stracpy(strchr(d,'?'));
	mem_free(loc);
	return p;
}


/* returns allocated string containing everything between # and ? in current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char *js_upcall_get_location_hash(void *data)
{
	struct f_data_c *fd;
	unsigned char *loc;
	unsigned char *d, *p;

	if (!data)internal_error("js_upcall_get_location called with NULL pointer!");
	fd=(struct f_data_c *)data;

	loc=mem_alloc(MAX_STR_LEN);

	if (!(get_current_url(fd->ses,loc,MAX_STR_LEN))){mem_free(loc);return NULL;}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL)){mem_free(loc);return NULL;}
	if (!d){mem_free(loc);return NULL;}
	d=strchr(d,'#');
	if (!d){mem_free(loc);return NULL;}
	d++;
	p=memacpy(d,strcspn(d,"?"));
	mem_free(loc);
	return p;
}


/* returns allocated field of all form elements
 * size of the field will be stored in len
 * when an error occurs, returns NULL
 */
long *js_upcall_get_form_elements(void *data, long document_id, long form_id, int *len)
{
	struct f_data_c *js_ctx=(struct f_data_c *)data;
	struct f_data_c *fd;
	struct form_control *fc, *fc2;
	struct list_head *lfc2;
	long *pole_Premysla_Zavorace;
	int b;

	if (!js_ctx)internal_error("js_upcall_get_form_elements called with NULL context pointer\n");
	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return NULL;   /* this isn't form */
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return NULL;

	fc=jsint_find_object(fd,form_id);
	if (!fc)return NULL;

	*len=0;

	foreach(struct form_control, fc2, lfc2, fd->f_data->forms)
		if (fc2->form_num==fc->form_num)(*len)++;

	if (!(*len))return NULL;

	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	pole_Premysla_Zavorace=mem_alloc((*len)*sizeof(long));

	b=0;
	foreachback(struct form_control, fc2, lfc2, fd->f_data->forms)
		if (fc2->form_num==fc->form_num)
		{
			switch (fc2->type)
			{
				case FC_TEXT:		pole_Premysla_Zavorace[b]=JS_OBJ_T_TEXT; break;
				case FC_PASSWORD:	pole_Premysla_Zavorace[b]=JS_OBJ_T_PASSWORD; break;
				case FC_TEXTAREA:	pole_Premysla_Zavorace[b]=JS_OBJ_T_TEXTAREA; break;
				case FC_CHECKBOX:	pole_Premysla_Zavorace[b]=JS_OBJ_T_CHECKBOX; break;
				case FC_RADIO:		pole_Premysla_Zavorace[b]=JS_OBJ_T_RADIO; break;
				case FC_IMAGE:
				case FC_SELECT:		pole_Premysla_Zavorace[b]=JS_OBJ_T_SELECT; break;
				case FC_SUBMIT:		pole_Premysla_Zavorace[b]=JS_OBJ_T_SUBMIT ; break;
				case FC_RESET:		pole_Premysla_Zavorace[b]=JS_OBJ_T_RESET ; break;
				case FC_HIDDEN:		pole_Premysla_Zavorace[b]=JS_OBJ_T_HIDDEN ; break;
				case FC_BUTTON:		pole_Premysla_Zavorace[b]=JS_OBJ_T_BUTTON ; break;
				default: /* internal_error("Invalid form element type.\n"); */
				(*len)--;
				continue;
			}
			pole_Premysla_Zavorace[b]|=((fc2->g_ctrl_num)<<JS_OBJ_MASK_SIZE);
			b++;
		}
	return pole_Premysla_Zavorace;
}


/* returns allocated field with anchors
 * size of the field is stored in len
 * when there're no anchors, *len is 0 and NULL is returned
 * on error NULL is returned
 */
long *js_upcall_get_anchors(void *hej_Hombre, long document_id, int *len)
{
	struct f_data_c *js_ctx=(struct f_data_c*)hej_Hombre;
	struct f_data_c *fd;
	int a;
	long *to_je_Ono;
	*len=0;

	if (!js_ctx)internal_error("js_upcall_get_anchors called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	if (!(fd->f_data))return NULL;
	*len = (int)list_size(&fd->f_data->tags);
	if (!*len) return NULL;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	to_je_Ono = mem_alloc((*len)*sizeof(long));

	for (a = 0; a < *len; a++)
		to_je_Ono[a] = JS_OBJ_T_ANCHOR + (a << JS_OBJ_MASK_SIZE);
	return to_je_Ono;

}


/* returns whether radio or checkbox is checked
 * return value: 0=not checked
 *               1=checked
 *              -1=error
 */
int js_upcall_get_checkbox_radio_checked(void *smirak, long document_id, long radio_tv_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)smirak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	int state;

	if (!js_ctx)internal_error("js_upcall_get_checkbox_radio_checked called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;

	if ((radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_RADIO&&(radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_CHECKBOX)return -1;   /* this isn't radio nor TV */

	hopla=jsint_find_object(fd,radio_tv_id);
	if (!hopla)return -1;

	state=hopla->fs->state;
	mem_free(hopla);
	return state;
}


/* checks/unchecks radio or checkbox
 */
void js_upcall_set_checkbox_radio_checked(void *smirak, long document_id, long radio_tv_id, int value)
{
	struct f_data_c *js_ctx=(struct f_data_c*)smirak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;

	if (!js_ctx)internal_error("js_upcall_set_checkbox_radio_checked called with NULL context pointer\n");
	if ((radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_RADIO&&(radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_CHECKBOX)return;   /* this isn't radio nor TV */
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;

	hopla=jsint_find_object(fd,radio_tv_id);
	if (!hopla)return;

	hopla->fs->state=!!value;
	mem_free(hopla);
	redraw_document(fd);
}


/* returns whether radio or checkbox is checked
 * return value: 0=default not checked
 *		 1=default checked
 *		-1=error
 */
int js_upcall_get_checkbox_radio_default_checked(void *bidak_smirak, long document_id, long radio_tv_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak_smirak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	int default_checked;

	if (!js_ctx)internal_error("js_upcall_get_checkbox_radio_default_checked called with NULL context pointer\n");
	if ((radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_RADIO&&(radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_CHECKBOX)return -1;   /* this isn't radio nor TV */
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;

	hopla=jsint_find_object(fd,radio_tv_id);
	if (!hopla)return -1;

	default_checked=hopla->fc->default_state;
	mem_free(hopla);
	return default_checked;
}


/* sets radio/checkbox default_checked in the form
 */
void js_upcall_set_checkbox_radio_default_checked(void *bidak_smirak, long document_id, long radio_tv_id, int value)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak_smirak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	int something_changed;
	value=!!value;

	if (!js_ctx)internal_error("js_upcall_set_checkbox_radio_default_checked called with NULL context pointer\n");
	if ((radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_RADIO&&(radio_tv_id&JS_OBJ_MASK)!=JS_OBJ_T_CHECKBOX)return;   /* this isn't radio nor TV */
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return;

	hopla=jsint_find_object(fd,radio_tv_id);
	if (!hopla)return;

	something_changed=(hopla->fc->default_state)^value;
	hopla->fc->default_state=value;
	fd->f_data->uncacheable|=something_changed;
	mem_free(hopla);
}


/* returns allocated string with name of the form element
 * don't forget to free the string after use
 * on error returns NULL
 */
unsigned char *js_upcall_get_form_element_name(void *bidak, long document_id, long ksunt_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	unsigned char *hele_ho_bidaka;

	if (!js_ctx)internal_error("js_upcall_get_form_element_name called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
		break;

		default:
		return NULL;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla)return NULL;

	hele_ho_bidaka=stracpy(hopla->fc->name);
	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets name of the form element
 * name is allocated string, this function deallocates it
 */
void js_upcall_set_form_element_name(void *bidak, long document_id, long ksunt_id, unsigned char *name)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;

	if (!js_ctx)internal_error("js_upcall_set_form_element_name called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd)){if (name)mem_free(name);return;}

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
		break;

		default:
		if(name) mem_free(name);
		return;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla){if (name)mem_free(name);return;}

	if ((name||(hopla->fc->name))&&strcmp(cast_const_char name,cast_const_char hopla->fc->name))
	{
		mem_free(hopla->fc->name);
		hopla->fc->name=stracpy(name);
		fd->f_data->uncacheable=1;
	}
	mem_free(hopla);
	if(name) mem_free(name);
}


/* returns allocated string with value of VALUE attribute of the form element
 * on error returns NULL
 * don't forget to free the string after use
 */
unsigned char *js_upcall_get_form_element_default_value(void *bidak, long document_id, long ksunt_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	unsigned char *hele_ho_bidaka;

	if (!js_ctx)internal_error("js_upcall_get_form_element_default_value called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return NULL;

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		break;

		default:
		return NULL;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla)return NULL;

	hele_ho_bidaka=convert(fd->f_data->opt.cp, fd->f_data->cp, hopla->fc->default_value, NULL);

	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets attribute VALUE of the form element
 * name is allocated string that, this function frees it
 * when name is NULL default value will be empty
 */
void js_upcall_set_form_element_default_value(void *bidak, long document_id, long ksunt_id, unsigned char *name)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;

	if (!js_ctx)internal_error("js_upcall_set_form_element_default_value called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd)){if (name)mem_free(name);return;}

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		break;

		default:
		if (name)mem_free(name);
		return;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla){if (name)mem_free(name);return;}

	if ((name||(hopla->fc->default_value))&&strcmp(cast_const_char name,cast_const_char hopla->fc->default_value))
	{
		mem_free(hopla->fc->default_value);
		hopla->fc->default_value=convert(fd->f_data->cp, fd->f_data->opt.cp, name, NULL);
		fd->f_data->uncacheable=1;
	}
	mem_free(hopla);
	if (name)mem_free(name);
}

static unsigned char **get_js_event_ptr(struct js_event_spec **j, long type)
{
	create_js_event_spec(j);
	if (type == Conkeydown) return &(*j)->keydown_code;
	else if (type == Conkeypress) return &(*j)->keypress_code;
	else if (type == Conkeyup) return &(*j)->keyup_code;
	else return NULL;
}

void js_upcall_set_form_element_event_handler(void *bidak, long document_id, long ksunt_id, long type, unsigned char *name)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	int i;

	if (!js_ctx)internal_error("js_upcall_set_form_element_event_handler called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd)){if (name)mem_free(name);return;}
	if ((ksunt_id&JS_OBJ_MASK) == JS_OBJ_T_FRAME || (ksunt_id&JS_OBJ_MASK) == JS_OBJ_T_DOCUMENT) {
		unsigned char **p = get_js_event_ptr(&fd->f_data->js_event, type);
		if (!p) {
			mem_free(name);
			return;
		}
		if (*p) mem_free(*p);
		*p = name;
		fd->f_data->uncacheable=1;
		return;
	}
	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla){if (name)mem_free(name);return;}
	for (i = 0; i < fd->f_data->nlinks; i++) {
		struct link *l = &fd->f_data->links[i];
		if (l->form == hopla->fc) {
			unsigned char **p = get_js_event_ptr(&l->js_event, type);
			mem_free(hopla);
			if (!p) {
				mem_free(name);
				return;
			}
			if (*p) mem_free(*p);
			*p = name;
			fd->f_data->uncacheable=1;
			return;
		}
	}
	mem_free(hopla);
	mem_free(name);
}


/* returns allocated string with actual value of password, text or textarea element
 * on error returns NULL
 * don't forget to free the string after use
 */
unsigned char *js_upcall_get_form_element_value(void *bidak, long document_id, long ksunt_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	unsigned char *hele_ho_bidaka;

	if (!js_ctx)internal_error("js_upcall_get_form_element_value called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return NULL;

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		break;

		default:
		return NULL;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla)return NULL;

	hele_ho_bidaka=convert(fd->f_data->opt.cp, fd->f_data->cp, hopla->fs->string, NULL);

	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets actual value of password, text or textarea element
 * name is allocated string that, this function frees it
 * when name is NULL default value will be empty
 */
void js_upcall_set_form_element_value(void *bidak, long document_id, long ksunt_id, unsigned char *name)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;

	if (!js_ctx)internal_error("js_upcall_set_form_element_value called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd)){if (name)mem_free(name);return;}

	fd=jsint_find_document(document_id);

	switch (ksunt_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		break;

		default:
		if (name)mem_free(name);
		return;   /* To neni Jim Beam! */
	}

	hopla=jsint_find_object(fd,ksunt_id);
	if (!hopla){if (name)mem_free(name);return;}

	free_format_text_cache_entry(hopla->fs);
	mem_free(hopla->fs->string);
	hopla->fs->string=convert(fd->f_data->cp, fd->f_data->opt.cp, name, NULL);

	if ((size_t)hopla->fs->state > strlen(cast_const_char hopla->fs->string))
		hopla->fs->state = strlen(cast_const_char hopla->fs->string);
	if ((ksunt_id&JS_OBJ_MASK) != JS_OBJ_T_TEXTAREA) {
		if ((size_t)hopla->fs->vpos > strlen(cast_const_char hopla->fs->string))
			hopla->fs->vpos = strlen(cast_const_char hopla->fs->string);
	}
	mem_free(hopla);
	if (name)mem_free(name);
	redraw_document(fd);
}


/* emulates click on everything */
void js_upcall_click(void *bidak, long document_id, long elem_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;

	if (!js_ctx)internal_error("js_upcall_click called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;

	switch (elem_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_BUTTON:
		{
			struct hopla_mladej *hopla;
			int a;
			struct link *l;

			if (!fd->f_data)return;
			hopla=jsint_find_object(fd,elem_id);
			if (!hopla)return;

			for (a=0;a<fd->f_data->nlinks;a++)
			{
				l=&(fd->f_data->links[a]);
				if (l->form&&l->form==hopla->fc)	/* to je on! */
				{
					int old_link=fd->vs->current_link;
					int old_orig_link=fd->vs->orig_link;
					fd->vs->current_link=a;
					fd->vs->orig_link=a;
					enter(fd->ses,fd,0);
					draw_fd(fd);
					fd->vs->current_link=old_link;
					fd->vs->orig_link=old_orig_link;
					change_screen_status(fd->ses);
					print_screen_status(fd->ses);
					break;
				}
			}
			mem_free(hopla);
		}
		break;
	}
}

#ifdef G
static int find_go_link_num;
static struct g_object *to_je_on_bidak;
static void find_go(struct g_object *p, struct g_object *c)
{
	if (c->draw == g_text_draw) {
		struct g_object_text *t = get_struct(c, struct g_object_text, goti.go);
		if (t->goti.link_num==find_go_link_num){to_je_on_bidak=c;return;}
	}
	if (c->get_list) c->get_list(c, find_go);
}
#endif

/* emulates focus on password, text and textarea */
void js_upcall_focus(void *bidak, long document_id, long elem_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;

	if (!js_ctx)internal_error("js_upcall_focus called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;

	switch (elem_id&JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		{
			struct hopla_mladej *hopla;
			int a;
			struct link *l;

			if (!fd->f_data)return;
			hopla=jsint_find_object(fd,elem_id);
			if (!hopla)return;

			for (a=0;a<fd->f_data->nlinks;a++)
			{
				l=&(fd->f_data->links[a]);
				if (l->form&&l->form==hopla->fc)	/* to je on! */
				{
					struct session *ses = fd->ses;
					/*int x = 0;*/
					while (fd != current_frame(ses)) next_frame(ses, 1)/*, x = 1*/;
					fd->vs->current_link=a;
					fd->vs->orig_link=a;
					if (fd->ses->term->spec->braille) {
						if (fd->f_data->links[a].n) {
							fd->vs->brl_x = fd->vs->orig_brl_x = fd->f_data->links[a].pos[0].x;
							fd->vs->brl_y = fd->vs->orig_brl_y = fd->f_data->links[a].pos[0].y;
						}
					}
#ifdef G
					if (F)
					{
						fd->ses->locked_link=1;
						to_je_on_bidak=NULL;
						find_go_link_num=a;

						/* tak tedka tu budu carovat g_object_text, kterej patri k tomuhle linku */
						if (fd->f_data->root->get_list)fd->f_data->root->get_list(fd->f_data->root,find_go);
						fd->f_data->locked_on=to_je_on_bidak;
					}
#endif
					if (l->js_event&&l->js_event->focus_code)
						jsint_execute_code(fd,l->js_event->focus_code,strlen(cast_const_char l->js_event->focus_code),-1,-1,-1, NULL);

					/*draw_fd(fd);*/
					draw_formatted(ses);
					change_screen_status(fd->ses);
					print_screen_status(fd->ses);
					break;
				}
			}
			mem_free(hopla);
		}
		break;
	}
}

/* emulates focus on password, text and textarea */
void js_upcall_blur(void *bidak, long document_id, long elem_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;

	if (!js_ctx)internal_error("js_upcall_blur called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;

	/* in text mode do nothing, because we don't know where to go with cursor */
#ifdef G
	if (F)
		switch (elem_id&JS_OBJ_MASK)
		{
			case JS_OBJ_T_TEXT:
			case JS_OBJ_T_PASSWORD:
			case JS_OBJ_T_TEXTAREA:
			{
				struct hopla_mladej *hopla;
				int a;
				struct link *l;

				if (!fd->f_data)return;
				hopla=jsint_find_object(fd,elem_id);
				if (!hopla)return;

				for (a=0;a<fd->f_data->nlinks;a++)
				{
					l=&(fd->f_data->links[a]);
					if (l->form&&l->form==hopla->fc)	/* to je on! */
					{
						fd->ses->locked_link=0;
						if (l->js_event&&l->js_event->blur_code)
							jsint_execute_code(fd,l->js_event->blur_code,strlen(cast_const_char l->js_event->blur_code),-1,-1,-1, NULL);

						/* pro jistotu */
						draw_fd(fd);
						change_screen_status(fd->ses);
						print_screen_status(fd->ses);
						break;
					}
				}
				mem_free(hopla);
			}
			break;
		}
#endif
}

/* emulates submit of a form */
void js_upcall_submit(void *bidak, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;
	struct form_control *form;
	int has_onsubmit;
	unsigned char *u;

	if (!js_ctx)internal_error("js_upcall_submit called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;

	if (fd->ses->rq && fd->ses->defered_url) return;

	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return;
	form=jsint_find_object(fd,form_id);
	if (!form)return;

	u=get_form_url(fd->ses,fd,form,&has_onsubmit);
	if (u) {
		goto_url_f(fd->ses,NULL,u,NULL,fd,form->form_num, has_onsubmit,0,0);
		mem_free(u);
	}
	draw_fd(fd);
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}


/* emulates reset of a form */
void js_upcall_reset(void *bidak, long document_id, long form_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)bidak;
	struct f_data_c *fd;

	if (!js_ctx)internal_error("js_upcall_reset called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return;
	if ((form_id&JS_OBJ_MASK)!=JS_OBJ_T_FORM)return;
	if (!fd->f_data)return;

	reset_form(fd,form_id>>JS_OBJ_MASK_SIZE);
	draw_fd(fd);
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}

/* returns length (number of radio buttons) of a radio
 * on error returns -1
 */
int js_upcall_get_radio_length(void *p, long document_id, long radio_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)p;
	struct f_data_c *fd;
	struct form_control *f;
	struct list_head *lf;
	struct hopla_mladej *hopla;
	struct form_control *radio;
	int count=0;

	if (!js_ctx)internal_error("js_upcall_get_radio_length called with NULL context pointer\n");
	if ((radio_id&JS_OBJ_MASK)!=JS_OBJ_T_RADIO) return -1;
	fd=jsint_find_document(document_id);
	if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return -1;

	hopla=jsint_find_object(fd,radio_id);
	if (!hopla)return -1;
	radio=hopla->fc;

	/* find form elements with the same type, form_num (belonging to the same form) and name */
	foreachback(struct form_control, f, lf, fd->f_data->forms)
		if (f->type==radio->type&&f->form_num==radio->form_num&&!strcmp(cast_const_char radio->name,cast_const_char f->name))count++;
	mem_free(hopla);
	return count;
}

/* returns number of items in a select form element
 * on error returns -1
 */
int js_upcall_get_select_length(void *p, long document_id, long select_id)
{
	int l;
	struct f_data_c *js_ctx=(struct f_data_c*)p;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;

	if (!js_ctx)internal_error("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id&JS_OBJ_MASK)!=JS_OBJ_T_SELECT) return -1;
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;

	hopla=jsint_find_object(fd,select_id);
	if (!hopla)return -1;

	l = hopla->fc->nvalues;
	mem_free(hopla);
	return l;
}


/* returns allocated field of select items
 * don't forget to free: text and value of each item and the field
 * on error returns NULL
 * n is number of items in the field
 */
struct js_select_item* js_upcall_get_select_options(void *p, long document_id, long select_id, int *n)
{
	struct f_data_c *js_ctx=(struct f_data_c*)p;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	struct js_select_item* elektricke_pole;
	int ukazme_si_na_nej;

	*n=0;
	if (!js_ctx)internal_error("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id&JS_OBJ_MASK)!=JS_OBJ_T_SELECT) return NULL;
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	hopla=jsint_find_object(fd,select_id);
	if (!hopla)return NULL;

	*n=hopla->fc->nvalues;
	if ((unsigned)*n > MAXINT / sizeof(struct js_select_item)) overalloc();
	elektricke_pole=mem_alloc((*n)*sizeof(struct js_select_item));

	for (ukazme_si_na_nej=0;ukazme_si_na_nej<(*n);ukazme_si_na_nej++)
	{
		elektricke_pole[ukazme_si_na_nej].text=stracpy((hopla->fc->labels)[ukazme_si_na_nej]);
		elektricke_pole[ukazme_si_na_nej].value=stracpy((hopla->fc->values)[ukazme_si_na_nej]);
		elektricke_pole[ukazme_si_na_nej].selected=(ukazme_si_na_nej==(hopla->fs->state));
		elektricke_pole[ukazme_si_na_nej].default_selected=(ukazme_si_na_nej==(hopla->fc->default_state));
	}
	mem_free(hopla);
	return elektricke_pole;
}

/* returns index of just selected item in a select form element
 * on error returns -1
 */
int js_upcall_get_select_index(void *p, long document_id, long select_id)
{
	struct f_data_c *js_ctx=(struct f_data_c*)p;
	struct f_data_c *fd;
	struct hopla_mladej *hopla;
	int l;

	if (!js_ctx)internal_error("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id&JS_OBJ_MASK)!=JS_OBJ_T_SELECT) return -1;
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;

	hopla=jsint_find_object(fd,select_id);
	if (!hopla)return -1;

	l = hopla->fs->state;
	mem_free(hopla);
	return l;
}


struct gimme_js_id_string
{
	long id;
	long js_id;
	unsigned char *string;
	int n;
};

/* open a link in a new xterm */
static void send_vodevri_v_novym_vokne(struct terminal *term, void *open_window_, void *ses_)
{
	int (*open_window)(struct terminal *, unsigned char *, unsigned char *) = *(int (* const *)(struct terminal *, unsigned char *, unsigned char *))open_window_;
	struct session *ses = (struct session *)ses_;
	if (ses->dn_url) {
		unsigned char *enc_url = encode_url(ses->dn_url);
		open_window(term, path_to_exe, enc_url);
		mem_free(enc_url);
	}
}

static void (* const send_vodevri_v_novym_vokne_ptr)(struct terminal *term, void *open_window_, void *ses_) = send_vodevri_v_novym_vokne;

/* aux function for js_upcall_goto_url */
static void js_upcall_goto_url_ok_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id_string *jsid=(struct gimme_js_id_string*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	/* it doesn't matter, that fd->js is NULL */
	if (jsid->n&&can_open_in_new(fd->ses->term)) /* open in new window */
	{
		if (fd->ses->dn_url) mem_free(fd->ses->dn_url);
		fd->ses->dn_url=stracpy(jsid->string);
		open_in_new_window(fd->ses->term, (void *)&send_vodevri_v_novym_vokne_ptr, fd->ses);
	}
	else
		goto_url(fd->ses,jsid->string);

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* aux function for js_upcall_goto_url */
static void js_upcall_goto_url_cancel_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id *jsid=(struct gimme_js_id*)data;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js)||jsid->js_id!=fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* gets struct fax_me_tender_int_string */
/* asks user whether to go to the url or not */
/* structure and the text are both deallocated */
/* must be called from select loop */
/* if num in fax_me_tender_int_string is not null, open in a new window */
void js_upcall_goto_url(void * data)
{
	struct fax_me_tender_int_string *s=(struct fax_me_tender_int_string*)data;
	struct f_data_c *fd;
	struct terminal *term;
	unsigned char *dest_url;
	int in_new_win;

	fd=(struct f_data_c*)(s->ident);
	term=fd->ses->term;

	if (!fd->js) return;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	if (!s)internal_error("js_upcall_goto_url called with NULL pointer\n");

	if (!s->string){js_mem_free(data);goto goto_url_failed;}
	if (fd->loc&&fd->loc->url) dest_url=join_urls(fd->loc->url,s->string);
	else dest_url=stracpy(s->string);
	if (!(dest_url)){js_mem_free(s->string);js_mem_free(data);goto goto_url_failed;}
	js_mem_free(s->string);
	in_new_win=s->num;

	if (js_manual_confirmation)
	{
		struct gimme_js_id_string* jsid;

		/* goto the same url */
		{
			unsigned char txt[MAX_STR_LEN];
			void *p;

			p=get_current_url(fd->ses,txt,MAX_STR_LEN);
			if (p&&fd->loc&&fd->loc->url&&!strcmp(cast_const_char txt,cast_const_char dest_url))
			{
				mem_free(dest_url);
				js_mem_free(data);
				goto goto_url_failed;
			}
		}

		jsid=mem_alloc(sizeof(struct gimme_js_id_string));

		/* context must be a valid pointer ! */
		/* fill in jsid */
		jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
		jsid->js_id=fd->js->ctx->js_id;
		jsid->string=dest_url;
		jsid->n=s->num;

		msg_box(
			term,   /* terminal */
			getml(jsid->string,jsid,NULL),   /* memory blocks to free */
			TEXT_(T_GOTO_URL),   /* title */
			AL_CENTER,   /* alignment */
			jsid->n?TEXT_(T_JS_IS_ATTEMPTING_TO_OPEN_NEW_WINDOW_WITH_URL):TEXT_(T_JS_IS_ATTEMPTING_TO_GO_TO_URL), " \"",jsid->string,"\".",MSG_BOX_END,   /* message */
			(void *)jsid,   /* data for button functions */
			3,   /* # of buttons */
			TEXT_(T_ALLOW),js_upcall_goto_url_ok_pressed,B_ENTER,
			TEXT_(T_REJECT),js_upcall_goto_url_cancel_pressed,B_ESC,
			TEXT_(T_KILL_SCRIPT), js_kill_script_pressed,NULL  /* dirty trick: gimme_js_id_string and gimme_js_id begins with the same long */
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		if (in_new_win&&can_open_in_new(fd->ses->term)) /* open in new window */
		{
			if (fd->ses->dn_url) mem_free(fd->ses->dn_url);
			fd->ses->dn_url=stracpy(dest_url);
			open_in_new_window(fd->ses->term, (void *)&send_vodevri_v_novym_vokne_ptr, fd->ses);
		}
		else
			goto_url(fd->ses,dest_url);
		js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
		mem_free(dest_url);
	}
	return;

goto_url_failed:
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
	return;
}


/* returns number of items in history */
int js_upcall_get_history_length(void *context)
{
	struct f_data_c *fd=(struct f_data_c*)context;

	if (!fd)internal_error("PerMe, PerMe, ja si te podam!\n");

	return (int)list_size(&fd->ses->history);
}


/* aux function for js_upcall_goto_history */
static void js_upcall_goto_history_ok_pressed(void *data)
{
	struct f_data_c *fd;
	struct gimme_js_id_string *jsid=(struct gimme_js_id_string*)data;
	int a;

	fd=jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	a = (int)list_size(&fd->ses->history);

	if (a<jsid->n&&(fd->js)&&jsid->js_id==fd->js->ctx->js_id){js_downcall_vezmi_null(fd->js->ctx);return;}   /* call downcall */

	go_backwards(fd->ses->term,(void*)(my_intptr_t)(jsid->n),fd->ses);
}


/* gets struct fax_me_tender_int_string
 * either num or string is set, but not both, the other must be NULL
 * asks user whether to go to the url or not
 * structure and the text are both deallocated
 * must be called from select loop
 * number can be:
 *		>0	go forward in history (not supported)
 *		 0	do nothing (means use string)
 *		<0	go backward in history (supported :) )
 * if string is defined - find appropriate history item and go to the url, when
 * the URL doesn't exist do nothing
 *
 * JAK TO FUNGUJE:
 * string se prekonvertuje na cislo (projde se historie)
 * po zmacknuti OK se spocita delka historie a pokud je dostatecna, n-krat
 * zavola go_back. Pokud neni, tak se chovame jako pri cancelu.
 */

void js_upcall_goto_history(void * data)
{
	struct fax_me_tender_int_string *s=(struct fax_me_tender_int_string*)data;
	struct f_data_c *fd;
	struct terminal *term;
	unsigned char *url=NULL;
	unsigned char txt[16];
	long history_num=0;

	/* context must be a valid pointer ! */
	fd=(struct f_data_c*)(s->ident);

	if (!fd->js) return;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx,0);

	if (!s)internal_error("Hele, tyhle prasarny si zkousej na nekoho jinyho, jo?!\n");
	if (!(s->num)&&!(s->string))internal_error("Tak tohle na mne nezkousej, bidaku!\n");
	if ((s->num)&&(s->string))internal_error("Ta sedla!\n");

	/* find the history item */
	if (s->num)	/* goto n-th item */
	{
		struct location *loc;
		struct list_head *lloc;
		int a=0;

		if ((s->num)>0){if (s->string)js_mem_free(s->string);js_mem_free(data);goto goto_history_failed;} /* forward not supported */
		s->num=-s->num;
		history_num=s->num;

		foreach(struct location, loc, lloc, fd->ses->history) {
			if (a==s->num){url=stracpy(loc->url);break;}
			a++;
		}
	}
	else	/* goto given url */
	{
		struct location *loc;
		struct list_head *lloc;
		int a=0;

		foreach(struct location, loc, lloc, fd->ses->history) {
			if (!strcmp(cast_const_char s->string,cast_const_char loc->url)){url=stracpy(s->string);history_num=a;break;}
			a++;
		}
	}

	if (s->string)js_mem_free(s->string);
	if (!url){js_mem_free(data);goto goto_history_failed;}

	term=fd->ses->term;

	if (js_manual_confirmation)
	{
		struct gimme_js_id_string* jsid;

		jsid=mem_alloc(sizeof(struct gimme_js_id_string));

		/* fill in jsid */
		jsid->id=((fd->id)<<JS_OBJ_MASK_SIZE)|JS_OBJ_T_DOCUMENT;
		jsid->js_id=fd->js->ctx->js_id;
		jsid->string=url;
		jsid->n=history_num;

		snprintf(txt,16," (-%d) ",jsid->n);
		msg_box(
			term,   /* terminal */
			getml(url,jsid,NULL),   /* memory blocks to free */
			TEXT_(T_GOTO_HISTORY),   /* title */
			AL_CENTER,   /* alignment */
			TEXT_(T_JS_IS_ATTEMPTING_TO_GO_INTO_HISTORY), txt, TEXT_(T_TO_URL), " \"",url,"\".",MSG_BOX_END,   /* message */
			(void *)jsid,   /* data for button functions */
			3,   /* # of buttons */
			TEXT_(T_ALLOW),js_upcall_goto_history_ok_pressed,B_ENTER,
			TEXT_(T_REJECT),js_upcall_goto_url_cancel_pressed,B_ESC,
			TEXT_(T_KILL_SCRIPT), js_kill_script_pressed,NULL  /* dirty trick: gimme_js_id_string and gimme_js_id begins with the same long */
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		mem_free(url);
		go_backwards(term,(void*)(history_num),fd->ses);
	}
	return;
goto_history_failed:
	js_downcall_vezmi_null(fd->js->ctx);
	return;
}


/* set default status-line text
 * tak_se_ukaz_Kolbene is allocated string or NULL
 */
void js_upcall_set_default_status(void *context, unsigned char *tak_se_ukaz_Kolbene)
{
	struct f_data_c *fd=(struct f_data_c*)context;
	unsigned char *trouba;

	if (!fd)internal_error("Tak tohle teda ne, bobanku!\n");

	if (!(*tak_se_ukaz_Kolbene)){mem_free(tak_se_ukaz_Kolbene);tak_se_ukaz_Kolbene=NULL;} /* Ale to hlavni jsme se nedozvedeli - s tim chrapanim jste mi neporadil... */

	if (fd->ses->default_status)mem_free(fd->ses->default_status);
	skip_nonprintable(tak_se_ukaz_Kolbene);
	if (fd->f_data&&tak_se_ukaz_Kolbene)
	{
		/* ... a ted ty pochybne reci o majetku ... */
		trouba=convert(fd->f_data->cp, fd->f_data->opt.cp, tak_se_ukaz_Kolbene, NULL); /* Taky to mate levnejsi - jinak by to stalo deset! */
		mem_free(tak_se_ukaz_Kolbene);
		/* a je to v troube... */
	}
	else
	{
		trouba=tak_se_ukaz_Kolbene;
	}

	fd->ses->default_status=trouba;
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}


/* returns allocated string with default status-line value or NULL when default value is empty
 */
unsigned char* js_upcall_get_default_status(void *context)
{
	struct f_data_c *fd=(struct f_data_c *)context;
	unsigned char *tak_se_ukaz_Danku=NULL;
	unsigned char *trouba;

	if (!fd)internal_error("Ale hovno!\n");

	if (fd->ses->default_status&&(*fd->ses->default_status))tak_se_ukaz_Danku=stracpy(fd->ses->default_status);
	skip_nonprintable(tak_se_ukaz_Danku);
	if (fd->f_data&&tak_se_ukaz_Danku)
	{
		trouba=convert(fd->f_data->opt.cp, fd->f_data->cp, tak_se_ukaz_Danku, NULL);
		mem_free(tak_se_ukaz_Danku);
	}
	else
	{
		trouba=tak_se_ukaz_Danku;
	}

	/* Tak to mame Kolben a Danek po peti korunach... */

	return trouba; /* No jo, je to v troube! */
}


/* set status-line text
 * tak_se_ukaz_Kolbene is allocated string or NULL
 */
void js_upcall_set_status(void *context, unsigned char *tak_se_ukaz_Kolbene)
{
	struct f_data_c *fd=(struct f_data_c*)context;
	unsigned char *trouba;

	if (!fd)internal_error("To leda tak -PRd!\n");

	if (!(*tak_se_ukaz_Kolbene)){mem_free(tak_se_ukaz_Kolbene);tak_se_ukaz_Kolbene=NULL;}

	if (fd->ses->st)mem_free(fd->ses->st);
	skip_nonprintable(tak_se_ukaz_Kolbene);
	if (fd->f_data&&tak_se_ukaz_Kolbene)
	{
		trouba=convert(fd->f_data->cp, fd->f_data->opt.cp, tak_se_ukaz_Kolbene, NULL);
		mem_free(tak_se_ukaz_Kolbene);
		/* a je to v troube... */
	}
	else
	{
		trouba=tak_se_ukaz_Kolbene;
	}

	fd->ses->st=trouba;
	print_screen_status(fd->ses);
}


/* returns allocated string with default status-line value or NULL when default value is empty
 */
unsigned char* js_upcall_get_status(void *context)
{
	struct f_data_c *fd=(struct f_data_c *)context;
	unsigned char *tak_se_ukaz_Danku=NULL;
	unsigned char *trouba;

	if (!fd)internal_error("To leda tak hovno!\n");

	if (fd->ses->st&&(*fd->ses->st))tak_se_ukaz_Danku=stracpy(fd->ses->st);
	skip_nonprintable(tak_se_ukaz_Danku);
	if (fd->f_data&&tak_se_ukaz_Danku)
	{
		trouba=convert(fd->f_data->opt.cp, fd->f_data->cp, tak_se_ukaz_Danku, NULL);
		mem_free(tak_se_ukaz_Danku);
	}
	else
	{
		trouba=tak_se_ukaz_Danku;
	}

	/* Kolben a Danek, to mame po peti korunach... */

	return trouba;
}

/* returns allocated string with cookies, or NULL on error */
unsigned char * js_upcall_get_cookies(void *context)
{
	struct f_data_c *fd=(struct f_data_c *)context;
	unsigned char *s=init_str();
	int l=0;
	int nc=0;
	time_t now;
	struct cookie *c;
	struct list_head *lc;
	unsigned char *server, *data;
	struct c_domain *cd;
	struct list_head *lcd;

	if (!fd)internal_error("Tak tomu rikam selhani komunikace...\n");

	/* zavolame set_cookies, ten zparsuje fd->js->ctx->cookies a necha tam nezparsovatelnej zbytek */

	if (!fd->js||!fd->js->ctx) {mem_free(s);return NULL;}
	if (!fd->rq) goto ty_uz_se_nevratis;

	jsint_set_cookies(fd,0);

	server = get_host_name(fd->rq->url);
	data = get_url_data(fd->rq->url);

	if (data > fd->rq->url) data--;
	foreach(struct c_domain, cd, lcd, c_domains) if (is_in_domain(cd->domain, server)) goto ok;
	mem_free(server);
ty_uz_se_nevratis:
	if (fd->js->ctx->cookies)add_to_str(&s,&l,fd->js->ctx->cookies);
	else {mem_free(s);s=NULL;}
	return s;
ok:
	now = get_absolute_seconds();
	foreach(struct cookie, c, lc, all_cookies) if (is_in_domain(c->domain, server)) if (is_path_prefix(c->path, data)) {
		if (cookie_expired(c, now)) {
			lc = lc->prev;
			del_from_list(c);
			free_cookie(c);
			continue;
		}
		if (c->secure) continue;
		if (!nc) nc = 1;
		else add_to_str(&s, &l, "; ");
		add_to_str(&s, &l, c->name);
		if (c->value) {
			add_to_str(&s, &l, "=");
			add_to_str(&s, &l, c->value);
		}
	}

	if (!nc) {mem_free(s);s=NULL;}
	mem_free(server);

	/* za strinzik sestaveny z vnitrni reprezentace susenek jeste prilepime nezparsovatelnej zbytek */
	if (fd->js->ctx->cookies)
	{
		if (!s)s=stracpy(fd->js->ctx->cookies);
		else {add_to_str(&s,&l,"; ");add_to_str(&s,&l,fd->js->ctx->cookies);}
	}
	/*debug("get_cookies: \"%s\"", s);*/
	return s;
}

/* FIXME: document.all nechodi, musi se prepsat, aby vracel dvojice frame:idcko */


/* adds all in given f_data_c, the f_data_c must be accessible by the javascript */
static void add_all_recursive_in_fd(long **field, int *len, struct f_data_c *fd, struct f_data_c *js_ctx)
{
	struct f_data_c *ff;
	struct list_head *lff;
	struct form_control *fc;
	struct list_head *lfc;

	/* add all accessible frames */
	foreach(struct f_data_c, ff, lff, fd->subframes)
		if (jsint_can_access(js_ctx,ff))
			if (!((*field)=add_fd_id(*field,len,js_upcall_get_frame_id(fd),js_upcall_get_frame_id(ff),ff->f_data?ff->f_data->opt.framename:NULL)))return;

	if (!(fd->f_data))goto tady_uz_nic_peknyho_nebude;

#ifdef G
	/* add all images */
	if (F) {
		struct g_object_image *gi;
		struct list_head *lgi;

		foreach(struct g_object_image, gi, lgi, fd->f_data->images) {
			if (!((*field)=add_fd_id(*field,len,js_upcall_get_frame_id(fd),JS_OBJ_T_IMAGE+((gi->id)<<JS_OBJ_MASK_SIZE),gi->name)))return;
		}
	}
#endif
	/* add all forms */
	foreachback(struct form_control, fc, lfc, fd->f_data->forms)
		if (!((*field)=add_fd_id(*field,len,js_upcall_get_frame_id(fd),((fc->form_num)<<JS_OBJ_MASK_SIZE)+JS_OBJ_T_FORM,fc->form_name)))return;

	/* add all form elements */
	foreachback(struct form_control, fc, lfc, fd->f_data->forms)
		{
			long tak_mu_to_ukaz=0;
			tak_mu_to_ukaz=(fc->g_ctrl_num)<<JS_OBJ_MASK_SIZE;
			switch (fc->type)
			{
				case FC_TEXT:		tak_mu_to_ukaz|=JS_OBJ_T_TEXT; break;
				case FC_PASSWORD:	tak_mu_to_ukaz|=JS_OBJ_T_PASSWORD; break;
				case FC_TEXTAREA:	tak_mu_to_ukaz|=JS_OBJ_T_TEXTAREA; break;
				case FC_CHECKBOX:	tak_mu_to_ukaz|=JS_OBJ_T_CHECKBOX; break;
				case FC_RADIO:		tak_mu_to_ukaz|=JS_OBJ_T_RADIO; break;
				case FC_IMAGE:
				case FC_SELECT:		tak_mu_to_ukaz|=JS_OBJ_T_SELECT; break;
				case FC_SUBMIT:		tak_mu_to_ukaz|=JS_OBJ_T_SUBMIT ; break;
				case FC_RESET:		tak_mu_to_ukaz|=JS_OBJ_T_RESET ; break;
				case FC_HIDDEN:		tak_mu_to_ukaz|=JS_OBJ_T_HIDDEN ; break;
				case FC_BUTTON:		tak_mu_to_ukaz|=JS_OBJ_T_BUTTON ; break;
				default:/* internal_error("Invalid form element type.\n"); */
				tak_mu_to_ukaz=0;break;
			}
			if (tak_mu_to_ukaz&&!((*field)=add_fd_id(*field,len,js_upcall_get_frame_id(fd),tak_mu_to_ukaz,fc->name)))return;
		}

tady_uz_nic_peknyho_nebude:

	foreach(struct f_data_c, ff, lff, fd->subframes)
		if (jsint_can_access(js_ctx,ff)) add_all_recursive_in_fd(field,len,ff,js_ctx);
}

/* returns allocated field of all objects in the document (document.all)
 * size of the field will be stored in len
 * the field has 3x more items than the number of objects
 * field[x+0]==id of frame
 * field[x+1]==id of the object
 * field[x+2]==allocated unsigned char* with name of the object or NULL (when there's no name)
 *
 * when an error occurs, returns NULL
 */
long * js_upcall_get_all(void *chuligane, long document_id, int *len)
{
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	long *pole_neorane; /* Premysle Zavoraci, kde se flakas? Zase forbesis, co? */
	struct f_data_c *fd;

	if (!js_ctx)internal_error("js_upcall_get_all called with NULL context pointer\n");
	fd=jsint_find_document(document_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	*len=0;

	pole_neorane=mem_alloc(sizeof(long));

	add_all_recursive_in_fd(&pole_neorane,len,fd,js_ctx);

	/* nothing was found */
	if (!pole_neorane)return NULL;
	if (!(*len))mem_free(pole_neorane),pole_neorane=NULL;

	return pole_neorane;
}


/* returns allocated field of all images
 * size of the field will be stored in len
 * when an error occurs, returns NULL
 */

long *js_upcall_get_images(void *chuligane, long document_id, int *len)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	long *pole_Premysla_Zavorace;
	int a;

	if (F) {
		struct g_object_image *gi;
		struct list_head *lgi;

		if (!js_ctx)internal_error("js_upcall_get_images called with NULL context pointer\n");
		fd=jsint_find_document(document_id);
		if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return NULL;

		*len = list_size(&fd->f_data->images);

		if (!(*len))return NULL;

		if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
		pole_Premysla_Zavorace=mem_alloc((*len)*sizeof(long));

		a=0;
		foreach(struct g_object_image, gi, lgi, fd->f_data->images) {
			unsigned id = gi->id;;
			pole_Premysla_Zavorace[a]=JS_OBJ_T_IMAGE+(id<<JS_OBJ_MASK_SIZE);
			a++;
		}
		return pole_Premysla_Zavorace;
	} else
#endif
	{
		document_id=document_id;
		*len=0;
		return NULL;
	}
}

/* returns width of given image or -1 on error */
int js_upcall_get_image_width(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct f_data_c *fd;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_width called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		return gi->goti.go.xw;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns height of given image or -1 on error */
int js_upcall_get_image_height(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct f_data_c *fd;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_height called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		return gi->goti.go.yw;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns border of given image or -1 on error */
int js_upcall_get_image_border(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_border called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		return gi->border;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns vspace of given image or -1 on error */
int js_upcall_get_image_vspace(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_vspace called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		return gi->vspace;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns hspace of given image or -1 on error */
int js_upcall_get_image_hspace(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_hspace called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		return gi->hspace;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns allocated string with name of given image or NULL on error */
unsigned char * js_upcall_get_image_name(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_name called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return NULL;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return NULL;

		return stracpy(gi->name);
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return NULL;
	}
}


/* returns allocated string with name of given image or NULL on error */
unsigned char * js_upcall_get_image_alt(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_alt called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return NULL;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return NULL;

		return stracpy(gi->alt);
	}else
#endif
	{
		chuligane=chuligane;
		document_id=document_id;
		image_id=image_id;
		return NULL;
	}
}


/* sets image name to given value */
/* name is deallocated after use with mem_free */
void js_upcall_set_image_name(void *chuligane, long document_id, long image_id, unsigned char *name)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_set_image_name called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return;

		if (gi->name)mem_free(gi->name);
		gi->name=stracpy(name);	/* radeji takhle, protoze to je bezpecnejsi: az PerM zase do neceho slapne, tak se to pozna hned tady a ne buhvikde */
		if (name)mem_free(name);
		return;
	}else
#endif
	{
		chuligane=chuligane;
		document_id=document_id;
		image_id=image_id;
		if (name)mem_free(name);
		return;
	}
}


/* sets image alt to given value */
/* alt is deallocated after use with mem_free */
void js_upcall_set_image_alt(void *chuligane, long document_id, long image_id, unsigned char *alt)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_set_image_alt called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return;
		fd=jsint_find_document(document_id);
		if (!fd||!fd->f_data||!jsint_can_access(js_ctx,fd))return;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return;

		if (gi->alt)mem_free(gi->alt);
		gi->alt=stracpy(alt);	/* radeji takhle, protoze to je bezpecnejsi: az PerM zase do neceho slapne, tak se to pozna hned tady a ne buhvikde */
		if (fd->f_data&&gi->goti.link_num>=0&&gi->goti.link_num<fd->f_data->nlinks)
		{
			struct link *l=&fd->f_data->links[gi->goti.link_num];

			if (l->img_alt)mem_free(l->img_alt);
			l->img_alt=stracpy(alt);
		}
		if (alt)mem_free(alt);
		change_screen_status(fd->ses);
		print_screen_status(fd->ses);
		return;
	}else
#endif
	{
		chuligane=chuligane;
		document_id=document_id;
		image_id=image_id;
		if (alt)mem_free(alt);
		return;
	}
}


/* returns allocated string with source URL of given image or NULL on error */
unsigned char * js_upcall_get_image_src(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_get_image_src called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return NULL;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return NULL;

		return stracpy(gi->orig_src);
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return NULL;
	}
}


/* changes image URL
 * gets struct fax_me_tender_string_2_longy
 *	num1 = document_id, num2 = image_id, string = url
 *
 * frees the string and the fax_me_tender struct with js_mem_free function
 */
void js_upcall_set_image_src(void *chuligane)
{
	unsigned char *zvrat;
	struct fax_me_tender_string_2_longy *fax=(struct fax_me_tender_string_2_longy*)chuligane;
	struct f_data_c *js_ctx;
#ifdef G
	struct f_data_c *fd;
	struct g_object_image *gi;
	long image_id,document_id;
	unsigned char *vecirek;
	if (F)
	{
		js_ctx=(struct f_data_c*)fax->ident;
		js_spec_vykill_timer(js_ctx->js->ctx,0);
		if (!chuligane)internal_error("js_upcall_set_image_src called with NULL argument\n");
		if (!js_ctx)internal_error("js_upcall_set_image_src called with NULL context pointer\n");
		image_id=fax->obj_id;
		document_id=fax->doc_id;
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)goto abych_tu_nepovecerel;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))goto abych_tu_nepovecerel;

		gi=jsint_find_object(fd,image_id);

		if (!gi||!fd->f_data)goto abych_tu_nepovecerel;

		/* string joinnem s url */
		if (fd->f_data&&fd->f_data->script_href_base) vecirek=join_urls(fd->f_data->script_href_base,fax->string);
		else if (fd->loc&&fd->loc->url) vecirek=join_urls(fd->loc->url,fax->string);
		else vecirek=stracpy(fax->string);
		/* a mame to kompatidebilni s verzi pred jointem */

		change_image(gi,vecirek,fax->string,fd->f_data);
		if (vecirek) mem_free(vecirek);
		fd->f_data->uncacheable = 1;
		abych_tu_nepovecerel:;
	}else
#endif
	{
		js_ctx=(struct f_data_c*)fax->ident;
		if (!js_ctx)internal_error("js_upcall_set_image_src called with NULL context pointer\n");
		js_spec_vykill_timer(js_ctx->js->ctx,0);
		if (!chuligane)internal_error("js_upcall_set_image_src called with NULL argument\n");
	}
	zvrat=stracpy(fax->string);
	js_mem_free(fax->string);
	js_mem_free(fax);
	js_downcall_vezmi_string(js_ctx->js->ctx,zvrat);
}


/* returns 1 if image has completed loading, 0 when not, -1 on error */
int js_upcall_image_complete(void *chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c *fd;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct g_object_image *gi;

	if (F)
	{
		if (!js_ctx)internal_error("js_upcall_image_complete called with NULL context pointer\n");
		if ((image_id&JS_OBJ_MASK)!=JS_OBJ_T_IMAGE)return -1;
		fd=jsint_find_document(document_id);
		if (!fd||!jsint_can_access(js_ctx,fd))return -1;

		gi=jsint_find_object(fd,image_id);

		if (!gi)return -1;

		if (!gi->af||!gi->af->rq||!gi->af->rq->state)return -1;
		return gi->af->rq->state==O_OK;
	}else
#endif
	{
		document_id=document_id;
		image_id=image_id;
		return -1;
	}
}


/* returns parent of given frame (or document), or -1 on error or no permissions */
/* if frame_id is already top frame returns the same frame */
long js_upcall_get_parent(void *chuligane, long frame_id)
{
	struct f_data_c *fd, *ff;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;

	if (!js_ctx)internal_error("js_upcall_get_parent called with NULL context pointer\n");
	if ((frame_id&JS_OBJ_MASK)!=JS_OBJ_T_FRAME&&(frame_id&JS_OBJ_MASK)!=JS_OBJ_T_DOCUMENT)return -1;
	fd=jsint_find_document(frame_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;

	for (ff=fd->parent;ff&&!ff->rq;ff=ff->parent);

	if (!ff)ff=fd->ses->screen;
	return jsint_can_access(fd,ff)?js_upcall_get_frame_id(ff):-1;
}


/* returns top of given frame (or document), or -1 on error */
/* returns highest grandparent accessible from given frame */
long js_upcall_get_frame_top(void *chuligane, long frame_id)
{
	struct f_data_c *fd, *ff;
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;

	if (!js_ctx)internal_error("js_upcall_get_frame_top called with NULL context pointer\n");
	if ((frame_id&JS_OBJ_MASK)!=JS_OBJ_T_FRAME&&(frame_id&JS_OBJ_MASK)!=JS_OBJ_T_DOCUMENT)return -1;
	fd=jsint_find_document(frame_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return -1;
	for (ff=fd->parent;ff;ff=ff->parent)
	{
		if (ff->rq)
		{
			if (!jsint_can_access(fd,ff))break;
			fd=ff;
		}
	}
	return js_upcall_get_frame_id(fd);
}


/* returns allocated field of subframes or NULL on error */
/* count cointains length of the field */
/* don't forget to free the field after use */
long * js_upcall_get_subframes(void *chuligane, long frame_id, int *count)
{
	struct f_data_c *js_ctx=(struct f_data_c*)chuligane;
	struct f_data_c *fd;
	struct f_data_c *f;
	struct list_head *lf;
	int a;
	long *pole;
	*count=0;

	if (!js_ctx)internal_error("js_upcall_get_subframes called with NULL context pointer\n");
	if ((frame_id&JS_OBJ_MASK)!=JS_OBJ_T_FRAME&&(frame_id&JS_OBJ_MASK)!=JS_OBJ_T_DOCUMENT)return NULL;
	fd=jsint_find_document(frame_id);
	if (!fd||!jsint_can_access(js_ctx,fd))return NULL;

	foreach(struct f_data_c, f, lf, fd->subframes)
		if (jsint_can_access(fd,f)) (*count)++;

	if (!*count)return NULL;
	if ((unsigned)*count > MAXINT / sizeof(long)) overalloc();
	pole=mem_alloc((*count)*sizeof(long));

	a=0;
	foreach(struct f_data_c, f, lf, fd->subframes)
		if (jsint_can_access(fd,f))
			{pole[a]=js_upcall_get_frame_id(f);a++;}
	return pole;
}


/*---------------------             DOWNCALLS          ---------------------------*/

void js_downcall_game_over(void *context)
{
	struct f_data_c *fd=(struct f_data_c*)(((js_context*)(context))->ptr);

	/* js_error(get_text_translation(TEXT_(T_SCRIPT_KILLED_BY_USER),fd->ses->term),context);
	 * Tato hlaska me srala. Na to bych neprisel, ze jsem prave zabil
	 * rucne javascript. */
	if (fd->ses->default_status)mem_free(fd->ses->default_status),fd->ses->default_status=NULL; /* pekne uklidime bordylek, ktery nam BFU nacintalo do status lajny */
	jsint_destroy(fd);
#if 0
	js_durchfall=0;
	if(((js_context*)context)->running)
		js_volej_kolbena(context);
	/* Kolben - ale nespi mi - co s tim budeme delat? */
	((js_context*)context)->running=0;
#endif
}


void js_downcall_vezmi_int(void *context, int i)
{
}


void js_downcall_vezmi_float(void *context, float f)
{
}

#else

void jsint_execute_code(struct f_data_c *fd, unsigned char *code, int len, int write_pos, int onclick_submit, int onsubmit, struct links_event *ev)
{
}

void jsint_destroy(struct f_data_c *fd)
{
}

void jsint_scan_script_tags(struct f_data_c *fd)
{
}

int jsint_get_source(struct f_data_c *fd, unsigned char **start, size_t *len)
{
	return 0;
}

#endif
