Nine weird things to code

So the weekend is here and you are sitting in front of your computer and don’t know what to do.
You are still bored after watching YouTube videos all day.

Don’t Worry, you have come to the right place. I will show you how to display funny text,
add a spinning 3D globe to your web page, build a calculator for your math homework or in case you work requires you to calculate the remaing fuel contents on your rocket entring an orbit around Mars. and more …

Here are 9 things you can code when you are bored.

Hello World

This “Hello World” jumps out and is a real attention seeking device. Your eyes are automatically caught paying more attention to it. If you want to drive people into madness you can display your whole page using this quirk.
You will certainly be rewarded with a firestorm of complaints.

<html>
<head><title>bored_1</title></head>
<body>

<p></p>&nbsp;<p></p>
<center><b><div id="OUT"></div></b></center>
<script>
  var div = document.getElementById ( "OUT" );
  div.innerHTML = "Hello World";
  
  function randomClr ( char )  {
    var r = parseInt ( Math.random ( ) * 255, 10 );
    var g = parseInt ( Math.random ( ) * 255, 10 );
    var b = parseInt ( Math.random ( ) * 255, 10 );
    var ret = "<font color='rgb(" + r + "," + g + "," + b + ");'>" + char + "</font>";
    return ret;
  }
  function randomType ( char )  {
    var ret=char, r = parseInt ( Math.random ( ) * 4, 10 );
    switch ( r )  {
      case 0: ret = "<b>" + char + "</b>"; break;
      case 1: ret = "<i>" + char + "</i>"; break;
      case 2: ret = "<sup>" + char + "</sup>"; break;
      case 3: ret = "<sub>" + char + "</sub>"; break;
    }
    return ret;
  }
  setInterval ( function ( ) {  
    var html="", text = "Hello World";
    for ( var t=0; t<text.length; t++ )  {
      var clr = randomClr ( text[t] );
      html += randomType ( clr );
    }
    div.innerHTML = "<h1>" + html + "</h1>";
  }, 500 );
</script>
</body>
</html>

Random Password generator

This generator will allow you to create your very own randomly generated password string.

<html>
<head><title>bored_2</title></head>
<body>
<p></p>&nbsp;<p></p>
<center><b><div id="OUT"></div></b></center>
  <script>
    var div = document.getElementById ( "OUT" );
    var randomString = function(length) {
      var text = "";
      var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      for(var i = 0; i < length; i++) {
          text += possible.charAt(Math.floor(Math.random() * possible.length));
      }
      return text;
    }
    div.innerHTML = randomString ( 20 );
  </script>
</body>
</html>

I got some interesting feedback wrt this generator and decided to provide another version which will use the crypto API
inside the browsers instead of Math.random ( );

<!DOCTYPE html>
<html>
<head><title>bored_2</title></head>
<body>

<p></p>&nbsp;<p></p>
<center><b><div id="OUT"></div></b></center>
  <script>
    var div = document.getElementById ( "OUT" );
    var randomString = function(length) {
      var text = "";
      var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      var cryp = new  Uint8Array ( possible.length );
      var rand = getRandomValues ( cryp );

      for ( var i = 0; i < length; i++ )  {
          text += possible.charAt ( rand[i] % possible.length );
      }
      return text;
    }
    getRandomValues = function  ( buf ) {
      if (!(buf instanceof Uint8Array)) {
        throw new Error('Invalid type: buf not an Uint8Array');
      }
      if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
        window.crypto.getRandomValues(buf);
      } else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') {
        window.msCrypto.getRandomValues(buf);
      } else if (nodeCrypto) {
        var bytes = nodeCrypto.randomBytes(buf.length);
        buf.set(bytes);
      } else if (this.randomBuffer.buffer) {
        this.randomBuffer.get(buf);
      } else {
        throw new Error('No secure random number generator available.');
      }
      return buf;
    }
    div.innerHTML = randomString ( 20 );
  </script>
</body>
</html>

Santas trip

Want to move Santa across your web page to celebrate the season or have a selfie take a trip over your web page …
Here is a fun snippet to achieve that.

<!DOCTYPE html>
<html>
<head><title>bored_3</title></head>
<body bgcolor="black">
  <img src="flying.gif" id="sleigh" style="position: absolute" />
  <audio src="bells.mp3" autoplay loop></audio>
<script>
  var img = document.getElementById ( "sleigh" );
  var left = 0;
  setInterval ( function ( ) {
    left += 3;
    img.style.left = left+"px";
    if ( left > window.innerWidth )
         left = -400;
  }, 100 );
</script>
</body>
</html>

Calculator

Still don’t know what to do ?
Have you ever wondered what your monthly mortgage rate would be like if you change the interest rate or the length of the loan ?
Or are you looking for a simple tip calculator. This snippet is using evil-eval to accomplish this task.

<!DOCTYPE html>
<html>
<head><title>bored_4</title>
    <script>
      function calc ( )  {
        var out = document.getElementById ( "OUT" );
        var txt = document.getElementById ( "eq" );
        var res = eval ( txt.value );
        out.innerHTML = res+"";
      }
    </script>
</head>
<body>
  <form>
    <input type="text" id="eq" value='var r=5.00, N=12*15, P=100000; var R=r/12/100; var M=R/(1-(1+R) ** -N ) * P; P+"@"+r+"% = "+M' size="70" \>
    <input type="button" value="Calculate" onclick="calc();" \>
  </form>
<p><b><div id="OUT"> -- VALUE --</div></b>
</body>
</html>

Polar Clock

Want to know what tmie it is but you wnat to realy try to use as much brain power as possible
Try the Polar Clock. A fun way to display time.

<html>
<head>
    <title>bored_5</title>
    <script src="clock.js"> </script>
</head>
<body bgcolor="white">
<div id="logo" >
  <canvas id="canvas" width="120" height="120" ></canvas>
</div>
</body>
</html>

Random Image

Do you want to use a random background image from Flicker.
This code snippet does just that using the flickr API.

<html>
<head><title>bored_6</title>
<style>    
body {
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center center;
}
</style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script type="text/javascript">
    var keyword = "mountains";
    function getImg ( )  {
      keyword = document.getElementById ( "key" ).value;
      var req = { tags: keyword, tagmode: "any", format: "json" };
      var url = "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?"; 
      $.getJSON ( url, req, function ( data )  {
        var rnd = Math.floor(Math.random() * data.items.length);
        var image_src = data.items[rnd]['media']['m'].replace("_m", "_b");
        $('body').css('background-image', "url('" + image_src + "')");
      } );
    }
    $(document).ready(function(){
      getImg ( );
    });
  </script>
</head>
<body>
  <button onclick="getImg ( );">New Image</button>
  <input type="text" value="mountain" id="key"/>
</body>
</html>

Chess Game

Granted this one is a bit too much to invent yourself. But fear not, you can get the tiny 1k code for a chess game from Here …

<!doctype html>
<html>
  <!--
  (c) &copy; js1k.com, 2010 - 2017
  Note: submissions belong to their respectful owner, do not copy without their consent
  -->
  <head>
    <title>JS1k 2010 - Demo 750 </title>
    <meta charset="utf-8">
    <meta name="author" content="Óscar Toledo G.">
    <meta name="description" content="JS1k 2010 demo: Tiny Chess.">
    <meta name="pubdate" content="20100909">
    <link rel="icon" type="image/png" href="http://js1k.com/favicon.png">
    <link rel="canonical" href="http://js1k.com/2010-first/demo/750">
    <link rel="alternate" type="application/rss+xml" title="New demo feed" href="http://js1k.com/feed.xml">
    <link rel="shortlink" href="http://js1k.com/750">
    <script>
      setTimeout(function(){
        var ga = document.createElement('script');
        ga.async = true;
        ga.defer = true;
        ga.src = 'http://www.google-analytics.com/ga.js';
        ga.onload = function(){try{_gat._getTracker('UA-19882353-1')._trackPageview();}catch(e){window.console&&console.log("ga fail :'( ");};};
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(ga, s);
      }, 10);
    </script>
    <style>
      /* http://qfox.nl/notes/333 */
      body,html,iframe{margin:0;padding:0;border:0;width:100%;height:100%}
      iframe{position:absolute;top:0;left:0;padding-top:50px;box-sizing:border-box}
      header{position:relative;z-index:1;height:47px;padding-top:2px;border-bottom:1px solid #000;box-shadow:0 -10px 25px #ccc inset;background-color:#eee}
      aside,div,h1,p{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:16px;font-weight:inherit;line-height:22px;padding:0;margin:0;cursor:default}
      aside,h1{display:inline}
      a{color:#000;text-decoration:none;border-bottom:1px dashed #000}
      a:hover{border-bottom:1px solid red}
      a[href="0"]{text-decoration:line-through;pointer-events:none;border-bottom:0;color:#ccc}
      .button{float:left;width:40px;height:40px;line-height:40px;text-align:center;padding:0;margin:2px 0 0 10px;border:1px solid #888;border-color:#ddd #888 #888 #ddd;font-family:sans-serif;font-size:30px;font-weight:700;cursor:pointer}
      .button:hover{color:red;border-bottom-color:#888}
      .r{margin-right:10px}
      time{display:none}
    </style>

  </head>
  <body>
    <header>
      <div>
        <h1>
          <a href="http://js1k.com">JS1k</a>
          <a href="http://js1k.com/2010-first">2010</a>
          <strong></strong> demo
          &mdash;
           by Óscar Toledo G.
        </h1>
        <p>
          <em>
            Tiny Chess.
          </em>
        </p>
        <aside>
          &mdash;
          1023 bytes
          &mdash;
          <!-- -->
          <a href="http://js1k.com/2010-first/details/750">demo details</a>
          &mdash;
          <a href="http://js1k.com/2010-first/demos">list of demos</a>
          <!-- 
          <b><a href="http://js1k.com/2010-first/demo/">
            Update available here!
          </a></b>
          <!-- -->
          &mdash;
          <a href="http://js1k.com/750" title="short link for your mobile devices" rel="nofollow">js1k.com/750</a>
        </aside>
      </div>

      <a href="749" class="button p">&Larr;</a>
      <a href="751" class="button n">&Rarr;</a>
    </header>

    <script type="demo">
for(B=i=y=u=b=i=5-5,x=10,I=[],l=[];B++<304;I[B-1]=B%x?B/x%x<2|B%x<2?7:B/x&4?0:l[i++]="ECDFBDCEAAAAAAAAIIIIIIIIMKLNJLKM@G@TSb~?A6J57IKJT576,+-48HLSUmgukgg OJNMLK  IDHGFE".charCodeAt(y++)-64:7);function X(c,h,e,s){c^=8;for(var o,S,C,A,R,T,G,d=e&&X(c,0)>1e4,n,N=-1e8,O=20,K=78-h<<9;++O<99;)if((o=I[T=O])&&(G=o^c)<7){A=G--&2?8:4;C=o-9?l[61+G]:49;do if(!(R=I[T+=l[C]])&&!!G|A<3||(R+1^c)>9&&G|A>2){if(!(R-2&7))return K;n=G|(c?T>29:T<91)?o:6^c;S=(R&&l[R&7|32]*2-h-G)+(n-o?110:!G&&(A<2)+1);if(e>h||1<e&e==h&&S>2|d){I[T]=n;I[O]=0;S-=X(c,h+1,e,S-N);if(!(h||e-1|B-O|T-b|S<-1e4))return W(),c&&setTimeout("X(8,0,2),X(8,0,1)",75);I[O]=o;I[T]=R}if(S>N||!h&S==N&&Math.random()<.5)if(N=S,e>1)if(h?s-S<0:(B=O,b=T,0))break}while(!R&G>2||(T=O,(G||A>2|(c?O>78:O<41)&!R)&&++C*--A))}return-K+768<N|d&&N}function W(){i="<table>";for(u=18;u<99;document.body.innerHTML=i+=++u%x-9?"<th width=60 height=60 onclick='I[b="+u+"]>8?W():X(0,0,1)'style='font-size:50px'bgcolor=#"+(u-B?u*.9&1||9:"d")+"0f0e0>&#"+(I[u]?9808+l[67+I[u]]:160):u++&&"<tr>")B=b}W()
    </script>
    <script>
      (function(){var doc=document;var header=doc.getElementsByTagName("header")[0];var firstChild=header.firstChild;var p=doc.getElementsByClassName("p")[0];var n=doc.getElementsByClassName("n")[0];header.insertBefore(p,firstChild);header.insertBefore(n,firstChild);header.appendChild(doc.getElementsByTagName("p")[0])})();
      (function reload(){var doc=document;var header=doc.getElementsByTagName("header")[0];var iframe=doc.createElement("iframe");doc.body.appendChild(iframe);var iwin=iframe.contentWindow;var idoc=iframe.contentDocument;idoc.open();idoc.close();idoc.write('<!doctype html><head><meta charset="utf-8"><body>');idoc.head.innerHTML="<style>\n"+"html, body { margin: 0; padding: 0; border: 0; width: 100%; height: 100%; }\n"+"</style>\n";idoc.body.innerHTML="\n\t\t"+"<canvas"+' id="c"'+''+"></canvas>\n"+
      (false?"<script>\x3c/script>\n":"")+"";var Audio=iwin.Audio;iwin.Audio=function(x){return new Audio(x)};if(false){var canvas=idoc.getElementsByTagName("canvas")[0];iwin.a=canvas.getContext("2d");iwin.b=idoc.body;iwin.c=canvas;var p2d=iwin.Path2D;function wrap(ctx){var fill=ctx.fill,clip=ctx.clip,stroke=ctx.stroke;ctx.scale=ctx.scale;ctx.drawFocusIfNeeded=ctx.drawFocusIfNeeded;ctx.ellipse=ctx.ellipse;ctx.fill=function(r){fill.call(ctx,r==="evenodd"?"evenodd":"nonzero")};ctx.stroke=
        function(p){if(p&&p2d&&p instanceof p2d)stroke.call(ctx,p);else stroke.call(ctx)};ctx.clip=function(p){if(p&&p2d&&p instanceof p2d)clip.call(ctx,p);else clip.call(ctx)};return ctx}if(false){var cvs=iwin.c;var cNode=cvs.cloneNode;cvs.cloneNode=function(){var clone=cNode.apply(cvs,arguments);var cloneGet=clone.getContext;clone.getContext=function(){return wrap(cloneGet.call(clone,"2d"))};return clone};var get=cvs.getContext;cvs.getContext=function(){return wrap(get.call(cvs,"2d"))}}if(false)wrap(iwin.a)}idoc.body.clientWidth;
        var demo=idoc.createElement("script");var scrpt=doc.querySelector('script[type="demo"]').textContent.replace(/m.location=m.location;/,"top.reload();");if(false)scrpt="A=0,B=0;"+scrpt;demo.textContent=scrpt;idoc.body.appendChild(demo);idoc.close();iframe.contentWindow.focus();var r=doc.createElement("div");r.innerHTML="&#8635;";r.className="button r";r.title="restart just the demo (local, without remote fetch)";window.reload=r.onclick=function(){doc.body.removeChild(iframe);r.parentElement.removeChild(r);
          iframe=null;r=null;idoc=null;header=null;reload()};var firstLine=doc.getElementsByTagName("div")[0];header.insertBefore(r,firstLine)})();
    </script>
  </body>
</html>

3D Globe

And finally here is a quick way to add a 3D globe to your web page using OpenGlobus.

<html>
<head><title>OpenGlobus - Earth planet</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdn.rawgit.com/OpenGlobus/OpenGlobus/eb8392c2/build/og.css" type="text/css" />
    <script src="https://cdn.rawgit.com/OpenGlobus/OpenGlobus/eb8392c2/build/og.js"></script>
</head>
<body>
    <div id="globus" style="width:100%;height:80%"></div>
    <button id="btnOSM">OSM</button>
    <button id="btnMQS">MapQuest Sat.</button>
    <script>

        document.getElementById("btnOSM").onclick = function () {
            osm.setVisibility(true);
        };

        document.getElementById("btnMQS").onclick = function () {
            sat.setVisibility(true);
        };

        var osm = new og.layer.XYZ("OpenStreetMap", {
            specular: [0.0003, 0.00012, 0.00001],
            shininess: 20,
            diffuse: [0.89, 0.9, 0.83],
            isBaseLayer: true,
            url: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
            visibility: true,
            attribution: 'Data @ OpenStreetMap contributors, ODbL'
        });

        var sat = new og.layer.XYZ("MapQuest Satellite", {
            shininess: 20,
            specular: og.math.vector3(0.00048, 0.00037, 0.00035),
            diffuse: og.math.vector3(0.88, 0.85, 0.8),
            ambient: og.math.vector3(0.15, 0.1, 0.23),
            isBaseLayer: true,
            url: "https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoibWdldmxpY2giLCJhIjoiY2o0ZmVudncwMGZvbjJ3bGE0OGpsejBlZyJ9.RSRJLS0J_U9_lw1Ti1CmsQ",
            visibility: false,
            attribution: '@2014 MapQuest - Portions @2014 "Map data @ <a target="_blank" href="http://www.openstreetmap.org/">OpenStreetMap</a> and contributors, <a target="_blank" href="http://opendatacommons.org/licenses/odbl/"> CC-BY-SA</a>"'
        });

        var controls = [
            og.control.mouseNavigation(),
            og.control.touchNavigation(),
            og.control.zoomControl(),
            og.control.earthCoordinates(),
            og.control.sun()
        ];

        globus = new og.Globus({
            "target": "globus",
            "name": "Earth",
            "controls": controls,
            "terrain": new og.terrainProvider.TerrainProvider("OpenGlobus"),
            "layers": [osm, sat]
        });
    </script>
</body>
</html>

Resources

So now that you know what to do it should be easy to take some of these suggestions and run with it. If nothing else you can find plenty more on gihub.com to help you kill off some of your time.

You can directly follow the link to the Battlefield to play with the code.

I also created a video for these drive-by-coding samples, which you can find here …

Login Form with php and QooxDoo

Sooner or later all web developer will have to create a Login form for one of their projects. You can find a lot of sample code and frameworks out there. In fact this is one of those times where you end up in information overload. There are millions of options but which one to pick ?

QooxDoo is a great framework to write complex web applications which acts and feels like a standard windows client. These so called SPAs ( Single Page Applications ) are a great way to create a valuable presence online and to offer your customers a very special treat.

But is QooxDoo flexible enough to build a stunning login form with it ?

I found that QooxDoo and php go together like peanut butter and jelly. And yes you can also use a RESTFul API to achieve the same however using PHP will offer you one advantage over E.g. a NodeJS based back-end. With most shared hosting services you will not be able to use NodeJS.

Form and Function:

So what are we looking for in a login form ? We want a nice and clean looking interface and we want to have the ability to

  1. Login
  2. Sign Up
  3. Recover password

So we are actually looking at three different forms and not just a single one.

three Login forms
three Login forms

That serves me well

For the PHP back-end I chose a single user class which I found on Github because it keeps things simple enough while at the same time offering some basic type of security. There are plenty of other options for the back-end available and you can shoe-horn any back-end into this solution really.

The PHP code was adjusted a bit and I added the code in here for your convenience. You can also download the PHP code from the following link Secure-User-Login-And-Authentication-Class

Here is the code which I added. Obviously in a real environment you want to separate this code out into its own file ( or files ).

// Simple input handling.
// Please note that no user input validation is done
$auth = new userAuth;
$type = $_GET['type'];
$name = $_GET['username'];
$pass = $_GET['password'];
$email= $_GET['email'];
$remember = $GET['remember'];
switch ( $type )  {
  case "login":  echo $auth->user_login  ( $name, $pass, $remember ) ? "true" : "false"; break;
  case "logout": echo $auth->user_logout ( ) ? "true" : "false"; break;
  case "forgot": echo $auth->forgot_password ( $email ) ? "true" : "false"; break;
  case "signup": echo $auth->create_user ( $name, $pass, $email, $remember ) ? "true" : "false"; break;
  case "check":  echo $auth->is_logged_in ( ) ? "true" : "false"; break;
  case "newDB":  echo $auth->create_user_table ( ) ? "true" : "false"; break;
  default: echo "Bad type"; break;
}

Now since we are going to use Ajax calls to the backend please keep in mind that any additional request to the backend ( E.g. like to display user data ) must be secured on the server side. You cannot rely on any security checks on the client side.

Login Form

In this post I want to mostly focus on the front-end part of the Login Form. Using QooxDoo to build the UI will require a few tweaks to the look and feel of the widgets in here.

I could have gone and created new styles following the qooxdoo way and I could have also created the LoginForm using the standard generate.py server side script. However I chose to use a functional complete, minified, qooxdoo library instead to bootstrap this application.

<!DOCTYPE html>
<html>
<head>
    <title>login</title>
    <meta charset="utf-8">
    <meta name="Author" CONTENT="SoftwareSamurai">
    <meta name="Description" CONTENT="login" />
    <meta name="Keywords" CONTENT="some keywords" />
    <meta name="Classification" CONTENT="Klassifizierung">
    <script type="text/javascript" src="script/qx-6.0.0a.min.js"></script>
    <script type="text/javascript" src="LoginWindow.js"></script>
    <script type="text/javascript" src="SignupWindow.js"></script>
    <script type="text/javascript" src="ForgotWindow.js"></script>
    <script type="text/javascript" src="ApplicationWindow.js"></script>
</head>
<body>
  <script type="text/javascript">
    qx.Class.define ( "LoginWindow.Application", {
      extend : qx.application.Standalone,
      members: {
        main : function ( )  {
          this.base ( arguments );
          if ( qx.core.Environment.get ( "qx.debug" ) )  {
            qx.log.appender.Native;
            qx.log.appender.Console;
          }
          var root = this.getRoot ( );
          var decorator = new qx.ui.decoration.Decorator ( );
          decorator.setBackgroundImage ( "background.jpg" );
          decorator.setBackgroundRepeat( "scale" );
          this.getRoot ( ).setDecorator( decorator );
          this.rpc ( "type=check", function ( success )  {
            var win = null;
            if ( success )
              win = new ao.apps.users.SoftwareSamurai.ApplicationWindow ( );
            else
              win = new ao.apps.users.SoftwareSamurai.LoginWindow ( );
            win.show ( );
          },this );
        },
        rpc : function ( str, cb, ctx )  {
          var rpc = new qx.io.request.Xhr ( "user.class.php?"+str );
          rpc.addListener ( "success", function ( e, x, y ) {
             var req = e.getTarget ( );
             var rsp = req.getResponse ( );
             cb.call ( ctx, ( rsp === "true" ) );
          }, this );
          rpc.send ( );
        }
      }
    } );
    qx.core.Environment.add ( "qx.application", "LoginWindow.Application" );
  </script>
</body>
</html>

Using this approach has the advantage of being able to rapidly develop your application and then use the standard QooxDoo tools to compile and minify once you are sure you have all you want.
For a standard QooxDoo application you have the Application.js file, which I have folded into this html file.

So lets go though this code above:
Line 10 : Here we load the complete qooxdoo library in a minified version.
Linew 11 – 14 : These are the classes we are going to create today.
Line 18 : Every QooxDoo application has to be derived off of qx.application.Standalone.
Line 21 : This is the main entry function which is called once the application is completely loadedinto the browser and the widgets are initialized.
Line 28 – 31 : In order to set the background image for the complete window we have to set the applications decorator.
Line 32 – 39: These lines of code do call the backend and if the user is already logged in, then we display the main application window, otherwise we display the login form.
Line 41 – 49 : This function will call the backend form ( “user.class.php ), and call the supplied callback function back in the supplied context ( object ).
Line 52 : In order to create a qooxdoo application you have to set this variable to allow the framework to instantiate the appropriate class as the main application.

Log me in Scotty

If you are not currently already logged in you will create a new instance of LoginWindow. So lets take a look at this class.

One word upfront though first. All three classes are very similar in structure, so I will only talk about the LoginWindow here. You can find the code of the other classes following the link below this blog.

The first thing we have to take care of is to create the actual widgets.

qx.Class.define ( "ao.apps.users.SoftwareSamurai.LoginWindow",
{
  extend : qx.ui.container.Composite,
  construct : function ( ) {
    this.base ( arguments );
    this.setLayout ( new qx.ui.layout.Canvas ( ) );
    this.set ( { width: 300, height: 200 } );
    this._buildLogin ( );
    var top  = parseInt ( ( qx.bom.Document.getHeight ( ) - 200 ) / 2, 10 );
    var left = parseInt ( ( qx.bom.Document.getWidth  ( ) - 300 ) / 2, 10 );
    var app  = qx.core.Init.getApplication ( );
    var root = app.getRoot( );
    root.add ( this, { top: top, left: left } );
  },
  destruct : function ( )  {
  },
  members  :  {
    _buildLogin : function ( )  {
      var semi = new qx.ui.core.Widget ( );
      semi.set ( { opacity: 0.6, backgroundColor: "#404040" } );
      this.add ( semi, { top: 1, left: 1, right: 1, bottom: 1 } );

      var font = new qx.bom.Font ( 24, [ "Arial" ] );
      font.setBold ( true );
      //font.setColor ( "#FFFFFF" );
      var lbl = new qx.ui.basic.Label ( "<center><b style='color: #FFFFFF'>" + this.tr ( "Log in" ) + "</b></center>" );
      lbl.setFont ( font );
      lbl.setRich ( true );
      lbl.setWidth( this.getWidth ( ) - 20  );
      this.add ( lbl, { top: 10, left: 10 } );

      var line = new qx.ui.core.Widget ( );
      line.set ( { height: 2, backgroundColor: '#FFFFFF' } );
      this.add ( line, { top: 50, left: 10, right: 10 } );

      var name = new qx.ui.form.TextField ( );
      name.setPlaceholder ( this.tr ( "Username" ) );
      this.add ( name, { top: 65, left: 10, right: 10 } );
      this._name = name;

      var pass = new qx.ui.form.PasswordField ( );
      pass.setPlaceholder ( this.tr ( "Password" ) );
      this.add ( pass, { top: 100, left: 10, right: 10 } );
      this._pass = pass;

      var chk = new qx.ui.form.CheckBox ( "<b style='color: #FFFFFF'>" + this.tr ( "Remember me ?" ) + "</b>" );
      lbl = chk.getChildControl ( "label" );
      lbl.setRich ( true );
      this.add ( chk, { top: 130, left: 10 } );
      this._remember = chk;

      var strForgot = "<center><i style='color: white'>" + this.tr ( "Forgot Password ?" ) + "</i></center>";
      var btnForgot = new qx.ui.basic.Atom ( strForgot );
      btnForgot.set ( { cursor: 'pointer' } );
      lbl = btnForgot.getChildControl ( "label" );
      lbl.setRich ( true );
      lbl.setAllowGrowY ( true );
      btnForgot.addListener( "mouseover", function ( )  {
        btnForgot.setLabel ( "<u style='color: white'>" + strForgot + "</u>" );
      },this );
      btnForgot.addListener( "mouseout", function ( )  {
        btnForgot.setLabel ( strForgot );
      },this );
      btnForgot.addListener( "click", function ( )  {
        this.forgot ( );
      },this );
      this.add ( btnForgot, { top: 130, right: 10 } );

      var width = parseInt ( ( this.getWidth ( ) - 30 ) / 2, 10 );
      var btnLogin = this._createBtn ( this.tr ( "Log in" ), "#AAAAFF70", width, function ( ) {
         this.login ( );
      }, this );
      this.add ( btnLogin, { bottom: 10, left: 10 } );
      var btnSignup = this._createBtn ( this.tr ( "Sign Up" ), "#AAFFAA70", width, function ( ) {
         this.signup ( );
      }, this );
      this.add ( btnSignup, { bottom: 10, right: 10 } );
    }
  }
} );

Line 1: We define the class name following the qooxdoo naming convention.
Line 3: Instead of creating a Window with a title etc we simply create a Container
Line 4 – 14: The constructor takes care of the initialization. I want to point out qx.bom.Document.getHeight ( ) which will give you the size of the browser window.
Line 11 – 13: Getting the applications root window and adding the LoginWindow to the root.
Line 19 – 21: The semi transparent background. If we would set the opacity of the actual LoginWindow we would also create the buttons and the text with the set opacity which is not wanted.
Line 46 – 50: For the checkbox we want to change the text color to white. In order to achieve this we have to get the actual Label object, and then use plain HTML to change the color.
Line 52 – 67: Here we build a widget which behaves like a HTML link. There are many ways this could be done, so consider this one of the solutions. Also please note that I am using a Atom instead of a Button object here.
Line 69 – 77: Finally we create the Log In and Sign up buttons. This is done using a function which takes in the required attributes and a callback function.

Lets inspect how we create the buttons and their custom looks.

    _createBtn : function ( txt, clr, width, cb, ctx )  {
      var btn = new qx.ui.form.Button ( "<b style='color: white'>" + txt + "</b>" );
      btn.set ( { width: width, cursor: 'pointer' } );
      lbl = btn.getChildControl ( "label" );
      lbl.setRich ( true );
      btn.addListenerOnce ( "appear", function ( )  {
        this.setBGColor ( btn, "#AAAAAA00", "#AAAAAA00" );
      },this );
      btn.addListener ( "mouseover", function ( )  {
        this.setBGColor ( btn, clr, clr );
      },this );
      btn.addListener ( "mouseout", function ( )  {
        this.setBGColor ( btn, "#AAAAAA00", "#AAAAAA00" );
      },this );
      btn.addListener ( "execute", function ( e )  {
         cb.call ( this );
      }, ctx );
      return btn;
    },
    setBGColor : function ( btn, clr1, clr2 ) {
       var elem = btn.getContentElement ( );
       var dom  = elem.getDomElement ( );
       var img  = "linear-gradient(" + clr1 + " 35%, " + clr2 + " 100%)";
       if ( dom.style.setProperty )
            dom.style.setProperty ( "background-image", img, null );
       else
            dom.style.setAttribute ( "backgroundImage", img );
    },

Line 2 – 5 : The first few lines of the _createBtn function we create a button and then change the text color to white.
Line 6 – 8 : The first time the button is rendered we will go ahead and change the background color. If we call the function before the button is visible, then the DOM element would be null.
Line 9 – 14 : These two callback function handle the case when the mouse gets into and out of the widget. In this case we are going to change the background color of the button.
Line 15 – 17 : We handle the execute event and are going to call the registered function back. Please take note of the way I implemented the callback, which will retain the context ( object ) inside the function. In object oriented programming you want to always use and handle the data inside an object so it is important to remain in the appropriate context.
Line 20 – 28 : In order to set the buttons background color we have to modify the DOM element directly. Please remember that this is not the way I would change the style of a button if I wanted to use it throughout the rest of the application. This is a quick way to get to change the gradient background color.

… and action.

The following functions handle the button action.

    forgot : function ( )  {
      var forgot = new ao.apps.users.SoftwareSamurai.ForgotWindow ( );
      forgot.show  ( );
      this.destroy ( );
    },
    login : function ( )  {
      var name = this._name.getValue ( );
      var pass = this._pass.getValue ( );
      var remember = this._remember.getValue ( );
      if ( name.length < 1 || pass.length < 1 )  {
        alert ( this.tr ( "Please provie username and password" ) );
        return;
      }

      var str = "type=login";
      str += "&username="+name;
      str += "&password="+pass;
      str += "&remember="+remember;
      var app = qx.core.Init.getApplication ( );
      app.rpc ( str, function ( success ) {
        var win = null;
        if ( success )  {
          win = new ao.apps.users.SoftwareSamurai.ApplicationWindow ( );
          win.show ( );
          this.destroy ( );
        }
        else  {
          alert ( this.tr ( "Could not log in." ) );
        }
      }, this );
    },
    signup : function ( )  {
      var signup = new ao.apps.users.SoftwareSamurai.SignupWindow ( );
      signup.show  ( );
      this.destroy ( );
    },

Line 1 – 5 : The forgot function will create and display the ForgotWindow form and destroy the Login form.
Line 6 – 13 : we retrieve the values the user entered and do some basic sanity checking.
Line 15 – 18 : Here we build the query string to send to the back-end.
Line 19 – 20 : We get the application object to invoke the rpc function.
Line 21 – 29 : Once the server returns we check if we were successful in logging in. If yes, then we hide the Login form and start the main ApplicationWindow.
Line 32 – 36 : The signup function will create and display the SignupWindow form and destroy the Login form.

Let’s wrap it up

Congratulations, you have done it. You are now able to build your own complex web applications on a shared hosting service, such as Dreamhost with as little as $8,- per month.

DreamHost.

Resources

Live sample

Create a stunning login form using QooxDoo and PHP

Note: try user “test” and password “abc123” or create a new user.

I also created a video on creating a stunning login form using QooxDoo, which you can find here …

Please follow this link here … to play with the code.