PageRenderTime 70ms CodeModel.GetById 2ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 1ms

/WCFWebApi/src/Microsoft.ApplicationServer.Http/Microsoft/ApplicationServer/Http/Test/testscript.js

#
JavaScript | 1090 lines | 882 code | 169 blank | 39 comment | 141 complexity | 6a66f0dea8071e77e809dcf85ee3813f MD5 | raw file
   1var contentTypePatterns = { 'xml': /(text|application)\s*\/\s*xml/i, 'json': /(text|application)\s*\/\s*json/i, 'raw': /(text)\s*\/\s*plain/i };
   2var defaultContentTypes = { 'xml': 'text/xml', 'json': 'application/json', 'raw': 'text/plain' };
   3var httpMethodsNoBody = ['GET', 'HEAD', 'MOVE', 'TRACE', 'DELETE', 'CONNECT', 'MKCOL', 'COPY', 'UNLOCK', 'OPTIONS'];
   4var formatList = ['Xml', 'Json', 'Raw'];
   5var server = {};
   6var currentResource = null;
   7var invokeHistory = {'records':[], 'maxWidth':0};
   8var autoCompletesResetList = [];
   9var currentFormat = 0;
  10var formatTabs = null;
  11var contentLengthRegex = /^\s*Content\-Length\s*(\:|$)/i;
  12var contentTypeRegex = /^\s*Content\-Type\s*(\:[^\;]*|$)/i;
  13var defaultHttpMethod = 'GET';
  14
  15(function ($) {
  16    $.tree = function (uri) {
  17        this.uri = uri;
  18        this.indexInServer = -1;
  19        this.subtree = new Array();
  20    };
  21
  22    $.extend($.tree.prototype, {
  23        show: function (level) {
  24            var content = ["<div>"];
  25
  26            for (var i = 0; i < level * 4; i++) {
  27                content.push("&nbsp;");
  28            }
  29
  30            if (this.indexInServer >= 0) {
  31                content.push("<a class='resourceTreeNode' href='javascript:void(0);' indexInServer='");
  32                content.push(this.indexInServer);
  33                content.push("' title='");
  34                content.push(server.Resources[this.indexInServer].Uri);
  35                content.push("'>");
  36                content.push(this.uri);
  37                content.push("</a>");
  38            }
  39            else {
  40                content.push(this.uri);
  41            }
  42
  43            content.push("</div>");            
  44
  45            for (var i = 0; i < this.subtree.length; i++) {
  46                content.push(this.subtree[i].show(level + 1));
  47            }
  48
  49            return content.join('');
  50        },
  51
  52        add: function (uri, indexInServer) {
  53            if (uri.length == 0) {
  54                this.indexInServer = indexInServer;
  55                return;
  56            }
  57            else {
  58                var j = uri.indexOf("/");
  59                if (j < 0) {
  60                    j = uri.length;
  61                }
  62                var sub_uri = uri.substring(0, j);
  63                var sub_tree_id = -1;
  64                for (var i = 0; i < this.subtree.length; i++) {
  65                    if (this.subtree[i].uri == sub_uri) {
  66                        sub_tree_id = i;
  67                        break;
  68                    }
  69                }
  70
  71                if (sub_tree_id < 0) {
  72                    this.subtree.push(new $.tree(sub_uri));
  73                    sub_tree_id = this.subtree.length - 1;
  74                }
  75
  76                this.subtree[sub_tree_id].add(uri.substring(j + 1), indexInServer);
  77            }
  78        }
  79
  80    });
  81
  82    $.resourceTree = function () {
  83    };
  84
  85    $.extend($.resourceTree.prototype, {
  86        stretchItemsWidthToSame: function(controls){
  87            var maxWidth = 0;
  88            var width = 0;
  89            for(var i=0; i<controls.length; i++)
  90            {
  91                width = $(controls[i]).width();
  92                maxWidth = (maxWidth > width)? maxWidth: width;
  93            }
  94            
  95            controls.width(maxWidth);
  96        },
  97		
  98		getHostNormalizedUri: function(uri){
  99			var i = uri.indexOf('//');
 100			if(i < 0){
 101				return uri;
 102			}
 103			
 104			i += 2;
 105			var j = uri.indexOf('/', i);
 106			if(j < 0){
 107				return uri;
 108			}
 109			
 110			return uri.slice(0, i) + window.location.host + uri.slice(j);		
 111		},
 112        
 113        build: function (placeHolder, callback) {
 114            var THIS = this;
 115            proxy.getResource(function (data) {
 116                    server = data;
 117                    if(server.Resources.length > 0){
 118                        var baseAddress = THIS.getHostNormalizedUri(server.Resources[0].BaseAddress);
 119                        var resourcesTree = new $.tree(baseAddress);
 120                        var length = (baseAddress.charAt(baseAddress.length-1) == '/')? baseAddress.length : baseAddress.length + 1;
 121                        
 122                        for (i = 0; i < server.Resources.length; i++) {
 123                            resourcesTree.add(THIS.getHostNormalizedUri(server.Resources[i].Uri).substring(length), i);
 124                            
 125                            server.Resources[i].operationMap = {};
 126                            for(j=0; j<server.Resources[i].Operations.length; j++){
 127                                server.Resources[i].operationMap[server.Resources[i].Operations[j].HttpMethod] = server.Resources[i].Operations[j].Name;
 128                            }
 129                        }
 130                        
 131                        var content = resourcesTree.show(0);
 132                        placeHolder.html(content);
 133                        THIS.stretchItemsWidthToSame(placeHolder.contents());
 134                        $(".resourceTreeNode").click(callback);
 135                        $("#resources").show();
 136                        
 137                        if(server.HelpEnabled){
 138                            $("#sampleButton").show();
 139                        }
 140                        else{
 141                            $("#sampleButton").hide();
 142                        }
 143                    }
 144                },
 145                function(){
 146                    $("#resources").hide();
 147                });
 148        }
 149    });
 150
 151    })(jQuery);
 152
 153function lengthInUtf8Bytes(str) {  
 154    var m = encodeURIComponent(str).match(/\%[89ABab]/g);  
 155    return str.length + (m ? m.length : 0);
 156    }
 157
 158function setContentLength(str) {
 159    var requestHeaderTextArea = $("#requestHeader");
 160    var headers = requestHeaderTextArea.val().split('\n');
 161    var contentLength = "Content-Length:" + lengthInUtf8Bytes(str);
 162    var contentLengthExists = false;
 163    for (var i = 0; i < headers.length; i++) {
 164        if (headers[i].match(contentLengthRegex)) {
 165            headers[i] = contentLength;
 166            contentLengthExists = true;
 167        }
 168    }
 169
 170    headers = headers.join('\n');
 171
 172    if (!contentLengthExists) {
 173        headers = headers.replace(/\s*$/, "");
 174        headers = headers + "\n" + contentLength;
 175    }
 176
 177    requestHeaderTextArea.val(headers);
 178}
 179
 180function setContentType(format) {
 181    if (format in defaultContentTypes) {
 182        var contentType = "Content-Type:" + defaultContentTypes[format];
 183        var requestHeaderTextArea = $("#requestHeader");
 184        var headers = requestHeaderTextArea.val().split('\n');
 185        var contentTypeExists = false;
 186        for (var i = 0; i < headers.length; i++) {
 187            var match = headers[i].match(contentTypeRegex)
 188            if (match) {
 189                contentTypeExists = true;
 190                if(match[0].match(contentTypePatterns[format]))
 191                {
 192                    return;
 193                }
 194
 195                headers[i] = headers[i].replace(contentTypeRegex, contentType);
 196            }
 197        }
 198
 199        headers = headers.join('\n');
 200
 201        if(!contentTypeExists)
 202        {
 203            headers = headers.replace(/\s*$/, "");
 204            headers = headers + "\n" + contentType;
 205        }
 206
 207        requestHeaderTextArea.val(headers);
 208    }
 209}
 210
 211function selectVariableInUriTextbox(textbox) {
 212    var val = textbox.val();
 213    var selectionEnd = textbox.getSelection().end;
 214    var s, e;
 215    s = val.lastIndexOf("{", selectionEnd);
 216    if (s < 0) {
 217        return;
 218    }
 219
 220    e = val.indexOf("}", s);
 221    e = (e < 0) ? val.length : e + 1;
 222
 223    textbox.setSelection({
 224        start: s,
 225        end: e
 226    });
 227}
 228
 229function enableIntellisense(control, postTextCallback) {
 230    var autoComplete = control.enableAutoComplete();
 231
 232    control.keydown(function (event) {
 233        return autoComplete.onKeyDown(event);
 234    });
 235
 236    control.keyup(function (event) {
 237        return autoComplete.onKeyUp(event, function (fullText, curPos, callback) {
 238
 239            return postTextCallback(fullText, curPos, callback);
 240        });
 241    });
 242
 243    control.click(function (event) {
 244        return autoComplete.onMouseClick(event);
 245    });
 246
 247    control.focus(function (event) {
 248        return autoComplete.onFocus(event);
 249    });
 250
 251    control.blur(function (event) {
 252        return autoComplete.onBlur(event);
 253    });
 254
 255    return autoComplete;
 256}
 257
 258function enableUriIntellisense(uri, uriTemplate, httpMethod) {
 259    var autoComplete = enableIntellisense(uri, function (fullText, curPos, callback) {
 260
 261        return proxy.postUriText(uriTemplate.val(), httpMethod.val(), curPos, fullText, callback);
 262    });
 263
 264    autoComplete.regexContextId(/^.*[\/\?\=\&\#]/);
 265    return autoComplete;
 266}
 267
 268function enableHeaderIntellisense(header) {
 269    var autoComplete = enableIntellisense(header, function (fullText, curPos, callback) {
 270
 271        return proxy.postHeaderText(curPos, fullText, callback);
 272    });
 273
 274    autoComplete.regexContextId(/^(.*\n)*([^\:]+\:)?/);
 275    return autoComplete;
 276}
 277
 278function enableRequestBodyXmlIntellisense(body, uriTemplate, httpMethod) {
 279    var autoComplete = enableIntellisense(body, function (fullText, curPos, callback) {
 280
 281        return proxy.postBodyText(uriTemplate.val(), httpMethod.val(), "xml", curPos, fullText, callback);
 282    });
 283
 284    // (\s|\S) is stronger than . because the former also matches \n
 285    autoComplete.regexContextId(/^(\s|\S)*(\<\/|\<([^\>]*(\s|\"))?|\>)/);         
 286    return autoComplete;
 287}
 288
 289function enableRequestBodyJsonIntellisense(body, uriTemplate, httpMethod) {
 290    var autoComplete = enableIntellisense(body, function (fullText, curPos, callback) {
 291
 292        return proxy.postBodyText(uriTemplate.val(), httpMethod.val(), "json", curPos, fullText, callback);
 293    });
 294
 295    autoComplete.regexContextId(/^(\s|\S)*[\{\}\"\:\[\]\,]/);
 296    return autoComplete;
 297}
 298
 299function enableHttpMethodIntellisense(httpMethod) {
 300    var autoComplete = enableIntellisense(httpMethod, function (fullText, curPos, callback) {
 301        return getHttpMethodAutoCompleteList(curPos, fullText, callback);
 302    });
 303
 304    autoComplete.filteringOn1stChar(true);
 305    autoComplete.comboboxMode(true);
 306    return autoComplete;
 307}
 308
 309function getHttpMethodAutoCompleteList(curPos, fullText, callback) {
 310    var data = {
 311        autoCompleteList: ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "TRACE", "OPTIONS"]
 312    };
 313    if (!currentResource) {
 314        // no resource is selected yet
 315        // assume every method is applicable
 316        return callback(data);
 317    }
 318
 319    var map = {};
 320    for (var j = 0; j < currentResource.Operations.length; j++) {
 321        map[currentResource.Operations[j].HttpMethod] = true;
 322    }
 323
 324    // need to mark not applicable methods
 325    for (var i = 0; i < data.autoCompleteList.length; i++) {
 326        var method = data.autoCompleteList[i];
 327        var applicable = map[method];
 328        if (!applicable) {
 329            data.autoCompleteList[i] = method + "\n" + method + "\n\n\nfalse";
 330        }
 331        else{
 332            delete map[method];
 333        }
 334    }
 335    
 336    for (var key in map){
 337        data.autoCompleteList.push(key);
 338    }
 339
 340    return callback(data);
 341}
 342
 343function clearLastRequest() {
 344    $("#httpMethodTextBox").val("");
 345    $("#uri").val("");
 346    $("#requestHeader").val("Accept:*/*");
 347    
 348    for(var i=0; i<formatList.length; i++){
 349        $("#requestBody"+formatList[i]).val("");
 350    }
 351    clearAllValidationResult();   
 352    clearAutoCompleteResetListContext(0);   // also reset http-method textbox	
 353}
 354
 355function clearLastResponse() {
 356    $("#statusBar").text("");
 357    $("#status").text("");
 358    $("#responseHeader").val("");
 359    $("#responseBody").val("");
 360}
 361
 362function clearLastSession() {
 363    clearLastResponse();
 364    clearLastRequest();
 365}
 366
 367function initResourceTree() {
 368    var rt = new $.resourceTree();
 369    rt.build(
 370        $("#resourceTree"),
 371        function (event) {
 372            clearLastSession();
 373
 374            var target = $(event.target);
 375            var i = target.attr("indexInServer") - 0;
 376            currentResource = server.Resources[i];
 377
 378            // host name in the url could be wrong, e.g. in Azure case, service returns urls with service's internal host name
 379            // fixed here with the real host name in order to invoke the service, e.g. in Azure case, must use the external host name of the service
 380            $("#uri").val(rt.getHostNormalizedUri(currentResource.Uri));
 381
 382            // the url template here is used to match web api operation on service side
 383            // so keep the host name got from service side
 384            $("#uriTemplate").val(currentResource.Uri);
 385
 386            // make 'GET' as the default httpmethod as long as the selected service supports 'GET'.
 387            if (defaultHttpMethod in currentResource.operationMap) {
 388                $("#httpMethodTextBox").val(defaultHttpMethod);
 389            }
 390            else {
 391                $("#httpMethodTextBox").val((currentResource.Operations[0].HttpMethod));
 392            }
 393
 394            $("#httpMethodTextBox").change();
 395
 396            enableFormattingButton(currentFormat);
 397
 398            $(".resourceSelected").removeClass("resourceSelected");
 399            target.parent().addClass("resourceSelected");
 400
 401            $("#uri").focus();
 402
 403            return false;
 404        });
 405}
 406
 407function dateToString(date) {
 408    var h = date.getHours();
 409    var m = date.getMinutes();
 410    var s = date.getSeconds();
 411    var ms = date.getMilliseconds();
 412    return "time:  " + Math.floor(h / 10) + h % 10 + "." + Math.floor(m / 10) + m % 10 + "." + Math.floor(s / 10) + s % 10+ "." + Math.floor(ms / 100) + Math.floor(ms / 10) + ms % 10;
 413}
 414
 415(function ($) {
 416    $.invokeRecord = function () {
 417        this._resource = null,
 418        this._httpMethod = "";
 419        this._uri = "";
 420        this._format = "";
 421        this._uriTemplate = "";
 422        this._requestHeader = "";
 423        this._requestBody = "";
 424        this._responseHeader = "";
 425        this._responseBody = "";
 426        this._duration = 0;
 427        this._statusCode = 0;
 428        this._statusText = "";
 429        this._requestTimeStr = "";
 430        this._responseTimeStr = "";
 431    };
 432
 433    $.extend($.invokeRecord.prototype,
 434        {
 435            resource: function (value) {
 436                if (value) {
 437                    // setter
 438                    this._resource = value;
 439                }
 440                else {
 441                    // getter
 442                    return this._resource;
 443                }
 444            },
 445
 446            httpMethod: function (value) {
 447                if (value) {
 448                    // setter
 449                    this._httpMethod = value;
 450                }
 451                else {
 452                    // getter
 453                    return this._httpMethod;
 454                }
 455            },
 456
 457            uri: function (value) {
 458                if (value) {
 459                    // setter
 460                    this._uri = value;
 461                }
 462                else {
 463                    // getter
 464                    return this._uri;
 465                }
 466            },
 467            
 468            format: function (value) {
 469                if (value != null) {
 470                    // setter
 471                    this._format = value;
 472                }
 473                else {
 474                    // getter
 475                    return this._format;
 476                }
 477            },
 478
 479
 480            uriTemplate: function (value) {
 481                if (value) {
 482                    // setter
 483                    this._uriTemplate = value;
 484                }
 485                else {
 486                    // getter
 487                    return this._uriTemplate;
 488                }
 489            },
 490
 491            requestHeader: function (value) {
 492                if (value) {
 493                    // setter
 494                    this._requestHeader = value;
 495                }
 496                else {
 497                    // getter
 498                    return this._requestHeader;
 499                }
 500            },
 501
 502            requestBody: function (value) {
 503                if (value) {
 504                    // setter
 505                    this._requestBody = value;
 506                }
 507                else {
 508                    // getter
 509                    return this._requestBody;
 510                }
 511            },
 512            
 513            
 514            responseHeader: function (value) {
 515                if (value) {
 516                    // setter
 517                    this._responseHeader = value;
 518                }
 519                else {
 520                    // getter
 521                    return this._responseHeader;
 522                }
 523            },
 524
 525            responseBody: function (value) {
 526                if (value) {
 527                    // setter
 528                    this._responseBody = value;
 529                }
 530                else {
 531                    // getter
 532                    return this._responseBody;
 533                }
 534            },
 535
 536            duration: function (value) {
 537                if (value || value == 0) {
 538                    // setter
 539                    this._duration = value;
 540                }
 541                else {
 542                    // getter
 543                    return this._duration;
 544                }
 545            },
 546            
 547            requestTimeStr: function (value) {
 548                if (value) {
 549                    // setter
 550                    this._requestTimeStr = value;
 551                }
 552                else {
 553                    // getter
 554                    return this._requestTimeStr;
 555                }
 556            },
 557            
 558            responseTimeStr: function (value) {
 559                if (value) {
 560                    // setter
 561                    this._responseTimeStr = value;
 562                }
 563                else {
 564                    // getter
 565                    return this._responseTimeStr;
 566                }
 567            },
 568            
 569            statusText: function (value) {
 570                if (value) {
 571                    // setter
 572                    this._statusText = value;
 573                }
 574                else {
 575                    // getter
 576                    return this._statusText;
 577                }
 578            },
 579
 580            statusCode: function (value) {
 581                if (value || value == 0) {
 582                    // setter
 583                    this._statusCode = value;
 584                }
 585                else {
 586                    // getter
 587                    return this._statusCode;
 588                }
 589            },
 590
 591            show: function (index) {
 592                var html = [];
 593                var i = 0;
 594                html.push("<a href='javascript:void(0);' class='historyRecord'");
 595                html.push("' queueIndex='");
 596                html.push(index);
 597                html.push("'>");
 598                
 599                html.push("<span class='historyIndex'>");
 600                html.push(index+1);
 601                html.push("</span>");
 602                
 603                html.push("<span class='historyDuration'>");
 604                html.push(this._duration);
 605                html.push("s</span>");
 606
 607                html.push("<span class='historyStatus ");
 608                if (this._statusCode < 400) {
 609                    html.push("requestStatusOK'>");
 610                }
 611                else {
 612                    html.push("requestStatusError'>");
 613                }
 614                html.push(this._statusCode);
 615                html.push("</span>");
 616
 617                html.push("<span class='historyHttpMethod'>");
 618                html.push(this._httpMethod);
 619                html.push("</span>");
 620
 621                html.push("<span>");
 622                
 623                if(this._resource && this._uri.indexOf(this._resource.BaseAddress)==0){
 624                    html.push((this._resource.BaseAddress.length < this._uri.length) ? this._uri.substring(this._resource.BaseAddress.length) : this._uri);
 625                }
 626                else{
 627                    html.push(this._uri);
 628                }
 629                
 630                html.push("</span>");
 631                html.push("</a>");
 632                
 633                var _a = $(html.join(""));
 634                _a.click(function (event) {
 635                    var target = $(this);
 636                    var queueIndex = target.attr("queueIndex") - 0;
 637                    var ihr = invokeHistory.records[queueIndex];
 638
 639                    clearLastSession();
 640
 641                    currentResource = ihr.resource();
 642
 643                    $("#uri").val(ihr.uri());
 644                    $("#uriTemplate").val(ihr.uriTemplate());
 645                    $("#requestHeader").val(ihr.requestHeader());
 646                    currentFormat = ihr.format();
 647                    enableFormattingButton(currentFormat);
 648                    $("#requestBody"+formatList[currentFormat]).val(ihr.requestBody());
 649                    formatTabs.tabs('select', currentFormat);                    
 650                    $("#httpMethodTextBox").val(ihr.httpMethod());
 651                    $("#httpMethodTextBox").change();
 652                    
 653                    $("#responseHeader").val(ihr.responseHeader());
 654                    $("#responseBody").val(ihr.responseBody());
 655                    $("#statusBar").text("Request " + ihr.requestTimeStr()+"   Response " + ihr.responseTimeStr() + "  Duration: " + ihr.duration()+"s");
 656                    
 657                    $("#status").text(ihr.statusCode() + "/" + ihr.statusText());
 658                    if (ihr.statusCode() < 400) {
 659                        $("#status").switchClass("requestStatusError","requestStatusOK", 0);
 660                    }
 661                    else {
 662                        $("#status").switchClass("requestStatusOK","requestStatusError", 0);
 663                    }
 664                });
 665
 666                return _a;
 667            }
 668        }
 669    );
 670})(jQuery);
 671
 672function enableAllIntellisense() {
 673    autoCompletesResetList.push(enableHttpMethodIntellisense($("#httpMethodTextBox")));
 674    enableHeaderIntellisense($("#requestHeader"));
 675    autoCompletesResetList.push(enableUriIntellisense($("#uri"), $("#uriTemplate"), $("#httpMethodTextBox")));
 676    autoCompletesResetList.push(enableRequestBodyXmlIntellisense($("#requestBodyXml"), $("#uriTemplate"), $("#httpMethodTextBox")));
 677    autoCompletesResetList.push(enableRequestBodyJsonIntellisense($("#requestBodyJson"), $("#uriTemplate"), $("#httpMethodTextBox")));
 678}
 679
 680function clearAutoCompleteResetListContext(index)
 681{
 682    for (var i = index; i < autoCompletesResetList.length; i++) {
 683        autoCompletesResetList[i].clearContext();
 684    }
 685}
 686
 687function clearValidationWarning(control){
 688    control.css('visibility', 'hidden');
 689    control.removeAttr('title');
 690}
 691
 692function setValidationWarning(control, warningMessage){
 693    control.css('visibility', 'visible');
 694    control.attr('title', warningMessage);
 695}
 696
 697function clearAllValidationResult(){
 698    clearValidationWarning($("#httpMethodValidationIndicator"));
 699    clearValidationWarning($("#uriValidationIndicator"));
 700    clearValidationWarning($("#requestBodyValidationIndicator"));   
 701}
 702
 703function validateHttpMethod(httpMethod){
 704    var indicator = $("#httpMethodValidationIndicator");
 705    clearValidationWarning(indicator);
 706    if(currentResource && !(httpMethod in currentResource.operationMap)) {
 707        setValidationWarning(indicator, "The selected method may not be available, and autocomplete of uri and request body will be affected.");
 708    }
 709}
 710
 711function validateUriOrRequestBody(control){
 712    var type = null;
 713    var indicator = null;
 714    if(control.attr('id') == 'uri'){
 715        type = 'uri';
 716        indicator = $("#uriValidationIndicator");
 717    }
 718    else{
 719        type = 'content';
 720        indicator = $("#requestBodyValidationIndicator");
 721    }
 722    
 723    clearValidationWarning(indicator);
 724    
 725    if(!currentResource){
 726        return;
 727    }
 728    
 729    var uriTemplate = $("#uriTemplate").val();
 730    var httpMethod  = $("#httpMethodTextBox").val();
 731    var fullText = control.val();
 732    if(uriTemplate.length==0 || httpMethod.length==0 || fullText.length==0){
 733        return;
 734    }
 735    
 736    var context = httpMethod + uriTemplate + currentFormat + fullText;
 737        
 738    proxy.validateText(
 739        type, 
 740        uriTemplate, 
 741        httpMethod, 
 742        formatList[currentFormat], 
 743        fullText, 
 744        function(isValid){
 745            var _context = $("#httpMethodTextBox").val() + $("#uriTemplate").val() + currentFormat + control.val();
 746            if(context != _context){
 747                return;  //the validation result is out of date.
 748            }
 749
 750
 751            if (!isValid) {
 752                if(type == 'uri') {
 753                    setValidationWarning(indicator, "The uri does not match currently selected resource.");
 754                }
 755                else {
 756                    setValidationWarning(indicator, "The request body may not be expected given currently selected resource.");
 757                }
 758            }
 759            else {
 760                clearValidationWarning(indicator);
 761            }
 762        },
 763        function(){
 764            clearValidationWarning(indicator);
 765    });
 766}
 767
 768function enableFormattingButton(format){
 769    if((format==0 || format==1)){  // xml or json
 770        if($("#formatButton").is('.unclickableLink')){
 771            $("#formatButton").attr('href',"javascript:void(0);");
 772            $("#formatButton").switchClass('unclickableLink', 'clickableLink', 0);
 773        }
 774    }
 775    else {
 776        if($("#formatButton").is('.clickableLink')){
 777            $('#formatButton').removeAttr('href');
 778            $("#formatButton").switchClass('clickableLink', 'unclickableLink', 0);
 779        }
 780    }
 781}
 782
 783function updateSampleLink(httpMethod){
 784    if(currentResource && currentFormat!=2 && currentResource.operationMap[httpMethod]){
 785        var href = currentResource.BaseAddress+"/help/operations/" + currentResource.operationMap[httpMethod] + "#request-" + formatList[currentFormat];
 786        $("#sampleButton").attr('href',href);
 787        $("#sampleButton").attr('title',href);
 788        $("#sampleButton").switchClass('unclickableLink', 'clickableLink', 0);
 789    }
 790    else{
 791        $('#sampleButton').removeAttr('href');
 792        $('#sampleButton').removeAttr('title');
 793        $("#sampleButton").switchClass('clickableLink', 'unclickableLink', 0);
 794    }
 795}
 796
 797function handleHttpResponse(httpRequest, ir){
 798    $("#status").text(httpRequest.status + "/" + httpRequest.statusText);
 799    if (httpRequest.status < 400) {
 800        $("#status").switchClass("requestStatusError", "requestStatusOK", 0);
 801    }
 802    else {
 803        $("#status").switchClass("requestStatusOK", "requestStatusError", 0);
 804    }
 805    ir.statusCode(httpRequest.status);
 806    ir.statusText(httpRequest.statusText);
 807
 808    $("#responseHeader").val(httpRequest.getAllResponseHeaders());
 809    ir.responseHeader($("#responseHeader").val());
 810
 811    var resTime = new Date();
 812    ir.responseTimeStr(dateToString(resTime));
 813
 814    ir.duration(Math.ceil((resTime - reqTime) / 10) / 100);
 815
 816    var format = null;
 817    var contentType = httpRequest.getResponseHeader("Content-Type");
 818    for (var key in contentTypePatterns) {
 819        if (contentType.match(contentTypePatterns[key])) {
 820            format = key;
 821            break;
 822        }
 823    }
 824
 825    var rawResponse = httpRequest.responseText;
 826
 827    if (format != null) {
 828        proxy.postResContText(
 829            rawResponse,
 830            format,
 831            function (formattedText) {
 832                $("#responseBody").val(formattedText);
 833            },
 834            function () {
 835                $("#responseBody").val(rawResponse);
 836            });
 837    }
 838    else {
 839        $("#responseBody").val(rawResponse);
 840    }
 841
 842    ir.responseBody($("#responseBody").val());
 843    $("#statusBar").text(" Request " + ir.requestTimeStr() + "  Response " + ir.responseTimeStr() + "  Duration: " + ir.duration() + "s");
 844    invokeHistory.records.push(ir);
 845
 846    var recordShow = ir.show(invokeHistory.records.length - 1);
 847    recordShow.prependTo($("#invokeList"));
 848    if (recordShow.width() > invokeHistory.maxWidth) {
 849        invokeHistory.maxWidth = recordShow.width();
 850        $("#invokeList").contents().width(invokeHistory.maxWidth);
 851    }
 852    else {
 853        recordShow.width(invokeHistory.maxWidth);
 854    }
 855}
 856
 857$(document).ready(function () {
 858    initResourceTree();
 859    formatTabs = $("#requestBodyTabs").tabs();
 860    enableAllIntellisense();
 861
 862    invokeHistory = { 'records': [], 'maxWidth': $('#history').width() };
 863    $("#clearHistory").click(function () {
 864        invokeHistory = { 'records': [], 'maxWidth': $('#history').width() };
 865        $("#invokeList").html("");
 866    });
 867
 868    $("#uri").dblclick(function () {
 869        selectVariableInUriTextbox($("#uri"));
 870    });
 871
 872    $("#uri").change(function () {
 873        validateUriOrRequestBody($("#uri"));
 874    });
 875
 876    $("#requestBodyXml").change(function () {
 877        validateUriOrRequestBody($("#requestBodyXml"));
 878    });
 879
 880    $("#requestBodyJson").change(function () {
 881        validateUriOrRequestBody($("#requestBodyJson"));
 882    });
 883
 884    for (var i = 0; i < formatList.length; i++) {
 885        var requestBodyTextArea = $("#requestBody" + formatList[i]);
 886        requestBodyTextArea.change(function () {
 887            setContentLength($("#requestBody" + formatList[currentFormat]).val());
 888        });
 889    }
 890
 891    $("#httpMethodTextBox").change(function () {
 892        var httpMethod = $("#httpMethodTextBox").val();
 893        var needHideRequestBody = false;
 894        for (var i = 0; i < httpMethodsNoBody.length; i++) {
 895            if (httpMethodsNoBody[i] == httpMethod) {
 896                needHideRequestBody = true;
 897                break;
 898            }
 899        }
 900
 901        validateHttpMethod(httpMethod);
 902        validateUriOrRequestBody($("#uri"));
 903        
 904        $("#requestHeader").val("Accept:*/*");
 905        if (needHideRequestBody) {
 906            $("#requestBodyDiv").hide();
 907            $("#requestBodyTabs").hide();
 908            $("#responseBody").height(490);
 909            $("#requestBody" + formatList[currentFormat]).val("");
 910        }
 911        else {
 912            $("#requestBodyDiv").show();
 913            $("#requestBodyTabs").show();
 914            $("#responseBody").height(284);
 915
 916            setContentType(formatList[currentFormat].toLowerCase());
 917            var requestBodyTextArea = $("#requestBody" + formatList[currentFormat]);
 918            setContentLength(requestBodyTextArea.val());
 919            
 920            validateUriOrRequestBody(requestBodyTextArea);
 921            updateSampleLink(httpMethod);
 922        }
 923
 924        clearAutoCompleteResetListContext(1);   // no need to reset http-method textbox
 925    });
 926
 927    $(".formatTabs").click(function (event) {
 928        var targetFormat = formatTabs.tabs('option', 'selected');
 929
 930        if (targetFormat != currentFormat) {
 931            var currentTextbox = $("#requestBody" + formatList[currentFormat]);
 932            var targetTextBox = $("#requestBody" + formatList[targetFormat]);
 933            var httpMethod = $("#httpMethodTextBox").val();
 934
 935            if (currentTextbox.val().length < 1) {
 936                currentFormat = targetFormat;
 937                updateSampleLink(httpMethod);
 938                enableFormattingButton(currentFormat);
 939                setContentType(formatList[currentFormat].toLowerCase());
 940                return;
 941            }
 942
 943            if (targetFormat == 2 || currentFormat == 2) {        //raw format.            
 944                targetTextBox.val(currentTextbox.val());
 945                currentFormat = targetFormat;
 946            }
 947            else {
 948                var uriTemplate = $("#uriTemplate").val();
 949                var content = currentTextbox.val();
 950
 951                var errorMessage = null;
 952                proxy.convertRequestBodyFormat(
 953                    uriTemplate,
 954                    httpMethod,
 955                    formatList[currentFormat].toLowerCase(),
 956                    formatList[targetFormat].toLowerCase(),
 957                    content,
 958                    function (convertResult) {
 959                        targetTextBox.val(convertResult);
 960                        currentTextbox.val("");
 961                        currentFormat = targetFormat;
 962                        setContentLength(targetTextBox.val());
 963                    },
 964                    function () {
 965                        errorMessage = ["The request body cannot be converted from "];
 966                        errorMessage.push(formatList[currentFormat]);
 967                        errorMessage.push(" to ");
 968                        errorMessage.push(formatList[targetFormat]);
 969                        errorMessage.push(". Do you want to paste it to ");
 970                        errorMessage.push(formatList[targetFormat] + " tab?");
 971                    });
 972
 973                if (errorMessage != null) {
 974                    var answer = window.confirm(errorMessage.join(""));
 975                    if (answer) {
 976                        targetTextBox.val(content);
 977                        currentTextbox.val("");
 978                        currentFormat = targetFormat;
 979                        setContentLength(targetTextBox.val());
 980                    }
 981                    else {
 982                        formatTabs.tabs('select', currentFormat);
 983                    }
 984                }
 985            }
 986
 987            enableFormattingButton(currentFormat);
 988            updateSampleLink(httpMethod);
 989            setContentType(formatList[currentFormat].toLowerCase());
 990        }
 991    });
 992
 993    $("#formatButton").click(function () {
 994        if (currentFormat == 0 || currentFormat == 1) {
 995            //TODO: prevent the user from clicking other tabs.
 996            var currentTextBox = $("#requestBody" + formatList[currentFormat]);
 997            var rawRequestBody = currentTextBox.val();
 998            if (rawRequestBody.length > 0) {
 999                proxy.formatRequestBody(
1000                    $("#uriTemplate").val(),
1001                    $("#httpMethodTextBox").val(),
1002                    rawRequestBody,
1003                    formatList[currentFormat].toLowerCase(),
1004                    function (formattedText) {
1005                        currentTextBox.val(formattedText);
1006                    },
1007                    function () {
1008                        //TODO: show Formatting failed message.
1009                    });
1010            }
1011        }
1012    });
1013
1014    $("#invokeButton").click(function () {
1015
1016        clearLastResponse();
1017
1018        var httpMethod = $("#httpMethodTextBox").val();
1019        var url = $("#uri").val();
1020        var requestHeader = $("#requestHeader").val();
1021
1022        if (httpMethod.length == 0) {
1023            alert("HTTP Method should not be empty");
1024            $("#httpMethodTextBox").focus();
1025            return false;
1026        }
1027
1028        if (url.length == 0) {
1029            alert("Url should not be empty");
1030            $("#uri").focus();
1031            return false;
1032        }
1033
1034        var httpRequest = new XMLHttpRequest();
1035        try {
1036            httpRequest.open(httpMethod, encodeURI(url), false);
1037        }
1038        catch (e) {
1039            alert("Cannot send request. Check the security setting of your browser if you are sending request to a different domain.");
1040            return false;
1041        }
1042        var requestHeaders = requestHeader.split("\n");
1043        httpRequest.setRequestHeader("If-Modified-Since", new Date(0));
1044        for (var i = 0; i < requestHeaders.length; i++) {
1045            var headerPair = requestHeaders[i].split(":", 2);
1046            if (headerPair.length == 2) {
1047                httpRequest.setRequestHeader(headerPair[0], headerPair[1]);
1048            }
1049        }
1050
1051        var ir = new $.invokeRecord();
1052
1053        ir.resource(currentResource);
1054        ir.httpMethod(httpMethod);
1055        ir.uri(url);
1056        ir.uriTemplate($("#uriTemplate").val());
1057        ir.requestHeader(requestHeader);
1058        ir.requestBody($("#requestBody" + formatList[currentFormat]).val());
1059        ir.format(currentFormat);
1060
1061        if ($.browser.mozilla) {  //since firefox 3.5/3.6 will not trigger XMLHttpRequest.onreadystatechange().
1062            httpRequest.onload = httpRequest.onerror = httpRequest.onabort = function(){
1063            handleHttpResponse(httpRequest, ir);
1064           };
1065        }
1066        else{
1067            httpRequest.onreadystatechange = function () {
1068                switch (this.readyState) {
1069                    case 4:
1070                        handleHttpResponse(httpRequest, ir);
1071                        break;
1072                    default:
1073                        break;
1074                }
1075            }
1076        }
1077
1078        httpRequest.ontimeout = function () {
1079            $("#status").text("Request timed out.");
1080        }
1081
1082        reqTime = new Date();
1083        ir.requestTimeStr(dateToString(reqTime));
1084        httpRequest.send($("#requestBody" + formatList[currentFormat]).val());
1085
1086        return false;
1087    });
1088
1089    $("#container").show();
1090});