Wednesday, March 23, 2016

jQuery Plugin and special javascript literal

When you read javascript codes written by other developers, chances are those code may contain jQuery plugin codes. Usually those codes containing following format:
Type 1:
(function($) {
    $.fn.jPluginName = {
    },

    $.fn.jPluginName.defaults = {
    }
})(jQuery);
Type 2:
(function($) {
    $.jPluginName = {
    }
})(jQuery);
Type 3:
(function($){
    //Attach this new method to jQuery
    $.fn.extend({ 
        var defaults = {  
        }  
        var options =  $.extend(defaults, options);  
        //This is where you write your plugin's name
        pluginname: function() {
            //Iterate over the current set of matched elements
            return this.each(function() {
                //code to be inserted here
            });
        }
    }); 
})(jQuery);
A js code block looks like (function(){})()  actually defined a function and execute it. The parameter $ means the parameter is a reference. 

In jQuery, the fn property is just an alias to the prototype property.
So jQuery.fn === jQuery.prototype

Following is a simple javascript constructor function using prototype to extend the original class:
function Test() {
  this.a = 'a';
}
Test.prototype.b = 'b';

var test = new Test(); 
test.a; // "a", own property
test.b; // "b", inherited property
Following is a normal structure similar to the architecture of jQuery:
(function() {
  var foo = function(arg) { // core constructor
      // ensure to use the `new` operator
      if (!(this instanceof foo))
          return new foo(arg);
      // store an argument for this example
      this.myArg = arg;
      //..
  };

  // create `fn` alias to `prototype` property
  foo.fn = foo.prototype = {
      init: function () {/*...*/}
    //...
  };

  // expose the library
  window.foo = foo;
})();

// Extension:
foo.fn.myPlugin = function () {
    alert(this.myArg);
    return this; // return `this` for chainability
};

foo("bar").myPlugin(); // alerts "bar"
Type 1: This is actually not a plugin, it's an object passed as a function, and new functions added to the object using prototype.

Type 2: This is again not a plugin as it does not extend the $.fn object. It's just an extension of the jQuery core, although the outcome is the same. This is if you want to add traversing functions such as toArray and so on.

Type 3: This is the best method to add a plugin, the extended prototype of jQuery takes an object holding your plugin name and function and adds it to the plugin library for you.

Reference:

Tuesday, March 22, 2016

Applying Node.js in ASP.Net MVC 4.5

Node.js is a server side javascript framework which mainly used for real time web application. The core idea is event driving with asynchronous programming. When  you develop your dashboard web applications, it's so great to let your web page to show real time charts and statistic data. There is also real time programming framework named Signal R from Microsoft, which is purely under ASP.Net and a great contender compared with Node.js.

Compared with Signal R, Node.js has another feather that all javascript code you wrote for client side can be easily reused on the server side.  When I worked with a Online Web Reporting project, I applied Chart.js javascript to create vivid charts on the front web end, and also I applied jsPDF javascript library on client side to create PDF report for those vivid charts. All these worked fine but we also have a requirement that the server side can create same PDF reports and deliver them to our client's email. To make those PDF reports have same look and feel, the server side should call the same client side javascript code to create the PDF doc. The difference is the server side service will not be launched from client web side. And Node.js offered great chance to show their shinning part for sharing same client code on server side.

So we finally choose Node.js, to do above 2 great jobs.

The following blog link would compare Node.js and Signal R.
http://sim4all.com/blogging/?p=454

Monday, March 14, 2016

JQuery UI Drag and Drop Introduction

This is an interesting javascript drag and drop game.
<!doctype html>
<html lang="en">
<head>

<title>A jQuery Drag-and-Drop Number Cards Game</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link rel="stylesheet" type="text/css" href="style.css">

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script type="text/javascript">

var correctCards = 0;
$( init );

function init() {
  // Hide the success message
  $('#successMessage').hide();
  $('#successMessage').css( {
    left: '580px',
    top: '250px',
    width: 0,
    height: 0
  } );

  // Reset the game
  correctCards = 0;
  $('#cardPile').html( '' );
  $('#cardSlots').html( '' );

  // Create the pile of shuffled cards
  var numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

  // Sort the array with a randomizing sort function to shuffle the numbers
  numbers.sort( function() { return Math.random() - .5 } );
  
  // loop through each element in the numbers array, creating a div card element
  // for each number. Inside the div we place the number, so that it appears on 
  // the card in the page.
  for ( var i=0; i<10; i++ ) {
    $('<div>' + numbers[i] + '</div>').data( 'number', numbers[i] )
      .attr('id', 'card'+numbers[i] ).appendTo( '#cardPile' ).draggable({
          containment: '#content',
          //to ensure that the dragged card always sits on top of the other cards
          stack: '#cardPile div',  
          cursor: 'move',

          //to make the card slide back to its initial position when dropped, 
          //so that the user can try again. When the card has been dragged to the correct
          //the option will be set as false.
          revert: true 
      });
  }

  // Create the card slots
  var words = [ 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 
'eight', 'nine', 'ten' ];

  for ( var i=1; i<=10; i++ ) {
    // store the card's number in a 'number' key inside the object by 
    // using jQuery's data() method.
    $('<div>' + words[i-1] + '</div>').data( 'number', i ).appendTo( 
'#cardSlots' ).droppable( {
      accept: '#cardPile div',
      hoverClass: 'hovered',
      drop: handleCardDrop
    } );
  }
}

function handleCardDrop( event, ui ) {
  var slotNumber = $(this).data( 'number' );
  var cardNumber = ui.draggable.data( 'number' );

  // If the card was dropped to the correct slot,
  // change the card colour, position it directly
  // on top of the slot, and prevent it being dragged
  // again
  if ( slotNumber == cardNumber ) {
    ui.draggable.addClass( 'correct' );
    ui.draggable.draggable( 'disable' );
    $(this).droppable( 'disable' );
    ui.draggable.position( { of: $(this), my: 'left top', at: 'left top' } );
    ui.draggable.draggable( 'option', 'revert', false );
    correctCards++;
  } 
  
  // If all the cards have been placed correctly then display a message
  // and reset the cards for another go
  if ( correctCards == 10 ) {
    $('#successMessage').show();
    $('#successMessage').animate( {
      left: '380px',
      top: '200px',
      width: '400px',
      height: '100px',
      opacity: 1
    } );
  }
}
</script>

<body>
<div class="wideBox">
  <h1>Drag-and-Drop with jQuery: Your Essential Guide</h1>
  <h2>A Number Cards Game</h2>
</div>

<div id="content">
  <div id="cardPile"> </div>
  <div id="cardSlots"> </div>

  <div id="successMessage">
    <h2>You did it!</h2>
    <button onclick="init()">Play Again</button>
  </div>
</div>
</body>
</html>
      
The HTML part:

In the head area we've included the jQuery and jQuery UI libraries, and linked to a style.css file. In the body we have:
  • A "#content" div to contain the game
  • A "#cardPile" div that will contain the set of 10 unsorted cards
  • A "#cardSlots" div that will contain the 10 slots to drop the cards into
  • A "#successMessage" div to display a "You did it!" message, along with a button to play again. 
The init() function:
  1. $( init ) is to call our init() function when the document is ready.
  2. To create the pile of cards, we first make an array, numbers, containing the numbers 1 to 10, then sort the array with a randomizing sort function to shuffle the numbers.
  3. Then loop through each element in the numbers array, creating a div card element for each number. Inside the div we place the number, so that it appears on the card in the page. Once we've created the divobject, we store the card's number in a 'number' key inside the object by using jQuery's data() method.
  4. Give the card div an id of 'cardn', where n is the card number. This lets us give each card a different colour in the CSS. We then append the card to the #cardPile div.
  5. The interesting bit in the loop is, of course, the call to the draggable()method. This:
    • Makes the card draggable
    • Uses containment to constrain it to the #content div
    • Uses stack to ensure that the dragged card always sits on top of the other cards
    • Uses cursor to turn the mouse cursor into a move cursor during the drag, and
    • Sets the revert option to true. This makes the card slide back to its initial position when dropped, so that the user can try again. We'll turn this option off when the user has dragged the card to the correct slot, as we'll see in a moment.
  6. The last part of the init() function creates the 10 slots to drag the cards onto. We create an array called words that contains the words "one" to "ten", then loop through the elements of the words array, creating a slot div for each number. As before, we use data() to record the number of the slot, so we can test if the user has dragged the correct card to the correct slot, and we append the slot to the #cardSlots div.
  7. This time, we call the droppable() method so that the slot can receive a draggable card. We use the accept option with the selector "#cardPile div" to ensure that the slot will only accept our number cards, and not any other draggable element. The hoverClass option adds a CSS class to a droppable when a draggable is hovered over it — we use this option to add a class of 'hovered' to the element, which we'll highlight using CSS. Finally, we set up a drop event handler called handleCardDrop(), which is triggered when the user drops a card on the droppable. We'll write this handler next.
The CSS Part: 
body {

  margin: 30px;

  font-family: "Georgia", serif;

  line-height: 1.8em;

  color: #333;

}



/* Give headings their own font */



h1, h2, h3, h4 {

  font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;

}



/* Main content area */



#content {

  margin: 80px 70px;

  text-align: center;

  -moz-user-select: none;

  -webkit-user-select: none;

  user-select: none;

}



/* Header/footer boxes */



.wideBox {

  clear: both;

  text-align: center;

  margin: 70px;

  padding: 10px;

  background: #ebedf2;

  border: 1px solid #333;

}



.wideBox h1 {

  font-weight: bold;

  margin: 20px;

  color: #666;

  font-size: 1.5em;

}



/* Slots for final card positions */



#cardSlots {

  margin: 50px auto 0 auto;

  background: #ddf;

}



/* The initial pile of unsorted cards */



#cardPile {

  margin: 0 auto;

  background: #ffd;

}



#cardSlots, #cardPile {

  width: 910px;

  height: 120px;

  padding: 20px;

  border: 2px solid #333;

  -moz-border-radius: 10px;

  -webkit-border-radius: 10px;

  border-radius: 10px;

  -moz-box-shadow: 0 0 .3em rgba(0, 0, 0, .8);

  -webkit-box-shadow: 0 0 .3em rgba(0, 0, 0, .8);

  box-shadow: 0 0 .3em rgba(0, 0, 0, .8);

}



/* Individual cards and slots */



#cardSlots div, #cardPile div {

  float: left;

  width: 58px;

  height: 78px;

  padding: 10px;

  padding-top: 40px;

  padding-bottom: 0;

  border: 2px solid #333;

  -moz-border-radius: 10px;

  -webkit-border-radius: 10px;

  border-radius: 10px;

  margin: 0 0 0 10px;

  background: #fff;

}



#cardSlots div:first-child, #cardPile div:first-child {

  margin-left: 0;

}



#cardSlots div.hovered {

  background: #aaa;

}



#cardSlots div {

  border-style: dashed;

}



#cardPile div {

  background: #666;

  color: #fff;

  font-size: 50px;

  text-shadow: 0 0 3px #000;

}



#cardPile div.ui-draggable-dragging {

  -moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);

  -webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);

  box-shadow: 0 0 .5em rgba(0, 0, 0, .8);

}



/* Individually coloured cards */



#card1.correct { background: red; }

#card2.correct { background: brown; }

#card3.correct { background: orange; }

#card4.correct { background: yellow; }

#card5.correct { background: green; }

#card6.correct { background: cyan; }

#card7.correct { background: blue; }

#card8.correct { background: indigo; }

#card9.correct { background: purple; }

#card10.correct { background: violet; }





/* "You did it!" message */

#successMessage {

  position: absolute;

  left: 580px;

  top: 250px;

  width: 0;

  height: 0;

  z-index: 100;

  background: #dfd;

  border: 2px solid #333;

  -moz-border-radius: 10px;

  -webkit-border-radius: 10px;

  border-radius: 10px;

  -moz-box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);

  -webkit-box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);

  box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);

  padding: 20px;

}

Source: http://www.elated.com/articles/drag-and-drop-with-jquery-your-essential-guide/
Game Link: http://www.elated.com/res/File/articles/development/javascript/jquery/drag-and-drop-with-jquery-your-essential-guide/card-game.html

Monday, February 1, 2016

Extending Chartjs


ChartJs is a free Javascript Chart library.Here is one example to extend the line chart with null data value.
    var ctx = document.getElementById("chart").getContext("2d");
    var data = {
        labels: ["January", "February", "March", "April", "May", "June", "July"],
        datasets: [{
                label: "My First dataset",
                fillColor: "rgba(220,220,220,0.2)",
                strokeColor: "rgba(220,220,220,1)",
                pointColor: "rgba(220,220,220,1)",
                pointStrokeColor: "#fff",
                pointHighlightFill: "#fff",
                pointHighlightStroke: "rgba(220,220,220,1)",
                data: [65, null, null, 81, 56, 55, 40]
            },
        ]
    };

    Chart.types.Line.extend({
        // Passing in a name registers this chart in the Chart namespace in the same way
        name: "MissingLine",
        initialize: function(data) {
            var helpers = Chart.helpers;
            //Declare the extension of the default point, to cater for the options passed in to the constructor
            this.PointClass = Chart.Point.extend({
                strokeWidth: this.options.pointDotStrokeWidth,
                radius: this.options.pointDotRadius,
                display: this.options.pointDot,
                hitDetectionRadius: this.options.pointHitDetectionRadius,
                ctx: this.chart.ctx,
                inRange: function(mouseX) {
                    return (Math.pow(mouseX - this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius, 2));
                }
            });

            this.datasets = [];

            //Set up tooltip events on the chart
            if (this.options.showTooltips) {
                helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
                    var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
                    this.eachPoints(function(point) {
                        point.restore(['fillColor', 'strokeColor']);
                    });
                    helpers.each(activePoints, function(activePoint) {
                        activePoint.fillColor = activePoint.highlightFill;
                        activePoint.strokeColor = activePoint.highlightStroke;
                    });
                    this.showTooltip(activePoints);
                });
            }

            //Iterate through each of the datasets, and build this into a property of the chart
            helpers.each(data.datasets, function(dataset) {

                var datasetObject = {
                    label: dataset.label || null,
                    fillColor: dataset.fillColor,
                    strokeColor: dataset.strokeColor,
                    pointColor: dataset.pointColor,
                    pointStrokeColor: dataset.pointStrokeColor,
                    points: []
                };

                this.datasets.push(datasetObject);

                helpers.each(dataset.data, function(dataPoint, index) {
                    /***
                     * Check for datapoints that are null
                     ***/
                    if (helpers.isNumber(dataPoint) || dataPoint === null) {
                        //Add a new point for each piece of data, passing any required data to draw.
                        datasetObject.points.push(new this.PointClass({
                            /*** 
                             * add ignore field so we can skip them later
                             ***/
                            ignore: dataPoint === null,
                            value: dataPoint,
                            label: data.labels[index],
                            datasetLabel: dataset.label,
                            strokeColor: dataset.pointStrokeColor,
                            fillColor: dataset.pointColor,
                            highlightFill: dataset.pointHighlightFill || dataset.pointColor,
                            highlightStroke: dataset.pointHighlightStroke || dataset.pointStrokeColor
                        }));
                    }
                }, this);

                this.buildScale(data.labels);

                this.eachPoints(function(point, index) {
                    helpers.extend(point, {
                        x: this.scale.calculateX(index),
                        y: this.scale.endPoint
                    });
                    point.save();
                }, this);

            }, this);

            this.render();
        },

        draw: function(ease) {
            var helpers = Chart.helpers;
            var easingDecimal = ease || 1;
            this.clear();

            var ctx = this.chart.ctx;

            this.scale.draw(easingDecimal);

            helpers.each(this.datasets, function(dataset) {
                //Transition each point first so that the line and point drawing isn't out of sync
                //We can use this extra loop to calculate the control points of this dataset also in this loop

                helpers.each(dataset.points, function(point, index) {
                    point.transition({
                        y: this.scale.calculateY(point.value),
                        x: this.scale.calculateX(index)
                    }, easingDecimal);

                }, this);

                // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
                // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
                if (this.options.bezierCurve) {
                    helpers.each(dataset.points, function(point, index) {
                        //If we're at the start or end, we don't have a previous/next point
                        //By setting the tension to 0 here, the curve will transition to straight at the end
                        if (index === 0) {
                            point.controlPoints = helpers.splineCurve(point, point, dataset.points[index + 1], 0);
                        } else if (index >= dataset.points.length - 1) {
                            point.controlPoints = helpers.splineCurve(dataset.points[index - 1], point, point, 0);
                        } else {
                            point.controlPoints = helpers.splineCurve(dataset.points[index - 1], point, dataset.points[index + 1], this.options.bezierCurveTension);
                        }
                    }, this);
                }

                //Draw the line between all the points
                ctx.lineWidth = this.options.datasetStrokeWidth;
                ctx.strokeStyle = dataset.strokeColor;

                var penDown = false;
                var start = null

                helpers.each(dataset.points, function(point, index) {
                    /***
                     * no longer draw if the last point was ignore (as we don;t have anything to draw from)
                     * or if this point is ignore
                     * or if it's the first
                     ***/
                    if (!point.ignore && !penDown) {
                        ctx.beginPath();
                        penDown = true;
                        start = point;
                    }
                    if (index > 0 && !dataset.points[index - 1].ignore && !point.ignore) {
                        if (this.options.bezierCurve) {
                            ctx.bezierCurveTo(
                                dataset.points[index - 1].controlPoints.outer.x,
                                dataset.points[index - 1].controlPoints.outer.y,
                                point.controlPoints.inner.x,
                                point.controlPoints.inner.y,
                                point.x,
                                point.y
                            );
                        } else {
                            ctx.lineTo(point.x, point.y);
                        }
                    } else if (index === 0 || dataset.points[index - 1].ignore) {
                        ctx.moveTo(point.x, point.y);
                    }

                    if (((dataset.points.length > index + 1 && dataset.points[index + 1].ignore) ||
                        dataset.points.length == index + 1) && !point.ignore) {
                        ctx.stroke();

                        if (this.options.datasetFill) {
                            ctx.lineTo(point.x, this.scale.endPoint);
                            ctx.lineTo(start.x, this.scale.endPoint);
                            ctx.fillStyle = dataset.fillColor;
                            ctx.closePath();
                            if (point.x != start.x) {
                                ctx.fill();
                            }
                        }
                        penDown = false;
                    }

                }, this);


                //Now draw the points over the line
                //A little inefficient double looping, but better than the line
                //lagging behind the point positions
                helpers.each(dataset.points, function(point) {
                    /**
                     * don't draw the dot if we are ignoring
                     */
                    if (!point.ignore)
                        point.draw();
                });

            }, this);
        }
    });

    var myLineChart = new Chart(ctx).MissingLine(data);
Resources:
http://jsfiddle.net/leighking2/sLgefm04/6/

Sunday, January 31, 2016

How to handle ASP.NET MVC JSON Result Date Format Issue

ASP.Net MVC controller will return JsonResult for action so that ajax can handle those Json data. But Json data has issues for type DateTime, especially Microsoft SQL Server DB. Here is some result if you just display those data in string format: "/Date(-501361200000)/". This is one very popular topic for ASP.Net MVC Json data processing. You can process those Json DateTime at client side using some Javascript tricks. But the best solution is on the Server side, you can use Json.Net from newtonsoft to do the magic.

This is one typical action method in controller:
        public JsonResult GetStudent(string name)
        {
            try
            {
                if (name == null)
                {
                    return Json(null, JsonRequestBehavior.AllowGet);
                }
                else
                {
                    using (StudentService _service = new StudentService())
                    {
                        Student = _service.GetStudent(name);
                        return Json(model, JsonRequestBehavior.AllowGet);
                    }
                }
            }
            catch (Exception ex)
            {
                return Json(ex.Message, JsonRequestBehavior.AllowGet);
            }
        }
You don't need to change above code. What you should do is:
First,  add a new class CustomJsonResult which inherits from JsonResult.
using System;
using System.Web;
using System.Web.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public class CustomJsonResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        if (!String.IsNullOrEmpty(ContentType))
        {
            response.ContentType = ContentType;
        }
        else
        {
            response.ContentType = "application/json";
        }
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data != null)
        {
            // Using Json.NET serializer
            var isoConvert = new IsoDateTimeConverter();
            isoConvert.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
            response.Write(JsonConvert.SerializeObject(Data, isoConvert));
        }
    }
}
Second, in your Base Controller, override Json method to return CustomJsonResult.
        protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding, JsonRequestBehavior behavior)
        {
            return new CustomJsonResult
            {
                Data = data,
                ContentType = contentType,
                ContentEncoding = contentEncoding,
                JsonRequestBehavior = behavior
            };
        }
Then all JsonResult call in your controller will return CustomJsonResult I created above, which use Newtonsoft.Json.Converters.IsoDateTimeConverter as serializer instead, and you will see the readable DateTime instead of annoying and wild date format in JsonResult.

Source:
http://stackoverflow.com/questions/726334/asp-net-mvc-jsonresult-date-format