Wednesday, November 23, 2016

Phone numbers - make it easy to read with JQuery

When entering and displaying phone numbers, sometimes its annoying to read all numbers together. Because it may be hard to read. If we can add a space in between few numbers to make it masked, it would be ideal for the user to read it quickly.

It is easy with JQuery. Look at the below function.

 function addSpace(control, flPaste) {    
   if (flPaste == false) {  
     var txtVal = $("#" + control).val();  
     if (txtVal.length == 2) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 5) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 8) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
     else if (txtVal.length == 11) {  
       $("#" + control).val($("#" + control).val() + " ");  
       flSpaceAdded = true;  
     }  
   }  
   else if (flPaste == true) {  
     setTimeout(function () {  
       var txtVal = $("#" + control).val();  
       var txtValNew = "";  
       for (var i = 0, len = txtVal.length; i < len; i++) {  
         txtValNew += txtVal[i];  
         if (i == 1) {  
           txtValNew += " ";  
         }  
         else if (i == 3) {  
           txtValNew += " ";  
         }  
         else if (i == 5) {  
           txtValNew += " ";  
         }  
         else if (i == 7) {  
           txtValNew += " ";  
         }  
       }  
       $("#" + control).val(txtValNew);  
     }, 100);  
   }  
 }  


This can be applied to the textboxes by binding the events below;

 //for typing  
 $("#txtadPhone").keyup(function () {  
     addSpace("txtadPhone",false);  
   });    
 // for pasting  
   $("#txtadPhone").bind('paste', function (e) {  
     addSpace("txtadPhone", true);  
   });  

So the textbox will look like this;


So you will have 14 characters for a 10 digit numbers here. Therefore you may need to cater it in the database or else you need to remove the space using a method in the codebehind. For example,


 public static string removeSpace(string str)  
     {  
       if (str == null)  
         return null;  
       var vVal = string.Empty;  
       foreach (var v in str.ToCharArray())  
       {  
         if ((int)v == 32)  
           continue;  
         vVal += v.ToString();  
       }  
       return vVal;  
     }  

This is a static method that remove the space. However, you may not need to apply this but if there is a restriction in the DB side, you can always use this.

Happy Coding... :)

Tuesday, November 1, 2016

Solved : ASP.Net form submit and page expire when navigate back

Each page we create in ASP.Net has at least one form. Those form can be submitted in many ways. Either a postback from a server control or an html button can submit a form using JavaScript.

Look at the below image.




















The page has expired due to a submission and user tries to navigate backward which the server needs the page to be submitted. Simulating the issue in a live environment would be like below;

1. You can have a filtering criteria as this.
2. The "Search" is an anchor tag that has a onClick event bind to it.




3. When user tick on each of the check boxes, the id values are stored in a hidden field and on "javascript:submitForm()" function the page (form) is resubmitted to the server to filter the product list. So the "submitForm" function looks like below;

 <input id="hdnFilterCriteria" name="hdnFilterCriteria" value="4339435,4339434,4339427," type="hidden">  


4. This causes the page to post back with the filtering criteria appended to the hidden field.
5. After the postback, if the user move in to another page and hit browser back button to see the previous page, the following error will display due to the document expiration.


















It is time to see how we can avoid this error. It is not that hard and all you need is to think outside the box. Simply avoid page postbacks / submissions. In this case we uses JQuery AJAX. Let's focus about the solution.

1. Create a class with the all properties included.
 public class SearchAttribute  
 {  
   public string idAttribute { get; set; }    
   public string strFilterCriteria { get; set; }    
   // You can have all the attributes as properties here  
 }  

2. Use the Cache / Session to maintain the search history. You can use Ajax to update the cache. See the web method below;

   [System.Web.Script.Services.ScriptMethod]  
   [WebMethod(EnableSession = true)]  
   public string catProductHiddenValueCaching(int idSearch,string idAttribute, string strFilterCriteria)  
   {  
     var cacheName = HttpContext.Current.Session.SessionID.ToString() + "_SearchAttribute";  
     Dictionary<int, SearchAttribute> searchHistory = new Dictionary<int, SearchAttribute>();  
     if (idSearch > 1)  
     {        
       var cacheSA = HttpContext.Current.Cache.Get(cacheName);  
       if (cacheSA != null)  
         searchHistory = (Dictionary<int, SearchAttribute>)cacheSA;  
     }  
     SearchAttribute sa = new SearchAttribute()  
     {  
       idAttribute = idAttribute,  
       strFilterCriteria = strFilterCriteria  
     };  
     searchHistory.Add(idSearch, sa);  
     HttpContext.Current.Cache.Insert(cacheName, searchHistory);      
     JavaScriptSerializer js = new JavaScriptSerializer() { MaxJsonLength = Int32.MaxValue };  
     return js.Serialize(idSearch.ToString());  
   }  

  • Use Session ID + Cache name to avoid conflicts with shared cache location.
  • Use Dictionary to store search attribute instances of each search
  • Use the Cache.Insert to overwrite the existing cache value.

3. Inside the onclick event of the "Search" button, call this function asynchronously.

     function submitForm(){  
          try{  
         var j = jQuery.noConflict();  
         var inSearch = getParameterByName('inSearch'); // to track the no of search histories  
         if(inSearch){  
             inSearch = parseInt(inSearch);  
             inSearch++;  
           }  
         else  
           inSearch = 1;  
         var urlAjax = 'http://'+window.location.hostname+'/your_service.asmx/setFilderValueCache'; // get the service path with full URL  
         var idAttribute = j("#idAttribute").val();  
         var strFilterCriteria = j("#strFilterCriteria").val();  
         var objPara = {  
           idSearch:inSearch,  
           idAttribute: idAttribute,  
           strFilterCriteria: strFilterCriteria  
         };  
         j.ajax({  
           type: "POST",  
           url: urlAjax,  
           data: JSON.stringify(objPara),  
           contentType: "application/json; charset=utf-8",  
           dataType: "json",  
           success: function (msg) {  
             var ret = JSON.parse(msg.d);     
             var url = window.location.href;    
             var newUrl = queryStringUrlReplacement(url, 'inSearch', inSearch); // append the inSearch as a query string parameter.  
             window.location.href = newUrl;               
           },  
           error: function (err) {  
             alert(err.responseText);  
           }  
         });  
          }catch(e){}  
     }  

  • idSearch is used to update the query string and reload the page with query string parameter.

 function queryStringUrlReplacement(url, param, value)   
     {  
       var re = new RegExp("[\\?&]" + param + "=([^&#]*)"), match = re.exec(url), delimiter, newString;  
       if (match === null) {          
         var hasQuestionMark = /\?/.test(url);   
         delimiter = hasQuestionMark ? "&" : "?";  
         newString = url + delimiter + param + "=" + value;  
       } else {  
         delimiter = match[0].charAt(0);  
         newString = url.replace(re, delimiter + param + "=" + value);  
       }  
       return newString;  
     }  

4. When "Search" is clicked, the relevant cache object is accessed and passed the search criteria to it's parameters inside "Page Load"

 protected void Page_Load(object sender, EventArgs e)  
   {      
     string strFilterCriteria= ((Request.Form["strFilterCriteria"] ?? Request.QueryString["strFilterCriteria"]) ?? string.Empty);  
     string inSearch = Request.QueryString["inSearch"];  
     if (!string.IsNullOrEmpty(inSearch))  
     {  
       int iSearch = int.Parse(inSearch);  
       var cacheName = HttpContext.Current.Session.SessionID.ToString() + "_SearchAttribute";  
       Dictionary<int, SearchAttribute> searchHistory = new Dictionary<int, SearchAttribute>();  
       var cacheSA = HttpContext.Current.Cache.Get(cacheName);  
       if (cacheSA != null)  
         searchHistory = (Dictionary<int, SearchAttribute>)cacheSA;  
       if (searchHistory.Count > 0)  
       {  
         SearchAttribute sa = searchHistory[iSearch];  
         if (sa == null)  
           return;  
         if (!string.IsNullOrEmpty(sa.idAttribute))  
           idAttribute = int.Parse(sa.idAttribute);  
         strFilterCriteria= sa.strFilterCriteria;               
       }  
     }  
    // Use 'strFilterCriteria' variable to store the currently filtered id list and pass it to the relevant method to get the result.  
 }  


Now each time the "Search" is clicked, it stores the search criteria in the cache and reload with inSearch as a query string parameter. There is no page postback and no document expiration happens. Each time it loads a fresh copy with the idSearch in the query string as "?inSearch=1" or "?inSearch=2" ..etc. The number increases on each Click to Search button. Navigate backward will retrieve the relevant result from the cached dictionary.

So, it is easy. Just a simple solution. :)

Happy coding... :)

Monday, October 31, 2016

Sticky notes in your webpage... Part 2 with Database

In part 1, we discussed most of the functions in postitall. Here we discuss how we can save them in the database so that we can show them as and when needed. Let's see how we need to do it.

1. Add a new save button for a new personal note.

 var r = $('<a id="pia_save_0" class="PIAicon" href="#" style="opacity: 1; display: inline;"> <i class="fa fa-floppy-o note-icon-save" aria-hidden="true" title="Save" onclick="SaveNote();"> </i> </a>');  
         $("#idPIAIconBottom_0").append(r);  //0 is the id for new note. only one note can be opened at first and upon saving it, another can be opened.

2. "onclick="SaveNote();" this will call the function below;

 // The id is 0 for the new personal note. This is called inside your new personal note popup  
 function SaveNote() {  
   try{  
     var p = jQuery.noConflict();   
     cstPersonalNoteUpd(0, '', p('#pia_editable_0').html(), true, p('#minicolors_bg_0').val(), p('#minicolors_text_0').val(), '', '');  
     p.PostItAll.destroy('#PIApostit_0');  //0 is the id
   }  
   catch (err) {  
     alert(err.message);  
   }  
 }  


3. "cstPersonalNoteUpd" is a JS function that uses JQuery AJAX to handle DB operations (Insert / Update)


 function cstPersonalNoteUpd(idPersonalNote, lbPersonalNote, flActive, lbBackGroundColor, lbFontColor, dtCallBack, idPNDescr) {  
   try {  
     if (lbPersonalNote.length == 0)   
       return;  
     var objPara = {  
       idPersonalNote: idPersonalNote,        
       lbPersonalNote: lbPersonalNote,  
       flActive: flActive,  
       lbBackGroundColor: lbBackGroundColor,  
       lbFontColor: lbFontColor,  
       dtCallBack: dtCallBack  
     };  
     $.ajax({  
       type: "POST",  
       url: 'yourservice.asmx/cstPersonalNoteUpd',  
       data: JSON.stringify(objPara),  
       contentType: "application/json; charset=utf-8",  
       dataType: "json",  
       success: function (msg) {  
         var ret = JSON.parse(msg.d);  
         $('#hdnIdPersonalNote').val(ret); // get the id to a hidden field          
       },  
       error: function (err) {  
         alert(err.responseText);  
       }  
     });  
   }  
   catch (err) {  
     alert(err.message);  
   }  
 }  

4. Write your own service with "cstPersonalNoteUpd" webmethod.

     [WebMethod(EnableSession = true)]  
     public string cstPersonalNoteUpd(int idPersonalNote, string lbTitlePersonalNote, string lbPersonalNote, bool flActive, string lbBackGroundColor, string lbFontColor, string dtCallBack)  
     {  
       if (HttpContext.Current.Session["LoggedInPerson"] == null) return new JavaScriptSerializer().Serialize(string.Empty);  
       JavaScriptSerializer js = new JavaScriptSerializer() { MaxJsonLength = Int32.MaxValue };  
       PersonEntity person = (PersonEntity)HttpContext.Current.Session["LoggedInPerson"];  
       PersonalNoteEntity pn = new PersonalNoteEntity();  
       pn.idPersonalNote = idPersonalNote;         
       pn.idPerson = person.idPerson;         
       pn.lbPersonalNote = string.IsNullOrEmpty(lbPersonalNote.Trim()) ? null : lbPersonalNote.Trim();   
       pn.flActive = flActive;  //boolean
       pn.lbBackGroundColor = string.IsNullOrEmpty(lbBackGroundColor.Trim()) ? null : lbBackGroundColor.Trim();   
       pn.lbFontColor = string.IsNullOrEmpty(lbFontColor.Trim()) ? null : lbFontColor.Trim();   
       if (!string.IsNullOrEmpty(dtCallBack))  //for hiding
       {  
         DateTime dtCB = DateTime.ParseExact(dtCallBack, "dd/MM/yyyy",CultureInfo.InvariantCulture);  
         pn.dtCallBack = dtCB;  
       }        
       new bllPersonalNote().cstPersonalNoteUpd(pn);  
       return js.Serialize(pn.idPersonalNote.ToString());  //return the id
     }  

5. The "cstPersonalNoteUpd" function should be called whenever a text is changed or a new personal note is created to save them in the database. If new, the id is returned and otherwise it updates. (You need to handle it in your procedure as below;)

 IF ISNULL(@idPersonalNote, 0) = 0 AND @idPerson > 0      
  BEGIN     
  -- Mode Insert    
  END  
 ELSE  
  BEGIN   
  -- update more  
  END  
 END  

6. Now you have done both the server side as well as client side function call. A new note will be saved. But to update an existing one, you can use "onChange" event of the postitall.

 onChange: function (id) {  
       cstPersonalNoteUpd(pid, '', p('#pia_editable_' + pid).html(), true, p('#minicolors_bg_' + pid).val(), p('#minicolors_text_' + pid).val(), '', idPNDescr);  
       return undefined;  
     },  

The pid is the id of the note where you can pass to update the existing record in the DB.

7. Same as above, you can have your own service call to return the list of notes available in the DB and show them in the web application.

Let's see how it is shown in the application.

Adding a new note.



To show the list in your dashboard. ( clicking on a single note can open it in the note editor.






HTML Special Characters in your web application

HTMLEncode and HTMLDecode are common methods which can be in HttpUtility or WebUtility to encode and decode html tags or templates in your web application. Usually, HTML templates are updated by the Admin which uses the same system with admin rights. 
But what if the application you develop is accessing different databases that are updated from various admin portals or platforms. Hence the templates may contain many tags or characters and the above HTMLDecode will not work for them. 
Following method represents the conversion of those special characters to the HTML Encoded values so that it can be understood by the browser to show the correct symbol.
To do so, you will need to first get the ASCII of each of the character and if the values falls in to a special character, replace it with a properly formatted string. Simply it looks like below;

 public static string GetSpecialCharactersEncoded(string text)  
     {  
       if (text == null)  
         return text;  
       string returnString = text;  
       Dictionary<int, string> dicList = new Dictionary<int, string>();  
       StringBuilder sb = new StringBuilder();  
       foreach (var c in text.ToCharArray())  
       {  
         sb.Append(GetEncodedValue(c.ToString()));  
       }  
       return sb.ToString();  
     }  

     private static string GetEncodedValue(string text)  
     {  
       string encTxt = text;  
       int iAscii = (int)Convert.ToChar(text);  
       if (iAscii == 8230)  
         encTxt = "&hellip;";  
       else if (iAscii == 8211)  
         encTxt = "&ndash;";  
       else if (iAscii == 8212)  
         encTxt = "&mdash;";  
       else if (iAscii == 8216)  
         encTxt = "&lsquo; ";  
       else if (iAscii == 8217)  
         encTxt = "&rsquo;";  
       else if (iAscii == 8218)  
         encTxt = "&lsquor;";  
       else if (iAscii == 8220)  
         encTxt = "&ldquo; ";  
       else if (iAscii == 8221)  
         encTxt = "&rdquo;";  
       else if (iAscii == 8222)  
         encTxt = "&ldquor;";  
       else if (iAscii == 8224)  
         encTxt = "&dagger;";  
       else if (iAscii == 8225)  
         encTxt = "&Dagger;";  
       else if (iAscii == 8226)  
         encTxt = "&bull;";  
       else if (iAscii == 8240)  
         encTxt = "&permil;";  
       else if (iAscii == 8364)  
         encTxt = "&euro;";  
       else if (iAscii == 8482)  
         encTxt = "&trade;";  
       else if (iAscii == 402)  
         encTxt = "&fnof;";  
       else if (iAscii == 376)  
         encTxt = "&yuml;";  
       else if (iAscii == 353)  
         encTxt = "&scaron;";  
       else if (iAscii == 352)  
         encTxt = "&Scaron;";  
       else if (iAscii == 339)  
         encTxt = "&oelig;";  
       else if (iAscii == 338)  
         encTxt = "&OElig;";  
       else if (iAscii == 255)  
         encTxt = "&yuml;";  
       else if (iAscii == 254)  
         encTxt = "&thorn;";  
       else if (iAscii == 253)  
         encTxt = "&yacute;";  
       else if (iAscii == 252)  
         encTxt = "&uuml;";  
       else if (iAscii == 251)  
         encTxt = "&ucirc;";  
       else if (iAscii == 250)  
         encTxt = "&uacute;";  
       else if (iAscii == 249)  
         encTxt = "&ugrave;";  
       else if (iAscii == 248)  
         encTxt = "&oslash;";  
       else if (iAscii == 247)  
         encTxt = "&divide;";  
       else if (iAscii == 246)  
         encTxt = "&ouml;";  
       else if (iAscii == 245)  
         encTxt = "&otilde;";  
       else if (iAscii == 244)  
         encTxt = "&ocirc;";  
       else if (iAscii == 243)  
         encTxt = "&oacute;";  
       else if (iAscii == 242)  
         encTxt = "&ograve;";  
       else if (iAscii == 241)  
         encTxt = "&ntilde;";  
       else if (iAscii == 240)  
         encTxt = "&eth;";  
       else if (iAscii == 239)  
         encTxt = "&iuml;";  
       else if (iAscii == 238)  
         encTxt = "&icirc;";  
       else if (iAscii == 237)  
         encTxt = "&iacute;";  
       else if (iAscii == 236)  
         encTxt = "&igrave;";  
       else if (iAscii == 235)  
         encTxt = "&euml;";  
       else if (iAscii == 234)  
         encTxt = "&ecirc;";  
       else if (iAscii == 233)  
         encTxt = "&eacute;";  
       else if (iAscii == 232)  
         encTxt = "&egrave;";  
       else if (iAscii == 231)  
         encTxt = "&ccedil;";  
       else if (iAscii == 230)  
         encTxt = "&aelig;";  
       else if (iAscii == 229)  
         encTxt = "&aring;";  
       else if (iAscii == 228)  
         encTxt = "&auml;";  
       else if (iAscii == 227)  
         encTxt = "&atilde;";  
       else if (iAscii == 226)  
         encTxt = "&acirc;";  
       else if (iAscii == 225)  
         encTxt = "&aacute;";  
       else if (iAscii == 224)  
         encTxt = "&agrave;";  
       else if (iAscii == 223)  
         encTxt = "&szlig;";  
       else if (iAscii == 222)  
         encTxt = "&THORN;";  
       else if (iAscii == 221)  
         encTxt = "&Yacute;";  
       else if (iAscii == 220)  
         encTxt = "&Uuml;";  
       else if (iAscii == 219)  
         encTxt = "&Ucirc;";  
       else if (iAscii == 218)  
         encTxt = "&Uacute;";  
       else if (iAscii == 217)  
         encTxt = "&Ugrave;";  
       else if (iAscii == 216)  
         encTxt = "&Oslash;";  
       else if (iAscii == 215)  
         encTxt = "&times;";  
       else if (iAscii == 214)  
         encTxt = "&Ouml;";  
       else if (iAscii == 213)  
         encTxt = "&Otilde;";  
       else if (iAscii == 212)  
         encTxt = "&Ocirc;";  
       else if (iAscii == 211)  
         encTxt = "&Oacute;";  
       else if (iAscii == 210)  
         encTxt = "&Ograve;";  
       else if (iAscii == 209)  
         encTxt = "&Ntilde;";  
       else if (iAscii == 208)  
         encTxt = "&ETH;";  
       else if (iAscii == 207)  
         encTxt = "&Iuml;";  
       else if (iAscii == 206)  
         encTxt = "&Icirc;";  
       else if (iAscii == 205)  
         encTxt = "&Iacute;";  
       else if (iAscii == 204)  
         encTxt = "&Igrave;";  
       else if (iAscii == 203)  
         encTxt = "&Euml;";  
       else if (iAscii == 202)  
         encTxt = "&Ecirc;";  
       else if (iAscii == 201)  
         encTxt = "&Eacute;";  
       else if (iAscii == 200)  
         encTxt = "&Egrave;";  
       else if (iAscii == 199)  
         encTxt = "&Ccedil;";  
       else if (iAscii == 198)  
         encTxt = "&AElig;";  
       else if (iAscii == 197)  
         encTxt = "&#197;";  
       else if (iAscii == 196)  
         encTxt = "&Auml;";  
       else if (iAscii == 195)  
         encTxt = "&Atilde;";  
       else if (iAscii == 194)  
         encTxt = "&Acirc;";  
       else if (iAscii == 193)  
         encTxt = "&Aacute;";  
       else if (iAscii == 192)  
         encTxt = "&Agrave;";  
       else if (iAscii == 191)  
         encTxt = "&iquest;";  
       else if (iAscii == 190)  
         encTxt = "&frac34;";  
       else if (iAscii == 189)  
         encTxt = "&frac12;";  
       else if (iAscii == 188)  
         encTxt = "&frac14;";  
       else if (iAscii == 187)  
         encTxt = "&raquo;";  
       else if (iAscii == 186)  
         encTxt = "&ordm;";  
       else if (iAscii == 185)  
         encTxt = "&sup1;";  
       else if (iAscii == 184)  
         encTxt = "&cedil;";  
       else if (iAscii == 183)  
         encTxt = "&middot;";  
       else if (iAscii == 182)  
         encTxt = "&para;";  
       else if (iAscii == 181)  
         encTxt = "&micro;";  
       else if (iAscii == 180)  
         encTxt = "&acute;";  
       else if (iAscii == 179)  
         encTxt = "&sup3;";  
       else if (iAscii == 178)  
         encTxt = "&sup2;";  
       else if (iAscii == 177)  
         encTxt = "&plusmn;";  
       else if (iAscii == 176)  
         encTxt = "&deg;";  
       else if (iAscii == 175)  
         encTxt = "&macr;";  
       else if (iAscii == 174)  
         encTxt = "&reg;";  
       else if (iAscii == 173)  
         encTxt = "&shy;";  
       else if (iAscii == 172)  
         encTxt = "&not;";  
       else if (iAscii == 171)  
         encTxt = "&laquo;";  
       else if (iAscii == 170)  
         encTxt = "&ordf;";  
       else if (iAscii == 169)  
         encTxt = "&copy;";  
       else if (iAscii == 168)  
         encTxt = "&uml;";  
       else if (iAscii == 167)  
         encTxt = "&sect;";  
       else if (iAscii == 166)  
         encTxt = "&brvbar;";  
       else if (iAscii == 165)  
         encTxt = "&yen;";  
       else if (iAscii == 164)  
         encTxt = "&curren;";  
       else if (iAscii == 163)  
         encTxt = "&pound;";  
       else if (iAscii == 162)  
         encTxt = "&cent;";  
       else if (iAscii == 161)  
         encTxt = "&iexcl;";  
       else if (iAscii == 160)  
         encTxt = "&nbsp;";  
       else if (iAscii == 34)  
         encTxt = "&quot;";  
       return encTxt;  
     }  

To call this method;

string res = GetSpecialCharactersEncoded(str);

For further reading, visit here (http://www.ascii.cl/htmlcodes.htm)

The character entity reference chart is here. (https://dev.w3.org/html5/html-author/charref)

Using this method will show the special characters without any issues.

Happy coding... :)


Monday, October 24, 2016

Sticky notes in your webpage...

Sticky notes are a good way to keep things noted such as "To Do Lists". There are many ways we can have sticky notes in a desktop or notebook. But if we have a portal or a website and needed to provide this facility, we have a good JQuery plug in.

http://postitall.txusko.com/plugin.html

You may find there is a Chrome plugin too but what you need is the JQuery one. Download it and add it to your project. After that, provide relevant references.
 <!-- CSS -->  
 <link rel="stylesheet" href="dist/jquery.postitall.css">  
 <!-- JS -->  
 <script src="libs/jquery/dist/jquery.min.js"></script>  
 <script src="dist/jquery.postitall.js"></script>  

You also may find some plug ins such as JQuery draggabletrumbowyg editor and minicolors for color picker.

You can find relevant examples and "How to" in the documentation here.

Now let's see how we can use this in our own website with more features.

 function addPersonalNote() {  
   var p = jQuery.noConflict();  // to avoid multiple JQuery references
   p.PostItAll.new({  //create new note
     content: 'Default content to show',  //
     id: 0,  // this has to be unique 
     posX: $(window).width() / 2,  // center of the page
     posY: '10',  //top of the page
     features: { addNew: false },  // adding new upon another is prohibited
     onCreated: function (id, options, obj) { return undefined; },  //fires at the creation time
     onChange: function (id) {  return undefined;  },  // fires when content is changed
     onSelect: function (id) { return undefined; },  // fires when selected
     onDelete: function (id) { return undefined; }  // fires when deleted
   });  
 }  

This is a simple way of adding a new note. The function "addPersonalNote()" creates a new sticky note as below.

 You can maximize it, minimize or even close it. or even can add new icons too. Also you can have the option to change the color through color picker too.



In our next part, we will talk about how to save this in the database. Until that, practice what we have done so far...

Happy Coding... :)

Monday, September 26, 2016

Using Google MAPs to show your store locations - Part 3 (Final)

Referring to part 1 and 2 (previous posts), today let's discuss how a fully dynamic store locator's are shown in Google Maps.

If you didn't read,
Refer Part 1 Here.
Refer Part 2 Here.

Step 1. Create a Google Map Developer Account and obtain a key. (See Part 1 for details)
Step 2. Add references to your page.

 <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />  
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>  
   <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"  
     type="text/javascript"></script>  
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />  
   <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB7Mg6XsXVsRdEOIA3sAM0CmVq03R_Y9kc"  
     type="text/javascript"></script>  
   <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" type="text/javascript"></script>  

Step 3. Add HTML for map canvas and store list. (Bootstrap is used )

 <div id="wrapper">  
     <div id="google_map">  
       <div id="map_canvas" style="min-height: 768px;">  
       </div>  
     </div>  
     <div>  
       <div id="over_map" class="postcodeSearch">  
         <div class="row">  
           <div class="col-lg-12">  
             <div class="PCHeader">  
               Trouvez Votre Hyperburo  
             </div>  
           </div>  
         </div>  
         <hr />  
         <div class="row">  
           <div class="col-lg-9 geolocate-me">  
             <i class="fa fa-chevron-right" aria-hidden="true"></i>Par géolocalisation</div>  
           <div class="col-lg-3 text-right">  
             <button class="btn btn-danger btn-sm" type="button" title="Geolocate me" onclick="geolocateme();">  
               <i class="fa fa-crosshairs" aria-hidden="true"></i>  
             </button>  
           </div>  
         </div>  
         <hr class="hr-devide" />  
         <div class="row">  
           <div class="col-lg-12">  
             <i class="fa fa-chevron-right" aria-hidden="true"></i>Par département ou code postal  
           </div>  
         </div>  
         <hr class="hr-devide" />  
         <div class="row">  
           <div class="col-lg-12">  
             <div class="input-group">  
               <input id="txtadPostCode" class="form-control" type="text"  
                 placeholder="Ex: 750001"></input>  
               <span class="input-group-btn">  
                 <button id="btnGoToPostCode" type="button" class="btn btn-danger" onclick="GoToPostCode();">  
                   <i class="fa fa-hand-o-right" aria-hidden="true"></i>  
                 </button>  
               </span>  
             </div>  
           </div>  
         </div>          
       </div>  
     </div>      
     <hr class="hr-devide" />  
     <div id="over_map_corp" class="postcodeSearch">  
       <div id="divCorpList" class="divCorpList">  
       </div>  
     </div>  
   </div>  

Step 4. Add a class that has all the properties of the store locations including address details.

 public class CorporateLocation  
   {  
     public int idCorporate { get; set; }  
     public string lbCorporate { get; set; }  
     public int idAddress { get; set; }  
     public string coMapCoordinate { get; set; }  
     public string Latitude { get; set; }  
     public string Longitude { get; set; }  
     public string adLine1 { get; set; }  
     public string adLine2 { get; set; }  
     public string adLine3 { get; set; }  
     public string adRegion { get; set; }  
     public string adPostCode { get; set; }  
     public string coCountry { get; set; }  
     public string lbCountry { get; set; }  
     public string adPhone { get; set; }  
     public string adFax { get; set; }  
     public string lbDomain { get; set; }  
   }  

coMapCoordinate has the latitude and longitude together separated by a comma.
Ex: 6.926610,79.896203

Step 5. Create your procedure that returns the list of store location result.

 CREATE PROCEDURE dbo.CorporateLocationSel (  
 @LatitudeFrom vcMax,  
 @LatitudeTo vcMax,  
 @LongitudeFrom vcMax,  
 @LongitudeTo vcMax,  
 @adPostCode vcPostCode = NULL)  
 AS  
 .........  

If the postcode is sent to the procedure, it should look the store location table that matches to the postcode and return the list.

Else you need to filter the store locations based on latitude and longitude. For that you need to do some calculations based on user's current geolocation. Do it inside your stored procedure and return the list. For example, see the below query.

 SELECT  
           idCorporate,            
           lbCorporate,  
           idAddress,  
           coMapCoordinate,  
           Latitude,  
           Longitude,  
           adLine1,  
           adLine2,  
           adLine3,  
           adRegion,  
           adPostCode,  
           coCountry,  
           lbCountry,  
           adPhone,  
           adFax,  
           lbDomain  
       FROM (SELECT  
           c.idCorporate,            
           c.lbCorporate,  
           ad.idAddress,  
           ad.coMapCoordinate,  
           SUBSTRING(ad.coMapCoordinate, 1, CASE CHARINDEX(',', ad.coMapCoordinate)  
            WHEN 0 THEN LEN(ad.coMapCoordinate)  
            ELSE CHARINDEX(',', ad.coMapCoordinate) - 1  
           END) AS 'Latitude',  
           SUBSTRING(ad.coMapCoordinate, CASE CHARINDEX(',', ad.coMapCoordinate)  
            WHEN 0 THEN LEN(ad.coMapCoordinate) + 1  
            ELSE CHARINDEX(',', ad.coMapCoordinate) + 1  
           END, 1000) AS 'Longitude',  
           ISNULL(ad.adLine1, '') AS adLine1,  
           ISNULL(ad.adLine2, '') AS adLine2,  
           ISNULL(ad.adLine3, '') AS adLine3,  
           ad.adCity,  
           ISNULL(ad.adRegion, '') AS adRegion,  
           ad.adPostCode,  
           ad.coCountry,  
           cn.lbCountry,  
           ISNULL(c.adPhone, '') AS adPhone,  
           ISNULL(c.adFax, '') AS adFax,  
           ISNULL(dns.lbDomain, '') AS lbDomain  
       FROM dbo.cstCorporate AS c  
       INNER JOIN dbo.gblAddress AS ad  
           ON c.idInvoiceAddress = ad.idAddress            
       JOIN dbo.gblCountry cn  
           ON ad.coCountry = cn.coCountry  
       LEFT JOIN dbo.cmcDns dns  
           ON dns.idCorporateLocal = c.idCorporate) Address  
 WHERE Latitude BETWEEN @LatitudeFrom AND @LatitudeTo  
 AND Longitude BETWEEN @LongitudeFrom AND @LongitudeTo  

The @LatitudeFrom, @LatitudeTo, @LongitudeFrom, @LongitudeTo are 4 parameters that we used with min and max values of latitude and longitude sent from web method that contains the user's current location.

Always better to keep a 1 degree difference between min and max values of latitude and longitude where the diameter of the area could equal to 111 kms. (Each degree of latitude is approximately 69 miles (111 kilometers) apart.)

Step 6. Add a web method that returns a serialized list of store location objects.

 [WebMethod]  
   public static string GetLocations(decimal Latitude, decimal Longitude, string adPostCode)  
   {  
     var clList = new cbCorporateLocation().SelStoreLocation(Latitude, Longitude, adPostCode);  
     JavaScriptSerializer Serializer = new JavaScriptSerializer();  
     return Serializer.Serialize(clList);  
   }  

Step 7. In your Business Logic Layer, you can define the min and max latitude / longitude values.

 public List<CorporateLocation> SelCorporateLocation(decimal Latitude, decimal Longitude, string adPostCode)  
     {  
       decimal LaUpper = Latitude + new decimal(0.5);  
       decimal LaLower = Latitude - new decimal(0.5);  
       decimal LoUpper = Longitude + new decimal(0.5);  
       decimal LoLower = Longitude - new decimal(0.5);  
       if (adPostCode == "0")  
         adPostCode = null;  
 ......... continue your method ...  

Step 8. Now you have the list of stores and you need to show them on Google Map. For that you have a bit of JavaScript works to do. Here I customize the map window for a custom theme. Look at the below JS functions.

 <script type="text/javascript">  
           //common function : get query string parameter values   
     function getParameterByName(name, url) {  
       if (!url) url = window.location.href;  
       name = name.replace(/[\[\]]/g, "\\$&");  
       var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),  
             results = regex.exec(url);  
       if (!results) return null;  
       if (!results[2]) return '';  
       return decodeURIComponent(results[2].replace(/\+/g, " "));  
     }  
           //common function : if the returned url does not have http; add it here  
     function addhttp(url) {  
       if (!/^(f|ht)tps?:\/\//i.test(url)) {  
         url = "http://" + url;  
       }  
       return url;  
     }  
           // use JQuery ready function   
     $(document).ready(function () {  
       $("#over_map").draggable(); //make the post code search box draggable  
       $("#over_map_corp").draggable(); //make the store list box draggable  
       $("#over_map").hide(); //hide the post code search box until map is loaded  
       $("#over_map_corp").hide(); //hide the store list box until map is loaded  
       var adPostCode = getParameterByName('adPostCode'); //get url parameter [postcode]  
       $("#txtadPostCode").val(adPostCode); //append to textbox  
       getLocation(adPostCode);  
     });  
           // get locations and draw them on map  
     function GetLocations(Latitude, Longitude, adPostCode) {  
       var objPara = {  
         Latitude: Latitude,  
         Longitude: Longitude,  
         adPostCode: adPostCode  
       };  
       $.ajax({  
         type: "POST",  
         url: "Default.aspx/GetLocations",  
         data: JSON.stringify(objPara),  
         contentType: "application/json; charset=utf-8",  
         dataType: "json",  
         success: function (msg) {  
           var ret = JSON.parse(msg.d);  
           var LaFrom = 0;  
           var LaTo = 0;  
           var LoFrom = 0;  
           var LoTo = 0;  
                          //this each will get the median values of latitude and longitude of returned values  
           $.each(ret, function (i, val) {  
             if (LaFrom == 0)  
               LaFrom = val.Latitude;  
             else {  
               if (val.Latitude < LaFrom)  
                 LaFrom = val.Latitude;  
             }  
             if (LaTo == 0)  
               LaTo = val.Latitude;  
             else {  
               if (val.Latitude > LaTo)  
                 LaTo = val.Latitude;  
             }  
             if (LoFrom == 0)  
               LoFrom = val.Longitude;  
             else {  
               if (val.Longitude < LoFrom)  
                 LoFrom = val.Longitude;  
             }  
             if (LoTo == 0)  
               LoTo = val.Longitude;  
             else {  
               if (val.Longitude > LoTo)  
                 LoTo = val.Longitude;  
             }  
           });  
           var la = (parseFloat(LaFrom) + parseFloat(LaTo)) / 2;  
           var lo = (parseFloat(LoFrom) + parseFloat(LoTo)) / 2;  
           if (la != 0 && lo != 0) {  
             Latitude = la;  
             Longitude = lo;  
           }  
                          // map with ceter values and zoom  
           var map = new google.maps.Map(document.getElementById('map_canvas'),  
           {  
             zoom: 10,  
             center: new google.maps.LatLng(Latitude, Longitude),  
             mapTypeId: google.maps.MapTypeId.ROADMAP  
           });  
                          //if post code is sent, change the zoom value  
           if (!adPostCode)  
             map.setZoom(10);  
           else  
             map.setZoom(8);  
           var infowindow = new google.maps.InfoWindow({ pixelOffset: new google.maps.Size(-10, 10) });  
           var marker, i;  
           var corpList = "";  
           $.each(ret, function (i, val) {  
             marker = new google.maps.Marker(  
             {  
               position: new google.maps.LatLng(val.Latitude, val.Longitude),  
               map: map,  
               icon: '2.png' //custom icon  
             });  
             var address = '';  
             if (val.adLine1.length > 0)  
               address += val.adLine1;  
             if (val.adLine2.length > 0)  
               address += val.adLine2;  
             if (val.adLine3.length > 0)  
               address += val.adLine3;  
             var postcode = '';  
             if (val.adPostCode.length > 0)  
               postcode += '<br/>' + val.adPostCode;  
             var country = '';  
             if (val.lbCountry.length > 0)  
               country += '<br/>' + val.lbCountry;  
             var phone = '';  
             if (val.adPhone.length > 0)  
               phone += '<br/> Phone : ' + val.adPhone;  
             var fax = '';  
             if (val.adFax.length > 0)  
               fax += '<br/> Fax : ' + val.adFax;  
             var lbDomain = '';  
             if (val.lbDomain.length > 0)  
               lbDomain += '<div class="iw-link" onclick="fnGoToResellerSite(\'' + val.lbDomain + '\')" title=' + val.lbDomain + '> Voir la fiche </div>';  
             var divId = "div" + val.idCorporate;  
             corpList += '<div class="row" id=' + divId + ' ><div class="col-lg-12"><div class="panel panel-red"><div class="panel-heading"><i class="fa fa-map-marker" aria-hidden="true"></i> ' + capitalizeFirstLetter(val.lbCorporate) + '</div><div class="panel-body">' + capitalizeFirstLetter(address) + postcode + capitalizeFirstLetter(country) + '</div></div></div></div>';  
                               //the infowindow content to show the store location when clicked  
             var contentString = '<div id="iw_content"><div id="iw-container">' +  
                       '<div class="iw-title">' + val.lbCorporate + '</div>' +  
                       '<div class="iw-content">' +  
                        '<div class="iw-address">' + address + postcode + country + '</div>' +  
                        '<div class="iw-contact">' + phone + fax + '</div>' +  
                        lbDomain +  
                       '</div>' +  
                       '</div></div>';  
                               // add click event to map                           
             google.maps.event.addListener(marker, 'click', (function (marker, i) {  
               return function () {  
                 infowindow.setContent(contentString);  
                 infowindow.pixelOffset = new google.maps.Size(-10, 10);  
                 infowindow.open(map, marker);  
                                         //DRAWING THE InfoWindow  
                 // Reference to the DIV that wraps the bottom of infowindow  
                 var iwOuter = $('.gm-style-iw');  
                 var iwBackground = iwOuter.prev();  
                 // Removes background shadow DIV  
                 iwBackground.children(':nth-child(2)').css({ 'display': 'none' });  
                 // Removes white background DIV  
                 iwBackground.children(':nth-child(4)').css({ 'display': 'none' });  
                 // Moves the infowindow 115px to the right.  
                 iwOuter.parent().parent().css({ left: '115px' });  
                 // Moves the shadow of the arrow 76px to the left margin.  
                 iwBackground.children(':nth-child(1)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
                 // Moves the arrow 76px to the left margin.  
                 iwBackground.children(':nth-child(3)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
                 // Changes the desired tail shadow color.  
                 iwBackground.children(':nth-child(3)').find('div').children().css({ 'box-shadow': 'rgba(72, 181, 233, 0.6) 0px 1px 6px', 'z-index': '1' });  
                 // Reference to the div that groups the close button elements.  
                 var iwCloseBtn = iwOuter.next();  
                 // Apply the desired effect to the close button  
                 iwCloseBtn.css({ opacity: '1', right: '38px', top: '3px', border: '7px solid #d01320', 'border-radius': '13px' });  
                 // The API automatically applies 0.7 opacity to the button after the mouseout event. This function reverses this event to the desired value.  
                 iwCloseBtn.mouseout(function () {  
                   $(this).css({ opacity: '1' });  
                 });  
                 //setting arrow color  
                 iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(1)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 0 0 1px' });  
                 iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(2)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 1px 0 0' });  
                 map.setCenter(marker.getPosition());  
               }  
             })(marker, i));  
           });  
                          //binding store list here  
           $('#divCorpList').html('');  
           $('#divCorpList').append(corpList);  
           $.each(ret, function (i, val) {  
             var address = '';  
             if (val.adLine1.length > 0)  
               address += val.adLine1;  
             if (val.adLine2.length > 0)  
               address += val.adLine2;  
             if (val.adLine3.length > 0)  
               address += val.adLine3;  
             var postcode = '';  
             if (val.adPostCode.length > 0)  
               postcode += '<br/>' + val.adPostCode;  
             var country = '';  
             if (val.lbCountry.length > 0)  
               country += '<br/>' + val.lbCountry;  
             var phone = '';  
             if (val.adPhone.length > 0)  
               phone += '<br/> Phone : ' + val.adPhone;  
             var fax = '';  
             if (val.adFax.length > 0)  
               fax += '<br/> Fax : ' + val.adFax;  
             var lbDomain = '';  
             if (val.lbDomain.length > 0)  
               lbDomain += '<div class="iw-link" onclick="fnGoToResellerSite(\'' + val.lbDomain + '\')" title=' + val.lbDomain + '> Voir la fiche </div>';  
             var contentString = '<div id="iw_content"><div id="iw-container">' +  
                       '<div class="iw-title">' + val.lbCorporate + '</div>' +  
                       '<div class="iw-content">' +  
                        '<div class="iw-address">' + address + postcode + country + '</div>' +  
                        '<div class="iw-contact">' + phone + fax + '</div>' +  
                        lbDomain +  
                       '</div>' +  
                       '</div></div>';  
             var divId = "div" + val.idCorporate;  
             var geocoder = new google.maps.Geocoder;  
             var elem = document.getElementById(divId);  
             if (elem) {  
               elem.addEventListener('click', function () {  
                 infowindow.setContent(contentString);  
                 infowindow.pixelOffset = new google.maps.Size(-115, 10);  
                 geocodeLatLng(geocoder, map, infowindow, val.coMapCoordinate, contentString);  
               });  
             }  
           });  
           $("#over_map").fadeIn(3000);  
           $("#over_map_corp").fadeIn(3000);  
         },  
         error: function (err) {  
           alert(err.responseText);  
         }  
       });  
     }  
           //if user click on a single store from the list shown in the list, this method is popping up the address of the given point  
     function geocodeLatLng(geocoder, map, infowindow, latlng, contentString) {        
       var latlngStr = latlng.split(',', 2);  
       var latlng = { lat: parseFloat(latlngStr[0]), lng: parseFloat(latlngStr[1]) };  
       geocoder.geocode({ 'location': latlng }, function (results, status) {  
         if (status === 'OK') {  
           if (results[1]) {  
             map.setZoom(16);  
             var marker = new google.maps.Marker({  
               position: latlng,  
               map: map,  
               icon: '2.png'  
             });  
             map.setCenter(marker.getPosition());  
             infowindow.open(map, marker);  
             var iwOuter = $('.gm-style-iw');  
             var iwBackground = iwOuter.prev();  
             iwBackground.children(':nth-child(2)').css({ 'display': 'none' });              
             iwBackground.children(':nth-child(4)').css({ 'display': 'none' });  
             iwOuter.parent().parent().css({ left: '115px' });  
             iwBackground.children(':nth-child(1)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
             iwBackground.children(':nth-child(3)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
             iwBackground.children(':nth-child(3)').find('div').children().css({ 'box-shadow': 'rgba(72, 181, 233, 0.6) 0px 1px 6px', 'z-index': '1' });  
             var iwCloseBtn = iwOuter.next();  
             iwCloseBtn.css({ opacity: '1', right: '38px', top: '3px', border: '7px solid #d01320', 'border-radius': '13px' });  
             iwCloseBtn.mouseout(function () {  
               $(this).css({ opacity: '1' });  
             });  
             iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(1)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 0 0 1px' });  
             iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(2)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 1px 0 0' });  
             google.maps.event.addListener(marker, 'click', (function (marker) {  
               return function () {                  
                 infowindow.setContent(contentString);  
                 infowindow.pixelOffset = new google.maps.Size(-10, 10);  
                 infowindow.open(map, marker);  
                 var iwOuter = $('.gm-style-iw');  
                 var iwBackground = iwOuter.prev();  
                 iwBackground.children(':nth-child(2)').css({ 'display': 'none' });  
                 iwBackground.children(':nth-child(4)').css({ 'display': 'none' });  
                 iwOuter.parent().parent().css({ left: '115px' });  
                 iwBackground.children(':nth-child(1)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
                 iwBackground.children(':nth-child(3)').attr('style', function (i, s) { return s + 'left: 76px !important;' });  
                 iwBackground.children(':nth-child(3)').find('div').children().css({ 'box-shadow': 'rgba(72, 181, 233, 0.6) 0px 1px 6px', 'z-index': '1' });  
                 var iwCloseBtn = iwOuter.next();  
                 iwCloseBtn.css({ opacity: '1', right: '38px', top: '3px', border: '7px solid #d01320', 'border-radius': '13px' });  
                 iwCloseBtn.mouseout(function () {  
                   $(this).css({ opacity: '1' });  
                 });  
                 map.setCenter(marker.getPosition());  
                 iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(1)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 0 0 1px' });  
                 iwOuter.parent().children(':nth-child(1)').children(':nth-child(3)').children(':nth-child(2)').children(':nth-child(1)').css({ 'border-color': '#d01320', 'border-style': 'solid', 'border-width': '0 1px 0 0' });  
               }  
             })(marker));  
           } else {              
             GetLocations(48.864716, 2.349014, 0);  
           }  
         } else {            
           GetLocations(48.864716, 2.349014, 0);  
         }  
       });  
     }  
           //common function : capitalize first letter of the words in a string  
     function capitalizeFirstLetter(string) {  
       return string.toLowerCase().replace(/\b[a-z]/g, function (letter) {  
         return letter.toUpperCase();  
       });  
     }  
           //common function : go to each store website  
     function fnGoToResellerSite(lbDomain) {  
       lbDomain = addhttp(lbDomain);        
       window.open(lbDomain, '_blank');  
     }  
           //common function : get geolocation of the user and load the store list  
     function getLocation(adPostCode) {  
       if (adPostCode && adPostCode.length > 0) {  
         GetLocations(0, 0, adPostCode);  
       }  
       else {  
         if (navigator.geolocation) {  
           var timeoutVal = 10 * 1000 * 1000;  
           navigator.geolocation.getCurrentPosition(showPosition, showDefault, { enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 });  
         } else {  
           GetLocations(48.864716, 2.349014, 0);  
         }  
       }  
     }  
     function showPosition(position) {  
       GetLocations(position.coords.latitude, position.coords.longitude, 0);  
     }  
     function showDefault(error) {  
       GetLocations(48.864716, 2.349014, 0);  
     }  
     function geolocateme() {  
       getLocation('');  
     }  
           //common function : search by postcode  
     function GoToPostCode() {  
       var txtadPostCode = $("#txtadPostCode").val();  
       if (txtadPostCode)  
         getLocation(txtadPostCode);  
     }  
   </script>  


Step 9. Run your project. :)




Leave a comment if you need any help..

And try this, it will be fun.

Happy coding.. :)

Wednesday, September 21, 2016

Using Google MAPs to show your store locations - Part 2

If you are a beginner, I hope you already have a bit of an idea about how to use Google Map API after reading the previous post.

Let's directly go in to a bit detailed version of how a dynamic ecommerce store locator is developed.
First you need to get the Geolocation of the user. For that you can use the HTML5 navigator.geolocation function.
 navigator.geolocation.getCurrentPosition(showPosition, showDefault, { enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 });  

The "showPosition" and "showDefault" are two functions.
- showPosition - get the current location and find the latitude and longitude.
- showDefault - when a user reject sharing the geolocation or if the browser does not support geolocation, we can use this method with predefined latitude and longitude to center the map.

 function getLocation() {  
   if (navigator.geolocation) {  
     var timeoutVal = 10 * 1000 * 1000;  
     navigator.geolocation.getCurrentPosition(showPosition, showDefault, {  
       enableHighAccuracy: true,  
       timeout: timeoutVal,  
       maximumAge: 0  
     });  
   } else {  
     GetLocations(48.864716, 2.349014, 0);  
   }  
 }  
 function showPosition(position) {  
   GetLocations(position.coords.latitude, position.coords.longitude, 0);  
 }  
 function showDefault(error) {  
   GetLocations(48.864716, 2.349014, 0);  
 }  

GetLocations is a function that works with picking up store locations depending on coordinates.

 position.coords.latitude and position.coords.longitude will provide the user's current latitude and longitude. If user block the geolocation, the default is used as Paris city in France. You may choose where ever possible by changing the values.

So, read the links below;

http://www.w3schools.com/html/html5_geolocation.asp
https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition

See you in Part 3.

Happy Coding... :)



Tuesday, September 20, 2016

Using Google MAPs to show your store locations - Part 1

If you have a commerce website that needs to show the store locations, Google Maps is the best tool to use and its free too.
You may need to follow few steps first to get a key to use for your code.

Click Here to get the key.

Create a project and get a key. Keep it saved because it's what you are going to use.



You can always use the documentation here.

Basically you can show  a static location as below;

 <!DOCTYPE html>  
 <html>  
  <head>  
   <meta name="viewport" content="initial-scale=1.0, user-scalable=no">  
   <meta charset="utf-8">  
   <title>Simple markers</title>  
   <style>  
    html, body {  
     height: 100%;  
     margin: 0;  
     padding: 0;  
    }  
    #map {  
     height: 100%;  
    }  
   </style>  
  </head>  
  <body>  
   <div id="map"></div>  
   <script>  
    function initMap() {  
     var myLatLng = {lat: -25.363, lng: 131.044};  
     var map = new google.maps.Map(document.getElementById('map'), {  
      zoom: 4,  
      center: myLatLng  
     });  
     var marker = new google.maps.Marker({  
      position: myLatLng,  
      map: map,  
      title: 'Hello World!'  
     });  
    }  
   </script>  
   <script async defer  
   src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">  
   </script>  
  </body>  
 </html>  

This is a sample you can follow as per Google Map Documentation. But What if we need more. Different locations, and more dynamics.

It will be our next post. So, wait for it. :)

Happy Coding...!






Friday, August 12, 2016

Classic ASP - Create Folder Structure

It is common that we have almost forgotten classic asp but what if you got some modification to do. Here is how we can create a folder structure if not exists...
   Set FS = Server.CreateObject("Scripting.FileSystemObject")       
   'Create folder if not exists  
   dim pathCounter  
   dim arrUploadPath
   dim newPath  
   newPath = ""  
   pathCounter=0  
   arrUploadPath = Split(uploadPath,"\")  
   For Each item in arrUploadPath  
     If Trim(item) <> "" Then  
       newPath = newPath + item + "\"  
       If pathCounter <> 0 Then          
         If Not FS.FolderExists(newPath) Then              
          FS.CreateFolder(newPath)            
         End If  
       End If  
       pathCounter = pathCounter + 1    
     End If         
   Next   



Monday, July 25, 2016

summernote editor for email editor

I found this amazing editor at http://summernote.org that accepts copy paste of images. I was looking for such editor to avoid re-inventing the wheel so that it can be used for an email application. This fits well and it is easy to use too. First, you may download the summernote editor here.

Lets implement summernote editor first.

1. link the js and css file.

 <link href="/script/summernote/summernote.css" rel="stylesheet" type="text/css" />  
 <script src="/script/summernote/summernote.js" type="text/javascript"></script>  

2. Add the editor textarea tag.
 <textarea id="txtSNBody" name="txtDescriptionIE9" runat="server" rows="15" cols="80" clientidmode="Static" style="width: 100%"></textarea>  

3. Convert it to Summernote Editor
 var f = jQuery.noConflict(); // To avoid JQuery conflict  
        f(document).ready(function () {  
             f('#txtSNBody').summernote({  
                  height: 300,         // set editor height  
                  minHeight: null,       // set minimum height of editor  
                  maxHeight: null,  
                  width: 150, // set width  
                  toolbar: [  
               // [groupName, [list of button]]  
               ['Insert', ['picture', 'link', 'video', 'table', 'hr', 'style', 'ol', 'ul', 'paragraph', 'height', 'fullscreen', 'codeview', 'undo', 'redo', 'help']],  
               ['style', ['fontname', 'fontsize', 'color', 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript', 'clear']]                
                  ],  
                  defaultFontName: 'Arial',  
                  callbacks: {  
                       onKeyup: function (e) {  
                            DrafttxtSNBody(f('#txtSNBody').summernote('code')); // This function saves the email as a draft asynchronously using jquery ajax  
                       }  
                  }  
             });  
             f('.note-toolbar').css('font-size', '11px'); // Set toolbar font size  
        });  

4. Now the editor will looks like this;
(The content is blurred using a photo editor)

5. Now you can use this as a standard email editor page that support HTML for the body. Even this editor support copy paste of images. But it uses Base64 encoding that is not understood by most browsers and email clients as valid inline images. So you will notice the red cross marks that says no valid images attached to the body.





6. To avoid this, We can write an email handler class. We discussed one here and lets modify it to cater the Base64 Encoded images. The following is a public class that you can use to send emails.

 public class EmailHelper  
   {  
     private MailMessage Message = null;  
     private SmtpClient smtpClient = null;  
     public MailAddress FromAddress { get; set; }  
     public string Subject { get; set; }  
     public string Body { get; set; }  
     public bool has64BaseImages { get; set; }  
     public EmailHelper()  
     {  
       smtpClient = new SmtpClient();  
       smtpClient.Host = ConfigurationManager.AppSettings["smtpHost"];  
       smtpClient.UseDefaultCredentials = false;  
       //smtpClient.EnableSsl = true;  // Enable this if SSL is needed 
       smtpClient.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["userName"], ConfigurationManager.AppSettings["password"]);  
       smtpClient.Port = Convert.ToInt32(ConfigurationManager.AppSettings["port"]);  
       Message = new MailMessage();  
     }  
     public EmailHelper(string host, int port, string userName, string password, bool ssl)  
       : this()  
     {  
       smtpClient.Host = host;  
       smtpClient.Port = port;  
       smtpClient.EnableSsl = ssl;  
       smtpClient.Credentials = new NetworkCredential(userName, password);  
     }  
     public void AddToAddress(string email, string name = null)  
     {  
       if (!string.IsNullOrEmpty(email))  
       {  
         email = email.Replace(",", ";");  
         string[] emailList = email.Split(';');  
         for (int i = 0; i < emailList.Length; i++)  
         {  
           if (!string.IsNullOrEmpty(emailList[i]))  
             Message.To.Add(new MailAddress(emailList[i].Trim(), name));  
         }  
       }  
     }  
     public void AddCcAddress(string email, string name = null)  
     {  
       if (!string.IsNullOrEmpty(email))  
       {  
         email = email.Replace(",", ";");  
         string[] emailList = email.Split(';');  
         for (int i = 0; i < emailList.Length; i++)  
         {  
           if (!string.IsNullOrEmpty(emailList[i]))  
             Message.CC.Add(new MailAddress(emailList[i].Trim(), name));  
         }  
       }  
     }  
     public void AddBccAddress(string email, string name = null)  
     {  
       if (!string.IsNullOrEmpty(email))  
       {  
         email = email.Replace(",", ";");  
         string[] emailList = email.Split(';');  
         for (int i = 0; i < emailList.Length; i++)  
         {  
           if (!string.IsNullOrEmpty(emailList[i]))  
             Message.Bcc.Add(new MailAddress(emailList[i].Trim(), name));  
         }  
       }  
     }  
     public void AddAttachment(string file, string mimeType)  
     {  
       Attachment attachment = new Attachment(file, mimeType);  
       Message.Attachments.Add(attachment);  
     }  
     public void AddAttachment(Attachment objAttachment)  
     {  
       Message.Attachments.Add(objAttachment);  
     }  
     public void SendMail()  
     {  
       try  
       {  
         string imgTempFolder = new Random().Next().ToString();  
         string imgSavePath = HttpContext.Current.Server.MapPath(@"~\App_Code\GBL\EmailHelper\" + imgTempFolder + @"\"); // You can have your own path  
         string msg = "<html><body>" + Body + "</html></body>";  
         foreach (Match m in Regex.Matches(msg, "<img.+?src=[\"'](.+?)[\"'].+?>", RegexOptions.IgnoreCase | RegexOptions.Multiline))  
         {  
           string src = m.Groups[1].Value;  
           if (src.StartsWith("data:image/png;base64"))  
           {  
             has64BaseImages = true;  
             break;  
           }  
         }  
         if (has64BaseImages)  // If Base64 found let them attach as LinkedResources 
         {  
           if (!Directory.Exists(imgSavePath))  
             Directory.CreateDirectory(imgSavePath);  
           Dictionary<int, string> dicImgs = new Dictionary<int, string>();  
           Dictionary<int, LinkedResource> dicImgLR = new Dictionary<int, LinkedResource>();  
           int c = 1;  
           foreach (Match m in Regex.Matches(msg, "<img.+?src=[\"'](.+?)[\"'].+?>", RegexOptions.IgnoreCase | RegexOptions.Multiline))  
           {  
             string src = m.Groups[1].Value;  
             if (!src.StartsWith("data:image/png;base64"))  
               continue;  
             int i = new Random().Next();  
             byte[] bytes = Convert.FromBase64String(src.Split(',')[1]);  
             Image image;  
             using (MemoryStream ms = new MemoryStream(bytes))  
             {  
               image = Image.FromStream(ms);  
             }  
             image.Save(imgSavePath + i.ToString() + ".png");  
             dicImgs.Add(c, imgSavePath + i.ToString() + ".png");  
             string mediaType = MediaTypeNames.Image.Jpeg;  
             LinkedResource ilImg = new LinkedResource(imgSavePath + i.ToString() + ".png");  
             ilImg.ContentId = c.ToString();  
             ilImg.ContentType.MediaType = mediaType;  
             ilImg.TransferEncoding = TransferEncoding.Base64;  
             ilImg.ContentType.Name = ilImg.ContentId;  
             ilImg.ContentLink = new Uri("cid:" + ilImg.ContentId);  
             dicImgLR.Add(c, ilImg);  
             src = src.Replace(src, "cid:" + c);  
             c++;  
           }  
           AlternateView htmlView = AlternateView.CreateAlternateViewFromString(msg, Encoding.UTF8, MediaTypeNames.Text.Html);  
           foreach (var item in dicImgLR)  
           {  
             htmlView.LinkedResources.Add(item.Value);  
           }  
           Message.AlternateViews.Add(htmlView);  
         }  
         if (FromAddress == null || (FromAddress != null && FromAddress.Address.Equals("")))  
         {  
           throw new Exception("From address not defined");  
         }  
         else  
         {  
           if (string.IsNullOrEmpty(FromAddress.DisplayName))  
             FromAddress = new MailAddress(FromAddress.Address, string.Empty);  
           Message.From = FromAddress;  
         }  
         if (Message.To.Count <= 0)  
         { throw new Exception("To address not defined"); }  
         Message.Subject = Subject;  
         Message.IsBodyHtml = true;  
         if (!has64BaseImages)  
           Message.Body = Body;  
         Message.BodyEncoding = Encoding.UTF8;  
         Message.SubjectEncoding = Encoding.UTF8;  
         smtpClient.Send(Message);  
       }  
       catch (Exception)  
       {  
         throw new Exception("Email failed to send");  
       }  
     }  
     public static string GetFileMimeType(string fileName)  
     {  
       string fileExt = Path.GetExtension(fileName.ToLower());  
       string mimeType = string.Empty;  
       switch (fileExt)  
       {  
         case ".htm":  
         case ".html":  
           mimeType = "text/html";  
           break;  
         case ".xml":  
           mimeType = "text/xml";  
           break;  
         case ".jpg":  
         case ".jpeg":  
           mimeType = "image/jpeg";  
           break;  
         case ".gif":  
           mimeType = "image/gif";  
           break;  
         case ".png":  
           mimeType = "image/png";  
           break;  
         case ".bmp":  
           mimeType = "image/bmp";  
           break;  
         case ".pdf":  
           mimeType = "application/pdf";  
           break;  
         case ".doc":  
           mimeType = "application/msword";  
           break;  
         case ".docx":  
           mimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";  
           break;  
         case ".xls":  
           mimeType = "application/x-msexcel";  
           break;  
         case ".xlsx":  
           mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";  
           break;  
         case ".csv":  
           mimeType = "application/csv";  
           break;  
         case ".ppt":  
           mimeType = "application/vnd.ms-powerpoint";  
           break;  
         case ".pptx":  
           mimeType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";  
           break;  
         case ".rar":  
           mimeType = "application/x-rar-compressed";  
           break;  
         case ".zip":  
           mimeType = "application/x-zip-compressed";  
           break;  
         default:  
           mimeType = "text/plain";  
           break;  
       }  
       return mimeType;  
     }  
   }  

7. Call this class as below to send email.
 EmailHelper emailHelper = new EmailHelper();  
 emailHelper.FromAddress = new System.Net.Mail.MailAddress("From@domain.com");  
 emailHelper.AddToAddress("To@domain.com");  
 emailHelper.AddCcAddress("Cc@domain.com");  
 emailHelper.AddCcAddress("Bcc@domain.com");  
 emailHelper.AddBccAddress(txtEmailBcc.Text.Trim());  
 emailHelper.AddAttachment("FilePath", EmailHelper.GetFileMimeType(item.Value.Value));  
 emailHelper.Subject = "Subject";  
 msgBody = txtEmailBody.InnerText.Trim();  // The summernote editor
 emailHelper.Body = msgBody;  
 emailHelper.SendMail();  

8. There is no 8th step. This is it. Try it in your application and copy paste the images to body as you wish and send as inline attachments.

Happy coding... And if any issue, just comment.












Thursday, June 2, 2016

JQuery Animation - Rotate your html control

Until the next fullcalendar article comes, here is a nice thing that you can try. Rotating html element is easy with jquery. Of course we use this for the fullcalendar. But lets see how it goes below.

 $(this).animate({ borderSpacing: 360 }, {  
         step: function (now, fx) {  
           $(this).css('-webkit-transform', 'rotate(' + now + 'deg)');  
           $(this).css('-moz-transform', 'rotate(' + now + 'deg)');  
           $(this).css('transform', 'rotate(' + now + 'deg)');  
         },  
         duration: 'slow'  
       }, 'linear');  

try it and lets see how we apply it for the fullcalendar in coming articles.

Happy coding...