Friday, December 16, 2011

Update an container or update panel from a modal popup

If you need to update an update panel from an outside child container you could do like this

1) Create an link with an associated method


asp:LinkButton ID="btnRefreshFolderContent" CssClass="ButtonOff"
asp:TextBox ID="test" runat="server" 

2) Create the javascript (included in the pop-up)



function UpdateFolder() {
window.dialogArguments.__doPostBack('Folder$btnRefreshFolderContent', '');
}


3) Trigger on some button

ScriptManager.RegisterStartupScript(this, this.GetType(), "refresh", "UpdateFolder();window.close();", true);

Monday, December 5, 2011

Analyze Report Execution and Usage Statistics in SQL Server Reporting Services

source : http://www.mssqltips.com/sqlservertip/1908/analyze-report-execution-and-usage-statistics-in-sql-server-reporting-services/


Analyze Report Execution and Usage Statistics in SQL Server Reporting Services

By:  | Comments (9) | Print |Share 



Related Tips: More | Become a paid author


ProblemWe have SQL Server Reporting Services (SSRS) 2005 running on a single server that supports a large user base.  There are specific times during the month where the response time is not acceptable.  Based on performance data that we have gathered we have been able to pinpoint the times when the server is extremely busy.  What we haven't been able to figure out is what reports were running and who was running those reports.  In this tip, I will go over some of the logging mechanisms available in SSRS to answer these questions.
Solution
SSRS has a built-in logging capability that can provide the information that you need.  The ReportServer database has a table named ExecutionLog which contains the following columns:
COLUMN_NAME                              DATA_TYPE
---------------------------------------- ----------------------------------------
InstanceName                             nvarchar
ReportID                                 uniqueidentifier
UserName                                 nvarchar
RequestType                              bit
Format                                   nvarchar
Parameters                               ntext
TimeStart                                datetime
TimeEnd                                  datetime
TimeDataRetrieval                        int
TimeProcessing                           int
TimeRendering                            int
Source                                   tinyint
Status                                   nvarchar
ByteCount                                bigint
RowCount                                 bigint
The column names are pretty self-explanatory.  You can get the report name from the Name column in the Catalog table by joining the ExecutionLog.ReportID to Catalog.ItemID.  However, you need to check if the built-in logging is enabled and how long the data is being retained.  To do that please refer to our earlier tip Logging Options for Reporting Services 2005
The amount of data collected in the ExecutionLog table will grow steadily on a busy server.  In order to conserve space you will want to retain only a small amount of history.  However, you would like to be able to go back and see over time how report usage patterns are changing.  The logical next step is to periodically extract data from the ReportServer database, transform it into a dimensional model, and load it into a data mart on another server to support analysis, ad-hoc queries, and reporting.  You can pickup a nice example of how to do this in the SQL Server 2005 samples available on the Code Plex site.  Assuming you installed the samples in the default location, you will find the sample in the following folder:
C:\Program Files\Microsoft SQL Server\90\Samples\Reporting Services\Report Samples\Server Management Sample Reports\Execution Log Sample Reports
The sample includes the following:
  • Createtables.sql - a T-SQL script to create the the tables for the data mart.  You should create a new database then execute this script in your new database.
  • Cleanup.sql - a T-SQL script to purge data from the data mart that is older than a specified report time start.
  • RSExecutionLog_Update.dtsx -  a SQL Server Integration Services (SSIS) package that you can run to periodically extract data from the ReportServer, transform it, and load it into the data mart.
  • Execution Summary SSRS Report - for a user-defined date range, shows report execution statistics such as total reports run, average reports run, number of successful reports, number of failed reports; also shows charts of report executions per day and week; shows top 10 of report users, most executed, longest running and largest reports.
  • Report Summary SSRS Report - drilldown for the Execution Summary Report.

Data Mart Schema
The Createtables.sql script creates the following schema for the data mart:
This is the typical star schema that we would expect.  There is no calendar dimension which would be convenient for analyzing reports run on weekends, holidays, etc.  The various dimensions can easily be used to populate dropdown boxes for report parameters, allowing you to filter by report, report type, format type, user, etc.

SSIS Package
The control flow for the SSIS package is shown below:
The following are the main points about the SSIS package control flow:
  • Get Max TimeEnd executes a query to retrieve the maximum report ending time in the data mart; this is used to extract data for reports that have ended since the last time the package was run.
  • Set Time Period formulates some queries based on the Max TimeEnd
  • Update Dimensions contains data flows to extract and populate the various dimensions
  • Update Execution Logs extracts and populates the ExecutionLogs fact table
  • Update Parameters extracts and populates the ExecutionParameters dimension which shows the parameters used when a report is run
  • Cleanup Parameters sets the Parameters column in the ExecutionLogs table to NULL (since the parameters have been populated in the ExecutionParameters dimension)
  • Write Run Log inserts rows into the RunLogs table detailing activity performed during execution of the package

Running the Reports
Now let's review some sample report from the Execution Summary SSRS Report.  The report is shown below in multiple screen shots just for convenience of copying and pasting.  The first section shows report executions per day of the month and per week:
You may recognize the pattern in the Report Executions per Day of Week chart above.  I worked on a project where the sales numbers and commissions for the previous week became available on Friday morning.  There is a definite spike in report usage on Fridays and Saturdays.  Since this data doesn't change after it's computed, it's an excellent candidate for a report snapshot where you run the report during off hours, a snapshot is created, then users get the results from the snapshot instead of retrieving the data from the database each time the report is run.  The snapshot typically provides a nice performance boost where the data changes happen at predictable times.
The second section of the report shows the top ten lists for most executed, longest running, largest, and users:
Finally the Report Name column above is a hyperlink to the Report Summary which provides details on the execution of a single report, including the parameters used by the report.
Next Steps
  • Take a close look at the Execution Log Sample Reports and SSIS package as they provide an excellent starting point for getting a handle on who's running reports, when are they running them, how much data is retrieved, and how long does it take to run the report.
  • To the extent that you are experiencing performance problems and you can identify specific reports as the culprits, you can use the sample reports to come up with various strategies to mitigate your performance problems.  Typical strategies include scheduling reports to run in off hours, using data-driven subscriptions to deliver reports to users, creating report snapshots, and taking advantage of report caching.
  • After creating the data mart and updating it by running the SSIS package, you now have the capability to run ad-hoc queries to perform additional analysis on report executions.

Friday, September 16, 2011

get html control content before render


source : http://msdn.microsoft.com/en-us/library/system.web.ui.control.rendercontrol.aspx

Control.RenderControl Method

Outputs server control content and stores tracing information about the control if tracing is enabled.
This member is overloaded. For complete information about this member, including syntax, usage, and examples, click a name in the overload list.
 NameDescription
Public methodRenderControl(HtmlTextWriter)Outputs server control content to a provided HtmlTextWriter object and stores tracing information about the control if tracing is enabled.
Protected methodRenderControl(HtmlTextWriter, ControlAdapter)Outputs server control content to a provided HtmlTextWriter object using a provided ControlAdapter object.
Top

Disable Caching in an HttpHandler

http://weblogs.asp.net/ashicmahtab/archive/2008/09/18/disable-caching-in-an-httphandler.aspx


source : 
I was generating some custom reports in Word 2007 format today. The reports were being served by an HttpHandler and various params are passed to it (mostly by query string). One report needed a list of ids to be passed and the query string wasn't an option there, so I put that in Session. [My other post today shows how]. The trouble was that the urls were identical and someone clever (the browser or the server) was caching the report. So, changing the parameter that was made up of ids resulted in no change of the report. Now, output caching is pretty simple to eliminate on pages, and for asmx web services for that matter, but I found that doing so for a handler is slightly tricky. Here's what I did:
context.Response.Clear();context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.MinValue);
 in the ProcessRequest method.
I would've thought context.Response.Cache.SetCacheability(HttpCacheability.None) would do it, but that kept giving me errors when downloading the docx file. Rather, enabling cacheing and forcing a timeout seems to do it.

Friday, September 9, 2011

Asp.net Ajax UpdatePanel Simultaneous Update - A Remedy


If you ever try to do more than one simultaneous partial update with Asp.net Ajax Update Panel, I guess  you already found  that Asp.net Ajax Framework cancel the current update request and starts the new one. You think I am kidding? Okay lets try the following code:
<%@ Page Language="C#"%>
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Paneltitle>
head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        asp:ScriptManager>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server">asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            ContentTemplate>
        asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server">asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                ContentTemplate>
            asp:UpdatePanel>
        div>
    form>
body>
html>
The example is quite simple, I have two update panel which UpdateMode is set to Conditional, upon clicking the buttons inside the update panel I am doing a fake delay of 5 seconds then printing the current time of the server in the labels. Okay now run the code and click the first button and then click the second button in 5 seconds so that you can do simultaneous update. What happens, the first label never gets updated? Yes this is the nature of update panel, it cancels the current call and start executing the new one. You can get a more clear picture if you run it in FireFox with FireBug, Open FireFox Click Tools->FireBug->Open FireBug, then move to the Console tab in the firebug and repeat the button clicks, you will find only the last request spinning  animation gets completed.
I can understand the Update Panel does a monsters job for us, executing the full life cycle of the page in the server, generating the updated part of the page, downloading new scripts, updating viewstates etc and that is why it needs to work serially. But how come it discards it current update request once it found a new request? is not it better if it queues the new request and execute it once the current request completes?

Now Consider the following page:
<%@ Page Language="C#"%>
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Panel Enhancedtitle>
head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Path="~/Test/PageRequestManagerEx.js" />
            Scripts>
        asp:ScriptManager>
        <script type="text/javascript">
            PageRequestManagerEx.init();
        script>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server">asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            ContentTemplate>
        asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server">asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                ContentTemplate>
            asp:UpdatePanel>
        div>
    form>
body>
html>
The only difference you will find from the previous one that I have added a new JavaScript file PageRequestManagerEx.js and called a method PageRequestManagerEx.init(). Now run this page and repeat the button clicks, you will find the complete source in the bottom of this post. If you run it in FireFox with FireBug, you will find that no matter how many clicks you made in those buttons there is only one concurrent call and once the current call completes it executes another call. Yes as you have guessed the new JavaScript file which I just added does the trick. Now let us see what is in the JavaScript file.
var PageRequestManagerEx =
{
    _initialized : false,

    init : function()
    {
        if (!PageRequestManagerEx._initialized)
        {
            var _callQueue = new Array();
            var _executingElement = null;
            var prm = Sys.WebForms.PageRequestManager.getInstance();

            _prm.add_initializeRequest(initializeRequest);
            _prm.add_endRequest(endRequest);

            PageRequestManagerEx._initialized = true;
        }

        function initializeRequest(sender, args)
        {
            if (_prm.get_isInAsyncPostBack())
            {
                //if we are here that means there already a call pending.

                //Get the element which cause the postback
                var postBackElement = args.get_postBackElement();

                //We need to check this otherwise it will abort the request which we made from the
                //end request
                if (_executingElement != postBackElement)
                {
                    //Does not match which means it is another control
                    //which request the update, so cancel it temporary and 
                    //add it in the call queue
                    args.set_cancel(true);
                    Array.enqueue(_callQueue, postBackElement);
                }

                //Reset it as we are done with our matching
                _executingElement = null;
            }
        }

        function endRequest(sender, args)
        {
            //Check if we have a pending call
            if (_callQueue.length > 0)
            {
                //Get the first item from the call queue and setting it
                //as current executing item
                _executingElement = Array.dequeue(_callQueue);

                //Now Post the from which will also fire the initializeRequest
                _prm._doPostBack(_executingElement.id, '');
            }
        }
    }
}

if (typeof(Sys) != 'undefined')
{
    Sys.Application.notifyScriptLoaded();
}
The PageRequestManagerEx is a Static class which hooks the initializeRequest and endRequest events of the original PageRequestManager. Then in the initializeRequest it first checks if there is an ongoing update, if so it cancel the new request and adds the control in a queue which cause the postback. Then in the endRequest it check if there is any pending call in the queue, if so it executes it. This loops gets executed until all the call in the queue get completed.
Lets me know if you found any issue. I will  be also very curious to know why update panel does not behave like this?
Download : Complete Source

Select a row in an asp:GridView without using a Select Command



ASP.Net's GridViews can be quite useful, but beware of binding them to huge datasets as this has an overhead on the ViewState.
Often you'll want to display a number of columns on each line and row space becomes an issue. What's worse is you then have to create a SELECT command button to be able to access that line's data.
<asp:CommandField ShowInsertButton="true" />
Use the following code on the event OnRowDataBound to eliminate the need for the SELECT command field and save yourself some valuable space.
Here is the HTML to create a GridView, I'm displaying a list of people, and the key for each record is the PERSON_ID.
<asp:GridView ID="PeopleGridView" runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="PERSON_ID" 
    DataSourceID="PeopleDataObject" 
    Width="200px" 
    OnRowDataBound="PeopleGridView_RowDataBound" 
    AllowPaging="True">
    <Columns>  
        <asp:BoundField 
            DataField="USER_NAME" 
            HeaderText="Name" 
            SortExpression="USER_NAME" >
        asp:BoundField>
    Columns>
asp:GridView>
The key event to note is the OnRowDataBound, use the following code to create SELECT functionality on the row.
protected void PeopleGridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            e.Row.Attributes["onmouseover"] = "this.style.cursor='hand';this.style.textDecoration='underline';";
            e.Row.Attributes["onmouseout"] = "this.style.textDecoration='none';";

            e.Row.Attributes["onclick"] = ClientScript.GetPostBackClientHyperlink(this.PeopleGridView, "Select$" + e.Row.RowIndex);
        }
    }
Each row will then behave like a link, and when you select one it can drive the behavior of another control(s) on your page, possibly a DetailsView allowing you to INSERT a complete record to the database.

Monday, August 8, 2011

Repeat Header / Keep Header Visible in Tables in RS 2008


source :
http://blogs.msdn.com/b/robertbruckner/archive/2008/10/13/repeat-header-and-visible-fixed-header-table.aspx


You selected "Repeat header rows on each page" or "Keep header rows visible while scrolling" in the tablix properties dialog, but it doesn't seem to work as expected?  You might want to try the following four steps if you are using a "table"-style layout:
  1. in the grouping pane, click on the small triangle and select "Advanced Mode" to show static members:
    Grouping pane in advanced mode
     
  2. verify that you have the properties grid turned on (in BI Development Studio use F4, in Report Builder go to the "View" tab and select "Properties")
     
  3. select the corresponding (static) item in the row group hierarchy
     
  4. in the properties grid:
    - set KeepWithGroup to After
    - set RepeatOnNewPage to true for repeating headers
    - set FixedData to true for keeping headers visible
Please read on if you are interested in more details and ever wondered about the meaning of double-dashed lines on the design surface.  In short, double-dashed lines show the row group, column group, corner, and tablix body areas of a data region.  In the "matrix"-style example shown below, the yellow area represents the corner of the tablix, the light blue areas are row group headers, dark blue areas denote column group headers.  The settings under "Row Headers" and "Column Headers" in the tablix properties dialog only apply to the row and column group areas (i.e. the blue areas on the left / above the double dashed lines). 
Matrix design surface with row and column group headers
If you have a "table"-style layout however, then the row/column group areas are often empty as you are using "headerless" table-style groupings.  In that case, you have to set the properties as explained above to make entire static members (rows / columns) repeat / visible.

Friday, July 8, 2011

Changing the ASP.NET Version of a Site Running on IIS6

Later Edit : please read the end of the section
If you attempt to change the ASP.NET version used by a website from the Internet Information Services (version 6)Manager, you will receive a warning message similar to the following:
Changing the Framework version requires a restart of the W3SVC service.  Alternatively, you can change the Framework version without restarting the W3SVC service by running: aspnet_regiis.exe -norestart -s IIS-Virtual-Path
Do you want to continue (this will change the Framework version and restart the W3SVC service)?
So what is the process for upgrading a site without restarting the W3SVC service and stopping all of the sites running on the server?
First, log on to the web server.
Next, open a command prompt and navigate to the folder for the framework that you wish to target, which is located within the OS folder (typically, the "Windows" folder on the C: drive).  For this example, we’ll assume you are upgrading to ASP.NET 4.  So, navigate to C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319.
Run the "aspnet_regiis -lk" command to get a list of web site instances running on the server.  The results of the command will look something like the following:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>aspnet_regiis -lk
W3SVC/  1.1.4322.2470
W3SVC/1177368573/root/  2.0.50727.3053
W3SVC/1292709509/root/  2.0.50727.3053
W3SVC/136133328/root/   2.0.50727.3053 
W3SVC/147603420/root/   4.0.30319.0
W3SVC/1724783696/root/  4.0.30319.0
W3SVC/393926990/root/   2.0.50727.3053
W3SVC/436374705/root/   2.0.50727.3053
W3SVC/44648490/root/    4.0.30319.0
W3SVC/44648490/root/maps/       2.0.50727.3053
W3SVC/462046935/root/   4.0.30319.0
W3SVC/583520931/root/   4.0.30319.0
W3SVC/583520931/root/alertmsg.txt/      4.0.30319.0
W3SVC/707791973/root/   2.0.50727.3053
W3SVC/886194175/root/   2.0.50727.3053
W3SVC/95358537/root/ImageWeb/   2.0.50727.3053
Notice that each line lists a web site instance name and the version of ASP.NET used by that instance.  (The highlighted line in the example output represents the site we will be upgrading.)
Open the Internet Information Services Manager on the server and select the "Web Sites" node, as shown here.
IISMgr 
Find the site you want to upgrade in the right-hand pane of the IIS Manager and note the value in the "Identifier" column.  Match that value to the instance names returned by the "aspnet_regiis -lk" command.  In our example, we are going to upgrade the site with an identifier value of "136133328", which we can see in the aspnet_regiis output has an instance name of "W3SVC/136133328/root/".
Run the "aspnet_regiis" command with the "-s" and "-norestart" arguments, as shown here:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>aspnet_regiis -s W3SVC/136133328/root/ -norestart
Start registering ASP.NET (4.0.30319) recursively at W3SVC/136133328/root/.
Finished registering ASP.NET (4.0.30319) recursively at W3SVC/136133328/root/.
Once this command completes, use the IIS Manager to recycle the application pool for the site. 
The site has now been upgraded.
You can now re-run the "aspnet_iis -lk" command to verify that the ASP.NET version has changed.  For example:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>aspnet_regiis -lk
W3SVC/  1.1.4322.2470
W3SVC/1177368573/root/  2.0.50727.3053
W3SVC/1292709509/root/  2.0.50727.3053
W3SVC/136133328/root/   4.0.30319.0 
W3SVC/147603420/root/   4.0.30319.0
W3SVC/1724783696/root/  4.0.30319.0
W3SVC/393926990/root/   2.0.50727.3053
W3SVC/436374705/root/   2.0.50727.3053
W3SVC/44648490/root/    4.0.30319.0
W3SVC/44648490/root/maps/       2.0.50727.3053
W3SVC/462046935/root/   4.0.30319.0
W3SVC/583520931/root/   4.0.30319.0
W3SVC/583520931/root/alertmsg.txt/      4.0.30319.0
W3SVC/707791973/root/   2.0.50727.3053
W3SVC/886194175/root/   2.0.50727.3053
W3SVC/95358537/root/ImageWeb/   2.0.50727.3053
Notice that the ASP.NET version associated with the upgraded site has changed from 2.0.50727.3053 to 4.0.30319.0.
IMPORTANT:  I think this should go without saying, but I’ll mention it just in case.  Make sure that you have made all of the necessary changes to your application and/or your application’s configuration to ensure that it will run correctly on the newly-selected version of ASP.NET.

There are situation when you are unable to find the site because theare are much more.
The solution will be iisweb command.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>iisweb /query test_site
Connecting to server ...Done.
Site Name (Metabase Path)                     Status  IP              Port  Host
==============================================================================
test _Site(W3SVC/1061845049)                    STARTED ALL             8081  N/A

W3SVC/1061845049

Now you can use the id of the site for registering 


Thursday, July 7, 2011


Re: how to clear validation summary?


function HideValidationSummary(){ 
if (typeof(Page_ValidationSummaries)!= "undefined"){ //hide the validation summaries
            for (sums = 0; sums < Page_ValidationSummaries.length; sums++) {
                summary = Page_ValidationSummaries[sums];
                summary.style.display = "none";
            }
        }

Hide Validator Error Message Using Javascript

function HideValidators()
 
05{
06   if (window.Page_Validators)
07     for (var vI = 0; vI < Page_Validators.length; vI++)
08     {
09        var vValidator = Page_Validators[vI];
10        vValidator.isvalid = true;
11        ValidatorUpdateDisplay(vValidator);
12     }
13}
source : http://forums.asp.net/t/1035309.aspx/1?how+to+clear+validation+summary+
source : http://yasserzaid.wordpress.com/2009/06/06/hide-validator-error-message-using-javascript/
source :