Thursday, January 12, 2012

Asp.net Culture Codes

List of Culture Codes

A comprehensive list of culture codes used for localising and in conjunction with the CultureInfo class.

The CultureInfo class specifies a unique name for each culture, based on RFC 4646 (Windows Vista and 
later). The name is a combination of an ISO 639 two-letter lowercase culture code associated with a 
language and an ISO 3166 two-letter uppercase subculture code associated with a country or region.   
CodeLanguage – Country/RegionCodeLanguage – Country/Region
afAfrikaanshu-HUHungarian – Hungary
af-ZAAfrikaans – South AfricaisIcelandic
sqAlbanianis-ISIcelandic – Iceland
sq-ALAlbanian – AlbaniaidIndonesian
arArabicid-IDIndonesian – Indonesia
ar-DZArabic – AlgeriaitItalian
ar-BHArabic – Bahrainit-ITItalian – Italy
ar-EGArabic – Egyptit-CHItalian – Switzerland
ar-IQArabic – IraqjaJapanese
ar-JOArabic – Jordanja-JPJapanese – Japan
ar-KWArabic – KuwaitknKannada
ar-LBArabic – Lebanonkn-INKannada – India
ar-LYArabic – LibyakkKazakh
ar-MAArabic – Moroccokk-KZKazakh – Kazakhstan
ar-OMArabic – OmankokKonkani
ar-QAArabic – Qatarkok-INKonkani – India
ar-SAArabic – Saudi ArabiakoKorean
ar-SYArabic – Syriako-KRKorean – Korea
ar-TNArabic – TunisiakyKyrgyz
ar-AEArabic – United Arab Emiratesky-KGKyrgyz – Kyrgyzstan
ar-YEArabic – YemenlvLatvian
hyArmenianlv-LVLatvian – Latvia
hy-AMArmenian – ArmenialtLithuanian
azAzerilt-LTLithuanian – Lithuania
az-AZ-CyrlAzeri (Cyrillic) – AzerbaijanmkMacedonian
az-AZ-LatnAzeri (Latin) – Azerbaijanmk-MKMacedonian – Former Yugoslav Republic of Macedonia
euBasquemsMalay
eu-ESBasque – Basquems-BNMalay – Brunei
beBelarusianms-MYMalay – Malaysia
be-BYBelarusian – BelarusmrMarathi
bgBulgarianmr-INMarathi – India
bg-BGBulgarian – BulgariamnMongolian
caCatalanmn-MNMongolian – Mongolia
ca-ESCatalan – CatalannoNorwegian
zh-HKChinese – Hong Kong SARnb-NONorwegian (Bokm?l) – Norway
zh-MOChinese – Macao SARnn-NONorwegian (Nynorsk) – Norway
zh-CNChinese – ChinaplPolish
zh-CHSChinese (Simplified)pl-PLPolish – Poland
zh-SGChinese – SingaporeptPortuguese
zh-TWChinese – Taiwanpt-BRPortuguese – Brazil
zh-CHTChinese (Traditional)pt-PTPortuguese – Portugal
hrCroatianpaPunjabi
hr-HRCroatian – Croatiapa-INPunjabi – India
csCzechroRomanian
cs-CZCzech – Czech Republicro-RORomanian – Romania
daDanishruRussian
da-DKDanish – Denmarkru-RURussian – Russia
divDhivehisaSanskrit
div-MVDhivehi – Maldivessa-INSanskrit – India
nlDutchsr-SP-CyrlSerbian (Cyrillic) – Serbia
nl-BEDutch – Belgiumsr-SP-LatnSerbian (Latin) – Serbia
nl-NLDutch – The NetherlandsskSlovak
enEnglishsk-SKSlovak – Slovakia
en-AUEnglish – AustraliaslSlovenian
en-BZEnglish – Belizesl-SISlovenian – Slovenia
en-CAEnglish – CanadaesSpanish
en-CBEnglish – Caribbeanes-ARSpanish – Argentina
en-IEEnglish – Irelandes-BOSpanish – Bolivia
en-JMEnglish – Jamaicaes-CLSpanish – Chile
en-NZEnglish – New Zealandes-COSpanish – Colombia
en-PHEnglish – Philippineses-CRSpanish – Costa Rica
en-ZAEnglish – South Africaes-DOSpanish – Dominican Republic
en-TTEnglish – Trinidad and Tobagoes-ECSpanish – Ecuador
en-GBEnglish – United Kingdomes-SVSpanish – El Salvador
en-USEnglish – United Stateses-GTSpanish – Guatemala
en-ZWEnglish – Zimbabwees-HNSpanish – Honduras
etEstonianes-MXSpanish – Mexico
et-EEEstonian – Estoniaes-NISpanish – Nicaragua
foFaroesees-PASpanish – Panama
fo-FOFaroese – Faroe Islandses-PYSpanish – Paraguay
faFarsies-PESpanish – Peru
fa-IRFarsi – Iranes-PRSpanish – Puerto Rico
fiFinnishes-ESSpanish – Spain
fi-FIFinnish – Finlandes-UYSpanish – Uruguay
frFrenches-VESpanish – Venezuela
fr-BEFrench – BelgiumswSwahili
fr-CAFrench – Canadasw-KESwahili – Kenya
fr-FRFrench – FrancesvSwedish
fr-LUFrench – Luxembourgsv-FISwedish – Finland
fr-MCFrench – Monacosv-SESwedish – Sweden
fr-CHFrench – SwitzerlandsyrSyriac
glGaliciansyr-SYSyriac – Syria
gl-ESGalician – GaliciantaTamil
kaGeorgianta-INTamil – India
ka-GEGeorgian – GeorgiattTatar
deGermantt-RUTatar – Russia
de-ATGerman – AustriateTelugu
de-DEGerman – Germanyte-INTelugu – India
de-LIGerman – LiechtensteinthThai
de-LUGerman – Luxembourgth-THThai – Thailand
de-CHGerman – SwitzerlandtrTurkish
elGreektr-TRTurkish – Turkey
el-GRGreek – GreeceukUkrainian
guGujaratiuk-UAUkrainian – Ukraine
gu-INGujarati – IndiaurUrdu
heHebrewur-PKUrdu – Pakistan
he-ILHebrew – IsraeluzUzbek
hiHindiuz-UZ-CyrlUzbek (Cyrillic) – Uzbekistan
hi-INHindi – Indiauz-UZ-LatnUzbek (Latin) – Uzbekistan
huHungarianviVietnamese

Wednesday, December 8, 2010

JavaScript Browser Detect

A useful but often overrated JavaScript function is the browser detect. Sometimes you want to give specific instructions or load a new page in case the viewer uses, for instance, Safari.


The script
Copy this script into your JavaScript files. It works immediately, and you can query three properties of the BrowserDetect object:

  • Browser name: BrowserDetect.browser
  • Browser version: BrowserDetect.version
  • OS name: BrowserDetect.OS

var BrowserDetect = {
 init: function () {
  this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
  this.version = this.searchVersion(navigator.userAgent)
   || this.searchVersion(navigator.appVersion)
   || "an unknown version";
  this.OS = this.searchString(this.dataOS) || "an unknown OS";
 },
 searchString: function (data) {
  for (var i=0;i<data.length;i++) {
   var dataString = data[i].string;
   var dataProp = data[i].prop;
   this.versionSearchString = data[i].versionSearch || data[i].identity;
   if (dataString) {
    if (dataString.indexOf(data[i].subString) != -1)
     return data[i].identity;
   }
   else if (dataProp)
    return data[i].identity;
  }
 },
 searchVersion: function (dataString) {
  var index = dataString.indexOf(this.versionSearchString);
  if (index == -1) return;
  return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
 },
 dataBrowser: [
  {
   string: navigator.userAgent,
   subString: "Chrome",
   identity: "Chrome"
  },
  {  string: navigator.userAgent,
   subString: "OmniWeb",
   versionSearch: "OmniWeb/",
   identity: "OmniWeb"
  },
  {
   string: navigator.vendor,
   subString: "Apple",
   identity: "Safari",
   versionSearch: "Version"
  },
  {
   prop: window.opera,
   identity: "Opera"
  },
  {
   string: navigator.vendor,
   subString: "iCab",
   identity: "iCab"
  },
  {
   string: navigator.vendor,
   subString: "KDE",
   identity: "Konqueror"
  },
  {
   string: navigator.userAgent,
   subString: "Firefox",
   identity: "Firefox"
  },
  {
   string: navigator.vendor,
   subString: "Camino",
   identity: "Camino"
  },
  {  // for newer Netscapes (6+)
   string: navigator.userAgent,
   subString: "Netscape",
   identity: "Netscape"
  },
  {
   string: navigator.userAgent,
   subString: "MSIE",
   identity: "Explorer",
   versionSearch: "MSIE"
  },
  {
   string: navigator.userAgent,
   subString: "Gecko",
   identity: "Mozilla",
   versionSearch: "rv"
  },
  {   // for older Netscapes (4-)
   string: navigator.userAgent,
   subString: "Mozilla",
   identity: "Netscape",
   versionSearch: "Mozilla"
  }
 ],
 dataOS : [
  {
   string: navigator.platform,
   subString: "Win",
   identity: "Windows"
  },
  {
   string: navigator.platform,
   subString: "Mac",
   identity: "Mac"
  },
  {
      string: navigator.userAgent,
      subString: "iPhone",
      identity: "iPhone/iPod"
     },
  {
   string: navigator.platform,
   subString: "Linux",
   identity: "Linux"
  }
 ]

};
BrowserDetect.init();
This script will only continue to work if you regularly check whether newer browsers still follow the rules set forth in the dataBrowser anddataOS arrays.

Browser detection

The dataBrowser array is filled with objects that contain the properties that help the script detect your users' browser. Note its general syntax:
dataBrowser: [
 {
  prop: window.opera,
  identity: "Opera" // note: no comma
 },
 {
  string: navigator.userAgent,
  subString: "MSIE",
  identity: "Explorer",
  versionSearch: "MSIE" // note: no comma
 } // note: no comma
];
The [] define an array literal, and all of its elements are object literals. Each object literal is enclosed in curly braces {} and contains a few properties (name: value,). Note that a comma between the objects and between the properties is required, but that the last comma is always forbidden.

Properties

Every object in the dataBrowser array can contain the following properties:
  1. string and subString properties. These say: "search for subStringin string". If the subString is found, the browser is identified.
  2. prop property. It says "see if prop is supported". If it is, the browser is identified.
  3. an identity string. This string becomes the value ofBrowserDetect.browser.
  4. versionSearch string. This is for searching for the version number (see below). If this property is absent, the identity string is used instead.
Every object must contain either 1 or 2 (never both!), must contain 3 and may contain 4.

Example: Safari

As an example, here's the Safari object:
{
 string: navigator.vendor,
 subString: "Apple",
 identity: "Safari"
},
The script takes the value of navigator.vendor and sees if it contains the string "Apple". If it does, BrowserDetect.browser is set to "Safari" and the browser detection quits. If it doesn't, the script moves on to the next object.

Example: Opera

The next object is Opera:
{
 prop: window.opera,
 identity: "Opera"
},
Here the script checks if the property window.opera exists. If it does,BrowserDetect.browser is set to "Opera" and the browser detection quits. If it doesn't, the script moves on to the next object.

userAgent and vendor

The trick of browser detection is knowing where to look for the information you need. Traditionally we use navigator.userAgent. However, precisely because this check is traditional many minor browsers change their navigator.userAgent string so that bad detects written by amateur web developers are fooled into identifying it as Explorer. Section 3D of the book discusses this problem, as well as some gory details of navigator.userAgent.
More recently, new browsers have started to support thenavigator.vendor property, which contains information about the vendor. In general I prefer to use this string for my detection, since it's less contaminated by obfuscation.
Of course, as soon as amateurs start using my detection script to detect Safari, Opera, Konqueror or other browsers, the browser vendors will be forced to change the value of navigator.vendor and my detect will not work any more.

Detection order

The objects in dataBrowser are used in the order they appear; that's whydataBrowser is an array. As soon as a positive identification is made the script ends, and it doesn't check the remaining objects.
Detection order is very important. The general rule is that you check for the minor browsers first. The reason is that many minor browsers give their users the opportunity to change identity in order to work around browser detects.
For instance, the Opera navigator.userAgent may contain "MSIE". If we'd check for Explorer first, we'd find the "MSIE" and would incorrectly conclude that the browser is Explorer. In order to avoid this false detection, we should check for Safari first. If the browser is Opera, the script never proceeds to the "MSIE" check.
Safari's navigator.userAgent also contains "Gecko". This causes the same problem: a check for Mozilla would reveal "Gecko", and Safari would be identified as Mozilla. Therefore the Mozilla check only takes place if the browser is not Safari.
If you add a new object to detect a new browser, always add it before the Explorer/Mozilla objects at the end of the array.

Version number

In general, the version number of a browser can be found directly after its name in navigator.userAgent. The script searches for this name and takes the number that appears after it. For instance, this is Konqueror'snavigator.userAgent:
Mozilla/5.0 (compatible; Konqueror/3; Linux)
The script searches for the string "Konqueror", skips the next character, and takes the number after that. This is the version number. The script uses parseFloat, so that decimal places in the version number also become part of BrowserDetect.version.
Unfortunately Safari's string never contains its official version; only its internal Apple version number (ie. not 1.3.2 but 312.6). Therefore the version number detect doesn't work in Safari. Since this is clearly Apple's fault (it doesn't follow established conventions), I don't care.

versionSearch

In general, the browser name as it appears in navigator.userAgent is the same as the identification string. If the browser is "iCab", the script searches for "iCab".
However, Explorer needs "MSIE", Mozilla needs "rv", and older Netscape versions need "Mozilla". In order to accomodate these problems you may add a versionSearch property to the browser object. If it's there it's used for the version detect; if it's not there the identity string is used instead.
Take the Firefox and Explorer objects:
{
 string: navigator.userAgent,
 subString: "Firefox",
 identity: "Firefox"
},
{
 string: navigator.userAgent,
 subString: "MSIE",
 identity: "Explorer",
 versionSearch: "MSIE"
},
If the browser is Firefox, the script should look for the "Firefox" string. Since this is also the browser identity string, a special versionSearch is not necessary.
Explorer, however, puts its version number after the string "MSIE". Since I use "Explorer" as identity string, I have to define the versionSearchproperty as "MSIE".

userAgent and appVersion

The version detect script searches for the browser name in bothnavigator.userAgent and navigator.appVersion. The reason is iCab: this browser's navigator.userAgent may not contain the string "iCab", but navigator.appVersion always does.

Operating system

The OS detect works the same as the browser detect. Currently all my OS detects use navigator.platform, since this property appears to always contain the correct information.

navigator

Below you see the objects contained by the object navigator. These variables can be read out and give information about the browser and computer of your users. Use this information to add new objects to the browser detect.
navigator.language = en-US
navigator.product = Gecko
navigator.mimeTypes = [object DOMMimeTypeArray]
navigator.appVersion = 5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10
navigator.plugins = [object DOMPluginArray]
navigator.onLine = true
navigator.platform = Win32
navigator.vendor = Google Inc.
navigator.appCodeName = Mozilla
navigator.cookieEnabled = true
navigator.geolocation = [object Geolocation]
navigator.appName = Netscape
navigator.productSub = 20030107
navigator.userAgent = Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10
navigator.vendorSub = 
navigator.javaEnabled = function javaEnabled() { [native code] }
navigator.getStorageUpdates = function getStorageUpdates() { [native code] }

Sunday, December 5, 2010


JavaScript SOAP Client

In this article we propose a solution based on AJAX that has a great advantage with respect to those commonly found in Internet: calls are made to the Web Services.
This permits:
  1. On the server side we only have to expose a Web Service with the required methods (instead of generating dynamic pages incorporating data that are based on a custom syntax or on a generic XML)
  2. On the client side we use the WSDL (Web Service Description Language) to automatically generate a JavaScript proxy class so as to allow using the Web Service return types - that is similar to what Visual Studio does when a Web Reference is added to the solution.
The following diagram shows the SOAP Client workflow for asynchronous calls:

The Client invokes the SOAPClient.invoke method using a JavaScript function and specifying the following:
  • Web Service URL (pls note that many browsers do not allow cross-domain calls for security reasons)
  • Web method name
  • Web method parameter values
  • Call mode (async = true, sync = false)
  • CallBack method invoked upon response reception (optional for sync calls)
The SOAPClient.invoke method executes the following operations (numbers refer to the previous diagram)
  1. It gets the WSDL and caches the description for future requests
  2. It prepares and sends a SOAP (v. 1.1) request to the server (invoking method and parameter values)
  3. It processes the server reply using the WSDL so as to build the corresponding JavaScript objects to be returned

If the call mode is async, the CallBack method is invoked, otherwise it returns the corresponding object

Implementation
After having exposed our idea about consuming a Web Service via JavaScript, we only have to analyze the code.
Let's start with the class for the definition of the parameters to be passed to the Web method:SOAPClientParameters
function SOAPClientParameters()
{
    var _pl = new Array();
    this.add = function(name, value)
    {
        _pl[name] = value;
        return this;
    }
    this.toXml = function()
    {
        var xml = "";
        for(var p in _pl)
            xml += "<" + p + ">" + SOAPClientParameters._serialize(_pl[p]) + "</" + p + ">";
        return xml;  
    }
}
The code simply consists of an internal dictionary (associative array) with parameter name (key) and related value; theadd method allows appending new parameters, while toXml method - by calling the recursive functionSOAPClientParameters._serialize - provides XML serialization for SOAP request (seeSOAPClient._sendSoapRequest):
SOAPClientParameters._serialize = function(o)
{
    var s = "";
    switch(typeof(o))
    {
        case "string":
            s += o.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); break;
        case "number":
        case "boolean":
            s += o.toString(); break;
        case "object":
            // Date
            if(o.constructor.toString().indexOf("function Date()") > -1)
            {
                var year = o.getFullYear().toString();
                var month = (o.getMonth() + 1).toString(); month = (month.length == 1) ? "0" + month : month;
                var date = o.getDate().toString(); date = (date.length == 1) ? "0" + date : date;
                var hours = o.getHours().toString(); hours = (hours.length == 1) ? "0" + hours : hours;
                var minutes = o.getMinutes().toString(); minutes = (minutes.length == 1) ? "0" + minutes : minutes;
                var seconds = o.getSeconds().toString(); seconds = (seconds.length == 1) ? "0" + seconds : seconds;
                var milliseconds = o.getMilliseconds().toString();
                var tzminutes = Math.abs(o.getTimezoneOffset());
                var tzhours = 0;
                while(tzminutes >= 60)
                {
                    tzhours++;
                    tzminutes -= 60;
                }
                tzminutes = (tzminutes.toString().length == 1) ? "0" + tzminutes.toString() : tzminutes.toString();
                tzhours = (tzhours.toString().length == 1) ? "0" + tzhours.toString() : tzhours.toString();
                var timezone = ((o.getTimezoneOffset() < 0) ? "+" : "-") + tzhours + ":" + tzminutes;
                s += year + "-" + month + "-" + date + "T" + hours + ":" + minutes + ":" + seconds + "." + milliseconds + timezone;
            }
            // Array
            else if(o.constructor.toString().indexOf("function Array()") > -1)
            {
                for(var p in o)
                {
                    if(!isNaN(p)) // linear array
                    {
                        (/function\s+(\w*)\s*\(/ig).exec(o[p].constructor.toString());
                        var type = RegExp.$1;
                        switch(type)
                        {
                            case "":
                                type = typeof(o[p]);
                            case "String":
                                type = "string"; break;
                            case "Number":
                                type = "int"; break;
                            case "Boolean":
                                type = "bool"; break;
                            case "Date":
                                type = "DateTime"; break;
                        }
                        s += "<" + type + ">" + SOAPClientParameters._serialize(o[p]) + "</" + type + ">"
                    }
                    else // associative array
                        s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">"
                }
            }
            // Object or custom function
            else
                for(var p in o)
                    s += "<" + p + ">" + SOAPClientParameters._serialize(o[p]) + "</" + p + ">";
            break;
        default:
            throw new Error(500, "SOAPClientParameters: type '" + typeof(o) + "' is not supported");
    }
    return s;
}
Let's define the SOAPClient class, which can only contain static methods in order to allow async calls, and the only "public" method within this class: SOAPClient.invoke
NOTE: since JavaScript does not foresee access modifiers - such as "public", "private", "protected", etc. - we'll use the "_" prefix to indicate private methods.
function SOAPClient() {}

SOAPClient.invoke = function(url, method, parameters, async, callback)
{
    if(async)
        SOAPClient._loadWsdl(url, method, parameters, async, callback);
    else
        return SOAPClient._loadWsdl(url, method, parameters, async, callback);
}
SOAPClient.invoke method interface is described above; our implementation checks whether the call is async (call result will be passed to the callback method) or sync (call result will be directly returned). The call to the Web Service begins by invoking SOAPClient._loadWsdl method:
SOAPClient._loadWsdl = function(url, method, parameters, async, callback)
{
    // load from cache?
    var wsdl = SOAPClient_cacheWsdl[url];
    if(wsdl + "" != "" && wsdl + "" != "undefined")
        return SOAPClient._sendSoapRequest(url, method, parameters, async, callback, wsdl);
    // get wsdl
    var xmlHttp = SOAPClient._getXmlHttp();
    xmlHttp.open("GET", url + "?wsdl", async);
    if(async)
    {
        xmlHttp.onreadystatechange = function()
        {
            if(xmlHttp.readyState == 4)
                SOAPClient._onLoadWsdl(url, method, parameters, async, callback, xmlHttp);
        }
    }
    xmlHttp.send(null);
    if (!async)
        return SOAPClient._onLoadWsdl(url, method, parameters, async, callback, xmlHttp);
}
The method searches the cache for the same WSDL in order to avoid repetitive calls:
SOAPClient_cacheWsdl = new Array();
If the WSDL is not found in the cache (it's the first call in current context) it is requested to the server by XMLHttpRequest, according the required mode (sync or not). Once an answer is obtained from the server, theSOAPClient._onLoadWsdl method is invoked:
SOAPClient._onLoadWsdl = function(url, method, parameters, async, callback, req)
{
    var wsdl = req.responseXML;
    SOAPClient_cacheWsdl[url] = wsdl;    // save a copy in cache
    return SOAPClient._sendSoapRequest(url, method, parameters, async, callback, wsdl);
}
A WSDL copy is stored into the cache and then the SOAPClient._sendSoapRequest method is executed:
SOAPClient._sendSoapRequest = function(url, method, parameters, async, callback, wsdl)
{
    // get namespace
    var ns = (wsdl.documentElement.attributes["targetNamespace"] + "" == "undefined") ? wsdl.documentElement.attributes.getNamedItem("targetNamespace").nodeValue : wsdl.documentElement.attributes["targetNamespace"].value;
    // build SOAP request
    var sr =
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<soap:Envelope " +
                "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
                "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                "<soap:Body>" +
                "<" + method + " xmlns=\"" + ns + "\">" +
                parameters.toXml() +
                "</" + method + "></soap:Body></soap:Envelope>";
    // send request
    var xmlHttp = SOAPClient._getXmlHttp();
    xmlHttp.open("POST", url, async);
    var soapaction = ((ns.lastIndexOf("/") != ns.length - 1) ? ns + "/" : ns) + method;
    xmlHttp.setRequestHeader("SOAPAction", soapaction);
    xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    if(async)
    {
        xmlHttp.onreadystatechange = function()
        {
            if(xmlHttp.readyState == 4)
                SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
        }
    }
    xmlHttp.send(sr);
    if (!async)
        return SOAPClient._onSendSoapRequest(method, async, callback, wsdl, xmlHttp);
}
The service namespace is taken out of the WSDL (using different XPath queries for Internet Explorer and Mozilla / FireFox), then a SOAP v. 1.1 request is created and submitted. The SOAPClient._onSendSoapRequest method will be invoked upon receiving the server response:
SOAPClient._onSendSoapRequest = function(method, async, callback, wsdl, req)
{
    var o = null;
    var nd = SOAPClient._getElementsByTagName(req.responseXML, method + "Result");
    if(nd.length == 0)
    {
        if(req.responseXML.getElementsByTagName("faultcode").length > 0)
        {
            if(async || callback)
                o = new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);
            else
                throw new Error(500, req.responseXML.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue);          
        }
    }
    else
        o = SOAPClient._soapresult2object(nd[0], wsdl);
    if(callback)
        callback(o, req.responseXML);
    if(!async)
        return o;      
}
The server response is processed looking for faults: if found, an error is raised. Instead, if a correct result is obtained, a recursive function will generate the return type by using the service description:
SOAPClient._soapresult2object = function(node, wsdl)
{
    var wsdlTypes = SOAPClient._getTypesFromWsdl(wsdl);
    return SOAPClient._node2object(node, wsdlTypes);
}

SOAPClient._node2object = function(node, wsdlTypes)
{
    // null node
    if(node == null)
        return null;
    // text node
    if(node.nodeType == 3 || node.nodeType == 4)
        return SOAPClient._extractValue(node, wsdlTypes);
    // leaf node
    if (node.childNodes.length == 1 && (node.childNodes[0].nodeType == 3 || node.childNodes[0].nodeType == 4))
        return SOAPClient._node2object(node.childNodes[0], wsdlTypes);
    var isarray = SOAPClient._getTypeFromWsdl(node.nodeName, wsdlTypes).toLowerCase().indexOf("arrayof") != -1;
    // object node
    if(!isarray)
    {
        var obj = null;
        if(node.hasChildNodes())
            obj = new Object();
        for(var i = 0; i < node.childNodes.length; i++)
        {
            var p = SOAPClient._node2object(node.childNodes[i], wsdlTypes);
            obj[node.childNodes[i].nodeName] = p;
        }
        return obj;
    }
    // list node
    else
    {
        // create node ref
        var l = new Array();
        for(var i = 0; i < node.childNodes.length; i++)
            l[l.length] = SOAPClient._node2object(node.childNodes[i], wsdlTypes);
        return l;
    }
    return null;
}

SOAPClient._extractValue = function(node, wsdlTypes)
{
    var value = node.nodeValue;
    switch(SOAPClient._getTypeFromWsdl(node.parentNode.nodeName, wsdlTypes).toLowerCase())
    {
        default:
        case "s:string":          
            return (value != null) ? value + "" : "";
        case "s:boolean":
            return value + "" == "true";
        case "s:int":
        case "s:long":
            return (value != null) ? parseInt(value + "", 10) : 0;
        case "s:double":
            return (value != null) ? parseFloat(value + "") : 0;
        case "s:datetime":
            if(value == null)
                return null;
            else
            {
                value = value + "";
                value = value.substring(0, (value.lastIndexOf(".") == -1 ? value.length : value.lastIndexOf(".")));
                value = value.replace(/T/gi," ");
                value = value.replace(/-/gi,"/");
                var d = new Date();
                d.setTime(Date.parse(value));                                      
                return d;              
            }
    }
}

SOAPClient._getTypesFromWsdl = function(wsdl)
{
    var wsdlTypes = new Array();
    // IE
    var ell = wsdl.getElementsByTagName("s:element");  
    var useNamedItem = true;
    // MOZ
    if(ell.length == 0)
    {
        ell = wsdl.getElementsByTagName("element");   
        useNamedItem = false;
    }
    for(var i = 0; i < ell.length; i++)
    {
        if(useNamedItem)
        {
            if(ell[i].attributes.getNamedItem("name") != null && ell[i].attributes.getNamedItem("type") != null)
                wsdlTypes[ell[i].attributes.getNamedItem("name").nodeValue] = ell[i].attributes.getNamedItem("type").nodeValue;
        }  
        else
        {
            if(ell[i].attributes["name"] != null && ell[i].attributes["type"] != null)
                wsdlTypes[ell[i].attributes["name"].value] = ell[i].attributes["type"].value;
        }
    }
    return wsdlTypes;
}

SOAPClient._getTypeFromWsdl = function(elementname, wsdlTypes)
{
    var type = wsdlTypes[elementname] + "";
    return (type == "undefined") ? "" : type;
}
The SOAPClient._getElementsByTagName method optimizes XPath queries according to the available XML parser:
SOAPClient._getElementsByTagName = function(document, tagName)
{
    try
    {
        // trying to get node omitting any namespaces (latest versions of MSXML.XMLDocument)
        return document.selectNodes(".//*[local-name()=\""+ tagName +"\"]");
    }
    catch (ex) {}
    // old XML parser support
    return document.getElementsByTagName(tagName);
}
A factory function returns the XMLHttpRequest according to browser type:
SOAPClient._getXmlHttp = function()
{
    try
    {
        if(window.XMLHttpRequest)
        {
            var req = new XMLHttpRequest();
            // some versions of Moz do not support the readyState property and the onreadystate event so we patch it!
            if(req.readyState == null)
            {
                req.readyState = 1;
                req.addEventListener("load",
                                    function()
                                    {
                                        req.readyState = 4;
                                        if(typeof req.onreadystatechange == "function")
                                            req.onreadystatechange();
                                    },
                                    false);
            }
            return req;
        }
        if(window.ActiveXObject)
            return new ActiveXObject(SOAPClient._getXmlHttpProgID());
    }
    catch (ex) {}
    throw new Error("Your browser does not support XmlHttp objects");
}

SOAPClient._getXmlHttpProgID = function()
{
    if(SOAPClient._getXmlHttpProgID.progid)
        return SOAPClient._getXmlHttpProgID.progid;
    var progids = ["Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
    var o;
    for(var i = 0; i < progids.length; i++)
    {
        try
        {
            o = new ActiveXObject(progids[i]);
            return SOAPClient._getXmlHttpProgID.progid = progids[i];
        }
        catch (ex) {};
    }
    throw new Error("Could not find an installed XML parser");
}


Conclusions

By using a little (less than 13 KB) single JavaScript library and, on the server side, simply exposing a Web Service with remote methods, you can use AJAX to create dynamic Web applications with no need for reloading the entire page.

Thursday, October 21, 2010

How to work on Jquery sortable to communicate DB

#1.
The following code you can see or you can paste the code in head tag:


    <script language="javascript" type="text/javascript">
        $(document).ready(function() {
            $.ajax({
                type: "POST",
                url: "AjaxServices.asmx/GetDataForSortable",
                data: "{}",
                contentType: "application/json; Charset=utf-8",
                dataType: "json",
                success: function(result){
                    //alert(result.d);
                    var results = eval('(' + result.d + ')');
                    var dd = '';
                    dd += '<ul id="defaultList" class="list">';
                    for(var i=0; i<results.length; i++)
                    {
                        dd += '<li id="item_'+ results[i].Id +'" originalId="'+ results[i].Id +'" class="itemStyle" seqnumber="'+ results[i].Sequence +'"><span>'+ results[i].Name +'</span></li>';
                    }
                    dd += '</ul>';
                    $('#SortableList').empty()
                        .html(dd)
                        .slideDown('slow',function(){
                            window.scrollTo(0, $('#SortableList').height())
                        });
                    $("#defaultList").sortable(
                    {
                        update: function(ev, ui)
                        {
                            var result = $('#defaultList').sortable('toArray');
                            updateSequenceNumber(result);
                        }
                    });
                }
            });
           
        });
        function updateSequenceNumber(items) {
            var originalIdAndSequenceNumber = '';
            var index = 0;
            for (i = 0; i <= items.length - 1; i++) {
                if (items[i].length == 0)
                    continue;
                var item = $('#' + items[i])[0];
                originalIdAndSequenceNumber += item.attributes["originalId"].nodeValue + ":" + index.toString();
                originalIdAndSequenceNumber += "|";
                index = index + 1;
            }
            persistPositionUsingAjax(originalIdAndSequenceNumber);
        }
        function persistPositionUsingAjax(originalIdAndSequenceNumber) {
            $.ajax({
                type: "POST",
                dataType: "text",
                url: "AjaxServices.asmx/UpdateSequenceNumber",
                data: "s=" + originalIdAndSequenceNumber,
                success: function() {
                }
            });
        }
    </script>


#2. Second thing apply styleseet code on the table. Copy the following code and then paste it in head tag:


<style type="text/css">
        .list
        {
            background: #ccc;
            border: solid 1px #98b8d8;
            background: #ecf1f5;
            padding: 2px;
        }
        li
        {
            list-style-type: none;
        }
        .sortableClass
        {
        }
        .itemStyle
        {
            background: #fff url(images/drag.png) no-repeat 5px 7px;
            border: solid 2px #c4d8ec;
            margin: 10px;
            height: 33px;
            cursor: move;
            text-align: left;
        }
        .itemStyle:hover
        {
            border: solid 2px #aaaaaa;
        }
        .itemStyle span
        {
            float: left;
            margin: 7px 0px 0px 40px;
        }
        .itemStyle .icon
        {
            float: right;
            margin: 3px 0px 25px 0px;
        }
        .arrow{ cursor:pointer;}
    </style>


#3. Third thing is that create the html form in the page and create a div tag like this with id SortableList.

<body>
    <form id="form1" runat="server">
    <div id="SortableList">
       
    </div>
    </form>
</body>
</html>


#4. Fourth one is that create the webservice.asmx file and write the code to communicate with database. The following code is helpful for you.
using System;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Web.Script.Serialization;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Configuration;
using System.Data;
using System.Text;

/// <summary>
/// Summary description for AjaxServices
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]

[ScriptService()]
public class AjaxServices : System.Web.Services.WebService {

    public AjaxServices () {

        //Uncomment the following line if using designed components
        //InitializeComponent();
    }

    [WebMethod]
    public void UpdateSequenceNumber(string s)
    {
        s = s.TrimEnd('|');
        string updateQuery = @"update MenuMaster set Sequence = {0} where MenuId = {1}";
        StringBuilder sb = new StringBuilder();

        string[] originalIdAndSeqNumberArray = s.Split('|');

        foreach (string originalIdAndSeqNumberCombined in originalIdAndSeqNumberArray)
        {
            string[] tempArray = originalIdAndSeqNumberCombined.Split(':');
            int originalId = Convert.ToInt32(tempArray[0]);
            int sequenceNumber = Convert.ToInt32(tempArray[1]);

            sb.Append(String.Format(updateQuery, sequenceNumber, originalId));
            sb.Append(System.Environment.NewLine);
        }
        SqlCommand cmd = new SqlCommand(sb.ToString());
        DbHelper.Save(cmd, false);
    }

    [WebMethod]
    public string GetDataForSortable()
    {
        List<MenuMaster> mm = new List<MenuMaster>();
        SqlCommand cmd = new SqlCommand("Select * from MenuMaster order by Sequence");
        DataTable dt = DbHelper.Get(cmd, false) as DataTable;
        if (dt.Rows.Count > 0)
        {
            foreach (DataRow row in dt.Rows)
            {
                MenuMaster m = new MenuMaster() {
                    Id = Convert.ToInt32(row["MenuId"].ToString()),
                    Name = row["MenuName"].ToString(),
                    Sequence = Convert.ToInt32(row["Sequence"].ToString())
                };
                mm.Add(m);
            }
            return new JavaScriptSerializer().Serialize(mm);
        }
        return new JavaScriptSerializer().Serialize("Hello");
    }
}

interface IMenu
{
    Nullable<int> Id { get; set; }
    string Name { get; set; }
    int Sequence { get; set; }
}

public sealed class MenuMaster : IMenu
{
    public Nullable<int> Id { get; set; }
    public string Name { get; set; }
    public int Sequence { get; set; }
}

public class DbHelper
{
    public static object Get(SqlCommand cmd, bool isStoredProcedure)
    {
        using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
        {
            cmd.CommandTimeout = 30;
            cmd.Connection = con;
            cmd.CommandType = (isStoredProcedure == true) ? CommandType.StoredProcedure : CommandType.Text;
            cmd.Connection.Open();
            DataTable dt = new DataTable();
            dt.Load(cmd.ExecuteReader());
            cmd.Connection.Close();
            return dt;
        }
    }
    public static object Save(SqlCommand cmd, bool isStoredProcedure)
    {
        int recordEffected = 0;
        using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
        {
            cmd.CommandTimeout = 30;
            cmd.Connection = con;
            cmd.CommandType = (isStoredProcedure == true) ? CommandType.StoredProcedure : CommandType.Text;
            cmd.Connection.Open();
            recordEffected = cmd.ExecuteNonQuery();
            cmd.Connection.Close();
            return recordEffected;
        }
    }
}


#5. First of all configure your web.config file for 2.0 frame work. If you work on 3.5 frame work then no problem with this code. And it will be execute successfully.


If you download the full code then click the following link: