/*
This file is part of Bolixo.
Bolixo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bolixo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bolixo. If not, see .
*/
/*
This file hold various test done to figure out how to use html and javascript
*/
// To learn and experiment with HTML
#include
#include
#include
#include
#include
#include
#include
#include "../instrument.h"
extern "C" void tlmp_initmod()
{
translat_load ("bolixo");
}
static void do_webtable(const WEBID &id, int nbrow, unsigned &cur)
{
glocal int nbrow = nbrow;
(id,cur,0,5);
sethead ("title1\ttitle2\ttitle3\ttitle4");
setrowstyle ("white","bgcolor=white");
setrowstyle ("white+current","bgcolor=lightblue");
setclickopt(true,"","");
for (int i=0; i
}
static void do_webtable(const WEBID &id, int nbrow)
{
unsigned cur = 0;
do_webtable (id,nbrow,cur);
}
static void layout (int width, int toprep, int middlerep)
{
//DIV main; main.w(width).h(100).disp("table").print();
DIV main; main.w(width).disp("flex").flowcol().print();
if (0){
DIV tb; tb.w(100).h(100).disp("flex").flowrow().print();
DIV c1; c1.flex("0 1 auto").print();
/- allo
c1.end();
DIV c2; c2.flex("0 1 auto").print();
/- comment ca va
c2.end();
}else if (0){
DIV tb; tb.h(5).disp("table-row").flex("0 1 auto").print();
DIV c1; c1.disp("table-cell").print();
/- allo
c1.end();
DIV c2; c2.disp("table-cell").print();
/- comment ca va
c2.end();
}
if (0){
DIV tb; tb.h(95).disp("table-row").flex("1 1 auto").print();
DIV c1; c1.disp("table-cell").print();
/- Une autre ligne
c1.end();
DIV c2; c2.disp("table-cell").print();
DIV col; col.bg("red").disp("flex").flowcol().print();
}
DIV to; to.bg("gray").disp("flex").flex("0 1 auto").print();
for (int i=0; i
Button4
Button5
/-
}
to.end();
DIV mi; mi.bg("yellow").w(100).disp("flex").flex("1 1 auto").print();
static int level = 0;
if (level == 0){
level++;
DIV d; d.w(100).disp("flex").flowrow().print();
layout(50,1,10);
layout(50,2,10);
level--;
}else{
DIV d; d.w(100).disp("flex").flowrow().print();
DIV dd; dd.w(30).flex("0 1 auto").print();
(WEBID("titi"));
for (int i=0; i<20; i++) setrow("white",0,"a\tb\tc");
dd.end();
DIV ddd; ddd.flex("1 1 auto").print();
/-
for (int i=0; i
field-%d\n",i);
}
/-
}
mi.end();
DIV bo; bo.bg("pink").flex("0 1 auto").print();
/- toto
bo.end();
//col.end();
/-
}
/*
This test was done to experiment with div inside div, to learn how it behave
*/
void test1()
{
tlmpweb_title ("This is the start of a long long journey");
?>
tlmpweb_body("white",NULL);
allo le monde
bababa
babababa
abababba
?>
layout(40,1,10);
layout(40,2,10);
/-
}
/*
This is replicating the complete layout of bolixo
*/
void bolixo()
{
?>
tlmpweb_body("white",NULL);
DIV main("main"); main.dispflex().flowcol().w(100).h(100).print();
{ // The title
DIV b0("main0","main0"); b0.w(100).flexfixe().print();
DIV banner; banner.w(100).dispflex().flowrow().bg("lightgray").print();
// Put some items
DIV it;
it.flexfixe().content("Bolixo.org");
it.flexgrow().content("
Welcome
");
it.flexfixe().content("
About
Logout
");
}
WEBID table1("table1");
WEBID table2("table2");
{
DIV body("main1"); body.flexgrow().w(100).bg("red").print();
DIV tabs("tabs"); tabs.w(100).dispflex().flowrow().bg("gray").align("stretch").print();
DIV t("tab","tab");
t.w(20).flexfixe().bg("yellow").print();
{
do_webtable(table1,20);
//DIV grow; grow.dispflex().flowcol().print();
//DIV b0; b0.bg("black").flexgrow().content("This is a test");
}
t.w(40).flexfixe().bg("lightblue").print();
{
DIV t; t.dispflex().flowcol().print();
DIV tab21("tab2","tab2"); tab21.bg("blue").flexfixe().print();
/-
THIS IS T2
tab21.end();
DIV tab22; tab22.flexgrow().print();
do_webtable(table2,20);
}
t.w(40).flexfixe().bg("pink").print();
/-
THIS IS T2
t.end();
/-
}
main.end();
?>
}
/*
Try to understand why a form creates some vertical spaces in a layout
The trick was "margin-bottom:0;"
*/
void form_space()
{
tlmpweb_body("white",NULL);
DIV d; d.h(100).dispflex().flowcol().print();
DIV sub; sub.h(10).bg("yellow").print();
/- 111111
{
DIV s; s.w(100).bg("blue").print();
// /-
return true;
sub.h(60).bg("green").print();
/- End
}
static W_SSTRING w_file("file");
static W_SSTRING w_name("name");
static W_UNSIGNED w_delay("delay");
static void fctanim()
{
static const char *waitid = "waitpopup";
const unsigned width = 100;
const unsigned height = 40;
{
DIV a("popup","popup-id"); a.print();
DIV d("popup-content",waitid); d.print();
DIV dd; dd.dispflex().flowrow().print();
DIV left; left.flexfixe().margins(10,10).vmargins(5,0).print();
htmlprintf ("
left.end();
#if 0
DIV right; right.print();
htmlout (
"\n");
#endif
}
htmlprintf ("\n",waitid);
}
/*
Add some animation when a form is submit after a delay
*/
void journey_formanim()
{
if (w_delay.getval() > 0) sleep (w_delay.getval());
glocal string error;
tlmpweb_body("white",NULL);
//tlmpweb_setscripts();
htmlout ("\n"
);
DIV d; d.h(100).dispflex().flowcol().print();
?>
("test");
/-
/-
/-
execution delay (seconds)
field_string (w_delay,"");
/-
Upload a file
field_file (w_file);
/-
Message
htmlout (glocal.error);
/-
bool ret = false;
if (strcmp(w_file.c_str(),"toto.txt")==0){
glocal.error = "toto.txt is not accepted";
}else if (strcmp(w_file.c_str(),"")!=0){
ret = true;
}else{
glocal.error = "You must specify a file";
}
return ret;
if (strcmp(w_file.c_str(),"")!=0){
const char *tempname = tlmpweb_getfilename(w_file);
tlmp_warning ("upload file temp=%s",tempname);
}
fctanim();
/- End
/- Retry
}
/*
Simple test to see the new HTML 5 tags section and friends
*/
void newtags()
{
?>
tlmpweb_body("white",NULL);
Main content: first in source order
?>
}
void scrollbar()
{
tlmpweb_body("white",NULL);
DIV d; d.dispflex().flowcol().print();
/-
static void index_script(unsigned scrollid1, unsigned scrollid2)
{
// Script to make sure the webtabs tabs use the full height of the screen
// Then it takes the webtables and grows them too.
?>
}
static void journey_setscroll()
{
();
tlmpweb_setscripts();
tlmpweb_body("white",NULL);
WEBID id1 ("id1");
WEBID id2 ("id2");
{
DIV m("","main"); m.w(100).h(100).print();
DIV d("","head"); d.w(100).h(10).print();
/- Hello
d.end();
unsigned cur1 = 10;
unsigned cur2 = 20;
d.id("body").w(100).dispflex().flowrow().print();
{
DIV tab("tabs"); tab.flexfixe().w(40).bg("lightblue").margins(5,5).vmargins(5,5).border(1,"black").print();
do_webtable(id1, 100,cur1);
}
{
DIV tab("tabs"); tab.flexfixe().w(60).bg("lightred").margins(5,5).vmargins(5,5).border(1,"black").print();
do_webtable(id2, 100,cur2);
}
}
unsigned w,h,l,scroll_top1=100,scroll_top2=400;
tlmpweb_gettablegeometry (id1,w,h,scroll_top1,l);
tlmpweb_gettablegeometry (id2,w,h,scroll_top2,l);
tlmp_error ("top %u %u\n",scroll_top1,scroll_top2);
index_script(scroll_top1,scroll_top2);
}
void util_endscript(PARAM_STRING);
static void journey_subtabs()
{
();
tlmpweb_setscripts();
tlmpweb_body("white",NULL);
WEBID id1 ("id1");
WEBID id2 ("id2");
{
DIV m("","main"); m.w(100).h(100).print();
DIV d("","head"); d.w(100).h(10).print();
/- Hello
d.end();
d.cls("tabs").id("body").w(100).dispflex().flowrow().print();
DIV d1("subtabs","d1"); d1.w(50).vpaddings(10,10).bg("pink").print();
DIV d2("subtabs","d2"); d2.w(95).vpaddings(10,10).bg("white").print();
DIV d3("subtabs","d3"); d3.w(95).vpaddings(10,10).bg("pink").print();
}
util_endscript("");
}
/*
When you click on a link, it jumps to a new tab.
If you click with the middle mouse button, it opens in a new tab, but stay in the current one (like normal).
*/
static void journey_click()
{
();
tlmpweb_setscripts();
htmlout (
"\n"
);
tlmpweb_body("white",NULL);
const char *url = "http://test1.bolixo.org/talk1.jpg";
htmlprintf ("",url);
htmlprintf ("\n",url);
htmlout ("\n");
}
static void test_displen (const char *s, const char *font)
{
unsigned len = tlmpweb_displaylen(s,1);
unsigned fact=1;
htmlprintf ("len=%u \n",len);
DIV d; d.id("box").border(1,"black").wpx(len*fact).hpx(tlmpweb_fontsize()*fact).print();
htmlprintf ("%s\n",fact,font,s);
d.end();
/-
?>
}
struct WORDINFO{
const char *txt;
unsigned len; // In pixel
unsigned txtlen; // strlen(txt);
WORDINFO(const char *_txt, unsigned _len){
txt = _txt;
len = _len;
txtlen = strlen(txt);
}
};
class PARAGRAPH{
vector tb; // Will contain the paragraph splitted into words.
public:
vector words; // The paragraph is decomposed into words
vector> lines; // Then the words are put together in lines
unsigned cursor_pos=0; // Cursor position inside the line in pixel
vector line_cursors; // character cursor position in each line, produced by locate_cursor()
vector line_homes; // character cursor (paragraph) position of the start of each line
vector line_ends; // character cursor (paragraph) position of the end of each line
public:
PARAGRAPH();
PARAGRAPH(PARAM_STRING txt, unsigned width);
void init(PARAM_STRING txt, unsigned width);
void locate_cursor(unsigned para_cursor, unsigned &noline, unsigned &line_cursor);
unsigned get_para_cursor (unsigned noline);
unsigned get_para_home (unsigned noline);
unsigned get_para_end (unsigned noline);
unsigned get_line_cursor (unsigned noline);
};
static float para_fontsize=1;
static unsigned displaylen_call=0;
static unsigned journey_displaylen (PARAM_STRING txt)
{
displaylen_call++;
return tlmpweb_displaylen (txt.ptr,para_fontsize);
}
PARAGRAPH::PARAGRAPH()
{
}
PARAGRAPH::PARAGRAPH(PARAM_STRING txt, unsigned width)
{
init (txt,width);
}
void PARAGRAPH::init(PARAM_STRING txt, unsigned width)
{
tb.clear();
words.clear();
lines.clear();
line_homes.clear();
line_ends.clear();
str_splitline (txt.ptr,' ',tb);
for (auto &w:tb){
w.insert(w.begin(),' ');
words.emplace_back(w.c_str(),journey_displaylen(w));
}
unsigned offset = 0;
unsigned wordnum = 1;
while (wordnum < words.size()){
// We assemble a line made of words until its width is larger than the div width.
auto &wo = words[offset];
if (wo.txt[0] == ' '){
// Remove the space for the first word
wo.txtlen--;
wo.txt++;
wo.len = journey_displaylen(wo.txt);
}
unsigned linelen = wo.len;
for (unsigned i=offset+1; i<=wordnum; i++) linelen += words[i].len;
//htmlprintf ("linelen=%u width=%u lines.size()=%lu \n",linelen,width,lines.size());
if (linelen < width){
wordnum++;
}else{
vector line;
for (unsigned i=offset; i line;
for (unsigned i=offset; i para_cursor){
// Ok the cursor is in that word
string tmp (w.txt,para_cursor-offset_cursor);
//htmlprintf ("last word :%s: \n",tmp.c_str());
cursor_pos += journey_displaylen(tmp);
break;
}
cursor_pos += w.len;
offset_cursor = next_offset;
}
}
break;
}
para_cursor -= line_size;
}
// We know the cursor horizonl position in pixel: cursor_pos
// Now we will find the corresponding cursor position in each lines of the paragraph.
// This will be stored in line_offsets. This is the cursor position in character.
line_cursors.clear();
for (auto &l:lines){
unsigned pixel_offset = 0;
unsigned offset = 0;
for (auto &w:l){
unsigned next_pixel_offset = pixel_offset + w.len;
if (next_pixel_offset > cursor_pos){
// The cursor is located in that word.
const char *s = w.txt;
string tmp;
unsigned target_pos = cursor_pos - pixel_offset;
while (*s != '\0'){
tmp += *s++;
#if 0
htmlprintf ("line=%lu tmp=:%s: offset=%u target_pos=%u dlen=%u \n"
,line_cursors.size(),tmp.c_str(),offset,target_pos,journey_displaylen(tmp));
#endif
if (journey_displaylen(tmp) > target_pos) break;
offset++;
}
break;
}
pixel_offset = next_pixel_offset;
offset += w.txtlen;
}
line_cursors.push_back(offset);
}
}
unsigned PARAGRAPH::get_para_home (unsigned noline)
{
unsigned ret = 0;
if (noline < line_cursors.size()){
return line_homes[noline];
}
return ret;
}
unsigned PARAGRAPH::get_para_end (unsigned noline)
{
unsigned ret = 0;
if (noline < line_cursors.size()){
return line_ends[noline];
}
return ret;
}
unsigned PARAGRAPH::get_para_cursor (unsigned noline)
{
unsigned ret = 0;
if (noline < line_cursors.size()){
return line_homes[noline] + line_cursors[noline];
}
return ret;
}
unsigned PARAGRAPH::get_line_cursor (unsigned noline)
{
unsigned ret = 0;
if (noline < line_cursors.size()) ret = line_cursors[noline];
return ret;
}
/*
We display a text in a div having a specified width. It forms a paragraph.
The cursor represents the offset in the text as if the text was displayed on a single line.
We do two job here. We try to convert the linear cursor into a line number and of column number inside the line.
Then we try to find the corresponding column number in each line of the paragraph.
We display first the paragraph with the cursor shown as a |.
Then we display the paragraph with | on each line.
*/
static void journey_paragraph(unsigned width, const char *txt, unsigned cursor)
{
DIV d; d.border(1,"black").wpx(width).print();
unsigned len = strlen(txt);
if (len > cursor){
htmlprintf ("%*.*s%c%s",cursor,cursor,txt,txt[cursor],txt+cursor+1);
}else{
htmlout (txt);
for (unsigned w=cursor; w\n");
htmlprintf ("exec=%Lu.%06Lu noline=%u cursor_pos=%u dispplaylen=%u \n",diff/1000000,diff%1000000,noline,para.cursor_pos,displaylen_call);
//for (auto o:line_offsets) htmlprintf ("offset=%u ",o);
htmlout (" \n");
// We make the DIV larger so the browser paragraph formatting is not used.
// We display the paragraph line by line as PARAGRAPH understands it.
DIV dd; dd.border(1,"black").wpx(width+100).print();
for (unsigned i=0; i\n",offset,offset,pts,pts+offset);
}
dd.end();
DIV ddd; dd.border(1,"black").wpx(width).print();
unsigned cur = para.get_para_cursor(noline);
htmlprintf("%*.*s%c%s \n",cur,cur,txt,txt[cur],txt+cur+1);
/-
}
void util_defstyles();
/*
See if tlmpweb_displaylen() is accurate
*/
static void journey_displaylen()
{
util_defstyles();
htmlprintf ("fontsize=%u \n",tlmpweb_fontsize());
for (auto s:{
"Inbox",
"hello",
"Hello_World",
"Jacques_Gelinas",
"Jacques_Gelinas@alpha.bolixo.org",
"Jacques_Gélinas",
"Jacques_Gélinas@alpha.bolixo.org",
"Jacques_éèôÉÀ",
"Jacques_éèôÉÀ@alpha.bolixo.org",
}){
test_displen(s,"Arial");
}
for (auto f:{
"Times New Roman", "Times", "serif",
"Arial", "Helvetica", "sans-serif",
"Lucida Console", "Courier", "monospace"
}){
htmlprintf ("%s \n",f);
test_displen("hello how are you today ? How was it yesterday ? hello how are you today ? how was it yesterday ? And now How do you feel ?",f);
}
if (!tlmpweb_is_repost()){
/-
}
#if 1
string txt1 = "hello how are you today ? How was it yesterday ?";
string txt;
for (int i=0; i<4; i++) txt += txt1;
for (auto cursor:{15,115}){
htmlprintf ("CURSOR=%d \n",cursor);
for (auto w:{400,500,600}){
journey_paragraph(w,txt.c_str(),cursor);
}
}
#endif
}
static void journey_keypress()
{
?>
}
/*
Try to move something over an SVG
*/
static void journey_animation()
{
?>
unsigned width=600;
unsigned height=600;
htmlprintf ("
}
static void journey_chat()
{
?>
}
static W_SSTRING w_upload ("upload");
/*
Customize the look of input type=file
*/
static void journey_filebutton()
{
util_defstyles();
?>
#if 1
?>
#endif
("f");
#if 0
DIV ddd; ddd.w(100).dispflex().flowrow().border(1,"red").print();
//DIV dddd; dddd.flexfixe().border(1,"blue").print();
field_file_attach (w_upload);
//dddd.end();
//dddd.style("margin-left","auto").flexfixe().border(1,"orange").print();
button_send ();
#else
DIV ddd; ddd.w(100).dispflex().flowrow().border(1,"red").print();
DIV dddd; dddd.flexfixe().border(1,"blue").print();
?>
dddd.end();
//
DIV dd; dd.flexfixe().border(1,"blue").style("margin-left","auto").print();
button_send();
#endif
return 0;
}
static void journey_forminforms()
{
util_defstyles();
/- You enter s to get to the sub-dialog.
/-
/- You enter ok to end the sub-dialog
/-
("f");
/- file
field_string (w_file,"");
glocal bool ret = true;
tlmp_warning ("validate1 name=:%s:",w_name.c_str());
htmlprintf ("w_file = :%s: \n",w_file.c_str());
return glocal.ret;
glocal bool fail = false;
htmlprintf ("Process file=:%s: \n",w_file.c_str());
if (strcmp(w_file.c_str(),"s")==0){
glocal.fail= true;
("f");
DIV d; d.bg("blue").print();
d.donotend();
/-
/- name
field_string (w_name,"");
bool ret = false;
tlmp_warning ("validate2 name=:%s:",w_name.c_str());
if (strcmp(w_name.c_str(),"ok")==0){
ret = true;
}else{
/- You must enter ok
}
htmlprintf ("subform validate ret=%d \n",ret);
return ret;
htmlprintf ("name=:%s: file=:%s:\n",w_name.c_str(),w_file.c_str());
glocal.fail = false;
/-
}
fail = glocal.fail;
keepediting = true;
/-
htmlprintf ("Restart\n",tlmpweb_curpage());
}
/*
Generate the javascript code to fetch a video segment from the server
*/
static void journey_generate_fetchsegment()
{
?>
}
/*
Generate a fetchsegment that simply create buffers using a local (server side) file
*/
static void journey_generate_jsfetch()
{
/-
}
/*
Try to play a video using mediasource
*/
static void journey_videostream()
{
if (0){
journey_generate_fetchsegment();
}else{
journey_generate_jsfetch();
}
?>
}
static void journey_webcam()
{
alo
fin
?>
}
static void journey_webrtc()
{
alo
fin
?>
}
/*
Display a grid holding video tags.
This is a prototype for the video conférence. Each guest is displayed in his own video tag.
Another video tag goes on top, or is hidden, showing the active guest
*/
static void journey_manyvideos()
{
alo
?>
///- for (int i=0; i<10; i++) printf ("
\n",i);
}
static void journey_selectinput()
{
?>
}
extern "C" void webmain()
{
tlmpweb_title ("This is the start of a long long journey");
//test1();
//bolixo();
//newtags();
//form_space();
//scrollbar();
//tabs();
//popup();
//webtable_dropdown();
//webtable_popup();
//journey_setscroll();
//journey_subtabs();
//journey_click();
//journey_formanim();
//journey_displaylen();
//journey_keypress();
//journey_animation();
//journey_chat();
//journey_filebutton();
//journey_forminforms();
//journey_videostream();
//journey_webcam();
//journey_webrtc();
//journey_manyvideos();
journey_selectinput();
}