Friday 11 October 2013

Start building your OOP JavaScript objects

Let's face it. Sometimes jQuery doesn't cut it. Either you don't want the user to download the library or it seems to be a sledgehammer to crack a nut. JavaScript has also matured into a great server-side language. Think of node.js. My example below uses OOP JavaScript for web interaction, but consider the concept on the server-side too.
Let's start with the HTML page:
<html>
<script src="alertster.class.js"></script>
<script>
var myalertster = new alertster('hello');
myalertster.sendit('goodbye');
</script>
</html>
Not too big is it? Essentially we are creating a new object called myalertster from a class called alertster which lies within a file called alertster.class.js. I am passing a couple of parameters here, but as you'll see from the contents of alertster.class.js (below), I've stuck a little error trapping in there too.
Now for alertster.class.js:
function alertster(message)
{
message = typeof message !== 'undefined' ? message : 'Default message';
this.m = message;
this.sendit = function(additional)
{
additional = typeof additional !== 'undefined' ? additional : 'Default additional message';
alert(this.m+"\n"+additional);
};
};
Easy!

Tuesday 24 September 2013

htaccess and HTML5 lines to force downloads

Here are a couple of tips to make life a little easier for your users with download links.
If you are running a Apache as your web server you can add the following line to your htaccess file:

AddType application/octet-stream .pdf

and/or ad a line like this within your HTML page:

<a href="document.pdf" download="document.pdf">Download the document</a>

In this case the pdf will automatically be downloaded when the user clicks on the link, rather than presenting them with a dialogue box asking them what to do.

Friday 9 August 2013

Automatically log users into your website using HTML5 Local Storage

So your users has been registered. They have gone through the pain of logging in on a mobile phone and you want them to authenticate more easily next time round, so that they don't give up on using your web app.

The solution : HTML5 Local Storage. In the code below, I take the username and password, and put them in localstorage variables. When the (login) page is visited again, I check to see if those localstorage variables exist. If they do, I add them as values to the username and password field, and perform a submit.

Enjoy!

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>HTML5 Save Password</title>
<!--[if IE]>
<script src="html5.js"></script>
<![endif]-->
<script src="jquery.min.js"></script>
</head>
<body>
<form method="POST" action="set.php">
<label for"username">Username</label>
<input name="username" type="email" />
<label for"password">Password</label>
<input name="password" type="password" />
<input type="submit" />
</form>
<script>
(function()
{
u = false;
p = false;
if(localStorage.getItem('username') != null)
{
$('form input[name=username]').val(localStorage.getItem('username'));
u = true;
}
if(localStorage.getItem('password') != null)
{
$('form input[name=password]').val(localStorage.getItem('password'));
p = true;
}
if((u == true) && (p == true))
{
$('form').submit();
}
$('form').submit(function()
{
localStorage.username = $('form input[name=username]').val();
localStorage.password = $('form input[name=password]').val();
});
})();
</script>
</body>
</html>

Thursday 1 August 2013

HTML5 contenteditable save, re-save and save again

The HTML5 contenteditable attribute is really useful, but when you've got my typing skills, you'd really like to keep saving as you go. In the example below I'm writing to a text file for simplicity. I've got a few things going on.

  • If the text file exists or has data in there, I use it. Otherwise, the editable paragraph holds a default string.
  • I use a form submit to kick off a jQuery event.
  • The jQuery function calls a PHP script to write the contents of my editable paragraph to the file.

This way I can keep pressing the submit button as I go, which will just update the text file with the current editable paragraph contents.

Here's the code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Editable jQuery save</title>
<!--[if IE]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="http://meyerweb.com/eric/tools/css/reset/reset.css" />
<script src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
</script>
</head>
<body>
<p id="content" contenteditable="true">
<?php
$text = file_get_contents('test.txt');
if(!empty($text))
{
echo $text;
}
else
{
echo 'Put some text here.';
}
?>
</p>
<form>
<input type="submit" />
</form>
<script>
(function()
{
$('form').submit(function()
    {
        $.post('set.php',
        {
            content:$('#content').text()
        }, function(data)
        {
        if(data)
        {
        console.log(data)
        }    
        });
        return false;
    });
})();
</script>
</body>
</html>

And set.php

<?php
if(isset($_POST['content']))
{
file_put_contents('test.txt', $_POST['content']);
}
else
{
echo 'write was not successful';
}
?>

Heredoc in PHP to reduce a lot of echo calls

Heredoc is a way of building strings for output. Funnily enough I found there was a mistake in some of the coding on the Wikipedia page, but I won't hold that against them. Also, in some of the examples I've come across on the web, they don't seem to recognise an important feature of heredoc. That is, it also provides HTML output.

Below is some example PHP code with the correct syntax, use of variables and HTML. That should speed things up a bit.

<?php
$recipient = 'Mick';
$sender = 'Johnnie';
$x = <<<EOF
Dear $recipient,
I wish you to leave Sunnydale and never return.<br />
Not Quite Love,
$sender
EOF;
echo $x;
?>

Wednesday 17 July 2013

HTML5 Data methods

Here are various ways you can use data through HTML5. The code is larger than usual, that's because I've provided code for 8 different methods of storing and using data. I have tried to highlight the related code and method using colours. To run this, you'll need 3 files:

  • index.php
  • twittieserver.php
  • webworker.plugin.js

The code for all of these files is below. So, let's start with index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Localstorage</title>
<!--[if IE]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link rel="stylesheet" href="http://meyerweb.com/eric/tools/css/reset/reset.css" />
<style>
body
{
font:0.9em/1.1em Sans-serif;
color:#808080;
}
.column
{
float:left;
width:440px;
}
form, section
{
margin:10px;
padding:10px;
width:400px;
border:2px solid #808080;
}
p.description, section article ul
{
margin:4px 4px 8px 0px;
padding:4px;
}
section article ul
{
background:#F5DA55;
}
p.description
{
background:#F5F555;
}
h1, h2
{
font:1.4em/1.5em Georgia, Serif;
}
h1
{
margin:14px 0 10px 10px;
}
strong
{
font-weight:bold;
}
</style>
<script src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
</script>
</head>
<body>
<h1>HTML5 Data Options</h1>
<div class="column">
<form>
<h2>Input</h2>
<p class="description">This form will be used to add content to the sessionStorage, localStorage and Web SQL Objects.</p>
<input type="text" id="one" /><br />
<button>Save</button>
</form>
<section id="customdataattribute">
<h2>Custom Data attributes</h2>
<p class="description">Allows you to hold data values within a selector.<p>
<p class="description">E.g &lt;li data-no="20"&gt;<br />Can therefore be called through jQuery as $('li').data('no');<p>
<article>
<ul>
<li data-no="22">This value is 22<li>
<li data-no="33">This value is 33<li>
</ul>
</article>
</section>
<section id="sessionstorage">
<h2>sessionStorage</h2>
<p class="description">This data will be lost when the browser is closed.<p>
<article></article>
</section>
<section id="localstorage">
<h2>localStorage</h2>
<p class="description">This data will be retained after the browser is closed. It persists until deleted by user through browser settings or by this application.<p>
<article></article>
</section>
</div>
<div class="column">
<section id="cachemanifest">
<h2>cache-manifest</h2>
<p class="description">A file which tells the browser what to cache, and not cache. <br /><strong>CACHE MANIFEST</strong> entries will be cached after they are downloaded for the first time.
<br /><strong>NETWORK</strong> entries require a connection to the server, and will not be cached.
<br /><strong>FALLBACK</strong> are fallback pages, if a page is unavailable.
<br />Instructions below:</p>
<article>
<p>First create a .htaccess file for your site (if you are using a proper web server) and add to it the following line:</p>
<p>AddType text/cache-manifest    .manifest</p>
<p>Next, create a file called cache.manifest. In this file, add the lines:</p>
<p><strong>CACHE MANIFEST</strong></p>
<p>index.html</p>
<p><strong>NETWORK</strong>:</p>
<p>login.html</p>
<p><strong>FALLBACK</strong>:</p>
<p>/html/ /offline.html</p>
</article>
</section>
<section id="sse">
<h2>Server-Sent Events</h2>
<p class="description">Allows this application to get data updates from a server. In this example I've simulated a twittie server using a PHP script called twittieserver.php. This data will be lost when the browser is closed.</p>
<article>
<ul>
</ul>
</article>
</section>
<section id="webworker">
<h2>Web Worker</h2>
<p class="description">A piece of JavaScript or jQuery running in the background, without affecting the performance of this application.</p>
<article></article>
</section>
<section id="websql">
<h2>Web SQL</h2>
<p class="description">Allows an application such as this to build a SQL database at the client end. It is also possible to synchronise this data at the server end. This supports applications which ahve both and online and offline state. It persists until deleted by user through browser settings or by this application.</p>
<article>
<ul>
</ul>
</article>
</section>
</div>
<script>
/* Calculates the custom data attributes and appends them to the result of a selector */
$.fn.calculatecustomattributes = function()
{
lis = $(this).children('li');
res = 0;
ve = 0;
$(lis).each(function()
{
ve = parseInt($(this).data('no'));
if(!isNaN(ve))
{
res += ve;
}
});
$(this).append('<li>Result is '+res+'</li>');
};

/* Sets the form contents which were stored in the sessionStorage object to the calling selector */
$.fn.displaysessionstorage = function()
{
$(this).html('<p>Content is '+sessionStorage.getItem('one')+'</p>');
};

/* Sets the form contents which were stored in the localStorage object to the calling selector */
$.fn.displaylocalstorage = function()
{
$(this).html('<p>Content is '+localStorage.one+'</p>');
};

/* Lists contents of the Web SQL object in the calling selector */
$.fn.displaywebsql = function(db)
{
container = $(this);
db.transaction(function(tx)
{
tx.executeSql('SELECT * FROM jimmy', [], function(tx, results)
{
  var len = results.rows.length, i;
for (i = 0; i < len; i++)
{
$(container).append('<li>'+results.rows.item(i).text+'</li>');
}
});
});
};

/* This function is called when the 'Save' button is clicked */
$.fn.saveit = function(db)
{
/* Put the value of the form input in a variable */
one = $('form #one').val();

/* Create an item in the sessionStorage object and apply the value of our variable */
sessionStorage.setItem('one', one);

/* Create an item in the localStorage object and apply the value of our variable */
localStorage.one = one;

/* Insert the value of our variable into the Web SQL object */
db.transaction(function(tx)
{
tx.executeSql('INSERT INTO jimmy (text) VALUES (?)',[one]);
});

/* Refresh the contents of our containers */
$('#sessionstorage article').displaysessionstorage();
$('#localstorage article').displaylocalstorage();
$('#websql article ul').displaywebsql(db);
};

(function()
{
/* Check to see if web storage is supported */
if(typeof(Storage) == 'undefined')
        {
            alert('Sorry! No web storage support..');
        };

        /* Check to see if web workers are supported */
        if(typeof(Worker) == 'undefined')
{
    alert('Sorry! No web worker support..');
}

/* Check to see if Server-Sent Events are supported */
if(typeof(EventSource) =='undefined')
{
alert('Sorry! No server-sent events support..');
}

/* Calculate custom attibutes */
$('#customdataattribute article ul').calculatecustomattributes();

/* Create a Web SQL object */
var db = openDatabase('mydb', '1.0', 'jimmy database', 2 * 1024 * 1024);
db.transaction(function(tx)
{
tx.executeSql('CREATE TABLE jimmy (id unique, text)');
});

/* Refresh the contents of our containers */
$('#sessionstorage article').displaysessionstorage();
$('#localstorage article').displaylocalstorage();
$('#websql article ul').displaywebsql(db);

/* Call the saveit function when the button is clicked */
$('button').click(function()
{
$(document).saveit(db);
});

/* Get updates for a server, here I've simulated the server with a PHP script */
var feed = new EventSource('twittieserver.php');
feed.onmessage = function(event)
{
/* Add new articles to the top of the list and limit the list to 4, thus removing the fourth item from the list */
$('#sse article ul').prepend(event.data);
if($('#sse article ul > li').size() == 4)
{
$('#sse article ul li:last-child').remove();
}
};

/* Get updates from a JavaScript, simply replacing the contents of the container with each new value returned */
w = new Worker("webworker.plugin.js");
w.onmessage = function(event)
{
$('#webworker article').html(event.data);
};
})();
</script>
</body>
</html>

Now twittieserve.php
<?php
header('Content-Type:text/event-stream');
header('Cache-Control:no-cache');
$seconds = date('s');
echo 'data:<li>Item '.$seconds.'</li>'.PHP_EOL.PHP_EOL;
flush();
?>

And finally, webworker.plugin.js
var i=0;
function timedCount()
{
i=i+1;
postMessage(i);
setTimeout("timedCount()",500);
}
timedCount();

Enjoy!

Friday 12 July 2013

Form changer jQuery plugin

Websites I work on often include the need to add a login, password reset and self-registration form. This requirement can either take up too much space on the page, or require a refresh to load each form. They could all easily be put in a container like a div and called upon (or shown) only when needed. This is what I've done with the example below.
The 'data-location' anchor attributes must correspond to the form ID's they are requesting.
First, I've hidden all the things not required to be visible on load and just spaced the links out (in blue).
Second, I've created the container, forms and links I need (in red).
Third, I've called my plugin to handle the interaction (in green).
Below the HTML, I present the plugin code. There is a little catch all at the top, just in case anyone creates a container with either an ID, class or neither.
Enjoy!
<!DOCTYPE html>
<html lang="en">
<head>
<title>Form changer</title>
<style>
#formholder #resetpassword, #formholder #selfregister, #formholder a[data-location="login"]
{
display:none;
}
#formholder a
{
margin:0.2em;
}
</style>
<script src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
</script>
</head>
<body>
<div id="formholder">
<form id="login">
<p>Hello from login</p>
</form>
<form id="resetpassword">
<p>Hello from reset password</p>
</form>
<form id="selfregister">
<p>Hello from self-register</p>
</form>
<a href="#" data-location="login">Login</a><a href="#" data-location="resetpassword">Reset password</a><a href="#" data-location="selfregister">Self-register</a>
</div>
<script src="formchanger.plugin.js"></script>
<script>
(function()
{
$('#formholder a').formchanger();
})();
</script>
</body>
</html>

Now the plugin code:
(function($)
{
$.fn.formchanger = function()
{
id = $(this).parent().attr('id');
clas = $(this).parent().attr('class');
tag = $(this).parent().get(0).tagName;
if(id != null)
{
container = '#'+id;
}
else if(clas != null)
{
container = '.'+clas;
}
else
{
container = tag;
}
anchors = $(container).children('a');
forms = $(container).children('form');
$(this).click(function()
{
$(anchors).show();
$(forms).hide();
$(container+' #'+$(this).data('location')).show();
$(this).hide();
});
};
})(jQuery);