Merge branch 'bug7018' of http://github.com/csnover/jquery into csnover-bug7018
[jquery.git] / speed / benchmarker.js
1   jQuery.benchmarker.tests = [
2         // Selectors from:
3         // http://ejohn.org/blog/selectors-that-people-actually-use/
4         /*
5         // For Amazon.com
6         "#navAmazonLogo", "#navSwmSkedPop",
7         ".navbar", ".navGreeting",
8         "div", "table",
9         "img.navCrossshopTabCap", "span.navGreeting",
10         "#navbar table", "#navidWelcomeMsg span",
11         "div#navbar", "ul#navAmazonLogo",
12         "#navAmazonLogo .navAmazonLogoGatewayPanel", "#navidWelcomeMsg .navGreeting",
13         ".navbar .navAmazonLogoGatewayPanel", ".navbar .navGreeting",
14         "*",
15         "#navAmazonLogo li.navAmazonLogoGatewayPanel", "#navidWelcomeMsg span.navGreeting",
16         "a[name=top]", "form[name=site-search]",
17         ".navbar li", ".navbar span",
18         "[name=top]", "[name=site-search]",
19         "ul li", "a img",
20         "#navbar #navidWelcomeMsg", "#navbar #navSwmDWPop",
21         "#navbar ul li", "#navbar a img"
22         */
23         // For Yahoo.com
24         "#page", "#masthead", "#mastheadhd",
25         ".mastheadbd", ".first", ".on",
26         "div", "li", "a",
27         "div.mastheadbd", "li.first", "li.on",
28         "#page div", "#dtba span",
29         "div#page", "div#masthead",
30         "#page .mastheadbd", "#page .first",
31         ".outer_search_container .search_container", ".searchbox_container .inputtext",
32         "*",
33         "#page div.mastheadbd", "#page li.first",
34         "input[name=p]", "a[name=marketplace]",
35         ".outer_search_container div", ".searchbox_container span",
36         "[name=p]", "[name=marketplace]",
37         "ul li", "form input",
38         "#page #e2econtent", "#page #e2e"
39   ];
40
41   jQuery.fn.benchmark = function() {
42     this.each(function() {
43       try {
44         jQuery(this).parent().children("*:gt(1)").remove();
45       } catch(e) { }
46     })
47     // set # times to run the test in index.html
48     var times = parseInt(jQuery("#times").val());
49     jQuery.benchmarker.startingList = this.get();
50     benchmark(this.get(), times, jQuery.benchmarker.libraries);
51   }
52
53   jQuery(function() {
54     for(i = 0; i < jQuery.benchmarker.tests.length; i++) {
55       jQuery("tbody").append("<tr><td class='test'>" + jQuery.benchmarker.tests[i] + "</td></tr>");
56     }
57     jQuery("tbody tr:first-child").remove();
58     jQuery("td.test").before("<td><input type='checkbox' checked='checked' /></td>");
59     jQuery("button.runTests").bind("click", function() {
60       jQuery('td:has(input:checked) + td.test').benchmark();
61     });
62
63     jQuery("button.retryTies").bind("click", function() { jQuery("tr:has(td.tie) td.test").benchmark() })
64
65     jQuery("button.selectAll").bind("click", function() { jQuery("input[type=checkbox]").each(function() { this.checked = true }) })
66     jQuery("button.deselectAll").bind("click", function() { jQuery("input[type=checkbox]").each(function() { this.checked = false }) })
67
68     jQuery("#addTest").bind("click", function() {
69       jQuery("table").append("<tr><td><input type='checkbox' /></td><td><input type='text' /><button>Add</button></td></tr>");
70       jQuery("div#time-test > button").each(function() { this.disabled = true; })
71       jQuery("tbody tr:last button").bind("click", function() {
72         var td = jQuery(this).parent();
73         td.html("<button>-</button>" + jQuery(this).prev().val()).addClass("test");
74         jQuery("div#time-test > button").each(function() { this.disabled = false; })
75         jQuery("button", td).bind("click", function() { jQuery(this).parents("tr").remove(); })
76       })
77     })
78
79     var headers = jQuery.map(jQuery.benchmarker.libraries, function(i,n) {
80       var extra = n == 0 ? "basis - " : "";
81       return "<th>" + extra + i + "</th>"
82     }).join("");
83
84     jQuery("thead tr").append(headers);
85
86     var footers = "";
87     for(i = 0; i < jQuery.benchmarker.libraries.length; i++)
88       footers += "<th></th>"
89
90     var wlfooters = "";
91     for(i = 0; i < jQuery.benchmarker.libraries.length; i++)
92       wlfooters += "<td><span class='wins'>W</span> / <span class='fails'>F</span></th>"
93
94     jQuery("tfoot tr:first").append(footers);
95     jQuery("tfoot tr:last").append(wlfooters);
96
97   });
98
99    benchmark = function(list, times, libraries) {
100      if(list[0]) {
101        var times = times || 50;
102        var el = list[0];
103        var code = jQuery(el).text().replace(/^-/, "");
104          var timeArr = []
105          for(i = 0; i < times + 2; i++) {
106            var time = new Date()
107            try {
108              window[libraries[0]](code);
109            } catch(e) { }
110            timeArr.push(new Date() - time);
111          }
112          var diff = Math.sum(timeArr) - Math.max.apply( Math, timeArr )
113                 - Math.min.apply( Math, timeArr );
114          try {
115            var libRes = window[libraries[0]](code);
116            var jqRes = jQuery(code);
117            if(((jqRes.length == 0) && (libRes.length != 0)) ||
118              (libRes.length > 0 && (jqRes.length == libRes.length)) ||
119              ((libraries[0] == "cssQuery" || libraries[0] == "jQuery") && code.match(/nth\-child/) && (libRes.length > 0)) ||
120              ((libraries[0] == "jQold") && jqRes.length > 0)) {
121              jQuery(el).parent().append("<td>" + Math.round(diff / times * 100) / 100 + "ms</td>");
122            } else {
123              jQuery(el).parent().append("<td class='fail'>FAIL</td>");
124            }
125          } catch(e) {
126            jQuery(el).parent().append("<td class='fail'>FAIL</td>");
127          }
128        setTimeout(benchmarkList(list, times, libraries), 100);
129      } else if(libraries[1]) {
130        benchmark(jQuery.benchmarker.startingList, times, libraries.slice(1));
131      } else {
132        jQuery("tbody tr").each(function() {
133          var winners = jQuery("td:gt(1)", this).min(2);
134          if(winners.length == 1) winners.addClass("winner");
135          else winners.addClass("tie");
136        });
137        setTimeout(count, 100);
138      }
139    }
140
141   function benchmarkList(list, times, libraries) {
142     return function() {
143       benchmark(list.slice(1), times, libraries);
144     }
145   }
146
147  function count() {
148    for(i = 3; i <= jQuery.benchmarker.libraries.length + 2 ; i++) {
149      var fails = jQuery("td:nth-child(" + i + ").fail").length;
150      var wins = jQuery("td:nth-child(" + i + ").winner").length;
151      jQuery("tfoot tr:first th:eq(" + (i - 1) + ")")
152       .html("<span class='wins'>" + wins + "</span> / <span class='fails'>" + fails + "</span>");
153    }
154  }
155
156
157  jQuery.fn.maxmin = function(tolerance, maxmin, percentage) {
158    tolerance = tolerance || 0;
159    var target = Math[maxmin].apply(Math, jQuery.map(this, function(i) {
160      var parsedNum = parseFloat(i.innerHTML.replace(/[^\.\d]/g, ""));
161      if(parsedNum || (parsedNum == 0)) return parsedNum;
162    }));
163    return this.filter(function() {
164      if( withinTolerance(parseFloat(this.innerHTML.replace(/[^\.\d]/g, "")), target, tolerance, percentage) ) return true;
165    })
166  }
167
168  jQuery.fn.max = function(tolerance, percentage) { return this.maxmin(tolerance, "max", percentage) }
169  jQuery.fn.min = function(tolerance, percentage) { return this.maxmin(tolerance, "min", percentage) }
170
171  function withinTolerance(number, target, tolerance, percentage) {
172    if(percentage) { var high = target + ((tolerance / 100) * target); var low = target - ((tolerance / 100) * target); }
173    else { var high = target + tolerance; var low = target - tolerance; }
174    if(number >= low && number <= high) return true;
175  }
176
177  Math.sum = function(arr) {
178    var sum = 0;
179    for(i = 0; i < arr.length; i++) sum += arr[i];
180    return sum;
181  }