tarantula-rails3 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGELOG +49 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +161 -0
  4. data/Rakefile +83 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/example_helper.rb +57 -0
  7. data/examples/relevance/core_extensions/ellipsize_example.rb +19 -0
  8. data/examples/relevance/core_extensions/file_example.rb +8 -0
  9. data/examples/relevance/core_extensions/response_example.rb +29 -0
  10. data/examples/relevance/core_extensions/test_case_example.rb +20 -0
  11. data/examples/relevance/tarantula/attack_handler_example.rb +29 -0
  12. data/examples/relevance/tarantula/basic_attack_example.rb +12 -0
  13. data/examples/relevance/tarantula/crawler_example.rb +375 -0
  14. data/examples/relevance/tarantula/form_example.rb +50 -0
  15. data/examples/relevance/tarantula/form_submission_example.rb +171 -0
  16. data/examples/relevance/tarantula/html_document_handler_example.rb +43 -0
  17. data/examples/relevance/tarantula/html_report_helper_example.rb +46 -0
  18. data/examples/relevance/tarantula/html_reporter_example.rb +82 -0
  19. data/examples/relevance/tarantula/invalid_html_handler_example.rb +33 -0
  20. data/examples/relevance/tarantula/io_reporter_example.rb +11 -0
  21. data/examples/relevance/tarantula/link_example.rb +84 -0
  22. data/examples/relevance/tarantula/log_grabber_example.rb +26 -0
  23. data/examples/relevance/tarantula/rails_integration_proxy_example.rb +88 -0
  24. data/examples/relevance/tarantula/result_example.rb +85 -0
  25. data/examples/relevance/tarantula/tidy_handler_example.rb +58 -0
  26. data/examples/relevance/tarantula/transform_example.rb +20 -0
  27. data/examples/relevance/tarantula_example.rb +23 -0
  28. data/laf/images/header_bg.jpg +0 -0
  29. data/laf/images/logo.png +0 -0
  30. data/laf/images/tagline.png +0 -0
  31. data/laf/javascripts/jquery-1.2.3.js +3408 -0
  32. data/laf/javascripts/jquery-ui-tabs.js +890 -0
  33. data/laf/javascripts/jquery.tablesorter.js +861 -0
  34. data/laf/javascripts/tarantula.js +10 -0
  35. data/laf/stylesheets/tarantula.css +346 -0
  36. data/lib/relevance/core_extensions/ellipsize.rb +34 -0
  37. data/lib/relevance/core_extensions/file.rb +9 -0
  38. data/lib/relevance/core_extensions/metaclass.rb +78 -0
  39. data/lib/relevance/core_extensions/response.rb +9 -0
  40. data/lib/relevance/core_extensions/string_chars_fix.rb +11 -0
  41. data/lib/relevance/core_extensions/test_case.rb +19 -0
  42. data/lib/relevance/tarantula.rb +58 -0
  43. data/lib/relevance/tarantula/attack.rb +18 -0
  44. data/lib/relevance/tarantula/attack_handler.rb +37 -0
  45. data/lib/relevance/tarantula/basic_attack.rb +40 -0
  46. data/lib/relevance/tarantula/crawler.rb +254 -0
  47. data/lib/relevance/tarantula/detail.html.erb +81 -0
  48. data/lib/relevance/tarantula/form.rb +23 -0
  49. data/lib/relevance/tarantula/form_submission.rb +88 -0
  50. data/lib/relevance/tarantula/html_document_handler.rb +36 -0
  51. data/lib/relevance/tarantula/html_report_helper.rb +39 -0
  52. data/lib/relevance/tarantula/html_reporter.rb +105 -0
  53. data/lib/relevance/tarantula/index.html.erb +37 -0
  54. data/lib/relevance/tarantula/invalid_html_handler.rb +18 -0
  55. data/lib/relevance/tarantula/io_reporter.rb +34 -0
  56. data/lib/relevance/tarantula/link.rb +94 -0
  57. data/lib/relevance/tarantula/log_grabber.rb +16 -0
  58. data/lib/relevance/tarantula/rails_integration_proxy.rb +68 -0
  59. data/lib/relevance/tarantula/recording.rb +12 -0
  60. data/lib/relevance/tarantula/response.rb +13 -0
  61. data/lib/relevance/tarantula/result.rb +77 -0
  62. data/lib/relevance/tarantula/test_report.html.erb +32 -0
  63. data/lib/relevance/tarantula/tidy_handler.rb +32 -0
  64. data/lib/relevance/tarantula/transform.rb +17 -0
  65. data/lib/relevance/tasks/tarantula_tasks.rake +42 -0
  66. data/lib/tarantula-rails3.rb +9 -0
  67. data/template/tarantula_test.rb +22 -0
  68. metadata +164 -0
@@ -0,0 +1,861 @@
1
+ /*
2
+ *
3
+ * TableSorter 2.0 - Client-side table sorting with ease!
4
+ * Version 2.0.1
5
+ * @requires jQuery v1.2.1
6
+ *
7
+ * Copyright (c) 2007 Christian Bach
8
+ * Examples and docs at: http://tablesorter.com
9
+ * Dual licensed under the MIT and GPL licenses:
10
+ * http://www.opensource.org/licenses/mit-license.php
11
+ * http://www.gnu.org/licenses/gpl.html
12
+ *
13
+ */
14
+ /**
15
+ *
16
+ * @description Create a sortable table with multi-column sorting capabilitys
17
+ *
18
+ * @example $('table').tablesorter();
19
+ * @desc Create a simple tablesorter interface.
20
+ *
21
+ * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
22
+ * @desc Create a tablesorter interface and sort on the first and secound column in ascending order.
23
+ *
24
+ * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
25
+ * @desc Create a tablesorter interface and disableing the first and secound column headers.
26
+ *
27
+ * @example $('table').tablesorter({ 0: {sorter:"integer"}, 1: {sorter:"currency"} });
28
+ * @desc Create a tablesorter interface and set a column parser for the first and secound column.
29
+ *
30
+ *
31
+ * @param Object settings An object literal containing key/value pairs to provide optional settings.
32
+ *
33
+ * @option String cssHeader (optional) A string of the class name to be appended to sortable tr elements in the thead of the table.
34
+ * Default value: "header"
35
+ *
36
+ * @option String cssAsc (optional) A string of the class name to be appended to sortable tr elements in the thead on a ascending sort.
37
+ * Default value: "headerSortUp"
38
+ *
39
+ * @option String cssDesc (optional) A string of the class name to be appended to sortable tr elements in the thead on a descending sort.
40
+ * Default value: "headerSortDown"
41
+ *
42
+ * @option String sortInitialOrder (optional) A string of the inital sorting order can be asc or desc.
43
+ * Default value: "asc"
44
+ *
45
+ * @option String sortMultisortKey (optional) A string of the multi-column sort key.
46
+ * Default value: "shiftKey"
47
+ *
48
+ * @option String textExtraction (optional) A string of the text-extraction method to use.
49
+ * For complex html structures inside td cell set this option to "complex",
50
+ * on large tables the complex option can be slow.
51
+ * Default value: "simple"
52
+ *
53
+ * @option Object headers (optional) An array containing the forces sorting rules.
54
+ * This option let's you specify a default sorting rule.
55
+ * Default value: null
56
+ *
57
+ * @option Array sortList (optional) An array containing the forces sorting rules.
58
+ * This option let's you specify a default sorting rule.
59
+ * Default value: null
60
+ *
61
+ * @option Array sortForce (optional) An array containing the forces sorting rules.
62
+ * This option let's you specify a default sorting rule.
63
+ * Default value: null
64
+ *
65
+ *
66
+ * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter should apply fixed widths to the table columns.
67
+ * This is usefull when using the pager companion plugin.
68
+ * This options requires the dimension jquery plugin.
69
+ * Default value: false
70
+ *
71
+ * @option Boolean cancelSelection (optional) Boolean flag indicating if tablesorter should cancel selection of the table headers text.
72
+ * Default value: true
73
+ *
74
+ * @option Boolean debug (optional) Boolean flag indicating if tablesorter should display debuging information usefull for development.
75
+ *
76
+ * @type jQuery
77
+ *
78
+ * @name tablesorter
79
+ *
80
+ * @cat Plugins/Tablesorter
81
+ *
82
+ * @author Christian Bach/christian.bach@polyester.se
83
+ */
84
+
85
+ (function($) {
86
+ $.extend({
87
+ tablesorter: new function() {
88
+
89
+ var parsers = [], widgets = [];
90
+
91
+ this.defaults = {
92
+ cssHeader: "header",
93
+ cssAsc: "headerSortUp",
94
+ cssDesc: "headerSortDown",
95
+ sortInitialOrder: "asc",
96
+ sortMultiSortKey: "shiftKey",
97
+ sortForce: null,
98
+ textExtraction: "simple",
99
+ parsers: {},
100
+ widgets: [],
101
+ widgetZebra: {css: ["even","odd"]},
102
+ headers: {},
103
+ widthFixed: false,
104
+ cancelSelection: true,
105
+ sortList: [],
106
+ headerList: [],
107
+ dateFormat: "us",
108
+ debug: false
109
+ };
110
+
111
+ /* debuging utils */
112
+ function benchmark(s,d) {
113
+ log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
114
+ }
115
+
116
+ this.benchmark = benchmark;
117
+
118
+ function log(s) {
119
+ if (typeof console != "undefined" && typeof console.debug != "undefined") {
120
+ console.log(s);
121
+ } else {
122
+ alert(s);
123
+ }
124
+ }
125
+
126
+ /* parsers utils */
127
+ function buildParserCache(table,$headers) {
128
+
129
+ if(table.config.debug) { var parsersDebug = ""; }
130
+
131
+ var rows = table.tBodies[0].rows;
132
+
133
+ if(table.tBodies[0].rows[0]) {
134
+
135
+ var list = [], cells = rows[0].cells, l = cells.length;
136
+
137
+ for (var i=0;i < l; i++) {
138
+ var p = false;
139
+
140
+ if($.meta && ($($headers[i]).data() && $($headers[i]).data().sorter) ) {
141
+
142
+ p = getParserById($($headers[i]).data().sorter);
143
+
144
+ } else if((table.config.headers[i] && table.config.headers[i].sorter)) {
145
+
146
+ p = getParserById(table.config.headers[i].sorter);
147
+ }
148
+ if(!p) {
149
+ p = detectParserForColumn(table.config,cells[i]);
150
+ }
151
+
152
+ if(table.config.debug) { parsersDebug += "column:" + i + " parser:" +p.id + "\n"; }
153
+
154
+ list.push(p);
155
+ }
156
+ }
157
+
158
+ if(table.config.debug) { log(parsersDebug); }
159
+
160
+ return list;
161
+ };
162
+
163
+ function detectParserForColumn(config,node) {
164
+ var l = parsers.length;
165
+ for(var i=1; i < l; i++) {
166
+ if(parsers[i].is($.trim(getElementText(config,node)))) {
167
+ return parsers[i];
168
+ }
169
+ }
170
+
171
+ // 0 is always the generic parser (text)
172
+ return parsers[0];
173
+ }
174
+
175
+ function getParserById(name) {
176
+ var l = parsers.length;
177
+ for(var i=0; i < l; i++) {
178
+ if(parsers[i].id.toLowerCase() == name.toLowerCase()) {
179
+ return parsers[i];
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+
185
+ /* utils */
186
+ function buildCache(table) {
187
+
188
+ if(table.config.debug) { var cacheTime = new Date(); }
189
+
190
+
191
+ var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
192
+ totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
193
+ parsers = table.config.parsers,
194
+ cache = {row: [], normalized: []};
195
+
196
+ for (var i=0;i < totalRows; ++i) {
197
+
198
+ /** Add the table data to main data array */
199
+ var c = table.tBodies[0].rows[i], cols = [];
200
+
201
+ cache.row.push($(c));
202
+
203
+ for(var j=0; j < totalCells; ++j) {
204
+ cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));
205
+ }
206
+
207
+ cols.push(i); // add position for rowCache
208
+ cache.normalized.push(cols);
209
+ cols = null;
210
+ };
211
+
212
+ if(table.config.debug) { benchmark("Building cache for " + totalRows + " rows:", cacheTime); }
213
+
214
+ return cache;
215
+ };
216
+
217
+ function getElementText(config,node) {
218
+
219
+ if(!node) return "";
220
+
221
+ var t = "";
222
+
223
+ if(config.textExtraction == "simple") {
224
+ if(node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
225
+ t = node.childNodes[0].innerHTML;
226
+ } else {
227
+ t = node.innerHTML;
228
+ }
229
+ } else {
230
+ if(typeof(config.textExtraction) == "function") {
231
+ t = config.textExtraction(node);
232
+ } else {
233
+ t = $(node).text();
234
+ }
235
+ }
236
+ return t;
237
+ }
238
+
239
+ function appendToTable(table,cache) {
240
+
241
+ if(table.config.debug) {var appendTime = new Date()}
242
+
243
+ var c = cache,
244
+ r = c.row,
245
+ n= c.normalized,
246
+ totalRows = n.length,
247
+ checkCell = (n[0].length-1),
248
+ tableBody = $(table.tBodies[0]),
249
+ rows = [];
250
+
251
+ // clear the table body
252
+ //$.tablesorter.clearTableBody(table);
253
+
254
+
255
+
256
+
257
+ for (var i=0;i < totalRows; i++) {
258
+ rows.push(r[n[i][checkCell]]);
259
+ if(!table.config.appender) {
260
+
261
+ var o = r[n[i][checkCell]];
262
+ var l = o.length;
263
+ for(var j=0; j < l; j++) {
264
+
265
+ tableBody[0].appendChild(o[j]);
266
+
267
+ }
268
+
269
+ //tableBody.append(r[n[i][checkCell]]);
270
+ }
271
+ }
272
+
273
+ if(table.config.appender) {
274
+
275
+ table.config.appender(table,rows);
276
+ }
277
+
278
+ rows = null;
279
+
280
+ if(table.config.debug) { benchmark("Rebuilt table:", appendTime); }
281
+
282
+ //apply table widgets
283
+ applyWidget(table);
284
+ };
285
+
286
+ function buildHeaders(table) {
287
+
288
+ if(table.config.debug) { var time = new Date(); }
289
+
290
+ var meta = ($.meta) ? true : false, tableHeadersRows = [];
291
+
292
+ for(var i = 0; i < table.tHead.rows.length; i++) { tableHeadersRows[i]=0; };
293
+
294
+ $tableHeaders = $("thead th",table);
295
+
296
+ $tableHeaders.each(function(index) {
297
+
298
+ this.count = 0;
299
+ this.column = index;
300
+ this.order = formatSortingOrder(table.config.sortInitialOrder);
301
+
302
+ if(checkHeaderMetadata(this) || checkHeaderOptions(table,index)) this.sortDisabled = true;
303
+
304
+ if(!this.sortDisabled) {
305
+ $(this).addClass(table.config.cssHeader);
306
+ }
307
+
308
+ // add cell to headerList
309
+ table.config.headerList[index]= this;
310
+ });
311
+
312
+ if(table.config.debug) { benchmark("Built headers:", time); log($tableHeaders); }
313
+
314
+ return $tableHeaders;
315
+
316
+ };
317
+
318
+ function checkCellColSpan(table, rows, row) {
319
+ var arr = [], r = table.tHead.rows, c = r[row].cells;
320
+
321
+ for(var i=0; i < c.length; i++) {
322
+ var cell = c[i];
323
+
324
+ if ( cell.colSpan > 1) {
325
+ arr = arr.concat(checkCellColSpan(table, headerArr,row++));
326
+ } else {
327
+ if(table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row+1])) {
328
+ arr.push(cell);
329
+ }
330
+ //headerArr[row] = (i+row);
331
+ }
332
+ }
333
+ return arr;
334
+ };
335
+
336
+ function checkHeaderMetadata(cell) {
337
+ if(($.meta) && ($(cell).data().sorter === false)) { return true; };
338
+ return false;
339
+ }
340
+
341
+ function checkHeaderOptions(table,i) {
342
+ if((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { return true; };
343
+ return false;
344
+ }
345
+
346
+ function applyWidget(table) {
347
+ var c = table.config.widgets;
348
+ var l = c.length;
349
+ for(var i=0; i < l; i++) {
350
+
351
+ getWidgetById(c[i]).format(table);
352
+ }
353
+
354
+ }
355
+
356
+ function getWidgetById(name) {
357
+ var l = widgets.length;
358
+ for(var i=0; i < l; i++) {
359
+ if(widgets[i].id.toLowerCase() == name.toLowerCase() ) {
360
+ return widgets[i];
361
+ }
362
+ }
363
+ };
364
+
365
+ function formatSortingOrder(v) {
366
+
367
+ if(typeof(v) != "Number") {
368
+ i = (v.toLowerCase() == "desc") ? 1 : 0;
369
+ } else {
370
+ i = (v == (0 || 1)) ? v : 0;
371
+ }
372
+ return i;
373
+ }
374
+
375
+ function isValueInArray(v, a) {
376
+ var l = a.length;
377
+ for(var i=0; i < l; i++) {
378
+ if(a[i][0] == v) {
379
+ return true;
380
+ }
381
+ }
382
+ return false;
383
+ }
384
+
385
+ function setHeadersCss(table,$headers, list, css) {
386
+ // remove all header information
387
+ $headers.removeClass(css[0]).removeClass(css[1]);
388
+
389
+ var h = [];
390
+ $headers.each(function(offset) {
391
+ if(!this.sortDisabled) {
392
+ h[this.column] = $(this);
393
+ }
394
+ });
395
+
396
+ var l = list.length;
397
+ for(var i=0; i < l; i++) {
398
+ h[list[i][0]].addClass(css[list[i][1]]);
399
+ }
400
+ }
401
+
402
+ function fixColumnWidth(table,$headers) {
403
+ var c = table.config;
404
+ if(c.widthFixed) {
405
+ var colgroup = $('<colgroup>');
406
+ $("tr:first td",table.tBodies[0]).each(function() {
407
+ colgroup.append($('<col>').css('width',$(this).width()));
408
+ });
409
+ $(table).prepend(colgroup);
410
+ };
411
+ }
412
+
413
+ function updateHeaderSortCount(table,sortList) {
414
+ var c = table.config, l = sortList.length;
415
+ for(var i=0; i < l; i++) {
416
+ var s = sortList[i], o = c.headerList[s[0]];
417
+ o.count = s[1];
418
+ o.count++;
419
+ }
420
+ }
421
+
422
+ /* sorting methods */
423
+ function multisort(table,sortList,cache) {
424
+
425
+
426
+ if(table.config.debug) { var sortTime = new Date(); }
427
+
428
+ var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length;
429
+
430
+ for(var i=0; i < l; i++) {
431
+
432
+ var c = sortList[i][0];
433
+ var order = sortList[i][1];
434
+ var s = (getCachedSortType(table.config.parsers,c) == "text") ? ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ? "sortNumeric" : "sortNumericDesc");
435
+
436
+ var e = "e" + i;
437
+
438
+ dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]); ";
439
+ dynamicExp += "if(" + e + ") { return " + e + "; } ";
440
+ dynamicExp += "else { ";
441
+ }
442
+
443
+ // if value is the same keep orignal order
444
+ var orgOrderCol = cache.normalized[0].length - 1;
445
+ dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
446
+
447
+ for(var i=0; i < l; i++) {
448
+ dynamicExp += "}; ";
449
+ }
450
+
451
+ dynamicExp += "return 0; ";
452
+ dynamicExp += "}; ";
453
+
454
+ eval(dynamicExp);
455
+
456
+ cache.normalized.sort(sortWrapper);
457
+
458
+ if(table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); }
459
+
460
+ return cache;
461
+ };
462
+
463
+ function sortText(a,b) {
464
+ return ((a < b) ? -1 : ((a > b) ? 1 : 0));
465
+ };
466
+
467
+ function sortTextDesc(a,b) {
468
+ return ((b < a) ? -1 : ((b > a) ? 1 : 0));
469
+ };
470
+
471
+ function sortNumeric(a,b) {
472
+ return a-b;
473
+ };
474
+
475
+ function sortNumericDesc(a,b) {
476
+ return b-a;
477
+ };
478
+
479
+ function getCachedSortType(parsers,i) {
480
+ return parsers[i].type;
481
+ };
482
+
483
+ /* public methods */
484
+ this.construct = function(settings) {
485
+
486
+ return this.each(function() {
487
+
488
+ if(!this.tHead || !this.tBodies) return;
489
+
490
+ var $this, $document,$headers, cache, config, shiftDown = 0, sortOrder;
491
+
492
+ this.config = {};
493
+
494
+ config = $.extend(this.config, $.tablesorter.defaults, settings);
495
+
496
+ // store common expression for speed
497
+ $this = $(this);
498
+
499
+ // build headers
500
+ $headers = buildHeaders(this);
501
+
502
+ // try to auto detect column type, and store in tables config
503
+ this.config.parsers = buildParserCache(this,$headers);
504
+
505
+
506
+ // build the cache for the tbody cells
507
+ cache = buildCache(this);
508
+
509
+ // get the css class names, could be done else where.
510
+ var sortCSS = [config.cssDesc,config.cssAsc];
511
+
512
+ // fixate columns if the users supplies the fixedWidth option
513
+ fixColumnWidth(this);
514
+
515
+ // apply event handling to headers
516
+ // this is to big, perhaps break it out?
517
+ $headers.click(function(e) {
518
+
519
+ var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
520
+
521
+ if(!this.sortDisabled && totalRows > 0) {
522
+ // store exp, for speed
523
+ var $cell = $(this);
524
+
525
+ // get current column index
526
+ var i = this.column;
527
+
528
+ // get current column sort order
529
+ this.order = this.count++ % 2;
530
+
531
+ // user only whants to sort on one column
532
+ if(!e[config.sortMultiSortKey]) {
533
+
534
+ // flush the sort list
535
+ config.sortList = [];
536
+
537
+ if(config.sortForce != null) {
538
+ var a = config.sortForce;
539
+ for(var j=0; j < a.length; j++) {
540
+ config.sortList.push(a[j]);
541
+ }
542
+ }
543
+
544
+ // add column to sort list
545
+ config.sortList.push([i,this.order]);
546
+
547
+ // multi column sorting
548
+ } else {
549
+ // the user has clicked on an all ready sortet column.
550
+ if(isValueInArray(i,config.sortList)) {
551
+
552
+ // revers the sorting direction for all tables.
553
+ for(var j=0; j < config.sortList.length; j++) {
554
+ var s = config.sortList[j], o = config.headerList[s[0]];
555
+ if(s[0] == i) {
556
+ o.count = s[1];
557
+ o.count++;
558
+ s[1] = o.count % 2;
559
+ }
560
+ }
561
+ } else {
562
+ // add column to sort list array
563
+ config.sortList.push([i,this.order]);
564
+ }
565
+ };
566
+
567
+ // trigger sortstart
568
+ $this.trigger("sortStart");
569
+
570
+ //set css for headers
571
+ setHeadersCss($this[0],$headers,config.sortList,sortCSS);
572
+
573
+ // javascript threading..
574
+ setTimeout(function() {
575
+ // sort the table and append it to the dom
576
+ appendToTable($this[0],multisort($this[0],config.sortList,cache));
577
+ // trigger sortstart
578
+ $this.trigger("sortEnd");
579
+ }, 0);
580
+ // stop normal event by returning false
581
+ return false;
582
+ }
583
+ // cancel selection
584
+ }).mousedown(function() {
585
+ if(config.cancelSelection) {
586
+ this.onselectstart = function() {return false};
587
+ return false;
588
+ }
589
+ });
590
+
591
+ // apply easy methods that trigger binded events
592
+ $this.bind("update",function() {
593
+
594
+ // rebuild parsers.
595
+ this.config.parsers = buildParserCache(this,$headers);
596
+
597
+ // rebuild the cache map
598
+ cache = buildCache(this);
599
+
600
+ }).bind("sorton",function(e,list) {
601
+
602
+ config.sortList = list;
603
+
604
+ // update and store the sortlist
605
+ var sortList = config.sortList;
606
+
607
+ // update header count index
608
+ updateHeaderSortCount(this,sortList);
609
+
610
+ //set css for headers
611
+ setHeadersCss(this,$headers,sortList,sortCSS);
612
+
613
+ // sort the table and append it to the dom
614
+ appendToTable(this,multisort(this,sortList,cache));
615
+
616
+ }).bind("appendCache",function() {
617
+
618
+ appendToTable(this,cache);
619
+
620
+ }).bind("applyWidgetId",function(e,id) {
621
+
622
+ getWidgetById(id).format(this);
623
+
624
+ }).bind("applyWidgets",function() {
625
+ // apply widgets
626
+ applyWidget(this);
627
+ });
628
+
629
+ if($.meta && ($(this).data() && $(this).data().sortlist)) {
630
+ config.sortList = $(this).data().sortlist;
631
+ }
632
+ // if user has supplied a sort list to constructor.
633
+ if(config.sortList.length > 0) {
634
+ $this.trigger("sorton",[config.sortList]);
635
+ }
636
+
637
+ // apply widgets
638
+ applyWidget(this);
639
+ });
640
+ };
641
+
642
+ this.addParser = function(parser) {
643
+ var l = parsers.length, a = true;
644
+ for(var i=0; i < l; i++) {
645
+ if(parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
646
+ a = false;
647
+ }
648
+ }
649
+ if(a) { parsers.push(parser); };
650
+ };
651
+
652
+ this.addWidget = function(widget) {
653
+ widgets.push(widget);
654
+ };
655
+
656
+ this.formatFloat = function(s) {
657
+
658
+ var i = parseFloat(s);
659
+ return (isNaN(i)) ? 0 : i;
660
+ };
661
+ this.formatInt = function(s) {
662
+ var i = parseInt(s);
663
+ return (isNaN(i)) ? 0 : i;
664
+ };
665
+
666
+ this.clearTableBody = function(table) {
667
+
668
+ if($.browser.msie) {
669
+
670
+ function empty() {
671
+
672
+ while ( this.firstChild ) this.removeChild( this.firstChild );
673
+
674
+ }
675
+
676
+ empty.apply(table.tBodies[0]);
677
+
678
+ } else {
679
+ table.tBodies[0].innerHTML = "";
680
+ }
681
+ };
682
+ }
683
+ });
684
+
685
+ // extend plugin scope
686
+ $.fn.extend({
687
+ tablesorter: $.tablesorter.construct
688
+ });
689
+
690
+ var ts = $.tablesorter;
691
+
692
+ // add default parsers
693
+ ts.addParser({
694
+ id: "text",
695
+ is: function(s) {
696
+ return true;
697
+ },
698
+ format: function(s) {
699
+ return $.trim(s.toLowerCase());
700
+ },
701
+ type: "text"
702
+ });
703
+
704
+ ts.addParser({
705
+ id: "integer",
706
+ is: function(s) {
707
+ return /^\d+$/.test(s);
708
+ },
709
+ format: function(s) {
710
+ return $.tablesorter.formatFloat(s);
711
+ },
712
+ type: "numeric"
713
+ });
714
+
715
+ ts.addParser({
716
+ id: "currency",
717
+ is: function(s) {
718
+ return /^[£$€?.]/.test(s);
719
+ },
720
+ format: function(s) {
721
+ return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));
722
+ },
723
+ type: "numeric"
724
+ });
725
+
726
+ ts.addParser({
727
+ id: "floating",
728
+ is: function(s) {
729
+ return s.match(new RegExp(/^(\+|-)?[0-9]+\.[0-9]+((E|e)(\+|-)?[0-9]+)?$/));
730
+ },
731
+ format: function(s) {
732
+ return $.tablesorter.formatFloat(s.replace(new RegExp(/,/),""));
733
+ },
734
+ type: "numeric"
735
+ });
736
+
737
+ ts.addParser({
738
+ id: "ipAddress",
739
+ is: function(s) {
740
+ return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
741
+ },
742
+ format: function(s) {
743
+ var a = s.split("."), r = "", l = a.length;
744
+ for(var i = 0; i < l; i++) {
745
+ var item = a[i];
746
+ if(item.length == 2) {
747
+ r += "0" + item;
748
+ } else {
749
+ r += item;
750
+ }
751
+ }
752
+ return $.tablesorter.formatFloat(r);
753
+ },
754
+ type: "numeric"
755
+ });
756
+
757
+ ts.addParser({
758
+ id: "url",
759
+ is: function(s) {
760
+ return /^(https?|ftp|file):\/\/$/.test(s);
761
+ },
762
+ format: function(s) {
763
+ return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));
764
+ },
765
+ type: "text"
766
+ });
767
+
768
+ ts.addParser({
769
+ id: "isoDate",
770
+ is: function(s) {
771
+ return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
772
+ },
773
+ format: function(s) {
774
+ return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),"/")).getTime() : "0");
775
+ },
776
+ type: "numeric"
777
+ });
778
+
779
+ ts.addParser({
780
+ id: "percent",
781
+ is: function(s) {
782
+ return /^\d{1,3}%$/.test(s);
783
+ },
784
+ format: function(s) {
785
+ return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));
786
+ },
787
+ type: "numeric"
788
+ });
789
+
790
+ ts.addParser({
791
+ id: "usLongDate",
792
+ is: function(s) {
793
+ return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
794
+ },
795
+ format: function(s) {
796
+ return $.tablesorter.formatFloat(new Date(s).getTime());
797
+ },
798
+ type: "numeric"
799
+ });
800
+
801
+ ts.addParser({
802
+ id: "shortDate",
803
+ is: function(s) {
804
+ return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
805
+ },
806
+ format: function(s,table) {
807
+ var c = table.config;
808
+ s = s.replace(/\-/g,"/");
809
+ if(c.dateFormat == "us") {
810
+ // reformat the string in ISO format
811
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
812
+ } else if(c.dateFormat == "uk") {
813
+ //reformat the string in ISO format
814
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
815
+ } else if(c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
816
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
817
+ }
818
+ return $.tablesorter.formatFloat(new Date(s).getTime());
819
+ },
820
+ type: "numeric"
821
+ });
822
+
823
+ ts.addParser({
824
+ id: "time",
825
+ is: function(s) {
826
+ return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
827
+ },
828
+ format: function(s) {
829
+ return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
830
+ },
831
+ type: "numeric"
832
+ });
833
+
834
+
835
+ ts.addParser({
836
+ id: "metadata",
837
+ is: function(s) {
838
+ return false;
839
+ },
840
+ format: function(s,table,cell) {
841
+ var c = table.config, p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
842
+ return $(cell).data()[p];
843
+ },
844
+ type: "numeric"
845
+ });
846
+
847
+ // add default widgets
848
+ ts.addWidget({
849
+ id: "zebra",
850
+ format: function(table) {
851
+ if(table.config.debug) { var time = new Date(); }
852
+ $("tr:visible",table.tBodies[0])
853
+ .filter(':even')
854
+ .removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0])
855
+ .end().filter(':odd')
856
+ .removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);
857
+ if(table.config.debug) { $.tablesorter.benchmark("Applying Zebra widget", time); }
858
+ }
859
+ });
860
+
861
+ })(jQuery);