# HTTP 1.0 web server+CGI-sample code ###### tags: `linux` `network programming` `http` `CGI` Author: CrazyMonkey E-mail: kccddb@gmail.com Date: 20230222 Copyright: CC BY-NC-SA <style> .blue { color: blue; } .red { color: red; } h1 {text-align: center;} </style> 現今 HTTP 除了 HTTP 1.0 與 HTTP 1.1 的差別外 還有一重要的改進: HTTP persistent connection (HTTP keep-alive) ![](https://i.imgur.com/beaONeB.png) :::success HTTP persistent connection, is an instruction that allows a <span class="bule">single TCP connection </span>to remain open for multiple HTTP requests and responses. ::: ![](https://i.imgur.com/7uG8x6m.jpg) 您可以使用 [Wireshark](https://www.wireshark.org/download.html) 幫助自己了解此現象! 詳見: RFC 2616 Hypertext Transfer Protocol -- HTTP/1.1 RFC 2616: Persistent HTTP connections have a number of advantages: - By opening and closing fewer TCP connections, CPU time is saved in routers and hosts (clients, servers, proxies, gateways, tunnels, or caches), and memory used for TCP protocol control blocks can be saved in hosts. - HTTP requests and responses can be pipelined on a connection. Pipelining allows a client to make multiple requests without waiting for each response, allowing a single TCP connection to be used much more efficiently, with much lower elapsed time. - Network congestion is reduced by reducing the number of packets caused by TCP opens, and by allowing TCP sufficient time to determine the congestion state of the network. - Latency on subsequent requests is reduced since there is no time spent in TCP's connection opening handshake. - HTTP can evolve more gracefully, since errors can be reported without the penalty of closing the TCP connection. Clients using future versions of HTTP might optimistically try a new feature, but if communicating with an older server, retry with old semantics after an error is reported. ![](https://i.imgur.com/mWuqK5j.jpg) *![](https://i.imgur.com/V6aClKn.jpg) <h1>Front-end/Back-end</h1> **此例子 這主要用 fork() 完成也沒有處理 timeout! 經不起壓力測試的!! demo 學習用 !** **練習 TRY IT, FIXME** [int dup2(int oldfd, int newfd);](http://man7.org/linux/man-pages/man2/dup.2.html) **shttpcgi.c** gcc shttpcgi.c -o shttpcgi shttpcgi 9000 TEST CGI: shttpcgi 9000 1 Author: WhoAmI 2019 ![](https://i.imgur.com/U9whQcK.png) 以下連結請務必研讀(引用自 [stackoverflow](https://stackoverflow.com/)) [You can use the SO_RCVTIMEO and SO_SNDTIMEO socket options to set timeouts for any socket operations.](https://stackoverflow.com/questions/4181784/how-to-set-socket-timeout-in-c-when-making-multiple-connections) [linux man dup2](https://www.man7.org/linux/man-pages/man2/dup.2.html) [linux man execvp](https://www.man7.org/linux/man-pages/man3/exec.3.html) [execve - execute program](https://www.man7.org/linux/man-pages/man2/execve.2.html) [linux man setenv](https://man7.org/linux/man-pages/man3/setenv.3.html) :::info 注意 backlog 的意義: The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. ::: <span class="red"> TCP SYN flood, server 會出問題, Why?</span> ![](https://i.imgur.com/ggANUSP.jpg) [int listen(int sockfd, int backlog);](https://www.man7.org/linux/man-pages/man2/listen.2.html) **很重要的理解 網路程式設計 的例子, 學以致用, 一定要實作** **當 process 執行時 事先 open 了 stdin (0), stdout (1), stderr (2)** Please study Probability, Statistics, OS and Algorithms. ```c= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/stat.h> #include <fcntl.h> #define debug001(x) #define debug000(x) x //CHECK THIS #define FORK 1 #define CYGWIN 1 #define READTIMEOUT 3 #define CANNOTREAD -110 //gcc httpcgi.c -o httpcgi #define debug(x) x #define HTTPHEADERLEN 1024 void PANIC(char* msg); #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } static void sig_handler(int signo) { pid_t pid; int stat; switch(signo) { case SIGCHLD: printf("SIGCHLD=%d\n",signo); pid=waitpid(0,&stat,WNOHANG); break; case SIGINT: printf("SIGINT=%d\n",signo); break; case SIGALRM: printf("Got SIGALRM=%d, exit!\n",signo); exit( EXIT_FAILURE); break; default: printf("signal=%d\n",signo); } return; } static off_t getfilesize(char *fname) { struct stat sb; /* off_t: total size, in bytes */ //%lld long long int if (stat(fname, &sb) == -1) { perror("stat"); return -1; } debug(printf("File size:%lld bytes\n",(long long) sb.st_size)); return sb.st_size; } char *readfile(char *fname, int *size) { off_t ssize; char *mem; int fd; int n; mem=NULL; ssize=getfilesize(fname); if(ssize>0) { fd=open(fname, O_RDONLY); if(fd>0) { mem=malloc(ssize); if(mem) { n= read(fd,mem,ssize); if(n>0) { *size=n; } else { *size=0; } close(fd); return mem; } else { close(fd); } } else { printf("Cannot open %s size=%lld",fname,ssize); //%lld long long int } }else{ printf("Cannot find %s\n",fname); return CANNOTREAD; } return NULL; } static void pdebug(char *argv[]) { int i; for(i=0;argv[i]!=NULL;i++) { printf("%s \n",argv[i]); } } static void http( struct sockaddr_in *client_addr ) { char buffer[1512]; char port[10]; int pid; #if CYGWIN //char *argv[3]={"./findp.exe","./findp.exe",0}; char *argv[3]={"ls","-l",0}; #else char *argv[3]={"./cgi","./cgi",0}; #endif #if DEBUG read(0,buffer,sizeof(buffer)); printf("%s\n",buffer); #endif setenv("REMOTE_ADDR", inet_ntoa(client_addr->sin_addr), 1); sprintf(port,"%d",ntohs(client_addr->sin_port)); setenv("REMOTE_PORT", port, 1); #if DEBUG pdebug(argv); #endif pid=getpid(); printf("My pid is %d\n",pid); if(pid>0) { printf("execvp...<BR>\n"); pdebug(argv); execvp(argv[0], argv); } else { printf("This is the new child(pid=%d) <BR>\n",getpid()); exit(0); } } static int httpResopnseBody(int fd) { char *body="<html><body><h1>It works!</h1></body></html>"; int n; //FIXME: Locale n=write(fd,body,strlen(body)); return n; } static int httpResponse(int fd) { int n; char *header="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; //FIXME: Locale n=write(fd,header,strlen(header));//write header n=httpResopnseBody(fd); return n; } static ssize_t readln(int sockfd, char *buf,size_t len, int time_out) { int n; fd_set readfds; struct timeval tval; ssize_t total; int ret; char *pc; total=0; FD_ZERO(&readfds); do { FD_SET(sockfd, &readfds); tval.tv_sec = time_out; tval.tv_usec = 0; if(time_out<=0) { if(( n = select(sockfd+1, &readfds, NULL, NULL, NULL))<0)return -1; } else { if ( (n = select(sockfd+1, &readfds, NULL, NULL, time_out ? &tval : NULL)) == 0) { errno = ETIMEDOUT; printf("Read timeout!\n"); return(-1); } } ret=read(sockfd,buf+total,1); //Notice that '\n' pc= buf+total; if(ret>0) { total+=ret; if(*pc=='\n')break; } else if(ret==0) { //eof break; } else { return -1; } } while(total<len); if(total<len)buf[total]=0; else buf[len-1]=0; return total; } static int dump_http(int fd) { //ssize_t read(int fd, void *buf, size_t count); char buf[HTTPHEADERLEN]; ssize_t n; size_t count; char *mem; int size; int line=0; char *fname,*p; char *header_html="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; char *header_jpg="HTTP/1.1 200 OK\nContent-Type: image/jpeg; charset=UTF-8\r\n\r\n"; char *header; count=sizeof(buf)-1; //FIXME: without timeout do { n=readln(fd,buf, count,READTIMEOUT); if(n>0) { buf[n]=0; //FIXME: CHECK THIS debug001(printf("[%d][%s]",line,buf)); debug000(printf("%s",buf)); if(line==0) { //fname=&buf[1]; //case 0: files p=strchr(buf,' '); if(p)fname=p+2; p=strchr(fname,' '); if(p)*p=0; debug001(printf("Read %s\n",fname)); header=header_html; if(strstr(fname,".jpg"))header=header_jpg; if(strstr(fname,".png"))header=header_jpg; mem=readfile(fname,&size); if(mem) { debug001(printf("Write %d bytes to %d...\n",size,fd)); write(fd,header,strlen(header)); write(fd,mem,size); free(mem); }else{ //cannot read file return CANNOTREAD; } } //case 1: open connection //----------------------------------------------------- //---------------------------------------------------------- line++; } else return n; } while(n>0); return n; } static int process_http(int fd) { int httpret=0; httpret=dump_http(fd); httpret=httpResponse(fd); return httpret; }//process_http int main(int argc, char *argv[]) { int sd; int port; int new_fd; int len; int state; int pid; int httpret; int size; char *fname="play.txt"; char *mem; int docgi=0; struct sockaddr_in server_addr,client_addr; if(argc>1)port=atoi(argv[1]); else port=80; if(argc==3)docgi=atoi(argv[2]); signal (SIGCHLD, sig_handler); //signal (SIGINT,sig_handler); //TRY IT! // signal (SIGALRM,sig_handler);//TRY IT! sd = socket(PF_INET, SOCK_STREAM, 0); memset(&server_addr,0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = INADDR_ANY; if ( bind(sd, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0 ) PANIC("Bind"); if ( listen(sd, 20) != 0 ) PANIC("Listen"); printf("Start server at port %d id=%d...",port,sd); while (1) { len = sizeof(client_addr); new_fd = accept(sd, (struct sockaddr*)&client_addr, &len); printf("%d,Client from %s:%d\n", new_fd,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if ( new_fd < 0 ) perror("Accept"); else { pid=fork(); if ( pid==0 ) { //child close(sd); httpret=process_http(new_fd); if(httpret<=0 || docgi>0) { dup2(new_fd, 0); dup2(new_fd, 1); dup2(new_fd, 2); close(new_fd); http( &client_addr ); wait(&state); } exit(EXIT_SUCCESS ); } close(new_fd); } } exit(EXIT_SUCCESS ); } ``` cgi.c ```c= #include <stdio.h> #include <stdlib.h> #include <time.h> #define QUERY_STRING getenv("QUERY_STRING") #define CONTENT_TYPE getenv("CONTENT_TYPE") #define CONTENT_LENGTH getenv("CONTENT_LENGTH") #define REQUEST_METHOD getenv("REQUEST_METHOD") #define REMOTE_HOST getenv("REMOTE_HOST") #define REMOTE_ADDR getenv("REMOTE_ADDR") #define REMOTE_PORT getenv("REMOTE_PORT") print_environment() { printf("<B>REQUEST_METHOD:</B> %s<BR>\n",REQUEST_METHOD); printf("<B>CONTENT_TYPE:</B> %s<BR>\n",CONTENT_TYPE); printf("<B>CONTENT_LENGTH:</B> %s<BR>\n",CONTENT_LENGTH); printf("<B>QUERY_STRING:</B> %s<BR>\n",QUERY_STRING); printf("<B>REMOTE_HOST:</B> %s<BR>\n",REMOTE_HOST); printf("<B>REMOTE_ADDR:</B> %s<BR>\n",REMOTE_ADDR); } static char *header="HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\r\n\r\n"; int main(int argc, char* argv[]) { time_t t; t=time(NULL); printf("%s",header); printf("<html><body>"); printf("<BR>Hello CGI!<BR> %s <BR><p style='color:blue;'>[Your IP:PORT %s:%s]</p><BR>",ctime(&t ),REMOTE_ADDR, REMOTE_PORT); print_environment(); printf("</body></html>\n\n"); } ``` Read Around: [Show the environment variables of a running process in Linux](https://ma.ttias.be/show-the-environment-variables-of-a-running-process-in-linux/) [How to Set Environment Variables in Linux](https://phoenixnap.com/kb/linux-set-environment-variable) [Environment Variables in Linux/Unix](https://www.geeksforgeeks.org/environment-variables-in-linux-unix/) [The /proc Filesystem](https://www.kernel.org/doc/html/latest/filesystems/proc.html?fbclid=IwAR1hIgeSAeepfZLXse_GaBomk33At17eVnL-mGNLYESIVrz2gX9T2UVPdsM) e.g., kclai@ubuntu16:/proc/1867$ sudo cat environ [sudo] password for kclai: XDG_SESSION_ID=3SHELL=/bin/bashSSH_CLIENT=192.168.1.10 5376 22USER=kclaiMAIL=/var/mail/kclaiPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/binPWD=/home/kclaiLANG=en_US.UTF-8SHLVL=1HOME=/home/kclaiLOGNAME=kclaiSSH_CONNECTION=192.168.1.10 5376 192.168.1.31 22XDG_RUNTIME_DIR=/run/user/1000_=/usr/lib/openssh/sftp-serverkclai@ubuntu16:/proc/1867$ --- **Homewok** 實作與思考的問題 1. **練習 Wireshark 觀察 HTTP 1.0 與 1.1 的不同** 2. ```c= dup2(new_fd, 0); dup2(new_fd, 1); dup2(new_fd, 2); close(new_fd); ``` Why?  [I/O redirection 運用 Linux system call 的實作方法 (redirect, pipe, …)](https://hackmd.io/@pingulinux/io-redirection) 3. 設計一 http server 可以 讓 user 由 browser 看到 文字與圖片 4. HTTP 中 Content-Length 有何用途? Why? 5. CGI 有何實務上的問題? 如何解決? (HINT: FastCGI) 6. HTTP keep-alive, Why? 與 TCP keep-alive 有何不同? 7. 如何取得 HTTP GET 的參數? :::info 附註: 因圖中有 **AJAX(Asynchronous JavaScript and XML)** **盡可能 使用 AJAX POST 不要用傳統 FORM submit!** **AJAX: Update a web page without reloading the page** 盡可能 用 CSS 有些 夾雜 格式 於 HTML 中 方便學習, 但卻是不當的方法 不要期待 團隊裡成員 都懂 Javascript, PHP. 例如 美工可能不懂~ 這是**故意**設計的例子把 callback, getElementsByName 加入 AJAX POST, callback and getElementsByName ```c= <!DOCTYPE html> <!--Author: WhoAmI --> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <style> body { background-color: lightblue; } .redButton { background-color:red; } </style> </head> <body> <h2>盡可能 使用 AJAX POST 不要用傳統 FORM submit!</h2> <h2>AJAX POST, callback and getElementsByName (call tdemo.php)</h2> First Name: <input name="tName" type="text" value="Linux"><br> First Name: <input name="tName" type="text" value="John"><br> city: <input name="cName" type="text" value="taichung"><br> city : <input name="cName" type="text" value="taipei"> <button onclick="searchdb()" class="redButton" >AJAX POST</button> <!-- Display Server Response --> <div id="result"></div> <script> //AJAX //callback function function myCallback(xmldoc){ document.getElementById("result").innerHTML=xmldoc.responseText; } function myPost(url, data, callback) { var xmlDoc = new XMLHttpRequest(); xmlDoc.open('POST', url, true); xmlDoc.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlDoc.onreadystatechange = function() { if (xmlDoc.readyState === 4 && xmlDoc.status === 200) { callback(xmlDoc); } } xmlDoc.send(data); } //setup POST data function searchdb() { //getElementsByName():returns a collection of all elements ( NodeList ) list=document.getElementsByName("tName"); console.error(list); var tvalue=[]; list.forEach( function(currentValue, currentIndex, listObj) { tvalue.push(currentValue.value); console.error(tvalue+' ::'+currentValue + ', ' + currentIndex + ', ' + this); }, 'myThisArg' ); list=document.getElementsByName("cName"); console.error(list); var cvalue=[]; list.forEach( function(currentValue, currentIndex, listObj) { cvalue.push(currentValue.value); console.error(cvalue+' ::'+currentValue + ', ' + currentIndex + ', ' + this); }, 'myThisArg' ); query='tName='+tvalue+'&cName='+cvalue; //write your jdemo.php myPost('tdemo.php',query,myCallback); } </script> </body> </html> ``` tdemo.php ```c= <?php if(isset($_POST["tName"])){ $tName=$_POST["tName"]; echo "Input:<br>"; print_r($tName); } echo "<br>"; if(isset($_POST["cName"])){ $cName=$_POST["cName"]; echo "Select:<br>"; print_r($cName); } ``` ::: JavaScript 定義 class 方便使用 CSS **jelems-1.0.js** ```cpp function crSelect(name,array,tarray=null,myclass="default-class"){ var selectList = document.createElement("select"); selectList.className = myclass; selectList.name=name; //Create and append the options for (var i = 0; i < array.length; i++) { var option = document.createElement("option"); option.value = array[i]; if(tarray===null) option.text = array[i]; else option.text = tarray[i]; selectList.appendChild(option); } return selectList; } function crBtn(name,click=null,myclass="default-class") { var e=document.createElement("INPUT"); e.setAttribute("type","button"); e.className=myclass; e.value=name; if(click!==null)e.addEventListener("click",click); return e; } function crImg(src,click=null,myclass="default-class") { var e=document.createElement("INPUT"); e.setAttribute("type","image"); e.src=src; //"img/enter.png"; if(click!==null)e.addEventListener("click",click); e.className=myclass; return e; } function createImage(src,click=null,myclass="default-class"){ var node = document.createElement('img'); node.src = src; node.className=myclass; if(click!==null)node.addEventListener("click",click); return node; } function crList(click=null,myclass="default-class"){ var data="item 1 click it!"; var node = document.createElement('li'); node.appendChild(document.createTextNode(data)); if(click!==null)node.addEventListener("click",click); node.className=myclass; return node; } function crLists(items,click=null,myclass="default-class"){ var ul = document.createElement('ul'); ul.setAttribute("class", "lists"); items.forEach((data)=>{ let li=document.createElement('li'); li.className=myclass; if( data.nodeType == Node.ELEMENT_NODE){ li.appendChild(data); }else { if(typeof data ==="string"){ let isimg= data.match(/\.(jpeg|jpg|png|gif|bmp)/i); if(isimg){ //automatic convert to image let img=createImage(data); li.appendChild(img); }else li.appendChild(document.createTextNode(data)); }else li.appendChild(document.createTextNode(data)); } //li.appendChild(document.createTextNode(data)); // console.warn(ul); if(click!==null)li.addEventListener("click",click); ul.appendChild(li); }); ul.className=myclass; return ul; } //creatre a TR via array function crTR(fields,click=null,myclass="default-class"){ let td; let tr = document.createElement('tr'); fields.forEach((field)=>{ td=document.createElement('td'); if( field.nodeType == Node.ELEMENT_NODE){ td.appendChild(field); }else{ if(typeof field ==="string"){ let isimg= field.match(/\.(jpeg|jpg|png|gif|bmp)/i); if(isimg){ //automatic convert to image let img=createImage(field); td.appendChild(img); }else td.appendChild(document.createTextNode(field)); } } tr.appendChild(td); }); if(click===null){ }else tr.addEventListener("click",click); tr.className=myclass; return tr; } //creatre a TR via array function crTable(cfields,click=null,myclass="default-class"){ let table=document.createElement('table'); cfields.forEach((fields)=>{ let tr= crTR(fields,click,myclass); table.appendChild(tr); } ); table.className=myclass; return table; } function insertRow(index=0,table,fields,myclass="default-class") { //index=0 Insert new row(s) at the first position of a table row=table.insertRow(index); let i=0; fields.forEach((field)=>{ cell=row.insertCell(i); //cell.appendChild(document.createTextNode(field)); if( field.nodeType == Node.ELEMENT_NODE){ cell.appendChild(field); }else{ if(typeof field ==="string"){ let isimg= field.match(/\.(jpeg|jpg|png|gif|bmp)/i); if(isimg){ //automatic convert to image let img=createImage(field); cell.appendChild(img); }else cell.appendChild(document.createTextNode(field)); } } i++; }); } //creatre a TH via array function crTH(fields,click=null,myclass="default-class"){ let td; let tr = document.createElement('th'); fields.forEach((field)=>{ td=document.createElement('td'); if( field.nodeType == Node.ELEMENT_NODE){ td.appendChild(field); }else{ td.appendChild(document.createTextNode(field)) } tr.appendChild(td); }); if(click===null){ }else tr.addEventListener("click",click); tr.className=myclass; return tr; } function getCells(elem,callback=null) { var result=[]; for (let i = 0; i < elem.length; i++) { if(callback===null){ }else callback(elem[i]); result.push(elem[i].innerHTML); } console.log(result); return result; } function genhref(href, linkname, click=null, myclass="default-class"){ var a = document.createElement('a'); // Create the text node for anchor element. var link = document.createTextNode(linkname); // Append the text node to anchor element. a.appendChild(link); // Set the title. a.title = linkname; a.href =href; if(click!=null)a.addEventListener("click",click); a.className=myclass; console.warn(a); //a.appendChild(link); return a; } function crElems(fields,etype='div',click=null,myclass="default-class"){ var e=document.createElement(etype); // let aa=[]; // aa=fields; // console.warn("fields:"+fields); // console.error(fields); if( Array.isArray(fields)) { fields.forEach((field)=>{ if( field.nodeType == Node.ELEMENT_NODE){ e.appendChild(field); } }); }else if( fields.nodeType == Node.ELEMENT_NODE){ //console.warn("element:"+fields); e.appendChild(fields); }else { //other ? console.warn("others:"+fields); } e.className=myclass; return e; } function crDivs(cfields,click=null,myclass="default-class"){ let div=document.createElement('div'); cfields.forEach((fields)=>{ //console.warn("crDivs:"+fields); let el= crElems(fields,'div',click,myclass); div.appendChild(el); } ); div.className=myclass; //console.warn("final:"+div); return div; } function deleteID(id){ var e=null; var child; if( id.nodeType == Node.ELEMENT_NODE)child=id; else child=document.getElementById(id); try{ e=child.parentNode.removeChild(child); }catch(e){ console.warn(e); } return e; } function appendElement(id,e){ if( id.nodeType == Node.ELEMENT_NODE){ return id.appendChild(e); }else return document.getElementById(id).appendChild(e); } function crInput(type,name,value='',myclass="default-class",elem='input'){ //crInput('TEXT','NAME',null,'CNAME'); var input = document.createElement(elem); input.setAttribute("type", type); if(type.toLowerCase()=='image'){ input.src=value; }else{ input.value=value; } //if(click!=null)input.click=click; input.className = myclass; // set the CSS class input.name=name; return input; } ``` 使用方法例如: ``` <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="js/jelems-1.0.js"></script> <style> body { background-color: lightblue; } .MYTABLE { background-color :#99ccff; text-align :center; width:100%; color:green; border: 1px solid black; text-align: left; } .MYTABLE2 { background-color :#99ccff; text-align :center; width:100%; color:green; border: 1px solid black; text-align: center; } </style> </head> <body> <div id='showit'></div><div id='showit2'></div> <script> let tableitems=[['row0','img/apple.jpg'],['row1','cell1'],['row2','cell1']]; let table=crTable(tableitems,null,'MYTABLE'); document.getElementById('showit').appendChild(table); let tableitems2=[['img/apple.jpg'],['caption- apple'],['img/banana.jpg'],['Fig 2. banana'],['row2','cell1']]; let table2=crTable(tableitems2,null,'MYTABLE2'); document.getElementById('showit2').appendChild(table2); </script> </body> </html> ``` ![](https://i.imgur.com/bBfi4jq.jpg) ctable 1.1.js ```c= /* Licence:GPL Author: WhoAmI Date: 2021/01/01 Version:1.1 e-mail: Notice that setup cid( class ID) and table ID See also HTMLTableElement.rows */ //cTable 20220701 via WhoAmI function display(id,elem,callback=null){ var v; if(callback!==null)v=callback(elem); else v=elem; if(typeof elem ==='string')document.getElementById(id).innerText=v; if(typeof elem ==='object') return document.getElementById(id).appendChild(v); return v; } class cTable { parentid; tobj; border=''; //'1px solid black'; title; rowclick=undefined; //callback //Notice: class id!!!! elementcid; //element ID elementid; firstrow; constructor(cid,tid='GEN_TABLEID'){ this.elementcid=cid; this.elementid=tid; //default parrent's id this.parentid=document.body; } //return parent show(id=this.parentid){ if(id==='undefined'){ var body= document.body; }else { var body=document.getElementById(id); } body.appendChild(this.tobj); return body; } //create table element create(objectArray, fieldTitles,trclass='TR_NAME') { this.title=fieldTitles; var _tobj = document.createElement('table'); _tobj.classList.add(this.elementcid); let thead = document.createElement('thead'); let thr = document.createElement('tr'); this.firstrow=thr; thr.className=trclass; if(this.rowclick!==undefined)thr.onclick=this.rowclick; fieldTitles.forEach((fieldTitle) => { //console.error(fieldTitle); let th = document.createElement('th'); th.className=trclass; th.appendChild(document.createTextNode(fieldTitle)); thr.appendChild(th); }); thead.appendChild(thr); _tobj.appendChild(thead); let tbdy = document.createElement('tbody'); let tr = document.createElement('tr'); objectArray.forEach((object) => { // console.error(object); let tr = document.createElement('tr'); tr.className=trclass; if(this.rowclick!==undefined)tr.onclick=this.rowclick; let i =0; this.title.forEach((field) => { var td = document.createElement('td'); if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); }else{ //find jpg image // if(object[i].match(/.jpg/g)){ if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } } tr.appendChild(td); i++; }); tbdy.appendChild(tr); }); _tobj.appendChild(tbdy); this.tobj=_tobj; return _tobj; } //delete one record delete(index=0){ var tbdy=this.tobj; tbdy.deleteRow(index); return tbdy.rows.length; } //select element createSelect(name,cname,array){ var selectList = document.createElement("select"); selectList.className = cname; selectList.name=name; //Create and append the options for (var i = 0; i < array.length; i++) { var option = document.createElement("option"); option.value = array[i]; option.text = array[i]; selectList.appendChild(option); } return selectList; } //img createImage(src,cname='IMG_CLASS'){ var img = document.createElement('img'); img.src = src; img.className=cname; return img; } createInput(type,name,cname,elem='input',value=''){ //createInput('TEXT','NAME','CNAME'); var input = document.createElement(elem); input.setAttribute("type", type); if(type.toLowerCase()=='image'){ input.src=value; }else{ input.value=value; } input.className = cname; // set the CSS class input.name=name; return input; } //insert one record insert(objectArray,trclass='TR_NAME' ){ //let body = document.getElementsByTagName('body')[0]; var tbdy=this.tobj; objectArray.forEach((object) => { let tr = document.createElement('tr'); if(this.rowclick!=='undefined')tr.onclick=this.rowclick; tr.className=trclass; let i =0; this.title.forEach((field) => { //let i =0; var td = document.createElement('td'); //console.error(field+object[i]); //check nodType //console.error(object[i].nodeType); if( object[i].nodeType === undefined){ //FIXIT!!!! // if( object[i]!== undefined){ //Node.ELEMENT_NODE //console.log(object[i].innerHTML); //td.appendChild((object[i])); console.log("undefined:"+object[i]); if(typeof object[i] === "string"){ if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } }else if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); } }else{ // JS RegExp if(typeof object[i] === "string") { console.log(object[i]); if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } }else if( object[i].nodeType === Node.ELEMENT_NODE){ td.appendChild((object[i])); } } tr.appendChild(td); i++; }); /* *You can try firstChild, lastChild * */ tbdy.appendChild(tr); }); return this.tobj.rows.length; } /* * *FIXME!!! */ insertBefore(index, objectArray,trclass='TR_NAME' ){ //let body = document.getElementsByTagName('body')[0]; var tbdy=this.tobj; objectArray.forEach((object) => { let tr = document.createElement('tr'); if(this.rowclick!=='undefined')tr.onclick=this.rowclick; tr.className=trclass; let i =0; this.title.forEach((field) => { //let i =0; var td = document.createElement('td'); //console.error(field+object[i]); //check nodType //console.error(object[i].nodeType); if( object[i].nodeType == undefined){ //FIXIT!!!! // if( object[i]!== undefined){ //Node.ELEMENT_NODE //console.log(object[i].innerHTML); //td.appendChild((object[i])); td.appendChild(document.createTextNode(object[i])); }else{ // JS RegExp if(object[i].match(/[\/.](gif|jpg|jpeg|tiff|png)$/i)){ let img=this.createImage(object[i]); td.appendChild(img); }else{ td.appendChild(document.createTextNode(object[i])); } } tr.appendChild(td); i++; }); /* *You can try firstChild, lastChild * *myrows = mytable.rows; *firstRow = mytable.rows[0]; *lastRow = mytable.rows.item(mytable.rows.length-1); *parentNode */ let firstRow=tbdy.rows[index]; firstRow.parentNode.insertBefore(tr,firstRow); //tbdy.insertBefore(tr,firstRow); //tbdy.appendChild(tr); }); return this.tobj.rows.length; } } //end of class ``` ```c= <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script src="ctable-1.1.js"></script> <style> body { background-color: lightblue; } .IMG_CLASS{ width: 50%; } .demodiv { border-style: solid; width:80%; } .TR_NAME { background-color: green; border-style: solid; } </style> </head> <body> <h2>ctable.js TEST</h2> <div id="elem"></div> <div id="demodiv"></div> <script> //demo class cTable tobj=new cTable('demodiv'); //tobj.border='1px solid black'; //Create INPUT TEborder-style: solid;XT var input=tobj.createInput('TEXT','tName','CNAME'); //setup onclick on input if you need it //input.onclick=add; //Create select var array=['car','fruit','dog']; //option array var sel=tobj.createSelect('sName','CNAME',array); document.getElementById('elem').appendChild(sel); //Create hidden var hidden=tobj.createInput('HIDDEN','sName','CNAME'); //setup onclick //hidden.onclick=add; //create button var btn=tobj.createInput('button','bName','BNAME','input','Submit'); //document.body.appendChild(input); //create checkbox var chk=tobj.createInput('checkbox','cName','CNAME'); //create image input var img=tobj.createInput('image','iName','CNAME','input','img/enter.png'); //create image var image=tobj.createImage('img/banana.jpg'); var ftitle=['--zero--','--one--','--two--','--three--']; var ffield=['ZERO',image,'TWO','THREE']; var ffield2=[image,chk,'img/dancing.gif','THREE']; //var iifield=[input,btn,image,sel]; tabobj=tobj.create([ffield2],ftitle); tobj.show('demodiv'); var iifield=[input,btn,'img/apple.jpg',sel]; //insert two arrays console.log(iifield); let index=tobj.insert([iifield,ffield2]); //insert textnode index=tobj.insert([ffield]); </script> </body> </html>