Software hints

Persistent Inappeasable Mind

Calendar

Back May '13 Forward
Mo Tu We Th Fr Sa Su
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Statistics

Last entry: 2013-05-20 12:21
275 entries written
90 comments have been made
Software hints

Monday, March 4. 2013

AngularJS: show progress of a form by counting the number of disabled fields

Software hints

Let say we have a survey with many questions. Some questions are enabled and some disabled at first and this keeps changing based on what is selected or entered. At any given time we want to know the count of all fields in a form, how many of them are disabled and how many still required. The number of all and required fields is easy as AngularJS keeps an array of all form elements. Disabled elements are something else.

Counting disabled fields can be a challenge if many radio buttons are used, as every radio button is counted as one element.

Lets take this code for example:

 <!doctype html>
 <html>
    <head>    
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
    </head>
    <body>
    
    <form>
     <input type="radio" name="typeComputer2" disabled> 1<br>
     <input type="radio" name="typeComputer2" disabled> 2<br>
     <input type="radio" name="typeComputer2" disabled> 3<br>
    </form>           
    <div id="res"></div>

    <script>
      var bla = $(':disabled').length;
      $('#res').text("Number of disabled elements: " + bla);
    </script>
    </body>
 </html>

Will produce this:

The number of disabled elements is 3. But in fact this is the same group of radio buttons and could be counted as one element (only one radio button can be selected at once).

Lets look at the solution using AngularJS.

We have this HTML document with 3 form elements.

  • First one is 3 radio buttons that are disabled and not required until the checkbox is selected
  • Second is a required radio button
  • Third is a required input field
<html ng-app>
  <head>
     <script src="http://code.angularjs.org/1.1.0/angular.min.js"></script>
     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
     <script src="controller.js"></script>
  </head>
  <body>           
    <div ng-controller="ctrlRead">

      <form name="survey">
        <input type="radio" name="n1" value="1" ng-model="s.el1" 
                        ng-required="s.el2" ng-disabled="!s.el2"> 1<br>
        <input type="radio" name="n1" value="2" ng-model="s.el1" 
                        ng-required="s.el2" ng-disabled="!s.el2"> 2<br>
        <input type="radio" name="n1" value="3" ng-model="s.el1" 
                        ng-required="s.el2" ng-disabled="!s.el2"> 3<br><br>

        <input type="checkbox" name="n2" ng-model="s.el2" ng-required="true"> a<br><br>

        <input type="text" name="n3" ng-model="s.el3" ng-required="true">
      </form>           

      <div id="res"> 
        Disabled: {{eDisabled}} <br>
        All: {{eAll}}  <br>
        Required: {{eRequired}} <br>
        Done in %: {{eDonePercent}} <br>
        Valid: {{survey.$valid}}
      </div>

    </div>
  </body>
</html>

Now we need to write our controller.js

function ctrlRead($scope, $timeout) {

  //a variable to store our survey data
  var s = {}; 
  $scope.s = s;

  //watch for the sur variable to change
  $scope.$watch("s", function () {
     //timeout to fire events 100ms later 
     //for DOM to register the changes
     $timeout($scope.countPercentageDone, 100);
  }, true);

  $scope.countPercentageDone = function() {      
    var requiredTrue=0;
    var requiredFalse=0;
    var disabledElementNames = new Array();    
 
    if ($scope.survey) { 
      //this traverses the form elements stored in form name
      $.each($scope.survey, function(index, value) {
        //survey holds other elements ... 
        //only form elements have $error object
        if ($scope.survey[index].$error) { 
          if ($scope.survey[index].$error.required == true) {
            requiredTrue++;
          } else {
            requiredFalse++;
            //check if the form element is disabled
            if ($("[name="+$scope.survey[index].$name+"]:disabled")) {
              //if it is and it is not just element filled in 
              //element also become disabled once filled in!!
              if ($("[name="+$scope.survey[index].$name+"]:disabled").length != 0) { 
                disabledElementNames.push($scope.survey[index].$name);
              }
            }
          }
        }
      });
    }

    $scope.eDisabled = disabledElementNames.length;
    $scope.eAll = requiredTrue+requiredFalse;
    $scope.eRequired = requiredTrue;
    $scope.eDonePercent = 100-((requiredTrue*100)/(requiredTrue+requiredFalse-disabledElementNames.length));
  }

};

To summarise the code:

  • We traverse all form elements in the $scope.survey array.
  • Because this array contains other elements as well as form elements, we have to look only at those that have an $error object defined.
  • In all elements that are not required we look for those that have lenght > 0.
    We do this because elements that we fill in also become disabled by AngularJS.

This example is also available in jsfiddle.

For the progress bar we could use bootstrap:

<div class="progress">      <div class="bar" style="width: {{eDonePercent}}%"></div> </div>

or any of the solutions I wrote about here to support older browsers.

A big thanks to Tine for all the suggestions and help.

Monday, February 11. 2013

Bootstrap progress bar in 100 images for older browsers

Software hints

These are assumptions taken here:

  • Bootstrap progress bars are nice
  • They do not work in IE 6-9 (I really wonder why 24% of web users don't upgrade their browsers):
    "Progress bars use CSS3 gradients, transitions, and animations to achieve all their effects. These features are not supported in IE7-9 or older versions of Firefox."
  • I needed a progress bar for a web form that would work in all browsers
  • I'm calculating how much of a web form is completed with AngularJS and change the progress bar on he fly.

Let's start.

1. Create 100 progress bars using a php loop and a Bootstrap progress bar code:

 <!doctype html>
 <html>
    <head>    
        <link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css">
        <script src="lib/bootstrap/js/bootstrap.min.js"></script>
    </head>
    <body>

        <div id="progress_bar" style="width: 500px; margin: 10px auto 0 auto">
            <?php
            for ($i=0; $i<101; $i++) { 
                echo '  <div class="progress" style="margin: 2px">
                            <div class="bar" style="width: '.$i.'%"></div>
                        </div>';
            }                    
            ?>
        </div>
    </body>
 </html>

It is possible to use other colours and stripes, etc ... but I'll use simplest progress bar. Keep in mind that my outer div is 500px wide as this is the size of a progress bar I wanted.

2. Crop the progress bars from a web page produces by the above code

The simplest way is to use Awesome screenshot extension for Chrome, Safari or Firefox. It is also good to cut down the image size with e.g. re-saving it for the web so it would be smaller in size (not resolution).

I ended up with this image (original size can be downloaded here):


This can be already the end of the story. The only thing wee need is to use this image as a background of a div and slide up or down based on the percentage we want to show.

3. Show the progress bar

3.1 Using CSS background-position and AngularJS binding

 <div style="float: left;">Progress: </div>
 <div style="width: 500px; height: 22px; float: left;
                  background-image:url('progressbar_small.png'); 
                  background-position:0px -{{Math.round(22*(percentage))}}px;">
 </div>

Each progress bar is 22px high and I multiply it with the percentage I calculate based on how much of the form is filled in. I also use the Math JS in binding for multiplication (maybe there's a better way but I'm not aware of any). To do this I had to define the Math object in my $scope this way $scope.Math = window.Math; 

For some reason the above did not work in IE 8 (which I really care about). Although it apparently works on some web sites. So i decided to slice up the above image in 100 images.

3.2 Using individual images

This is not the optimum solution as for every change there is an Ajax get call to retrieve a desired image. We did this only once in the above example.

This procedure was also explained in a previous post.

First I sliced up the image with ImageMagick using a crop flag (the second command should be in one line) using a bash one line script:

 mk@here/$ ls
 progressbar_small.png

 mk@here/$ for((i=0; i<101; i++)) ; do n=$[i*22]; \ 
          convert progressbar_small.png -crop 500x22+0+$n +repage progressbar${i}.png; \
          done

 mk@here/$ ls
 progressbar0.png    progressbar32.png  progressbar56.png  progressbar7.png
 ...
 progressbar2.png    progressbar53.png  progressbar77.png  progressbar_small.png

The -crop flag slices up images 500px wide and 22px high and starts at 0px on the X axis and goes down 22*i where i runs from 0 to 100. The end results are 100 images.

And finally the HTML bit:

 <div style="float: left;">Progress: </div>
 <div style="width: 500px; height: 22px; float: left;">
     <img src=progressbar{{Math.round(22*(percentage))}}.png">
 </div>

All  images are available in this zip file.

I hope this can help someone.

Any other suggestions greatly appreciated.

A big thank you to Tine for all the suggestions and help.

Monday, February 4. 2013

Slice up an image in equal slices with ImageMagick or a progress bar in 100 images

Software hints

Let's assume we need a progress bar in 101 images where each shows a particular percentage form 0 to 100. How to create such image will be explained in the next post (not ready yet at the time of writing). I already prepared the image here.

The easiest way to slice up an image is in a command line using ImageMagick's a crop flag. The first command shows the image file we are going to slice. The second command (which should be in one line) is a simple a bash for loop and in each iteration we create a new slice. The third command just lists files that we created.

 mk@here/$ ls
 progressbar_small.png

 mk@here/$ for((i=0; i<101; i++)) ; do n=$[i*22]; \ 
           convert progressbar_small.png -crop 500x22+0+$n +repage progressbar${i}.png; \
           done

 mk@here/$ ls
 progressbar0.png    progressbar32.png  progressbar56.png  progressbar7.png
 ...
 progressbar2.png    progressbar53.png  progressbar77.png  progressbar_small.png

The -crop flag slices up images 500px wide and 22px high and starts at 0px on the X axis and goes down 22*i where i runs from 0 to 100. The end results are 100 images.

All images are available in this zip file.

Monday, December 17. 2012

LaTeX - insert a PDF table as an image in a table environment

Software hints

On of the most painful (at least IMHO) things to create in LaTeX are complex or big tables. It is easier to make them in a spreadsheet software (Libreoffice Calc, MS Excel or even Word) and export them as PDFs (either File->Export as PDF, File->Save as or using PDFCreator). It is possible to insert PDFs as images in figure environment like this:

\begin{figure}[!ht]
 \begin{center}                                      
   \includegraphics[width=0.85\textwidth]{images/table-frameworks-overlap.pdf}                                                                  
 \end{center}
 \caption{Similarities and overlap between the two PIM frameworks}
 \label{fig:table-frameworks-overlap}
\end{figure}

However, this caption starts with the word "Image X: ..." while I want to have a caption stating with "Table X: ...".

It is possible to insert a PDF in a table environment as well like this:

\begin{table}[ht]
  \caption{A table}
  \centering
  \begin{tabular}{c}
    \begin{center}                                                                         
      \includegraphics[width=0.85\textwidth]{images/table-studies.pdf} 
    \end{center}     
  \end{tabular}
  \label{tab:}
\end{table}

If a PDF needs to be cropped you can do it like this:

                                                       %left, bottom, right, top
\includegraphics[width=0.85\textwidth, clip=true, trim=0cm 0cm 0cm 0cm]

You might also need the following package:

\usepackage{graphicx}



Saturday, July 14. 2012

Add a blur to a part of a video with Avidemux

Software hints

I made a video of how to emphasize (add a circle over a face) things in a video with Avidemux. The same can be done for adding a blur with a different filter - Mplayer delogo.

However, blurring just one part of the video with the Mplayer delogo filter once failed on me. I wanted to add a blur just for five seconds over a face in a longer video. If I didn't specify the frames (Partial button in step 5 of the previous tutorial) I got the blur over the whole length of the video. With Partial selected I ended up with a destroyed video.

Let say I want to add a blur from the frame 500 to the frame 600 on a video of 1000 frames.

1. Split the video in 3 parts
Use the reddish buttons A & B at the bottom of the video to select UNDESIRED parts of it and use Edit->Delete to cut bits of the video out. Save each part as it's own file (no encoding needed so both video and audio remain on Copy) so we end up with 3 files:  first from frame 0-499, second from frame 500-600 and third from frame 601-100.

2. Add a blur to the second video file
Use the procedure of the previous tutorial but select Mplayer delogo as a filter (and don't forget to encode the video).

3. Concatenate the three videos.
Open the first one and select File->Append to add the other two in the right order

Here's the video (sorry I forgot to turn the mic on but it's short and self explanatory).