GUI Browser(client) - Server (03-2008/12-2009/12-2010)

Aanleiding .

In de huidige tijd neemt een gebruiker geen genoegen meer met een simple commandbox .
Het moet muis gestuurd zijn en mogelijk in kleur en voorzien zijn van duidelijke iconen .

Daar bijna ieder window-systeem weer zijn eigen , volstrekt incompatibel , aansturing kent
betekend dit dat je een GUI voor bijna ieder verschillend systeem moet maken .
Zo is MSWindows weer anders dan GTK , welk weer verschilt van QT .

Er zijn , mij , een paar systemen bekent die OS en Window onafhangkelijk zijn .
Een is JAPI . Een windowsysteem dat voor veel toepassingen een wrapper kent naar Java .
De andere is GraphAppl wel een window systeem is gebouwd op MSWindows en XWindows .

Een andere benadering is gebruikt te maken van een Browser welke als besturing het HTML protocol
gebruiken . Dit is geheel OS onafhankelijk . Ofschoon er wel verschil zit in de uitvoering van de
browsers . Mozilla (en wat daar vanaf stamt) houd zich goed aan de W2C afspraken .
IE houd er zo af en toe wel een zeer speciaal idee opna hoe de W2C afspraken worden uitgevoerd .
Safira (Mac) heb ik geen ervaring mee .

Een mooie desktop-toepassing van zo'n Browser interface is die zoals gebruikt door eyeOS .

Mijn idee is dat je een GUI zou moeten kunnen maken door een Browser als uitvoer tegebruiken .
Dit betekend dat je programma een server met HTTP protocol moet gaan bevatten .
Dan kan je programma de uitvoer maken in HTML/CSS en via de server sturen naar de Browser .
De Browser stuurd dan de formulieren terug naar de server . Deze herformateerd deze zodanig
dat je programma ze kan gebruiken als invoer .


                                     !--------------------------------!
	!---------!                  ! !--------!      !------------! !
	! Browser ! ... < ---- > ... ! ! server ! <--> ! Toepassing ! !
	!---------!                  ! !--------!      !------------! !
                                     !--------------------------------!
 
       je in/uit voer                         je applicatie


Wensen :

1) Eigenlijk wil ik dat we meerdere zelfde applicatie kunnen starten .
2) Tevens wil ik dat als de gebruiker het window waarin de applicatie getoond wordt sluit 
   dit resulteerd in het gedrag dat de applicatie zich ook afsluit .
3) Eigenlijk wil ik dat het beeindigen van de applicatie ook het window in de browser zich sluit .

--------------------------------------------------------------------------------------------------

Probleem 1) is bedacht dat de gestarte applicatie zoekt naar een vrije poort .

Voor XP vond ik dat 4990-5000 vrij zijn . Waardat gedeffineerd wordt , geen idee .
Want vanaf 5001 worden de aanvragen door XP geweigerd .
 
Voorbeeld : 
      data serveradres/'127.0.0.1:'/
      serverpoort = 4990
      server = 0
c     zoek een niet gebruikte poort 
      do while((server.ge.0).and.(serverpoort.le.5001))
       serverpoort=serverpoort+1
       write(serveradres(11:14),'(I4,$)')serverpoort
       write(6,*)serveradres
c      mode = client
       mode='c'
       server=netwerk_open(serveradres,mode)
c      indien gevonden geeft dit fouten aan de server site 
c      daar er geen HTTP is . Dit wordt gezien als een EOF 
       if(server.ge.0)call netwerk_close(server)
      end do
c     mode = server
      mode='s'
      server=netwerk_open(serveradres,mode)
      if(server.lt.0)then
        write(6,*)'error cann''t open server'
        stop
      end if
c     start browser
      call system('defaultbrowser http://'//serveradres)
c     return to application
Dus de browser wordt gestart zodra de server is gestart . Dit houdt wel in dat de browser als backgroundjob c.q. parallel process moet kunnen worden gestart . In Linux start je een backgroundjob door achter de commandline een & te zetten . In Windows start je een backgroundjob door de commandline te beginnen met de opdracht 'start' . In XP windows geld voor defaultbrowser : @echo off start c:\"Program Files\Mozilla Firefox"\firefox.exe %1 In Linux : #!/bin/sh firefox @1 & ------------------------------------------------------------------------------------------------------ Het probleem voor 2) is wat ingewikkelder . Er wordt heel wat over geschreven als je wat zoekt op Internet . Maar geen van al de aangedragen oplossing werkt echt bevredigend . Het basis probleem is dat een Browser geen informatie terug geeft als een gebruiker de browser sluit . Opzich logisch als je bedenkt dat de gebruiker feitelijk de browser stopt , hoe kan deze dan wat doen . Omdit niet alte ingewikkeld te krijgen heb ik teruggegrepen op een heel oud principe n.l. clock-tick . De server staat de heletijd te kijken of er een aanvraag komt van de Browser . (Hiertoe moet de onderliggende ACCEPT() functie niet geblokeerd is ! Je kunt de socket openen met een UNBLOCK of de SELECT() functie gebruiken met een time-out . Het openen van een UNBLOCK socket geeft 100% cpu bezetting in XP . Daarom gebruik ik de select() met een time-out van 1 sec . ) Laat de browser , als er een tijd niets gebeurd zelf , een (klok)tick naar de server sturen . Dit geeft de server de informatie dat de gebruiker het window nog aktief heeft (127.0.0.1:4992/tick) . De server kan opgrond van deze aanvraag de time-out weer op 0 zetten en een antwoord sturen . Voor dit antwoord heeft het HTTP protocol een dummy antwoord wat de aanwezige pagina niet wijzigd . Hiervoor zorg de response header HTTP/1.1 204 OKE . Komt er gedurende enige tijd geen tick dan start de server een process om de applicatie te sluiten . Voorbeeld server accept :
       client=0
       timeout=time()+75
       do while(client.eq.0)
c       mode = accept
        client=netwerk_open(server,mode)
        if (timeout.le.time())then
c         time-out error : close server and dump program
          call netwerk_close(server)
          call netwerk_end()
          write(6,*)'Progam abort due Time-Out at '
          stop
        end if
       end do
De server heeft een time-out van 60+15 sec dit daar de browser een tick geeft om de 60 sec . Ieder antwoord wat gestuurd wordt naar de browser met HTTP1.1/ 200 OKE moet nu een script meekrijgen dat om de 60 sec een tick stuurd als er niet anders gebeurd is . Dit wordt gedaan door in het antwoord onderstaand java-script meetesturen .
   <script Language=JavaScript>
         function tik() {setTimeout("tik()",60000);window.location.pathname="/tick"}
         setTimeout("tik()",60000)
     </script>
----------------------------------------------------------------------------------------------------- Probleem 3) is niet in alle browser optelossen . In IE kun je een window.opener="onzin";window.close() het venster sluiten . In de Mozilla versie is dit na versie 2.x en hoger niet meer mogelijk . Het argument van de Mozilla groep is dat alleen de gebruiker een venster kan sluiten . Behalve als een venster is gemaakt door een script dan kan deze ook door een script worden gesloten . ----------------------------------------------------------------------------------------------------- Subroutine fserver.f Allereerst moet je de server initieeren d.m.v. call netserver . Dit opend een Webbrowser met http://ipadres:poort . De kommunikatie gaat via een commonblok /serverdata/cmd,cmdbuf cmd geeft de opdracht weer en cmdbuf de data . Je hoofd programma is een lus welke begin met : call command_from_client Op grond van cmd en/of cmdbuf moet je nu een antwoord geven . Dit kan zijn : 1 regel text call responsetext(text-line) 1 bestand call responseclient(file-name) of een intern gemaakt html antwoord call write_open_client(aanhef) gevolgd door de teschrijven text via het common cmdbuf call write_client_buf of d.m.v. een text regel call write_client(text-line) afsluiten met (wat ook een </body></html> geeft) call write_close_client De subroutne fserver.f heeft tevens nodig het programma netlib.c deze bevat de interface naar de netwerkroutines voor Linux en MSWindows en de wrappers naar Fortran . ------------------------------------------------------------------------------------------------------- Pagina opbouw Ofschoon er vele mogelijkheden zijn heb ik gekozen om de pagina optebouwen m.b.v. construktie . Als eerste wordt een routine new_page(titel,style-file,achtergrond-kleur) aangroepen . Deze zorgt voor de de opbouw van een HTML aanhef met style-file als style-sheet . Nu wordt een table gedefinieerd . De routine programtitle() vult de eerste rij van de tabel met een aanhef van iets als : naam van het programma , versie nummer , datum , tijd . De routine menutab(menu) vult de tweede rij met een menu selectie . open , bewerk , info . De hierop volgende rijen kunnen gevuld worden met datgene wat het antwoord is op een menu-selectie . Afgesloten wordt het geheel met een write_close_client(client) welke tevens een </body></html> zend . ------------------------------------------------------------------------------------------------------- Procedure's pagina opbouw add_table(class,align,width,border) add_row(class,align,width,colspan) add_col(class,align,width,colspan) add_text(class,align,text-string) add_form(form-name) add_text_input(class,accesskey,size,name,value) add_href(class,accesskey,href,text) add_radio(class,accesskey,name,value,default,text) add_check(class,accesskey,name,value,default,text) add_reset(class,accesskey,name,value) add_submit(class,accesskey,name,value) een form moet je zelf afsluiten met </form> een tabel moet je zelf afsluiten met </table> De class is het nummer uit de CSS file die de afbeelding verfraaid . accesskey mag leeg zijn maar anders moet het een character bevatten . default is 1 als dit de vooringestelde selctie is . width is in procenten . Voorbeeld :
      subroutine zoekOrg()
      implicit integer *2 (a-z)
c     parameter voor align
      parameter (left=1,center=2,right=3)
c     parameter voor valign
      parameter (top=10,middle=20,bottom=30)
      call add_form('zo')
      call add_table(104,center,70,0)
c     defineer kopregel
c     lege regel
      call add_row(0,0,0,4,0)
      call add_text(0,0,' ')
c      title
       call add_row(109,0,0,4,0)
       call add_text(0,center,"Een organisatie zoeken")
       call add_row(0,0,0,4,0)
       call add_text(0,0," ")
c      linker helft
       call add_row(107,0,25,1,0)
       call add_col(107,0,25,1,0)
       call add_text(0,0,'Zoek naar')
c      rechter helft
       call add_col(107,0,0,2,0)
       call add_input_text(1000,ichar('z'),20,100,'')
c      commentaar
       call add_row(108,0,0,4,0)
       call add_text(0,0,'Geef het aansluitnummer of (deel van) de naam')
c     opdrachten
      call add_row(0,0,0,4,0)
      call d_oke_reset_niets()
c     uitleg sneltoetsen
      call add_row(0,0,0,4,0)
      call d_sneltoetsen
      call write_client('</table>')
      call write_client('</form>')
      end
Dit geeft als beeld :
Judo Tournooi Programmaversie Nov 30 2010Vandaag is : 
dinsdag 30 november 2010 14:06:28
 

 
Een organisatie zoeken
 
Zoek naar
Geef het aansluitnummer of (deel van) de naam
 
Opdracht
Terug
 
 
Sneltoetsen zijn gegeven door de hoofdletters .
(geeft shift+alt+letter) .
Waardit over gaat is weer heel iets anders , het gaat om het voorbeeld c.q. idee er achter . En niet of je de gekozen kleuren en of lettertypes mooi vind . Als je nu een "Oke" klik dan stuur de browser een vraag met http://server/zo?100='ingevuld' naar de server . Dit omdat de form 'zo' heet en de naam van het inputveld 100 is . ------------------------------------------------------------------------------------------------------- Toegangstijd Op de voorgestelde manier wordt iedere klik op het menu of submit beantwoord door een gehele nieuwe pagina over te sturen . Dit is redelijk snel zolang de script , css e.d. inline in de HTML text staan . Bij het laden van externe scripts en css neemt de toegangstijd (turn around time) beduidend af . Een mogelijke oplossing is gebruik te maken van dynamische pagina loading (XMLHttpRequest) ook wel AJAX gemeomd . Hiervoor gebruikt men dit javascript .
function createRequestObject() {
    var ro;
    var browser = navigator.appName;
    if(browser == "Microsoft Internet Explorer"){
        ro = new ActiveXObject("Microsoft.XMLHTTP");
    }else{
        ro = new XMLHttpRequest();
        /* only firefox 3 to get ride of the syntax error */
        if (ro.overrideMimeType) ro.overrideMimeType("text/plain");
    }
    return ro;
}

var http = createRequestObject();

function sndReq(action) {
 var noHttp = new Array();
 if (action.indexOf('_') != -1) {
    noHttp = action.split('_');
    /* standaard HTTP call */
    location.href=location.protocol+'//'+location.host+'/'+noHttp[1];
 } else {
    /* standaard AJAX call */
    http.open('get', action );
    http.onreadystatechange = handleResponse;
    http.send(null);
 }
}

function handleResponse() {
    if(http.readyState == 4){
        var response = http.responseText;
        var update = new Array();
        if(response.indexOf('|') != -1) {
            update = response.split('|');
            document.getElementById(update[0]).innerHTML = update[1];
        }
    }
}
Alle aanroepen zoals href en submit gaan via sndReq(action) . De action is de opdracht aan de server b.v. no_zo?100='ingevuld' of zo?100='ingevuld' . De eerste geeft aan de server een HTTP oproep die beantwoord moet worden met een HTTP header gevolgt door text(data) . De tweede geeft aan de server een AJAX oproep die beantwoord moet worden met text string ID(naam)|text(data) . Het antwoord van de server schrijft in dat deel van de pagina dat omvat is door <div ID=foo></div> . Het wijzigd de inhoud maar laat de rest van de pagina ongemoeit , dus geen exstra laad tijd . vb :
<div ID=FOO>Hier staat mijn onzinnige text</div>
.
.
<a href=javascript:sndReq(newtext)>Haal nieuwe text op . </a>
Nu stuurd de server terug : FOO|Dit is <b>nieuwe</b> text . ( het | is een scheidings teken , mag ook iets anders zijn .) Nu wordt de text "Hier staat mijn onzinnige text" vervangen door "Dit is nieuwe text" . In praktijk :
Hier staat mijn onzinnige text
Haal nieuwe text op .
(wil je de oude text weer zien doe een reload van de browser) ------------------------------------------------------------------------------------------------------- Submit Als je in een normale (HTTP oproep) een submit doet wordt het formulier keurig vertaald in b.v. http//:server/zo?100='ingevuld' Maar als je gebruikt maakt van de AJAX methode moet je deze vertaalslag zelf doen . Er is mij geen interne procedure bekent die dit doet .
  // Serializes a form
  // Will return a string which is a vald HTTP POST body for the given form
  // If no form is given, it will search for the _FIRST_ form on the page
  function serializeForm(level) {
    // Return value
    var retVal = '';
    // Getting ALL elements inside of form element
    var els = document.forms[level].getElementsByTagName('*');
    // Looping through all elements inside of form and checking to see if they're "form elements"
    for( var idx = 0; idx < els.length; idx++ ) {
      var el = els[idx];
      // According to the HTTP/HTML specs we shouldn't serialize disabled controls
      // Notice also that according to the HTTP/HTML standards we should also serialize the
      // name/value pair meaning that the name attribute are being used as the ID of the control
      // Though for Ra controls the name attribute will have the same value as the ID attribute
      if( !el.disabled && el.name && el.name.length > 0 ) {
        switch(el.tagName.toLowerCase()) {
          case 'input':
            switch( el.type ) {
              // Note we SKIP Buttons and Submits since there are no reasons as to why we 
              // should submit those anyway
              case 'checkbox':
              case 'radio':
                if( el.checked ) {
                  retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
                }
                break;
              case 'hidden':
              case 'password':
              case 'text':
                retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
                break;
            }
            break;
          case 'select':
          case 'textarea':
            retVal += '&' + el.name + '=' + encodeURIComponent(el.value);
            break;
        }
      }
    }
    if( retVal.length > 0 ) { retVal='?'+retVal.substring(1); }
    return retVal;
}
De form moet nu luiden : <form method=get action=javascript:sndReg(form-naam+serielForm(0))> . form-naam is de (action)naam die je aan de form geeft , de (0) slaat er op dat je maar 1 form hebt die heeft dan index 0 . ------------------------------------------------------------------------------------------------------- Slot opmerking Denk niet dat ik zo goed ben in Java , het meeste vindt je door rond te snuffellen op het Internet . Het menu werkt niet in IE . FileBrowsing geeft in IE het komplete pad doch in Firefox(3 en hoger) alleen de file naam . ------------------------------------------------------------------------------------------------------- Verbeteringen altijd welkom eMail mij dan of Skype mij .