Monday 8 December 2014

Dynamically activate navigation items in Bootstrap using my jQuery plugin

My challenge was to write a piece of jQuery which:

  • Takes note of the page path.
  • Compares it with the Bootstrap navigation.
  • Makes the navigation element 'active' to remind the users where they are in relation to the site.

First the HTML. Notice, that I have not set any navigation item as active. The plugin will do this. The plugin call is in red.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Activate Navigation Entry</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-default" role="navigation">
      <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Brand</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><a href="index.php">Home</a></li>
            <li><a href="about.php">About</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Dropdown <span class="caret"></span></a>
              <ul class="dropdown-menu" role="menu">
                <li><a href="action.php">Action</a></li>
                <li><a href="anotheraction.php">Another action</a></li>              
              </ul>
            </li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container -->
    </nav>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <h1>Home</h1>
        </div>
      </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
    <script src="activatenav.plugin.js"></script>
    <script>
    (function()
    {
      $(document).activatenav(
      {
        toplevel:'ul.nav.navbar-nav > li > a',
        dropdown:'ul.nav.navbar-nav > li.dropdown > ul.dropdown-menu > li > a'
      });
    })();
    </script>
  </body>
</html>
Next the jQuery plugin named 'activatenav.plugin.js'.
First calculate the end of the path. If there is no name, it must be the home page, so set that as active.
I also check if the current page is from the drop down menu. If so, set both the drop down heading and the corresponding navigation item as 'active'.
(function($)
{
    $.fn.extend(
    {
        activatenav:function(options)
        {
        var pname = window.location.pathname;
        var pArr = pname.split('/');        
            var defaults =
            {
                toplevel:'',
        dropdown:''
            };
            options = $.extend(defaults, options);
if(options.toplevel)
{
handled = $(this).handleTopLevel(options.toplevel, pArr[pArr.length-1]);
if(handled == false)
{
$(this).handleDropdown(options.dropdown, pArr[pArr.length-1]);
}
}
        }
    });

    $.fn.handleTopLevel = function(str, pth)
    {
    var hd = false;  
    if(!pth)
    {
    $(str).first().parent().addClass('active');
    }
    else
    {
    $(str).each(function()
    {
    if($(this).attr('href') == pth)
    {
    $(this).parent().addClass('active');
    hd = true;
    }    
    });
    }
    return hd;  
    };

    $.fn.handleDropdown = function(str, pth)
    {
        $(str).each(function()
    {
    if($(this).attr('href') == pth)
    {
    $(this).parent().addClass('active');
    $(this).closest('li.dropdown').addClass('active');
    }    
    });
    };
})(jQuery);

Monday 10 November 2014

Extend your footer to the browser window using jQuery

Some pages only have a small amount of content and aesthetic reasons you'l like your page footer to extend all the way to the edge of the browser window. There are ways to do this using CSS, but this method also requires to to code your pages differently too. I like to keep the code of my pages straight forward. HTML should not be altered for styling purposes.
So I wrote a jQuery plugin to do this. See below.
(function($)
{
    $.fn.extend(
    {
        footersizeset: function(options)
        {
            var defaults =
            {
                footertag:'footer',
                ofh:100
            };
            options = $.extend(defaults, options);
    var windowheight = $(this).height();
var footer = $(options.footertag);
var footerposition = footer.position();
var footerheight = options.ofh;
var footerbottom = footerheight + footerposition.top;

if(windowheight > footerbottom)
{
difference = windowheight - footerbottom;
footer.height(footerheight + difference);
}
        }
    });
})(jQuery);

Here's how to call the plugin. First you find what the height of the footer would be before the footer is reset. This is done outside all other jQuery calls so that this value doesn't keep getting reset as the browser window size changes.
Then, I make 2 calls to the plugin, once for the page load and subsequently for browser window changes.

var originalfooterheight = $('footer').height();
(function()
{
$(window).footersizeset(
{
footertag:'footer',
ofh:originalfooterheight
});
$(window).resize(function()
{
$(this).footersizeset(
{
footertag:'footer',
ofh:originalfooterheight
});
});
})();

Hope that makes sense.

Tuesday 4 November 2014

Twitter Bootstrap Simple One Page Template

The code below will give you a good starting position if you need to create a one page website using Twitter Bootstrap. It uses basic jQuery animation techniques so there's no need to load another library. The most important elements which get it to work are highlighted in red.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap One Pager</title>
    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
    <style>
    .row
    {
      padding-top:4em;
    }
    figure > img
    {
      width:100%;
    }    
    </style>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">My Website</a>
        </div><!-- .navbar-header -->
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li><a href="#home">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!-- .nav-collapse -->
      </div><!-- .container -->
    </nav>

    <div class="container">
      <div class="row" name="home" id="home">
        <article class="col-md-6">
          <h2>Home</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>  
          <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Estne, quaeso, inquam, sitienti in bibendo voluptas? Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Cum salvum esse flentes sui respondissent, rogavit essentne fusi hostes. Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. Obsecro, inquit, Torquate, haec dicit Epicurus? Quamquam haec quidem praeposita recte et reiecta dicere licebit. Non dolere, inquam, istud quam vim habeat postea videro; Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Duo Reges: constructio interrete. Dici enim nihil potest verius.</p>
          <p>Quid ergo attinet dicere: Nihil haberem, quod reprehenderem, si finitas cupiditates haberent? Deprehensus omnem poenam contemnet. Ego vero volo in virtute vim esse quam maximam; Omnis enim est natura diligens sui. Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Hoc loco tenere se Triarius non potuit.</p>
          <p>Tu quidem reddes; Dicet pro me ipsa virtus nec dubitabit isti vestro beato M.</p>
          <p>Quod iam a me expectare noli. Hoc est dicere: Non reprehenderem asotos, si non essent asoti. Saepe ab Aristotele, a Theophrasto mirabiliter est laudata per se ipsa rerum scientia; Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Cur iustitia laudatur?</p>
          <p>Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus; Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Unum est sine dolore esse, alterum cum voluptate. Qua tu etiam inprudens utebare non numquam. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Quod ea non occurrentia fingunt, vincunt Aristonem;</p>
        </article>
        <figure class="col-md-6">  
          <img src="http://lorempixel.com/400/200/sports" alt="sports" class="img-responsive" />
          <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
        </figure>
      </div><!-- .row -->
      <div class="row" name="about" id="about">
        <article class="col-md-6">
          <h2>About</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>  
          <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Estne, quaeso, inquam, sitienti in bibendo voluptas? Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Cum salvum esse flentes sui respondissent, rogavit essentne fusi hostes. Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. Obsecro, inquit, Torquate, haec dicit Epicurus? Quamquam haec quidem praeposita recte et reiecta dicere licebit. Non dolere, inquam, istud quam vim habeat postea videro; Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Duo Reges: constructio interrete. Dici enim nihil potest verius.</p>
          <p>Quid ergo attinet dicere: Nihil haberem, quod reprehenderem, si finitas cupiditates haberent? Deprehensus omnem poenam contemnet. Ego vero volo in virtute vim esse quam maximam; Omnis enim est natura diligens sui. Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Hoc loco tenere se Triarius non potuit.</p>
          <p>Tu quidem reddes; Dicet pro me ipsa virtus nec dubitabit isti vestro beato M.</p>
          <p>Quod iam a me expectare noli. Hoc est dicere: Non reprehenderem asotos, si non essent asoti. Saepe ab Aristotele, a Theophrasto mirabiliter est laudata per se ipsa rerum scientia; Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Cur iustitia laudatur?</p>
          <p>Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus; Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Unum est sine dolore esse, alterum cum voluptate. Qua tu etiam inprudens utebare non numquam. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Quod ea non occurrentia fingunt, vincunt Aristonem;</p>
        </article>
        <figure class="col-md-6">  
          <img src="http://lorempixel.com/400/200/cats" alt="sports" class="img-responsive" />
          <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
        </figure>
      </div><!-- .row -->
      <div class="row" name="contact" id="contact">
        <article class="col-md-6">
          <h2>Contact</h2>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quid ad utilitatem tantae pecuniae? Age, inquies, ista parva sunt. Quibus ego vehementer assentior. Quae cum essent dicta, discessimus.</p>  
          <p>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</p>
          <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Estne, quaeso, inquam, sitienti in bibendo voluptas? Servari enim iustitia nisi a forti viro, nisi a sapiente non potest. Cum salvum esse flentes sui respondissent, rogavit essentne fusi hostes. Audax negotium, dicerem impudens, nisi hoc institutum postea translatum ad philosophos nostros esset. Obsecro, inquit, Torquate, haec dicit Epicurus? Quamquam haec quidem praeposita recte et reiecta dicere licebit. Non dolere, inquam, istud quam vim habeat postea videro; Nobis Heracleotes ille Dionysius flagitiose descivisse videtur a Stoicis propter oculorum dolorem. Duo Reges: constructio interrete. Dici enim nihil potest verius.</p>
          <p>Quid ergo attinet dicere: Nihil haberem, quod reprehenderem, si finitas cupiditates haberent? Deprehensus omnem poenam contemnet. Ego vero volo in virtute vim esse quam maximam; Omnis enim est natura diligens sui. Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Hoc loco tenere se Triarius non potuit.</p>
          <p>Tu quidem reddes; Dicet pro me ipsa virtus nec dubitabit isti vestro beato M.</p>
          <p>Quod iam a me expectare noli. Hoc est dicere: Non reprehenderem asotos, si non essent asoti. Saepe ab Aristotele, a Theophrasto mirabiliter est laudata per se ipsa rerum scientia; Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Nec vero sum nescius esse utilitatem in historia, non modo voluptatem. Cur iustitia laudatur?</p>
          <p>Atque hoc loco similitudines eas, quibus illi uti solent, dissimillimas proferebas. Quamquam ab iis philosophiam et omnes ingenuas disciplinas habemus; Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Ita enim vivunt quidam, ut eorum vita refellatur oratio. Unum est sine dolore esse, alterum cum voluptate. Qua tu etiam inprudens utebare non numquam. An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia? Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Quod ea non occurrentia fingunt, vincunt Aristonem;</p>
        </article>
        <figure class="col-md-6">  
          <img src="http://lorempixel.com/400/200/people" alt="people" class="img-responsive" />
          <figcaption>Invidiosum nomen est, infame, suspectum. Duo Reges: constructio interrete. Quibus ego vehementer assentior. Non igitur bene.</figcaption>
        </figure>
      </div><!-- .row -->
    </div><!-- .container -->

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
    <script>
    (function()
    {
      $('.navbar-inverse .navbar-nav > li > a').click(function()
      {
        var target = $(this).attr('href');
        var targetLoc = $(target).offset().top;        
        $('html, body').stop().animate(
        {
          scrollTop:targetLoc,
          easing:'swing'
        }, 1000);
        return false;
      });
    })();
    </script>
  </body>
</html>

Monday 6 October 2014

Using PHP to get YouTube Channel feed into Twitter Bootstrap thumbnails

I've been working on a Twitter Bootstrap site. The client created a YouTube channel and wanted the feed coming into the site. Some of the videos added are uploaded by the users, some come from subscriptions. There is a nice class called thumbnail in Twitter Bootstrap for such things, so I thought I'd use that. I'm happiest getting external data using PHP. Here's how it's done.
Replace the word 'Google' with the name of your channel.
<?php
$url = 'https://gdata.youtube.com/feeds/api/videos?q=Google&max-re%20sults=5&v=2&alt=jsonc&orderby=published';
$json = file_get_contents($url);
$data = json_decode($json);
$items = $data->data->items;
foreach($items as $child)
{
echo '<a href="'.$child->player->default.'" class="thumbnail">';
echo '<h4>'.$child->title.'</h4>';
echo '<img src="'.$child->thumbnail->sqDefault.'" alt="'.$child->title.'" />';
echo '<footer>'.date("jS F Y",strtotime($child->updated)).'</footer>';
echo '</a>';
}
?>

Wednesday 1 October 2014

jQuery file upload in Twitter Bootstrap modal

For a recent project, I have needed to add a file upload form to a modal. This has required me to perform the upload through jQuery in order to avoid the page reloading when the form is submitted.
To accomplish this I have used the jQuery form plugin at http://malsup.com/jquery/form/.
Here's the code with the important bits highlighted in red.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <style>
    body
    {
    padding-top:2em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
  <div class="container">
  <div class="col-md-6">
  <button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">Launch demo modal</button>
 
  </div>
  <div id="myResult" class="col-md-6 alert alert-success" role="alert">
 
  </div><!-- myResult -->
  </div><!-- .container -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
 <div class="modal-dialog">
   <div class="modal-content">
     <div class="modal-header">
       <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
       <h4 class="modal-title">Modal title</h4>
     </div>
     <div class="modal-body">
       <form action="uploadfile.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="file">File input</label>
<input type="file" name="file">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
     </div>
   </div><!-- /.modal-content -->
 </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery.form/3.50/jquery.form.min.js"></script>
    <script>
(function()
{
$('#myResult').hide();
$('form').submit(function()
{
$(this).ajaxSubmit(
{
url:$(this).attr('action'),
method:$(this).attr('method'),
target: '#myResult'
});
$('#myModal').modal('hide');
$('#myResult').show();
return false;
});
})();
</script>
  </body>
</html>

Tuesday 30 September 2014

Forms within modal-body in Twitter Bootstrap.

I've been working on a Twitter Bootstrap site recently. It contains modal which require a little interaction within the modal box itself. So here is an example of how to achieve this, this time using forms. First, I load the form into the modal, then I handle the form still within the modal. Most of the code is taken directly from the Twitter Bootstrap website. The only real difference is that I've loaded jQuery and the bootstrap.min.js before the body. I've highlighted the important parts in red.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>  
    <!-- Button trigger modal -->
    <button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">    Launch demo modal</button>
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
        <h4 class="modal-title">Modal title</h4>
      </div>
      <div class="modal-body">      
        <form class="form-inline" role="form" method="POST" action="http.php">
  <div class="form-group">
    <label class="sr-only" for="exampleInputEmail2">Email address</label>
    <input type="email" class="form-control" name="exampleInputEmail2" placeholder="Enter email">
  </div>  
  <button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script>
(function()
{
  $('form').submit(function()
  {
    $.post($(this).attr('action'),$(this).serialize(), function(data)
    {
      $('#myModal .modal-body').append(data);
    });
    return false;
  });
})();
</script>
</body>
</html>

Get links to target modal-body in Twitter Bootstrap

I've been working on a Twitter Bootstrap site recently. It contains modal which require a little interaction within the modal box itself. So here is an example of how to achieve this, first using links (hyperlinks). Most of the code is taken directly from the Twitter Bootstrap website. The only real difference is that I've loaded jQuery and the bootstrap.min.js before the body. I've highlighted the important parts in red.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>  
    <!-- Button trigger modal -->
    <button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">    Launch demo modal</button>
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
        <h4 class="modal-title">Modal title</h4>
      </div>
      <div class="modal-body">
        <a href="hello.php" data-target="#myModal">Click me</a>
       </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script>
(function()
{
  $('a[data-target=#myModal]').click(function()
  {
    var target = $(this).attr('href');
    $('#myModal .modal-body').load(target);
    return false;
  });  
})();
</script>  </body>
</html>

Tuesday 15 July 2014

Post all form fields to PHP using jQuery

The example below simplifies the process of simply passing all HTML form data to a PHP script (or any other HTTP handler), obviously without a refresh. Below that are a couple of suggestions on how the jQuery and PHP could be improved beyond the simplified versions.
First the HTML and jQuery (in red).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Form test</title>
<style>
body
{
font-family:Sans-serif;
line-height:1.5em;
}
</style>
</head>
<body>
<form action="response.php" method="POST">
<input type="text" name="firstname" /><br />
<input type="text" name="lastname" /><br />
<button type="submit">Submit</button>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
(function()
{
$('form').submit(function()
{
$.post($(this).attr('action'),$(this).serialize(), function(data)
{
alert(data)
});
return false;
});
})();
</script>
</body>
</html>
Now the PHP script.
<?php
echo 'Received';
?>
We could improve the jQuery by clearing the form content thus.
$('form').submit(function()
{
thisform = $(this);
$.post($(this).attr('action'),$(this).serialize(), function(data)
{
thisform.trigger('reset'),
alert(data)
});
return false;
});
We could also use the PHP to create a MySQL call using the form array, thus.
<?php
$valueString = '';
$sqlString = '"INSERT INTO `tablename` (';
foreach($_POST as $key => $value)
{
$sqlString .= '`'.$key.'`,';
$valueString .= '\''.htmlspecialchars($value).'\',';
}
$sqlString = rtrim($sqlString,',');
$valueString = rtrim($valueString,',');
$sqlString .= ') VALUES ('.$valueString.')"';
echo 'Received';
?>
Have fun!

Friday 6 June 2014

jQuery sticky plugin

The jQuery plugin below allows you to name an element as a sticky header or footer. You can also set the height, width and background colour. This is similar to those available in Twitter Bootstrap and ZURB Foundation.

First the HTML page:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>jQuery sticky footer</title>
    <style>
    body
    {
      font:90%/1.6 sans-serif;
    }
    section
    {
      margin-top:6em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <header>
      <h1>Hello, world!</h1>
    </header>
    <section>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac pretium velit. Morbi scelerisque vel risus sed ullamcorper. Integer nec dui vel magna malesuada tempus. Vivamus dictum elit sem, at mattis leo volutpat in. Phasellus felis metus, dapibus semper nisi non, sagittis lacinia erat. Fusce in diam et velit commodo rutrum in in dui. Sed imperdiet est non dui molestie consequat. Duis ac ipsum in purus tempor dictum. Curabitur ac neque sed sem semper congue. Pellentesque enim enim, congue sed consequat et, lobortis id tortor. Cras a faucibus sem. Mauris sed enim quam. Aenean sollicitudin posuere mauris. Curabitur sollicitudin orci orci, a sagittis neque feugiat nec.</p>
    </section>
    <footer>
      <p>Hello footer world!</p>
    </footer>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="sticky.plugin.js"></script>
    <script>
      /* Example parameters
      $('header').sticky(
      {
        position:'top',
        height:'10em',
        width:'50%',
        background:'red'
      });
      */
      $('header').sticky(
      {
        position:'top'
      });
      $('footer').sticky(
      {
        position:'bottom'
      });
    </script>
  </body>
</html>

Now the jQuery plugin, sticky.plugin.js
(function($)
{
$.fn.extend(
{
sticky:function(options)
{
var defaults =
{
position:'bottom',
height:'4em',
width:'100%',
background:'white'
}

var options = $.extend(defaults, options);

return this.each(function()
{
var o = options;
   $(this).css('position','fixed').css('_position','absolute').css(o.position,0).css('background',o.background);
   $(this).height(o.height);
   $(this).width(o.width);
       $(this).css('z-index', 9999);
});
}
});
})(jQuery);

Monday 12 May 2014

Introduction to the PHP5 Command Line Interface (Ubuntu version)

So you'd like to write and run your PHP5 code without using a web browser. Where you run it from the command-line....

First make sure that the php5 cli is installed by typing this on the command line (terminal, shell, whatever you want to call it) :

sudo apt-get install php5-cli

Now it's time to create your first command line script to test. Use your favourite editor to create the following program and save it as hello.php:

<?php
echo 'hello world!';
?>

Now back to the command line. Make sure you are in the same directory as hello.php was saved. Now type:
php5 hello.php

You should see that hello world! is echo'd to the command line.

Now the following program:

  • Takes the first number
  • Assigns it to a variable ($first)
  • Takes the second number
  • Assigns it to a variable ($second)
  • Adds the two numbers together and assigns them to the variable $answer
  • Echo's the value of the variable $answer, then adds a line break.


<?php
echo 'Enter first number ';
$first = fgets(STDIN);
echo 'Enter second number ';
$second = fgets(STDIN);
$answer = $first + $second;
echo 'And the answer is '.$answer.PHP_EOL;
?>

Now you're ready to go.

Tuesday 29 April 2014

jQuery equalise selector widths

Now for making the width of selectors the same. This gets slightly more complicated on 4 counts:
1. Firefox can't turn null values into zero, so you have to force it.
2. You also need to get margin, border and padding values to make your calculations. Including parent padding.
3. You have to handle the browser being resized.
4. You have to add a little contingency for browser rendering differences.
Let's start with the HTML:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>equalise width</title>
    <link rel="stylesheet" type="text/css" href="style.css">  
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <article>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliud igitur esse censet gaudere, aliud non dolere. Ad eos igitur converte te, quaeso. Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. Ut optime, secundum naturam affectum esse possit. Videsne quam sit magna dissensio? Duo Reges: constructio interrete. Sed quanta sit alias, nunc tantum possitne esse tanta. At enim hic etiam dolore. Facile est hoc cernere in primis puerorum aetatulis.</p>
    </article>
    <article>
      <p>Summus dolor plures dies manere non potest? At ego quem huic anteponam non audeo dicere; Nemo nostrum istius generis asotos iucunde putat vivere. Pugnant Stoici cum Peripateticis. Istam voluptatem perpetuam quis potest praestare sapienti? Quid sequatur, quid repugnet, vident.</p>
    </article>
    <article>
      <p>Nihil opus est exemplis hoc facere longius. Quam illa ardentis amores excitaret sui! Cur tandem? Sic enim censent, oportunitatis esse beate vivere.</p>
    </article>
    <footer>
      <p>footer</p>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>  
    <script src="equalisewidth.plugin.js"></script>
    <script>
    (function()
    {
      $('article').equalisewidth();
      $(window).resize(function()
      {
        $('article').equalisewidth();
      });
    })();
    </script>
  </body>
</html>
Now for the CSS (style.css):
body
{
  font: 90%/1.618 sans-serif;
  padding-top:2em;
}
article
{
float:left;
margin:1em;
padding:1em;
background:red;
color:white;
}
footer
{
clear:left;
}
And finally the jQuery plugin (equalisewidth.plugin.js):
(function($)
{
  $.fn.equalisewidth = function()
  {
    var thisMargin = parseInt($(this).css('margin-left').replace(/px/,'')) + parseInt($(this).css('margin-right').replace(/px/,''));
    if(isNaN(thisMargin)) thisMargin = 0;
    var thisBorder = parseInt($(this).css('border-left').replace(/px/,'')) + parseInt($(this).css('border-right').replace(/px/,''));
    if(isNaN(thisBorder)) thisBorder = 0;
    var thisPadding = parseInt($(this).css('padding-left').replace(/px/,'')) + parseInt($(this).css('padding-right').replace(/px/,''));
    if(isNaN(thisPadding)) thisPadding = 0;
    var thisParentPadding = parseInt($(this).parent().css('padding-left').replace(/px/,'')) + parseInt($(this).parent().css('padding-right').replace(/px/,''));
    if(isNaN(thisParentPadding)) thisParentPadding = 0;
    var equalWidth = $(this).parent().width() / $(this).length;
    var spacing = (thisMargin + thisParentPadding) + (thisBorder + thisPadding) + 2;
    var widthResize = equalWidth - spacing;  
    $(this).each(function()
    {
      $(this).width(widthResize);    
    });
  }
})(jQuery);
Enjoy!

jQuery equalise height plugin

Sometimes we just want a group of element to be the same height. It's more visually pleasing. This plugin is designed to do just that.
First the HTML:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>equalise height</title>
    <link rel="stylesheet" type="text/css" href="style.css">  
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <article>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliud igitur esse censet gaudere, aliud non dolere. Ad eos igitur converte te, quaeso. Atqui iste locus est, Piso, tibi etiam atque etiam confirmandus, inquam; Cum autem in quo sapienter dicimus, id a primo rectissime dicitur. Ut optime, secundum naturam affectum esse possit. Videsne quam sit magna dissensio? Duo Reges: constructio interrete. Sed quanta sit alias, nunc tantum possitne esse tanta. At enim hic etiam dolore. Facile est hoc cernere in primis puerorum aetatulis.</p>
    </article>
    <article>
      <p>Summus dolor plures dies manere non potest? At ego quem huic anteponam non audeo dicere; Nemo nostrum istius generis asotos iucunde putat vivere. Pugnant Stoici cum Peripateticis. Istam voluptatem perpetuam quis potest praestare sapienti? Quid sequatur, quid repugnet, vident.</p>
    </article>
    <article>
      <p>Nihil opus est exemplis hoc facere longius. Quam illa ardentis amores excitaret sui! Cur tandem? Sic enim censent, oportunitatis esse beate vivere.</p>
    </article>
    <footer>
      <p>footer</p>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="equaliseheight.plugin.js"></script>
    <script>
    (function()
    {
      $('article').equaliseheight();
    })();
    </script>
  </body>
</html>
Now the CSS (style.css):
body
{
  font: 90%/1.618 sans-serif;
  padding-top:2em;
}
article
{
float:left;
margin:1em;
padding:1em;
width:15em;
background:red;
color:white;
}
footer
{
clear:left;
}
And finally the jQuery plugin (equaliseheight.plugin.js):
(function($)
{
  $.fn.equaliseheight = function()
  {
    var maxHeight = 0;
    $(this).each(function()
    {
      if($(this).height() > maxHeight)
      {
        maxHeight = $(this).height();
      }
    });
    $(this).height(maxHeight);
  }
})(jQuery);

Monday 28 April 2014

jQuery second word plugin

You've seen those fancy titles where the second word is a different colour from the first. To achieve this, you really don't want to have to put span tags all around your code. Below I've created a plugin which allows you to decide which elements have their second word looking different from the first. You can use it with any framework. Please excuse the separate CSS file, but I've been using LiveStyle with Sublime Text 2. It really speeds development up.
First the HTML:
<!DOCTYPE html>
<html lang="en">
  <head>  
    <title>Second word</title>  
    <link href='style.css' rel='stylesheet' type='text/css'>  
  </head>
  <body>
    <header>
      <h1>Second word</h1>
    </header>
    <section>
      <article>
        <h2>Sub header</h2>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sin tantum modo ad indicia veteris memoriae cognoscenda, curiosorum. Si verbum sequimur, primum longius verbum praepositum quam bonum. Duo Reges: constructio interrete. Falli igitur possumus. Egone non intellego, quid sit don Graece, Latine voluptas? Sed virtutem ipsam inchoavit, nihil amplius. Sed eum qui audiebant, quoad poterant, defendebant sententiam suam.</p>
      </article>
      <article>
        <h2>Sub heading</h2>
        <p>Quis est, qui non oderit libidinosam, protervam adolescentiam? Nam si amitti vita beata potest, beata esse non potest. Recte, inquit, intellegis. Idemne potest esse dies saepius, qui semel fuit? Venit enim mihi Platonis in mentem, quem accepimus primum hic disputare solitum; Aliter enim explicari, quod quaeritur, non potest. Profectus in exilium Tubulus statim nec respondere ausus; Nos vero, inquit ille;</p>
      </article>        
    </section>
    <footer><p>&copy; Second word 2014</p></footer>  
    <script src="https://code.jquery.com/jquery.js"></script>
    <script src="secondword.plugin.js"></script>
    <script>
    (function()
    {
        $('header > h1, article > h2').secondword();
    })();
    </script>
  </body>
</html>

Now the style.css:
body
{
padding:1em;
font-family:Sans-serif;
}
header, footer, article > h2
{
font-family:Serif;
color:red;
font-style:italic;
}
section > article
{
text-align:justify;
line-height:1.7;
}
body, header > h1 span.secondword, article > h2 span.secondword
{
color:#777;
}

And finally, the jQuery plugin, secondword.plugin.js:
(function($)
{
  $.fn.secondword = function()
  {
    $(this).each(function()
    {
      var text = $(this).text().split(' ');
      if(text.length < 2) return;
      text[1] = '<span class="secondword">'+text[1]+'</span>';
      $(this).html(text.join(' '));
    });
  }
})(jQuery);

Friday 25 April 2014

Get .htaccess files working on Ubuntu after LAMP installation

Step 1. Configure Apache mod_rewrite
sudo a2enmod rewrite

Step 2. Within the file /etc/apache2/apache2.conf
Under the section labelled <Directory /var/www/>
Change the line
AllowOverride None
to
AllowOverride All

Step 3. Restart apache
sudo /etc/init.d/apache2 restart

jQuery accordion plugin you can style how you like

The jQuery accordion plugins I have come across also come with some complex CSS. They rely on the CSS to hide elements and you have to dig your way through complex CSS elements in order to adapt the look for your own purposes. On of the the worst culprits for this is the one which comes with the jQuery UI library.
So I devised my own plugin which operates without any CSS, is easy to style, is lightweight, does not require anchors to open items, and provides flexibility to the item headers.
First the HTML.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Accordion</title>  
    <style>
    body
    {
      font: 90%/1.618 sans-serif;
      padding-top:2em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div id="micksaccordion">
      <section>
        <header>
          <h1>Header 1</h1>
        </header>
        <article>
          <p>This is from article 1</p>
        </article>
      </section>
      <section>
        <header>
          <h2>Header 2</h2>
        </header>
        <article>
          <p>This is from article 2</p>
        </article>
      </section>
      <section>
        <header>
          <h3>Header 3</h3>
        </header>
        <article>
          <p>This is from article 3</p>
        </article>
      </section>
      <section>
        <header>
          <h4>Header 4</h4>
        </header>
        <article>
          <p>This is from article 4</p>
        </article>
      </section>
    </div><!-- micksaccordion -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="accordion.plugin.js"></script>
    <script>
    (function()
    {
      /* Easy version
      $('#micksaccordion').accordion();
      */
      $('#micksaccordion').accordion(
      {
        /* 'first', 'last', false */
        show:'last'
      });
    })();
    </script>
  </body>
</html>

And now the jQuery plugin:
(function($)
{
  $.fn.extend(
  {
    accordion:function(options)
    {
      var defaults =
      {
          show:false
      }

      var options = $.extend(defaults, options);

      return this.each(function()
      {
        var o = options;              

        var articles = $(this).find('article');

        var firstSection = $(this).children().first();      
        var firstArticle = firstSection.find('article');

        var lastSection = $(this).children().last();      
        var lastArticle = lastSection.find('article');

        articles.hide();

        if(o.show == 'first')
        {
          firstArticle.show();
        }
       
        if(o.show == 'last')
        {
          lastArticle.show();
        }

        var sections = $(this).find('section');

        $(sections).on('click','header', function()
        {
          thesection = $(this).parent();
          thearticle = thesection.find('article');        
          if (thearticle.css('paddingTop') == '0px') thearticle.css({paddingTop: '1px'});
          if (thearticle.css('paddingBottom') == '0px') thearticle.css({paddingBottom: '1px'});        
          thearticle.slideToggle('slow');
        });

      });
    }
  });
})(jQuery);

Enjoy!

Monday 14 April 2014

HTML Entities jQuery plugin for the code tag

Try presenting HTML in a web page. It's a pain in the neck. Even in between the code tag, you have to create HTML Entities for greater than and less than symbols etc.
Not wanting to go through that pain ever again, I have created a small JavaScript plugin which helps.
First, here is the HTML page important differences in red:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Convert to HTML Entities</title>  
  </head>
  <body>
    <code>Hello, <h1>world!</h1></code>
    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script src="htmlentities.plugin.js"></script>
    <script>
    (function()
    {
      $('code').htmlentities();
    })();
    </script>
  </body>
</html>
Now, htmlentities.plugin.js:
(function($)
{
  $.fn.htmlentities = function()
  {
 
    beforeString = $(this).html();  
    afterString = beforeString.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    $(this).html(afterString);
  }
})(jQuery);

Happy coding!

Wednesday 9 April 2014

Twitter Bootstrap Progress Bar example using jQuery and PHP

So you'd like to see Twitter Bootstrap Progress Bar actually work. Not too many examples out there are there. For this, after you've downloaded Twitter Bootstrap, you will need 3 more files:

  • Your demo file, index.html
  • Your post handler, posthandler.php
  • Your empty data file, datafile.dat which is writeable

So let's start with index.html. Get the basic template from http://getbootstrap.com/getting-started/
and make the following additions (in red). This file:

  • Creates a progress bar
  • Kicks off the post handler using the submit button
  • Checks the value contained in the data file
  • Uses the data gathered to increase the width of the progress bar

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">  
    <style>
    body
    {
      padding-top:2em;
    }  
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <form role="form">
            <button type="submit" class="btn btn-default">Submit</button>
          </form><br />
          <div class="progress progress-striped active">
            <div class="progress-bar" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width:0%">
            </div>
          </div>
        </div>
      </div>
    </div>    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script>
    (function()
    {
      $('form').submit(function()
      {
        $.post('posthandler.php', function(data1)
        {
          console.log(data1)
        });

        var refreshId = setInterval(function()
        {
          $.get('datafile.dat', function(data2)
          {
            $('.progress-bar').css('width',data2+'%');
            if(data2 >= 100)
            {
              console.log('finished');
              $('.progress').removeClass('active');
              $('.progress').removeClass('progress-striped');
              $('.progress-bar').text('Complete');
              clearInterval(refreshId);
            }
          });
        }, 100);
        return false;
      });      
    })();
    </script>
  </body>
</html>

Now for the posthandler.php
This script:

  • Clears the data file
  • Every so often, updates the data file contents

<?php
file_put_contents('datafile.dat', '');
for($i = 1; $i <= 100; $i++)
{
usleep(200000);
file_put_contents('datafile.dat', $i);
}
?>

Enjoy!

Monday 7 April 2014

Simple pagination plugin for Twitter Bootstrap

Let's say you have content in a Twitter Bootstrap web page and you want to paginate through it. If you've ever tried looking for examples on doing this, you would know that very few of them contain content. Or in other words the examples contain all the numbers at the bottom but none of the content at the top and how to paginate through it.
I then found plugins which looked very clever, but didn't actually work.
So I have created a jQuery plugin (which works) and an example of how it could be used.
First the HTML page which I took form getbootstrap.com, then added the necessary code for the pagination which I highlight below.
Actually, it works for any site which uses jQuery. Another strength to this simplified method is that you can name the content IDs whatever you like and the plugin with still pick them up as labels.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">
    <style>
    body
    {
      padding-top:2em;
    }
    #mickspagination #navigation a
    {
      background:#e5e5e5;
      margin:0.1em;
      padding:0.5em;
    }
    </style>
    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <div class="row">
      <div class="container">
        <div class="col-md-12">
          <div id="mickspagination">
            <div id="content">
              <div id="1" class="active">
                <p>Content for 1</p>
              </div>
              <div id="2">
                <p>Content for 2</p>
              </div>
              <div id="3">
                <p>Content for 3</p>
              </div>
              <div id="4">
                <p>Content for 4</p>
              </div>
            </div>
            <div id="navigation"></div>
          </div>
        </div>
      </div>
    </div>  
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
    <script src="pagination.plugin.js"></script>
    <script>
    (function()
    {
      $('#mickspagination').pagination();
    })();
    </script>
  </body>
</html>

Now the jQuery plugin contained in pagination.plugin.js

(function($)
{
    $.fn.extend(
    {
        pagination:function(options)
        {
            var defaults = 
            {
                showarrows:false,                
                classesext:null,
                alignment:'center',
                navigationtag:'#navigation'
            }

            var options = $.extend(defaults, options);

            return this.each(function() 
            {
                var o = options;                

                var content = $(this).find('#content');
                var contentDivs = content.find(' > div');
                var firstDiv = content.children().first();
                var lastDiv = content.children().last();    
                var leftDiv = content.children().first();    
                var rightDiv = content.children().eq(1);
                var leftArrow, rightArrow;
                var activeDiv  = content.find('.active');
                var navigation = null;
                var contentArr = Array();

                contentDivs.not('.active').hide();

                /* Provide option for navigation outside the pagination */
                if(o.navigationtag == '#navigation')
                {
                  navigation = $(this).find('#navigation');
                }
                else
                {

                  navigation = $(document).find(o.navigationtag);
                }

                /* Show the first arrows */
                if(o.showarrows == true)
                {
                  link = $(this).arrowlink(firstDiv.attr('id').replace(/ /g,''),'«', o.classesext);
                  navigation.append(link);
                  link = $(this).arrowlink(leftDiv.attr('id'),'<', o.classesext);
                  navigation.append(link);                  
                  leftArrow = navigation.find("a:contains('<')"); 
                }    

                /* Show all the pagination navigation */
                contentDivs.each(function()
                {
                  encodedID = $(this).attr('id').replace(/ /g,'');
                  $(this).attr('title', $(this).attr('id'));                  
                  $(this).attr('id', encodedID);                  
                  contentArr.push($(this).attr('id'));
                  link = $(this).buildlink(o.classesext);
                  navigation.append(link);
                });

                /* Show the last arrows */
                if(o.showarrows == true)
                {
                  link = $(this).arrowlink(rightDiv.attr('id'),'>', o.classesext);
                  navigation.append(link);
                  link = $(this).arrowlink(lastDiv.attr('id'),'»', o.classesext);
                  navigation.append(link);                  
                  rightArrow = navigation.find("a:contains('>')");                  
                }

                /* Centre the pagination navigation. May wish to set this as an option */
                navigation.css('text-align',o.alignment);

                /* Handle what happens when someone clicks on a navigation link */
                $(navigation).on('click', 'a', function()
                {
                  /* Make the currently clicked ID the active one */
                  activeDiv  = content.find('.active');
                  activeDiv.removeClass('active');
                  currentID = $(this).attr('href');
                  newActiveDiv = content.find(' > div'+currentID);
                  newActiveDiv.addClass('active');

                  /* Change what the arrows now point at */
                  if(o.showarrows == true)
                  {
                    leftArrow.updateleftlink(newActiveDiv, contentArr);
                    rightArrow.updaterightlink(newActiveDiv, contentArr);
                  }

                  /* Show the newly selected content */
                  contentDivs.not('.active').hide();
                  newActiveDiv.show();      
                });
            });
        }
    }); /* End of pagination plugin. Other function may go below this */

    /* Create the pagination navigation links */    
    $.fn.buildlink = function(classesext)
    {
        var ct = $.trim(classesext);
        link = '<a href="#';
        link += $(this).attr('id')+'"';        
        if(classesext !== null)
        {
            link += ' class="'+ct+'"';
        }
        link += '>';        
        link += $(this).attr('title');
        link += '</a>';
        return link;
    };

    $.fn.arrowlink = function(dynamicloc, linkchar, classesext)
    {
        var ct = $.trim(classesext);
        link = '<a href="#';        
        link += dynamicloc+'"';        
        link += ' class="pagarrow';
        if(classesext !== null)
        {
            link += ' '+ct;
        }        
        link += '">';
        link += linkchar;
        link += '</a>';
        return link;
    };

    /* Update the first arrow link */
    $.fn.updateleftlink = function(ad,cr)
    {
        var ov = 0;
        var hrefstring = '#';
        var ap = cr.indexOf(ad.attr('id'));
        if((ap-1) <= 0)
        {
            ov = 0;
        }
        else
        {
            ov = (ap-1);
        }
        hrefstring += cr[ov];
        $(this).attr('href',hrefstring);    
    };

    $.fn.updaterightlink = function(ad,cr)
    {
        var ov = 0;
        var hrefstring = '#';
        var ap = cr.indexOf(ad.attr('id'));    
        if((ap+1) >= cr.length)
        {
            ov = cr.length-1;      
        }
        else
        {
            ov = (ap+1);
        }
        hrefstring += cr[ov];    
        $(this).attr('href',hrefstring);    
    };

})(jQuery);