├── VERSION.txt ├── .gitignore ├── tests ├── runserver.bat ├── data.js ├── test_site │ ├── ajax_content.html │ ├── img_input.html │ ├── page1.html │ ├── page4.html │ ├── loop1.html │ ├── loop2.html │ ├── jquery_versions.html │ ├── page2.html │ ├── page3.html │ ├── index.html │ ├── duplicates.html │ ├── ready.html │ ├── csv_page.html │ ├── pjscrape_client.js │ ├── jquery-1.3.1.js │ └── jquery-1.4.1.min.js ├── base_config.js ├── test_img_input.js ├── test_ready.js ├── test_loadscript.js ├── test_recursive_nomaxdepth.js ├── test_recursive_maxdepth.js ├── test_recursive_noloop.js ├── test_basic.js ├── test_prescrape.js ├── test_recursive_allowrepeat.js ├── test_multiple_urls.js ├── test_multiple_suites.js ├── test_csv_autofields.js ├── test_jquery_versions.js ├── test_csv.js ├── test_ignore_duplicates.js ├── test_ignore_duplicates_id.js ├── test_csv_autofields_obj.js ├── test_syntax.js └── runtests.py ├── bin └── pjscrape.bat ├── LICENSE.txt ├── README.md ├── client └── pjscrape_client.js ├── lib └── md5.js └── pjscrape.js /VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo -------------------------------------------------------------------------------- /tests/runserver.bat: -------------------------------------------------------------------------------- 1 | python -m SimpleHTTPServer 8888 -------------------------------------------------------------------------------- /tests/data.js: -------------------------------------------------------------------------------- 1 | var myVar = "test1"; 2 | _pjs.myVar = "test2"; -------------------------------------------------------------------------------- /tests/test_site/ajax_content.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/base_config.js: -------------------------------------------------------------------------------- 1 | // configuration for all tests 2 | 3 | pjs.config({ 4 | timeoutInterval: 10, 5 | log: 'none' 6 | }); -------------------------------------------------------------------------------- /tests/test_img_input.js: -------------------------------------------------------------------------------- 1 | 2 | pjs.addSuite({ 3 | url: 'http://localhost:8888/test_site/img_input.html', 4 | scraper: function() { 5 | return $('h1').text(); 6 | } 7 | }); -------------------------------------------------------------------------------- /bin/pjscrape.bat: -------------------------------------------------------------------------------- 1 | :: Example batch file - place the pjscrape\bin directory in your system PATH. 2 | :: You might change pyphantomjs to phantomjs if that's what you're using. 3 | 4 | pyphantomjs ..\pjscrape\pjscrape.js %1 -------------------------------------------------------------------------------- /tests/test_site/img_input.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 | 8 |

Test Page: Weird Image Input Issue

9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/test_site/page1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Page 1

8 | 9 |

Page 1 content

10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/test_site/page4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Page 4

8 | 9 |

Page 4 content

10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/test_site/loop1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Loop 1

8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/test_site/loop2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Loop 2

8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/test_ready.js: -------------------------------------------------------------------------------- 1 | 2 | pjs.addSuite({ 3 | url: 'http://localhost:8888/test_site/ready.html', 4 | ready: function() { 5 | return $('#ajax_content li').length > 0; 6 | }, 7 | scraper: function() { 8 | return _pjs.getText('#ajax_content li'); 9 | } 10 | }); -------------------------------------------------------------------------------- /tests/test_site/jquery_versions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 | 8 |

Test Page: jQuery, different versions

9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/test_loadscript.js: -------------------------------------------------------------------------------- 1 | 2 | pjs.addSuite({ 3 | url: 'http://localhost:8888/test_site/index.html', 4 | loadScript: "data.js", 5 | scrapers: [ 6 | function() { 7 | return myVar; 8 | }, 9 | function() { 10 | return _pjs.myVar; 11 | } 12 | ] 13 | }); -------------------------------------------------------------------------------- /tests/test_recursive_nomaxdepth.js: -------------------------------------------------------------------------------- 1 | 2 | var scraper = function() { 3 | return $('h1').first().text(); 4 | }; 5 | 6 | pjs.addSuite({ 7 | url: 'http://localhost:8888/test_site/index.html', 8 | moreUrls: function() { 9 | return _pjs.getAnchorUrls('li a'); 10 | }, 11 | scraper: scraper 12 | }); -------------------------------------------------------------------------------- /tests/test_site/page2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Page 2

8 | 9 |

Page 2 content

10 | 11 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/test_site/page3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Page 3

8 | 9 |

Page 3 content

10 | 11 |

14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/test_recursive_maxdepth.js: -------------------------------------------------------------------------------- 1 | 2 | var scraper = function() { 3 | return $('h1').first().text(); 4 | }; 5 | 6 | pjs.addSuite({ 7 | url: 'http://localhost:8888/test_site/index.html', 8 | moreUrls: function() { 9 | return _pjs.getAnchorUrls('li a'); 10 | }, 11 | maxDepth: 1, 12 | scraper: scraper 13 | }); -------------------------------------------------------------------------------- /tests/test_recursive_noloop.js: -------------------------------------------------------------------------------- 1 | 2 | var scraper = function() { 3 | return $('h1').first().text(); 4 | }; 5 | 6 | pjs.addSuite({ 7 | url: 'http://localhost:8888/test_site/loop1.html', 8 | moreUrls: function() { 9 | return _pjs.getAnchorUrls('li a'); 10 | }, 11 | maxDepth: 4, 12 | scraper: scraper 13 | }); -------------------------------------------------------------------------------- /tests/test_site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Index

8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test_basic.js: -------------------------------------------------------------------------------- 1 | pjs.addSuite({ 2 | url: 'http://localhost:8888/test_site/index.html', 3 | scrapers: [ 4 | function() { 5 | var items = []; 6 | items.push($('h1').first().text()); 7 | $('li a').each(function() { 8 | items.push($(this).text()); 9 | }); 10 | return items; 11 | } 12 | ] 13 | }); -------------------------------------------------------------------------------- /tests/test_prescrape.js: -------------------------------------------------------------------------------- 1 | 2 | pjs.addSuite({ 3 | url: 'http://localhost:8888/test_site/index.html', 4 | preScrape: function() { 5 | window.myVar = "test1"; 6 | _pjs.myVar = "test2"; 7 | }, 8 | scrapers: [ 9 | function() { 10 | return myVar; 11 | }, 12 | function() { 13 | return _pjs.myVar; 14 | } 15 | ] 16 | }); -------------------------------------------------------------------------------- /tests/test_recursive_allowrepeat.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | allowRepeatUrls: true 3 | }); 4 | 5 | var scraper = function() { 6 | return $('h1').first().text(); 7 | }; 8 | 9 | pjs.addSuite({ 10 | url: 'http://localhost:8888/test_site/loop1.html', 11 | moreUrls: function() { 12 | return _pjs.getAnchorUrls('li a'); 13 | }, 14 | maxDepth: 4, 15 | scraper: scraper 16 | }); -------------------------------------------------------------------------------- /tests/test_multiple_urls.js: -------------------------------------------------------------------------------- 1 | 2 | pjs.addSuite({ 3 | title: 'Basic Scraper Suite', 4 | url: [ 5 | 'http://localhost:8888/test_site/index.html', 6 | 'http://localhost:8888/test_site/page1.html', 7 | 'http://localhost:8888/test_site/page2.html' 8 | ], 9 | scrapers: [ 10 | function() { 11 | return $('h1').first().text(); 12 | } 13 | ] 14 | }); -------------------------------------------------------------------------------- /tests/test_multiple_suites.js: -------------------------------------------------------------------------------- 1 | 2 | var scraper = function() { 3 | return $('h1').first().text(); 4 | }; 5 | 6 | pjs.addSuite({ 7 | url: 'http://localhost:8888/test_site/index.html', 8 | scraper: scraper 9 | }); 10 | pjs.addSuite({ 11 | url: 'http://localhost:8888/test_site/page1.html', 12 | scraper: scraper 13 | }); 14 | 15 | pjs.addSuite({ 16 | url: 'http://localhost:8888/test_site/page2.html', 17 | scraper: scraper 18 | }); -------------------------------------------------------------------------------- /tests/test_site/duplicates.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: Duplicates

8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/test_csv_autofields.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | format: 'csv' 3 | }); 4 | 5 | pjs.addSuite({ 6 | title: 'CSV Scraper Suite', 7 | url: 'http://localhost:8888/test_site/csv_page.html', 8 | scrapers: [ 9 | function() { 10 | return $('tr').map(function() { 11 | return [$('td', this).map(function() { 12 | return $(this).text() 13 | }).toArray()] 14 | }).toArray() 15 | } 16 | ] 17 | }); -------------------------------------------------------------------------------- /tests/test_jquery_versions.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | allowRepeatUrls: true 3 | }); 4 | 5 | pjs.addSuite({ 6 | url: 'http://localhost:8888/test_site/jquery_versions.html', 7 | scraper: function() { 8 | return [$().jquery, _pjs.$().jquery]; 9 | } 10 | }); 11 | 12 | pjs.addSuite({ 13 | noConflict: true, 14 | url: 'http://localhost:8888/test_site/jquery_versions.html', 15 | scraper: function() { 16 | return [$().jquery, _pjs.$().jquery]; 17 | } 18 | }); -------------------------------------------------------------------------------- /tests/test_csv.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | format: 'csv', 3 | csvFields: ['a', 'b', 'c', 'd', 'e'] 4 | }); 5 | 6 | pjs.addSuite({ 7 | title: 'CSV Scraper Suite', 8 | url: 'http://localhost:8888/test_site/csv_page.html', 9 | scrapers: [ 10 | function() { 11 | return $('tr').map(function() { 12 | return [$('td', this).map(function() { 13 | return $(this).text() 14 | }).toArray()] 15 | }).toArray() 16 | } 17 | ] 18 | }); -------------------------------------------------------------------------------- /tests/test_ignore_duplicates.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | ignoreDuplicates: true 3 | }); 4 | 5 | pjs.addSuite({ 6 | url: 'http://localhost:8888/test_site/duplicates.html', 7 | scrapers: [ 8 | function() { 9 | return $('li').map(function() { 10 | var $item = $(this); 11 | return { 12 | a: $('span.a', $item).text(), 13 | b: $('span.b', $item).text() 14 | } 15 | }).toArray(); 16 | } 17 | ] 18 | }); -------------------------------------------------------------------------------- /tests/test_site/ready.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 13 | 14 | 15 |

Test Page: AJAX-loaded readiness

16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/test_ignore_duplicates_id.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | ignoreDuplicates: true 3 | }); 4 | 5 | pjs.addSuite({ 6 | url: 'http://localhost:8888/test_site/duplicates.html', 7 | scrapers: [ 8 | function() { 9 | return $('li').map(function(i) { 10 | var $item = $(this); 11 | return { 12 | a: $('span.a', $item).text(), 13 | id: $('span.b', $item).text(), 14 | i: i // so they won't be identical 15 | } 16 | }).toArray(); 17 | } 18 | ] 19 | }); -------------------------------------------------------------------------------- /tests/test_csv_autofields_obj.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | format: 'csv' 3 | }); 4 | 5 | pjs.addSuite({ 6 | title: 'CSV Scraper Suite', 7 | url: 'http://localhost:8888/test_site/csv_page.html', 8 | scrapers: [ 9 | function() { 10 | return $('tr').map(function() { 11 | var vals = $('td', this).map(function() { 12 | return $(this).text() 13 | }).toArray(), 14 | keys = ['a','b','c','d','e'], 15 | o = {}; 16 | keys.forEach(function(k, i) { 17 | o[k] = vals[i]; 18 | }); 19 | return o; 20 | }).toArray() 21 | } 22 | ] 23 | }); -------------------------------------------------------------------------------- /tests/test_site/csv_page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TEST 5 | 6 | 7 |

Test Page: CSV

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
1stringstring'sa "quoted" string111
2stringstring'sa "quoted" string222
3stringstring'sa "quoted" string333
32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/test_syntax.js: -------------------------------------------------------------------------------- 1 | pjs.config({ 2 | allowRepeatUrls: true 3 | }); 4 | 5 | pjs.addSuite({ 6 | url: 'http://localhost:8888/test_site/index.html', 7 | scraper: function() { 8 | return $('h1').first().text(); 9 | } 10 | }); 11 | 12 | pjs.addSuite({ 13 | urls: 'http://localhost:8888/test_site/index.html', 14 | scrapers: function() { 15 | return $('li a').first().text(); 16 | } 17 | }); 18 | 19 | pjs.addSuite( 20 | { 21 | url: 'http://localhost:8888/test_site/index.html', 22 | scraper: function() { 23 | return $('h1').first().text(); 24 | } 25 | }, 26 | { 27 | urls: 'http://localhost:8888/test_site/index.html', 28 | scrapers: function() { 29 | return $('li').first().text(); 30 | } 31 | } 32 | ); 33 | 34 | pjs.addScraper( 35 | 'http://localhost:8888/test_site/index.html', 36 | function() { 37 | return $('li a').last().text(); 38 | } 39 | ); -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Nick Rabinowitz. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/test_site/pjscrape_client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace 3 | * Namespace for client-side utility functions. 4 | */ 5 | window._pjs = (function($) { 6 | 7 | // munge the location 8 | var loc = window.location, 9 | base = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : ''), 10 | path = loc.pathname.split('/').slice(0,-1).join('/') + '/'; 11 | 12 | /** 13 | * Check whether a URL is local to this site 14 | * @name _pjs.isLocalUrl 15 | * @param {String} url URL to check 16 | */ 17 | function isLocalUrl(url) { 18 | return !url.match(/^(https?:\/\/|mailto:)/) || url.indexOf(base) === 0; 19 | } 20 | 21 | /** 22 | * Convert a local URL to a fully qualified URL (with domain name, etc) 23 | * @name _pjs.toFullUrl 24 | * @param {String} url URL to convert 25 | */ 26 | function toFullUrl(url) { 27 | // non-existent, or fully qualified already 28 | if (!url || url.indexOf(base) === 0 || !isLocalUrl(url)) return url; 29 | // absolute url 30 | if (url[0] == '/') return base + url; 31 | // relative url - browser can figure out .. 32 | return base + path + url; 33 | } 34 | 35 | /** 36 | * Convenience function - find all anchor tags on the page matching the given 37 | * selector (or jQuery object) and return an array of fully qualified URLs 38 | * @name _pjs.getAnchorUrls 39 | * @param {String|jQuery} selector Selector or jQuery object to find anchor elements 40 | * @param {Boolean} includeOffsite Whether to include off-site links 41 | */ 42 | function getAnchorUrls(selector, includeOffsite) { 43 | return $(selector).map(function() { 44 | var href = $(this).attr('href'); 45 | return (href && href.indexOf('#') !== 0 && (includeOffsite || isLocalUrl(href))) ? 46 | toFullUrl(href) : undefined; 47 | }).toArray(); 48 | } 49 | 50 | /** 51 | * Convenience function - find all tags on the page matching the given 52 | * selector (or jQuery object) and return inner text for each 53 | * @name _pjs.getText 54 | * @param {String|jQuery} selector Selector or jQuery object to find elements 55 | */ 56 | function getText(selector) { 57 | return $(selector).map(function() { 58 | return $(this).text(); 59 | }).toArray(); 60 | } 61 | 62 | /** 63 | * Property will be set to true when $(document).ready is called 64 | * @name _pjs.ready 65 | * @type {Boolean} 66 | */ 67 | 68 | return { 69 | isLocalUrl: isLocalUrl, 70 | toFullUrl: toFullUrl, 71 | getAnchorUrls: getAnchorUrls, 72 | getText: getText, 73 | '$': $ 74 | }; 75 | }(_pjs$)); 76 | 77 | // bind to .ready() 78 | _pjs.$(function() { 79 | _pjs.ready = true; 80 | }); 81 | 82 | // console.log('Client initialized'); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Homepage: http://nrabinowitz.github.com/pjscrape/ 2 | 3 | Overview 4 | -------- 5 | 6 | **pjscrape** is a framework for anyone who's ever wanted a command-line tool for web scraping using Javascript and [jQuery](http://jquery.com/). Built to run with [PhantomJS](http://phantomjs.org), it allows you to scrape pages in a fully rendered, Javascript-enabled context from the command line, no browser required. 7 | 8 | Features 9 | -------- 10 | 11 | * Client-side, Javascript-based scraping environment with full access to jQuery functions 12 | * Easy, flexible syntax for setting up one or more scrapers 13 | * Recursive/crawl scraping 14 | * Delay scrape until a "ready" condition occurs 15 | * Load your own scripts on the page before scraping 16 | * Modular architecture for logging and writing/formatting scraped items 17 | * Client-side utilities for common tasks 18 | * Growing set of unit tests 19 | 20 | Usage 21 | -------- 22 | 23 | 1. [Download and install PhantomJS](http://code.google.com/p/phantomjs/downloads/list) or PyPhantomJS, v.1.2. In order to use file-based logging or data writes, you'll need to use PyPhantomJS with the [Save to File plugin](http://dev.umaclan.com/projects/pyphantomjs/wiki/Plugins#Save-to-File) (though I think this feature will be rolled into the PhantomJS core in the next version). 24 | 25 | 2. Make a config file to define your scraper(s). Config files can set global pjscrape settings via `pjs.config()` and add one or more scraper suites via `pjs.addSuite()`. 26 | 27 | 3. A scraper suite defines a set of scraper functions for one or more URLs. More docs on this coming soon, but a sample config file might look like this: 28 | 29 | pjs.addSuite({ 30 | title: 'My Scraper Suite', 31 | // single URL or array 32 | urls: [ 33 | 'http://www.example.com/page1', 34 | 'http://www.example.com/page2' 35 | ], 36 | // one or more functions, evaluated in the client 37 | scrapers: [ 38 | function() { 39 | var items = []; 40 | $('h2').each(function() { 41 | items.push($(this).text()); 42 | }); 43 | return items; 44 | } 45 | ] 46 | }); 47 | 48 | A simple scraper can be added with the `pjs.addScraper()` function: 49 | 50 | pjs.addScraper( 51 | 'http://www.example.com/page.html', 52 | function() { 53 | return $('h1').first().text(); 54 | } 55 | ); 56 | 57 | 4. To run pjscrape from the command line, type: `pyphantomjs /path/to/pjscrape.js my_config_file.js` 58 | 59 | By default, the log output is pretty verbose, and the scraped data is written as JSON to stdout at the end of the scrape. You can configure logging, formatting, and writing data using `pjs.config()`: 60 | 61 | pjs.config({ 62 | // options: 'stdout', 'file' (set in config.logFile) or 'none' 63 | log: 'stdout', 64 | // options: 'json' or 'csv' 65 | format: 'json', 66 | // options: 'stdout' or 'file' (set in config.outFile) 67 | writer: 'file', 68 | outFile: 'scrape_output.json' 69 | }); 70 | 71 | Questions? 72 | ---------- 73 | 74 | Comments and questions welcomed at nick (at) nickrabinowitz (dot) com. 75 | -------------------------------------------------------------------------------- /client/pjscrape_client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @overview 3 | * Client-side helpers for pjscrape 4 | * @name pjscrape_client.js 5 | */ 6 | 7 | /** 8 | * @namespace 9 | * Namespace for client-side utility functions. This will be available 10 | * to scrapers as _pjs or window._pjs. 11 | * @name _pjs 12 | */ 13 | window._pjs = (function($) { 14 | 15 | // munge the location 16 | var loc = window.location, 17 | base = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : ''), 18 | path = loc.pathname.split('/').slice(0,-1).join('/') + '/'; 19 | 20 | /** 21 | * Check whether a URL is local to this site 22 | * @name _pjs.isLocalUrl 23 | * @param {String} url URL to check 24 | * @return {Boolean} Whether this URL is local 25 | */ 26 | function isLocalUrl(url) { 27 | return !url.match(/^(https?:\/\/|mailto:)/) || url.indexOf(base) === 0; 28 | } 29 | 30 | /** 31 | * Convert a local URL to a fully qualified URL (with domain name, etc) 32 | * @name _pjs.toFullUrl 33 | * @param {String} url URL to convert 34 | * @return {String} Fully qualified URL 35 | */ 36 | function toFullUrl(url) { 37 | // non-existent, or fully qualified already 38 | if (!url || url.indexOf(base) === 0 || !isLocalUrl(url)) return url; 39 | // absolute url 40 | if (url[0] == '/') return base + url; 41 | // relative url - browser can figure out .. 42 | return base + path + url; 43 | } 44 | 45 | /** 46 | * Convenience function - find all anchor tags on the page matching the given 47 | * selector (or jQuery object) and return an array of fully qualified URLs 48 | * @name _pjs.getAnchorUrls 49 | * @param {String|jQuery} selector Selector or jQuery object to find anchor elements 50 | * @param {Boolean} includeOffsite Whether to include off-site links 51 | * @return {String[]} Array of fully qualified URLs 52 | */ 53 | function getAnchorUrls(selector, includeOffsite) { 54 | return $(selector).map(function() { 55 | var href = $(this).attr('href'); 56 | return (href && href.indexOf('#') !== 0 && (includeOffsite || isLocalUrl(href))) ? 57 | toFullUrl(href) : undefined; 58 | }).toArray(); 59 | } 60 | 61 | /** 62 | * Convenience function - find all tags on the page matching the given 63 | * selector (or jQuery object) and return inner text for each 64 | * @name _pjs.getText 65 | * @param {String|jQuery} selector Selector or jQuery object to find elements 66 | * @return {String[]} Array of text contents 67 | */ 68 | function getText(selector) { 69 | return $(selector).map(function() { 70 | return $(this).text(); 71 | }).toArray(); 72 | } 73 | 74 | /** 75 | * Flag that will be set to true when $(document).ready is called. 76 | * Generally your code will not need to deal with this - use the "ready" 77 | * configuration parameter instead. 78 | * @type Boolean 79 | * @name _pjs.ready 80 | */ 81 | 82 | return { 83 | isLocalUrl: isLocalUrl, 84 | toFullUrl: toFullUrl, 85 | getAnchorUrls: getAnchorUrls, 86 | getText: getText, 87 | /** 88 | * Reference to jQuery. This is guaranteed to be 89 | * the pjscrape.js version of the jQuery library. 90 | * Scrapers using the 'noConflict' config option 91 | * should use this reference in their code. 92 | * @type jQuery 93 | * @name _pjs.$ 94 | */ 95 | '$': $ 96 | }; 97 | }(_pjs$)); 98 | 99 | // bind to .ready() 100 | window._pjs.$(function() { 101 | window._pjs.ready = true; 102 | }); 103 | 104 | // for reasons I can't fathom, omitting this line throws an 105 | // error on pages with . Go figure. 106 | console.log('Client-side code initialized'); -------------------------------------------------------------------------------- /tests/runtests.py: -------------------------------------------------------------------------------- 1 | import SimpleHTTPServer 2 | import SocketServer 3 | import threading 4 | import unittest 5 | import subprocess 6 | import os 7 | 8 | PORT = 8888 9 | COMMAND_BASE = ["pyphantomjs", os.path.join('..', 'pjscrape.js'), 'base_config.js'] 10 | 11 | def getPjscrapeOutput(script_name): 12 | return subprocess.check_output(COMMAND_BASE + [script_name]).strip() 13 | 14 | class QuietHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 15 | def log_message(self, format, *args): 16 | pass 17 | 18 | 19 | class TestPjscrape(unittest.TestCase): 20 | 21 | @classmethod 22 | def setUpClass(cls): 23 | # set up server 24 | cls.httpd = SocketServer.TCPServer(("", PORT), QuietHTTPRequestHandler) 25 | httpd_thread = threading.Thread(target=cls.httpd.serve_forever) 26 | httpd_thread.setDaemon(True) 27 | httpd_thread.start() 28 | 29 | @classmethod 30 | def tearDownClass(cls): 31 | # tear down server 32 | cls.httpd.shutdown() 33 | 34 | def test_basic(self): 35 | out = getPjscrapeOutput('test_basic.js') 36 | self.assertEqual(out, '["Test Page: Index","Page 1","Page 2"]') 37 | 38 | def test_multiple_urls(self): 39 | out = getPjscrapeOutput('test_multiple_urls.js') 40 | self.assertEqual(out, '["Test Page: Index","Test Page: Page 1","Test Page: Page 2"]') 41 | 42 | def test_multiple_suites(self): 43 | out = getPjscrapeOutput('test_multiple_suites.js') 44 | self.assertEqual(out, '["Test Page: Index","Test Page: Page 1","Test Page: Page 2"]') 45 | 46 | def test_recursive_maxdepth(self): 47 | out = getPjscrapeOutput('test_recursive_maxdepth.js') 48 | self.assertEqual(out, '["Test Page: Index","Test Page: Page 1","Test Page: Page 2"]') 49 | 50 | def test_recursive_nomaxdepth(self): 51 | out = getPjscrapeOutput('test_recursive_nomaxdepth.js') 52 | self.assertEqual(out, '["Test Page: Index","Test Page: Page 1","Test Page: Page 2","Test Page: Page 3","Test Page: Page 4"]') 53 | 54 | def test_recursive_noloop(self): 55 | out = getPjscrapeOutput('test_recursive_noloop.js') 56 | self.assertEqual(out, '["Test Page: Loop 1","Test Page: Loop 2"]') 57 | 58 | def test_recursive_allowrepeat(self): 59 | out = getPjscrapeOutput('test_recursive_allowrepeat.js') 60 | self.assertEqual(out, '["Test Page: Loop 1","Test Page: Loop 2","Test Page: Loop 1","Test Page: Loop 2","Test Page: Loop 1"]') 61 | 62 | def test_csv(self): 63 | out = getPjscrapeOutput('test_csv.js') 64 | # not sure why stdout uses \r\r\n, but that seems to be the case 65 | self.assertEqual(out, '"a","b","c","d","e"\r\r\n"1","string","string\'s","a ""quoted"" string","111"\r\r\n"2","string","string\'s","a ""quoted"" string","222"\r\r\n"3","string","string\'s","a ""quoted"" string","333"') 66 | 67 | def test_csv_autofields(self): 68 | out = getPjscrapeOutput('test_csv_autofields.js') 69 | self.assertEqual(out, '"Column 1","Column 2","Column 3","Column 4","Column 5"\r\r\n"1","string","string\'s","a ""quoted"" string","111"\r\r\n"2","string","string\'s","a ""quoted"" string","222"\r\r\n"3","string","string\'s","a ""quoted"" string","333"') 70 | 71 | def test_csv_autofields_obj(self): 72 | out = getPjscrapeOutput('test_csv_autofields_obj.js') 73 | self.assertEqual(out, '"a","b","c","d","e"\r\r\n"1","string","string\'s","a ""quoted"" string","111"\r\r\n"2","string","string\'s","a ""quoted"" string","222"\r\r\n"3","string","string\'s","a ""quoted"" string","333"') 74 | 75 | def test_prescrape(self): 76 | out = getPjscrapeOutput('test_prescrape.js') 77 | self.assertEqual(out, '["test1","test2"]') 78 | 79 | def test_loadscript(self): 80 | out = getPjscrapeOutput('test_loadscript.js') 81 | self.assertEqual(out, '["test1","test2"]') 82 | 83 | def test_syntax(self): 84 | out = getPjscrapeOutput('test_syntax.js') 85 | self.assertEqual(out, '["Test Page: Index","Page 1","Test Page: Index","Page 1","Page 2"]') 86 | 87 | def test_ready(self): 88 | out = getPjscrapeOutput('test_ready.js') 89 | self.assertEqual(out, '["Content 1","Content 2"]') 90 | 91 | def test_jquery_versions(self): 92 | out = getPjscrapeOutput('test_jquery_versions.js') 93 | self.assertEqual(out, '["1.6.1","1.6.1","1.4.1","1.6.1"]') 94 | 95 | def test_ignore_duplicates(self): 96 | out = getPjscrapeOutput('test_ignore_duplicates.js') 97 | self.assertEqual(out, '[{"a":"test","b":"1"},{"a":"test","b":"2"}]') 98 | 99 | def test_ignore_duplicates_id(self): 100 | out = getPjscrapeOutput('test_ignore_duplicates_id.js') 101 | # keys in alphabetical order due to http://code.google.com/p/phantomjs/issues/detail?id=170 102 | self.assertEqual(out, '[{"a":"test","i":0,"id":"1"},{"a":"test","i":1,"id":"2"}]') 103 | 104 | def test_img_input(self): 105 | out = getPjscrapeOutput('test_img_input.js') 106 | self.assertEqual(out, '["Test Page: Weird Image Input Issue"]') 107 | 108 | if __name__ == '__main__': 109 | # run tests 110 | suite = unittest.TestLoader().loadTestsFromTestCase(TestPjscrape) 111 | unittest.TextTestRunner(verbosity=2).run(suite) -------------------------------------------------------------------------------- /lib/md5.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Javascript MD5 implementation by Joseph Myers 3 | * http://www.myersdaily.org/joseph/javascript/md5-text.html 4 | */ 5 | var md5 = (function() { 6 | function md5cycle(x, k) { 7 | var a = x[0], b = x[1], c = x[2], d = x[3]; 8 | 9 | a = ff(a, b, c, d, k[0], 7, -680876936); 10 | d = ff(d, a, b, c, k[1], 12, -389564586); 11 | c = ff(c, d, a, b, k[2], 17, 606105819); 12 | b = ff(b, c, d, a, k[3], 22, -1044525330); 13 | a = ff(a, b, c, d, k[4], 7, -176418897); 14 | d = ff(d, a, b, c, k[5], 12, 1200080426); 15 | c = ff(c, d, a, b, k[6], 17, -1473231341); 16 | b = ff(b, c, d, a, k[7], 22, -45705983); 17 | a = ff(a, b, c, d, k[8], 7, 1770035416); 18 | d = ff(d, a, b, c, k[9], 12, -1958414417); 19 | c = ff(c, d, a, b, k[10], 17, -42063); 20 | b = ff(b, c, d, a, k[11], 22, -1990404162); 21 | a = ff(a, b, c, d, k[12], 7, 1804603682); 22 | d = ff(d, a, b, c, k[13], 12, -40341101); 23 | c = ff(c, d, a, b, k[14], 17, -1502002290); 24 | b = ff(b, c, d, a, k[15], 22, 1236535329); 25 | 26 | a = gg(a, b, c, d, k[1], 5, -165796510); 27 | d = gg(d, a, b, c, k[6], 9, -1069501632); 28 | c = gg(c, d, a, b, k[11], 14, 643717713); 29 | b = gg(b, c, d, a, k[0], 20, -373897302); 30 | a = gg(a, b, c, d, k[5], 5, -701558691); 31 | d = gg(d, a, b, c, k[10], 9, 38016083); 32 | c = gg(c, d, a, b, k[15], 14, -660478335); 33 | b = gg(b, c, d, a, k[4], 20, -405537848); 34 | a = gg(a, b, c, d, k[9], 5, 568446438); 35 | d = gg(d, a, b, c, k[14], 9, -1019803690); 36 | c = gg(c, d, a, b, k[3], 14, -187363961); 37 | b = gg(b, c, d, a, k[8], 20, 1163531501); 38 | a = gg(a, b, c, d, k[13], 5, -1444681467); 39 | d = gg(d, a, b, c, k[2], 9, -51403784); 40 | c = gg(c, d, a, b, k[7], 14, 1735328473); 41 | b = gg(b, c, d, a, k[12], 20, -1926607734); 42 | 43 | a = hh(a, b, c, d, k[5], 4, -378558); 44 | d = hh(d, a, b, c, k[8], 11, -2022574463); 45 | c = hh(c, d, a, b, k[11], 16, 1839030562); 46 | b = hh(b, c, d, a, k[14], 23, -35309556); 47 | a = hh(a, b, c, d, k[1], 4, -1530992060); 48 | d = hh(d, a, b, c, k[4], 11, 1272893353); 49 | c = hh(c, d, a, b, k[7], 16, -155497632); 50 | b = hh(b, c, d, a, k[10], 23, -1094730640); 51 | a = hh(a, b, c, d, k[13], 4, 681279174); 52 | d = hh(d, a, b, c, k[0], 11, -358537222); 53 | c = hh(c, d, a, b, k[3], 16, -722521979); 54 | b = hh(b, c, d, a, k[6], 23, 76029189); 55 | a = hh(a, b, c, d, k[9], 4, -640364487); 56 | d = hh(d, a, b, c, k[12], 11, -421815835); 57 | c = hh(c, d, a, b, k[15], 16, 530742520); 58 | b = hh(b, c, d, a, k[2], 23, -995338651); 59 | 60 | a = ii(a, b, c, d, k[0], 6, -198630844); 61 | d = ii(d, a, b, c, k[7], 10, 1126891415); 62 | c = ii(c, d, a, b, k[14], 15, -1416354905); 63 | b = ii(b, c, d, a, k[5], 21, -57434055); 64 | a = ii(a, b, c, d, k[12], 6, 1700485571); 65 | d = ii(d, a, b, c, k[3], 10, -1894986606); 66 | c = ii(c, d, a, b, k[10], 15, -1051523); 67 | b = ii(b, c, d, a, k[1], 21, -2054922799); 68 | a = ii(a, b, c, d, k[8], 6, 1873313359); 69 | d = ii(d, a, b, c, k[15], 10, -30611744); 70 | c = ii(c, d, a, b, k[6], 15, -1560198380); 71 | b = ii(b, c, d, a, k[13], 21, 1309151649); 72 | a = ii(a, b, c, d, k[4], 6, -145523070); 73 | d = ii(d, a, b, c, k[11], 10, -1120210379); 74 | c = ii(c, d, a, b, k[2], 15, 718787259); 75 | b = ii(b, c, d, a, k[9], 21, -343485551); 76 | 77 | x[0] = add32(a, x[0]); 78 | x[1] = add32(b, x[1]); 79 | x[2] = add32(c, x[2]); 80 | x[3] = add32(d, x[3]); 81 | 82 | } 83 | 84 | function cmn(q, a, b, x, s, t) { 85 | a = add32(add32(a, q), add32(x, t)); 86 | return add32((a << s) | (a >>> (32 - s)), b); 87 | } 88 | 89 | function ff(a, b, c, d, x, s, t) { 90 | return cmn((b & c) | ((~b) & d), a, b, x, s, t); 91 | } 92 | 93 | function gg(a, b, c, d, x, s, t) { 94 | return cmn((b & d) | (c & (~d)), a, b, x, s, t); 95 | } 96 | 97 | function hh(a, b, c, d, x, s, t) { 98 | return cmn(b ^ c ^ d, a, b, x, s, t); 99 | } 100 | 101 | function ii(a, b, c, d, x, s, t) { 102 | return cmn(c ^ (b | (~d)), a, b, x, s, t); 103 | } 104 | 105 | function md51(s) { 106 | txt = ''; 107 | var n = s.length, 108 | state = [1732584193, -271733879, -1732584194, 271733878], i; 109 | for (i=64; i<=s.length; i+=64) { 110 | md5cycle(state, md5blk(s.substring(i-64, i))); 111 | } 112 | s = s.substring(i-64); 113 | var tail = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; 114 | for (i=0; i>2] |= s.charCodeAt(i) << ((i%4) << 3); 116 | tail[i>>2] |= 0x80 << ((i%4) << 3); 117 | if (i > 55) { 118 | md5cycle(state, tail); 119 | for (i=0; i<16; i++) tail[i] = 0; 120 | } 121 | tail[14] = n*8; 122 | md5cycle(state, tail); 123 | return state; 124 | } 125 | 126 | /* there needs to be support for Unicode here, 127 | * unless we pretend that we can redefine the MD-5 128 | * algorithm for multi-byte characters (perhaps 129 | * by adding every four 16-bit characters and 130 | * shortening the sum to 32 bits). Otherwise 131 | * I suggest performing MD-5 as if every character 132 | * was two bytes--e.g., 0040 0025 = @%--but then 133 | * how will an ordinary MD-5 sum be matched? 134 | * There is no way to standardize text to something 135 | * like UTF-8 before transformation; speed cost is 136 | * utterly prohibitive. The JavaScript standard 137 | * itself needs to look at this: it should start 138 | * providing access to strings as preformed UTF-8 139 | * 8-bit unsigned value arrays. 140 | */ 141 | function md5blk(s) { /* I figured global was faster. */ 142 | var md5blks = [], i; /* Andy King said do it this way. */ 143 | for (i=0; i<64; i+=4) { 144 | md5blks[i>>2] = s.charCodeAt(i) 145 | + (s.charCodeAt(i+1) << 8) 146 | + (s.charCodeAt(i+2) << 16) 147 | + (s.charCodeAt(i+3) << 24); 148 | } 149 | return md5blks; 150 | } 151 | 152 | var hex_chr = '0123456789abcdef'.split(''); 153 | 154 | function rhex(n) 155 | { 156 | var s='', j=0; 157 | for(; j<4; j++) 158 | s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] 159 | + hex_chr[(n >> (j * 8)) & 0x0F]; 160 | return s; 161 | } 162 | 163 | function hex(x) { 164 | for (var i=0; i> 16) + (y >> 16) + (lsw >> 16); 187 | return (msw << 16) | (lsw & 0xFFFF); 188 | } 189 | } 190 | 191 | return md5; 192 | }()); 193 | -------------------------------------------------------------------------------- /pjscrape.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * pjscrape Copyright 2011 Nick Rabinowitz. 3 | * Licensed under the MIT License (see LICENSE.txt) 4 | */ 5 | 6 | /** 7 | * @overview 8 | *

Scraping harness for PhantomJS. Requires PhantomJS or PyPhantomJS v.1.2 9 | * (with saveToFile() support, if you want to use the file writer or logger).

10 | * 11 | * @name pjscrape.js 12 | * @author Nick Rabinowitz (www.nickrabinowitz.com) 13 | * @version 0.1 14 | */ 15 | 16 | /* 17 | TODO: 18 | - Get the HTTP response code for the loaded page and fail if it's 40x or 50x 19 | - tests for client utilities? 20 | - docs for the Logger, Formatter, Writer, and HashFunction interfaces? 21 | - test for file writes 22 | - Some sort of test harness (as a bookmarklet, maybe?) to do client-side scraper dev 23 | (could call in a file that's hosted on github, or just do the whole thing in 24 | a bookmarklet - not much code I think) - I'm thinking either pop-up window or just 25 | code injection + console. pjs.addSuite or pjs.addScraper would run immediately, returning 26 | scraper results. pjs.config() would be moot, I think. 27 | - Better docs, obviously. 28 | */ 29 | 30 | phantom.injectJs('lib/md5.js'); 31 | 32 | function fail(msg) { 33 | console.log('FATAL ERROR: ' + msg); 34 | phantom.exit(); 35 | }; 36 | 37 | /** 38 | * @namespace 39 | * Root namespace for PhantomJS-side code 40 | * @name pjs 41 | */ 42 | var pjs = (function(){ 43 | var config = { 44 | timeoutInterval: 100, 45 | timeoutLimit: 3000, 46 | log: 'stdout', 47 | writer: 'stdout', 48 | format: 'json', 49 | logFile: 'pjscrape_log.txt', 50 | outFile: 'pjscrape_out.txt' 51 | }; 52 | 53 | var suites = []; 54 | 55 | 56 | // utils 57 | function isFunction(f) { 58 | return typeof f === 'function'; 59 | } 60 | function funcify(f) { 61 | return isFunction(f) ? f : function() { return f }; 62 | } 63 | function isArray(a) { 64 | return Array.isArray(a); 65 | } 66 | function arrify(a) { 67 | return isArray(a) ? a : a ? [a] : []; 68 | } 69 | function getKeys(o) { 70 | var keys = []; 71 | for (var key in o) keys.push(key); 72 | return keys; 73 | } 74 | function extend(obj) { 75 | Array.prototype.slice.call(arguments, 1).forEach(function(source) { 76 | for (var prop in source) { 77 | if (source[prop] !== void 0) obj[prop] = source[prop]; 78 | } 79 | }); 80 | return obj; 81 | }; 82 | 83 | /** 84 | * @name pjs.loggers 85 | * @namespace 86 | * Logger namespace. You can add new loggers here; new logger classes 87 | * should probably extend pjs.loggers.base and redefine the 88 | * log method. 89 | * @example 90 | // create a new logger 91 | pjs.loggers.myLogger = function() { 92 | return new pjs.loggers.base(function(msg) { 93 | // do some special logging stuff 94 | }); 95 | }; 96 | // tell pjscrape to use your logger 97 | pjs.config({ 98 | log: 'myLogger' 99 | }); 100 | */ 101 | var loggers = { 102 | 103 | /** 104 | * @name pjs.loggers.base 105 | * @class Abstract base logger class 106 | * @private 107 | */ 108 | base: function(logf) { 109 | var log = this; 110 | log.log = logf || function(msg) { console.log(msg) }; 111 | log.msg = function(msg) { log.log('* ' + msg) }; 112 | log.alert = function(msg) { log.log('! ' + msg) }; 113 | log.error = function(msg) { log.log('ERROR: ' + msg) }; 114 | }, 115 | 116 | /** 117 | * Log to config.logFile 118 | * @name pjs.loggers.file 119 | * @type Logger 120 | */ 121 | file: function() { 122 | return new loggers.base(function(msg) { 123 | phantom.saveToFile(msg + "\n", config.logFile, 'a'); 124 | }); 125 | }, 126 | 127 | /** 128 | * Disable logging 129 | * @name pjs.loggers.none 130 | * @type Logger 131 | */ 132 | none: function() { 133 | return new loggers.base(function() {}); 134 | } 135 | }; 136 | 137 | /** 138 | * Log to STDOUT 139 | * @name pjs.loggers.stdout 140 | * @type Logger 141 | */ 142 | loggers.stdout = loggers.base; 143 | 144 | /** 145 | * @name pjs.formatters 146 | * @namespace 147 | * Formatter namespace. You can add new formatters here; new formatter classes 148 | * should have the properties start, end, and 149 | * delimiter, and the method format(item). You might 150 | * save some time by inheriting from formatters.raw or formatters.json. 151 | * @example 152 | // create a new formatter 153 | pjs.formatters.pipe = function() { 154 | var f = new pjs.formatters.raw(); 155 | f.delimiter = '|'; 156 | return f; 157 | }; 158 | // tell pjscrape to use your formatter 159 | pjs.config({ 160 | format: 'pipe' 161 | }); 162 | */ 163 | var formatters = { 164 | 165 | /** 166 | * Raw formatter - just uses toString() 167 | * @name pjs.formatters.raw 168 | * @type Formatter 169 | */ 170 | raw: function() { 171 | var f = this; 172 | f.start = f.end = f.delimiter = ''; 173 | f.format = function(item) { 174 | return item.toString(); 175 | }; 176 | }, 177 | 178 | /** 179 | * Format output as a JSON array 180 | * @name pjs.formatters.json 181 | * @type Formatter 182 | */ 183 | json: function() { 184 | var f = this; 185 | f.start = '['; 186 | f.end = ']'; 187 | f.delimiter = ','; 188 | f.format = function(item) { 189 | return JSON.stringify(item); 190 | }; 191 | }, 192 | 193 | /** 194 | * CSV formatter - takes arrays or objects, fields defined by 195 | * config.csvFields or auto-generated based on first item 196 | * @name pjs.formatters.csv 197 | * @type Formatter 198 | */ 199 | csv: function() { 200 | var f = this, 201 | fields = config.csvFields, 202 | makeRow = function(a) { return a.map(JSON.stringify).join(',') }; 203 | 204 | f.delimiter = "\r\n"; 205 | f.start = fields ? makeRow(fields) + f.delimiter : ''; 206 | f.end = ''; 207 | f.format = function(item) { 208 | if (item && typeof item == 'object') { 209 | var out = ''; 210 | // make fields if not defined 211 | if (!fields) { 212 | if (isArray(item)) { 213 | fields = []; 214 | for (var i=0; iWriter namespace. You can add new writers here; new writer classes 248 | * should probably extend pjs.writers.base and redefine the 249 | * write method.

250 | *

Items returned by scrapers will be added to the output via 251 | * Writer.add(item), which can take any type of object. If 252 | * an array is provided, multipled items will be added. 253 | * @example 254 | // create a new writer 255 | pjs.writer.myWriter = function(log) { 256 | var w = new pjs.writers.base(log); 257 | w.write = function(s) { 258 | // write s to some special place 259 | } 260 | return w; 261 | }; 262 | // tell pjscrape to use your writer 263 | pjs.config({ 264 | writer: 'myWriter' 265 | }); 266 | */ 267 | var writers = { 268 | /** 269 | * @name pjs.writers.base 270 | * @class Abstract base writer class 271 | * @private 272 | */ 273 | base: function(log) { 274 | var w = this, 275 | count = 0, 276 | items = [], 277 | batchSize = config.batchSize, 278 | format = config.format || 'json', 279 | firstWrite = true, 280 | lastWrite = false; 281 | 282 | // init formatter 283 | var formatter = new formatters[format](); 284 | 285 | // write output 286 | var writeBatch = function(batch) { 287 | log.msg('Writing ' + batch.length + ' items'); 288 | w.write( 289 | (firstWrite ? formatter.start : formatter.delimiter) + 290 | batch.map(formatter.format).join(formatter.delimiter) + 291 | (lastWrite ? formatter.end : '') 292 | ); 293 | firstWrite = false; 294 | }; 295 | 296 | /** 297 | * Add an item to be written to output 298 | * @name pjs.writers.base#add 299 | * @function 300 | * @param {Object|String|Array} Item to add 301 | */ 302 | w.add = function(i) { 303 | // add to items 304 | if (i) { 305 | i = arrify(i); 306 | items = items.concat(i); 307 | count += i.length; 308 | // write if necessary 309 | if (batchSize && items.length > batchSize) { 310 | writeBatch(items.splice(0, batchSize)); 311 | } 312 | } 313 | }; 314 | 315 | /** 316 | * Finish up writing output 317 | * @name pjs.writers.base#finish 318 | * @function 319 | */ 320 | w.finish = function() { 321 | lastWrite = true; 322 | writeBatch(items); 323 | }; 324 | 325 | /** 326 | * Get the number of items written to output 327 | * @name pjs.writers.base#count 328 | * @function 329 | * @return {Number} Number of items written 330 | */ 331 | w.count = function() { 332 | return count; 333 | }; 334 | 335 | /** 336 | * Write a string to output 337 | * @name pjs.writers.base#write 338 | * @function 339 | * @param {String} s String to write 340 | */ 341 | w.write = function(s) { 342 | console.log(s); 343 | }; 344 | }, 345 | 346 | /** 347 | * Writes output to config.outFile 348 | * @name pjs.writers.file 349 | * @type Writer 350 | */ 351 | file: function(log) { 352 | var w = new writers.base(log); 353 | // clear file 354 | phantom.saveToFile('', config.outFile, 'w'); 355 | // write method 356 | w.write = function(s) { 357 | phantom.saveToFile(s, config.outFile, 'a'); 358 | }; 359 | return w; 360 | }, 361 | 362 | /** 363 | * Writes output to one file per item. Items may be provided 364 | * in the format { filename: "file.txt", content: "string" } 365 | * if you'd like to specify the filename in the scraper. Otherwise, 366 | * files are written to config.outFile with serial numbering. 367 | * @name pjs.writers.itemfile 368 | * @type Writer 369 | */ 370 | itemfile: function(log) { 371 | var w = this, 372 | count = 0, 373 | format = config.format || 'raw', 374 | formatter = new formatters[format](); 375 | 376 | w.add = function(items) { 377 | // add to items 378 | if (items) { 379 | items = arrify(items); 380 | // write to separate files 381 | items.forEach(function(item) { 382 | var filename; 383 | // support per-item filename syntax 384 | if (item.filename && item.content) { 385 | filename = item.filename; 386 | item = item.content; 387 | } 388 | // otherwise add a serial number to config.outFile 389 | else { 390 | var fileparts = config.outFile.split('.'), 391 | ext = fileparts.pop(); 392 | filename = fileparts.join('.') + '-' + (count++) + '.' + ext; 393 | } 394 | phantom.saveToFile(formatter.format(item), filename, 'w'); 395 | count++; 396 | }); 397 | } 398 | }; 399 | 400 | w.finish = function() {}; 401 | 402 | w.count = function() { 403 | return count; 404 | }; 405 | }, 406 | }; 407 | 408 | /** 409 | * Write output to STDOUT 410 | * @name pjs.writers.stdout 411 | * @type Writer 412 | */ 413 | writers.stdout = writers.base; 414 | 415 | /** 416 | * @name pjs.hashFunctions 417 | * @namespace 418 | * Hash function namespace. You can add new hash functions here; hash functions 419 | * should take an item and return a unique (or unique-enough) string. 420 | * @example 421 | // create a new hash function 422 | pjs.hashFunctions.myHash = function(item) { 423 | return item.mySpecialUID; 424 | }; 425 | // tell pjscrape to ignore dupes 426 | pjs.config({ 427 | ignoreDuplicates: true 428 | }); 429 | // tell pjscrape to use your hash function 430 | pjs.addScraper({ 431 | hashFunction: 'myHash', 432 | // etc 433 | }); 434 | */ 435 | var hashFunctions = { 436 | /** UID hash - assumes item.id; falls back on md5 437 | * @name pjs.hashFunctions.id 438 | * @type HashFunction 439 | */ 440 | id: function(item) { 441 | return ('id' in item) ? item.id : hashFunctions.md5(item); 442 | }, 443 | /** md5 hash - collisions are possible 444 | * @name pjs.hashFunctions.md5 445 | * @type HashFunction 446 | */ 447 | md5: function(item) { 448 | return md5(JSON.stringify(item)); 449 | } 450 | }; 451 | 452 | 453 | // suite runner 454 | var runner = (function() { 455 | var visited = {}, 456 | itemHashes = {}, 457 | log, 458 | writer; 459 | 460 | /** 461 | * @class 462 | * Singleton: Manage multiple suites 463 | * @private 464 | */ 465 | var SuiteManager = new function() { 466 | var mgr = this, 467 | complete, 468 | suiteq = []; 469 | 470 | // create a single WebPage object for reuse 471 | var page = new WebPage(); 472 | // set up console output 473 | page.onConsoleMessage = function(msg, line, id) { 474 | id = id || 'injected code'; 475 | if (line) msg += ' (' + id + ' line ' + line + ')'; 476 | log.msg('CLIENT: ' + msg); 477 | }; 478 | page.onAlert = function(msg) { log.alert('CLIENT: ' + msg) }; 479 | 480 | mgr.getPage = function() { 481 | return page; 482 | }; 483 | 484 | // set the completion callback 485 | mgr.setComplete = function(cb) { 486 | complete = cb; 487 | }; 488 | 489 | // add a ScraperSuite 490 | mgr.add = function(suite) { 491 | suiteq.push(suite); 492 | }; 493 | 494 | // run the next ScraperSuite in the queue 495 | mgr.runNext = function() { 496 | var suite = suiteq.shift(); 497 | if (suite) suite.run(); 498 | else complete(); 499 | }; 500 | }(); 501 | 502 | /** 503 | * @class 504 | * Scraper suite class - represents a set of urls to scrape 505 | * @private 506 | * @param {String} title Title for verbose output 507 | * @param {String[]} urls Urls to scrape 508 | * @param {Object} opts Configuration object 509 | */ 510 | var ScraperSuite = function(title, urls, opts) { 511 | var s = this, 512 | truef = function() { return true }; 513 | // set up options 514 | s.title = title; 515 | s.urls = urls; 516 | s.opts = extend({ 517 | ready: function() { return _pjs.ready; }, 518 | scrapable: truef, 519 | preScrape: truef, 520 | hashFunction: hashFunctions.id 521 | }, opts); 522 | // deal with potential arrays and syntax variants 523 | s.opts.loadScript = arrify(opts.loadScripts || opts.loadScript); 524 | s.opts.scrapers = arrify(opts.scrapers || opts.scraper); 525 | // set up completion callback 526 | s.complete = function() { 527 | log.msg(s.title + " complete"); 528 | SuiteManager.runNext(); 529 | }; 530 | s.depth = 0; 531 | } 532 | 533 | ScraperSuite.prototype = { 534 | 535 | /** 536 | * Add an item, checking for duplicates as necessary 537 | * @param {Object|Array} items Item(s) to add 538 | * @private 539 | */ 540 | addItem: function(items) { 541 | var s = this; 542 | if (items && config.ignoreDuplicates) { 543 | // ensure array 544 | items = arrify(items); 545 | items = items.filter(function(item) { 546 | var hash = s.opts.hashFunction(item); 547 | if (!itemHashes[hash]) { 548 | // hash miss - new item 549 | itemHashes[hash] = true; 550 | return true; 551 | } else { 552 | // hash hit - likely duplicate 553 | // Could do a second-layer check against the actual object, 554 | // but that requires retaining items in memory - skip for now 555 | return false; 556 | } 557 | }); 558 | } 559 | writer.add(items); 560 | }, 561 | 562 | /** 563 | * Run the suite, scraping each url 564 | * @private 565 | */ 566 | run: function() { 567 | var s = this, 568 | scrapers = s.opts.scrapers, 569 | i = 0; 570 | log.msg(s.title + " starting"); 571 | // set up scraper functions 572 | var scrapePage = function(page) { 573 | if (page.evaluate(s.opts.scrapable)) { 574 | // load script(s) if necessary 575 | if (s.opts.loadScript) { 576 | s.opts.loadScript.forEach(function(script) { 577 | page.injectJs(script); 578 | }) 579 | } 580 | // run prescrape 581 | page.evaluate(s.opts.preScrape); 582 | // run each scraper and send any results to writer 583 | if (scrapers && scrapers.length) { 584 | scrapers.forEach(function(scraper) { 585 | s.addItem(page.evaluate(scraper)) 586 | }); 587 | } 588 | } 589 | }, 590 | // get base URL for avoiding repeat visits and recursion loops 591 | baseUrl = function(url) { 592 | return s.opts.newHashNewPage ? url.split('#')[0] : url; 593 | }, 594 | // completion callback 595 | complete = function(page) { 596 | // recurse if necessary 597 | if (page && s.opts.moreUrls) { 598 | // look for more urls on this page 599 | var moreUrls = page.evaluate(s.opts.moreUrls); 600 | if (moreUrls && (!s.opts.maxDepth || s.depth < s.opts.maxDepth)) { 601 | if (moreUrls.length) { 602 | log.msg('Found ' + moreUrls.length + ' additional urls to scrape'); 603 | // make a new sub-suite 604 | var ss = new ScraperSuite(s.title + '-sub' + i++, moreUrls, s.opts); 605 | ss.depth = s.depth + 1; 606 | SuiteManager.add(ss); 607 | } 608 | } 609 | } 610 | runNext(); 611 | }; 612 | // run each 613 | var runCounter = 0 614 | function runNext() { 615 | if (runCounter < s.urls.length) { 616 | url = baseUrl(s.urls[runCounter++]); 617 | // avoid repeat visits 618 | if (!config.allowRepeatUrls && url in visited) { 619 | runNext(); 620 | } else { 621 | // scrape this url 622 | s.scrape(url, scrapePage, complete); 623 | } 624 | } else { 625 | s.complete(); 626 | } 627 | } 628 | runNext(); 629 | }, 630 | 631 | /** 632 | * Scrape a single page. 633 | * @param {String} url Url of page to scrape 634 | * @param {Function} scrapePage Function to scrape page with 635 | * @param {Function} complete Callback function to run when complete 636 | * @private 637 | */ 638 | scrape: function(url, scrapePage, complete) { 639 | var opts = this.opts, 640 | page = SuiteManager.getPage(); 641 | log.msg('Opening ' + url); 642 | // run the scrape 643 | page.open(url, function(status) { 644 | if (status === "success") { 645 | // mark as visited 646 | visited[url] = true; 647 | log.msg('Scraping ' + url); 648 | // load jQuery 649 | page.injectJs('client/jquery.js'); 650 | page.evaluate(function() { 651 | window._pjs$ = jQuery.noConflict(true); 652 | }); 653 | // load pjscrape client-side code 654 | page.injectJs('client/pjscrape_client.js'); 655 | // reset the global jQuery vars 656 | if (!opts.noConflict) { 657 | page.evaluate(function() { 658 | window.$ = window.jQuery = window._pjs$; 659 | }); 660 | } 661 | // run scraper(s) 662 | if (page.evaluate(opts.ready)) { 663 | // run immediately 664 | scrapePage(page); 665 | complete(page); 666 | } else { 667 | // poll ready() until timeout or success 668 | var elapsed = 0, 669 | timeoutId = window.setInterval(function() { 670 | if (page.evaluate(opts.ready) || elapsed > config.timeoutLimit) { 671 | if (elapsed > config.timeoutLimit) { 672 | log.alert('Ready timeout after ' + ~~(elapsed / 1000) + ' seconds'); 673 | } 674 | scrapePage(page); 675 | window.clearInterval(timeoutId); 676 | complete(page); 677 | } else { 678 | elapsed += config.timeoutInterval; 679 | } 680 | }, config.timeoutInterval); 681 | } 682 | } else { 683 | log.error('Page did not load: ' + url); 684 | complete(false); 685 | } 686 | }); 687 | } 688 | }; 689 | 690 | /** 691 | * Run the set of configured scraper suites. 692 | * @name pjs.init 693 | */ 694 | function init() { 695 | // check requirements 696 | if (!suites.length) fail('No suites configured'); 697 | if (!(config.log in loggers)) fail('Could not find logger: "' + config.log + '"'); 698 | if (!(config.writer in writers)) fail('Could not find writer "' + config.writer + '"'); 699 | 700 | // init logger 701 | log = new loggers[config.log](); 702 | // init writer 703 | writer = new writers[config.writer](log); 704 | 705 | // init suite manager 706 | SuiteManager.setComplete(function() { 707 | // scrape complete 708 | writer.finish(); 709 | log.msg('Saved ' + writer.count() + ' items'); 710 | phantom.exit(); 711 | }); 712 | // make all suites 713 | suites.forEach(function(suite, i) { 714 | SuiteManager.add(new ScraperSuite( 715 | suite.title || "Suite " + i, 716 | arrify(suite.url || suite.urls), 717 | suite 718 | )); 719 | }); 720 | // start the suite manager 721 | SuiteManager.runNext(); 722 | } 723 | 724 | return { 725 | init: init 726 | } 727 | }()); 728 | 729 | // expose namespaces and API functions 730 | return { 731 | loggers: loggers, 732 | formatters: formatters, 733 | writers: writers, 734 | hashFunctions: hashFunctions, 735 | init: runner.init, 736 | 737 | /** 738 | * Set one or more config variables, applying to all suites 739 | * @name pjs.config 740 | * @param {String|Object} key Either a key to set or an object with 741 | * multiple values to set 742 | * @param {mixed} [val] Value to set if using config(key, val) syntax 743 | */ 744 | config: function(key, val) { 745 | if (!key) { 746 | return config; 747 | } else if (typeof key == 'object') { 748 | extend(config, key); 749 | } else if (val) { 750 | config[key] = val; 751 | } 752 | }, 753 | 754 | /** 755 | * Add one or more scraper suites to be run. 756 | * @name pjs.addSuite 757 | * @param {Object} suite Scraper suite configuration object 758 | * @param {Object} [...] More suite configuration objects 759 | */ 760 | addSuite: function() { 761 | suites = Array.prototype.concat.apply(suites, arguments); 762 | }, 763 | 764 | /** 765 | * Shorthand function to add a simple scraper suite. 766 | * @name pjs.addScraper 767 | * @param {String|String[]} url URL or array of URLs to scrape 768 | * @param {Function|Function[]} Scraper function or array of scraper functions 769 | */ 770 | addScraper: function(url, scraper) { 771 | suites.push({url:url, scraper:scraper}); 772 | } 773 | }; 774 | }()); 775 | 776 | 777 | // make sure we have a config file 778 | if (!phantom.args.length) { 779 | // die 780 | console.log('Usage: pjscrape.js ...'); 781 | phantom.exit(); 782 | } else { 783 | // load the config file(s) 784 | phantom.args.forEach(function(configFile) { 785 | if (!phantom.injectJs(configFile)) { 786 | fail('Config file not found: ' + configFile); 787 | } 788 | }); 789 | } 790 | // start the scrape 791 | pjs.init(); -------------------------------------------------------------------------------- /tests/test_site/jquery-1.3.1.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery JavaScript Library v1.3.1 3 | * http://jquery.com/ 4 | * 5 | * Copyright (c) 2009 John Resig 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://docs.jquery.com/License 8 | * 9 | * Date: 2009-01-21 20:42:16 -0500 (Wed, 21 Jan 2009) 10 | * Revision: 6158 11 | */ 12 | (function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.makeArray(E))},selector:"",jquery:"1.3.1",size:function(){return this.length},get:function(E){return E===g?o.makeArray(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,find:function(E){if(this.length===1&&!/,/.test(E)){var G=this.pushStack([],"find",E);G.length=0;o.find(E,this[0],G);return G}else{var F=o.map(this,function(H){return o.find(E,H)});return this.pushStack(/[^+>] [^+>]/.test(E)?o.unique(F):F,"find",E)}},clone:function(F){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.cloneNode(true),H=document.createElement("div");H.appendChild(I);return o.clean([H.innerHTML])[0]}else{return this.cloneNode(true)}});var G=E.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(F===true){this.find("*").andSelf().each(function(I){if(this.nodeType==3){return}var H=o.data(this,"events");for(var K in H){for(var J in H[K]){o.event.add(G[I],K,H[K][J],H[K][J].data)}}})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var F=o.expr.match.POS.test(E)?o(E):null;return this.map(function(){var G=this;while(G&&G.ownerDocument){if(F?F.index(G)>-1:o(G).is(E)){return G}G=G.parentNode}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML:null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(K,N,M){if(this[0]){var J=(this[0].ownerDocument||this[0]).createDocumentFragment(),G=o.clean(K,(this[0].ownerDocument||this[0]),J),I=J.firstChild,E=this.length>1?J.cloneNode(true):J;if(I){for(var H=0,F=this.length;H0?E.cloneNode(true):J)}}if(G){o.each(G,z)}}return this;function L(O,P){return N&&o.nodeName(O,"table")&&o.nodeName(P,"tr")?(O.getElementsByTagName("tbody")[0]||O.appendChild(O.ownerDocument.createElement("tbody"))):O}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(G,E,I){if(E=="width"||E=="height"){var K,F={position:"absolute",visibility:"hidden",display:"block"},J=E=="width"?["Left","Right"]:["Top","Bottom"];function H(){K=E=="width"?G.offsetWidth:G.offsetHeight;var M=0,L=0;o.each(J,function(){M+=parseFloat(o.curCSS(G,"padding"+this,true))||0;L+=parseFloat(o.curCSS(G,"border"+this+"Width",true))||0});K-=Math.round(M+L)}if(o(G).is(":visible")){H()}else{o.swap(G,F,H)}return Math.max(0,K)}return o.curCSS(G,E,I)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,R){if(typeof R==="number"){R+=""}if(!R){return}if(typeof R==="string"){R=R.replace(/(<(\w+)[^>]*?)\/>/g,function(T,U,S){return S.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?T:U+">"});var O=o.trim(R).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div

","
"]||[0,"",""];L.innerHTML=Q[1]+R+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var N=!O.indexOf(""&&O.indexOf("=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(R)){L.insertBefore(K.createTextNode(R.match(/^\s*/)[0]),L.firstChild)}R=o.makeArray(L.childNodes)}if(R.nodeType){G.push(R)}else{G=o.merge(G,R)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E*",this).remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); 13 | /* 14 | * Sizzle CSS Selector Engine - v0.9.3 15 | * Copyright 2009, The Dojo Foundation 16 | * Released under the MIT, BSD, and GPL Licenses. 17 | * More information: http://sizzlejs.com/ 18 | */ 19 | (function(){var Q=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]+['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,K=0,G=Object.prototype.toString;var F=function(X,T,aa,ab){aa=aa||[];T=T||document;if(T.nodeType!==1&&T.nodeType!==9){return[]}if(!X||typeof X!=="string"){return aa}var Y=[],V,ae,ah,S,ac,U,W=true;Q.lastIndex=0;while((V=Q.exec(X))!==null){Y.push(V[1]);if(V[2]){U=RegExp.rightContext;break}}if(Y.length>1&&L.exec(X)){if(Y.length===2&&H.relative[Y[0]]){ae=I(Y[0]+Y[1],T)}else{ae=H.relative[Y[0]]?[T]:F(Y.shift(),T);while(Y.length){X=Y.shift();if(H.relative[X]){X+=Y.shift()}ae=I(X,ae)}}}else{var ad=ab?{expr:Y.pop(),set:E(ab)}:F.find(Y.pop(),Y.length===1&&T.parentNode?T.parentNode:T,P(T));ae=F.filter(ad.expr,ad.set);if(Y.length>0){ah=E(ae)}else{W=false}while(Y.length){var ag=Y.pop(),af=ag;if(!H.relative[ag]){ag=""}else{af=Y.pop()}if(af==null){af=T}H.relative[ag](ah,af,P(T))}}if(!ah){ah=ae}if(!ah){throw"Syntax error, unrecognized expression: "+(ag||X)}if(G.call(ah)==="[object Array]"){if(!W){aa.push.apply(aa,ah)}else{if(T.nodeType===1){for(var Z=0;ah[Z]!=null;Z++){if(ah[Z]&&(ah[Z]===true||ah[Z].nodeType===1&&J(T,ah[Z]))){aa.push(ae[Z])}}}else{for(var Z=0;ah[Z]!=null;Z++){if(ah[Z]&&ah[Z].nodeType===1){aa.push(ae[Z])}}}}}else{E(ah,aa)}if(U){F(U,T,aa,ab)}return aa};F.matches=function(S,T){return F(S,null,null,T)};F.find=function(Z,S,aa){var Y,W;if(!Z){return[]}for(var V=0,U=H.order.length;V":function(X,T,Y){if(typeof T==="string"&&!/\W/.test(T)){T=Y?T:T.toUpperCase();for(var U=0,S=X.length;U=0){if(!U){S.push(X)}}else{if(U){T[W]=false}}}}return false},ID:function(S){return S[1].replace(/\\/g,"")},TAG:function(T,S){for(var U=0;S[U]===false;U++){}return S[U]&&P(S[U])?T[1]:T[1].toUpperCase()},CHILD:function(S){if(S[1]=="nth"){var T=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(S[2]=="even"&&"2n"||S[2]=="odd"&&"2n+1"||!/\D/.test(S[2])&&"0n+"+S[2]||S[2]);S[2]=(T[1]+(T[2]||1))-0;S[3]=T[3]-0}S[0]="done"+(K++);return S},ATTR:function(T){var S=T[1].replace(/\\/g,"");if(H.attrMap[S]){T[1]=H.attrMap[S]}if(T[2]==="~="){T[4]=" "+T[4]+" "}return T},PSEUDO:function(W,T,U,S,X){if(W[1]==="not"){if(W[3].match(Q).length>1){W[3]=F(W[3],null,null,T)}else{var V=F.filter(W[3],T,U,true^X);if(!U){S.push.apply(S,V)}return false}}else{if(H.match.POS.test(W[0])){return true}}return W},POS:function(S){S.unshift(true);return S}},filters:{enabled:function(S){return S.disabled===false&&S.type!=="hidden"},disabled:function(S){return S.disabled===true},checked:function(S){return S.checked===true},selected:function(S){S.parentNode.selectedIndex;return S.selected===true},parent:function(S){return !!S.firstChild},empty:function(S){return !S.firstChild},has:function(U,T,S){return !!F(S[3],U).length},header:function(S){return/h\d/i.test(S.nodeName)},text:function(S){return"text"===S.type},radio:function(S){return"radio"===S.type},checkbox:function(S){return"checkbox"===S.type},file:function(S){return"file"===S.type},password:function(S){return"password"===S.type},submit:function(S){return"submit"===S.type},image:function(S){return"image"===S.type},reset:function(S){return"reset"===S.type},button:function(S){return"button"===S.type||S.nodeName.toUpperCase()==="BUTTON"},input:function(S){return/input|select|textarea|button/i.test(S.nodeName)}},setFilters:{first:function(T,S){return S===0},last:function(U,T,S,V){return T===V.length-1},even:function(T,S){return S%2===0},odd:function(T,S){return S%2===1},lt:function(U,T,S){return TS[3]-0},nth:function(U,T,S){return S[3]-0==T},eq:function(U,T,S){return S[3]-0==T}},filter:{CHILD:function(S,V){var Y=V[1],Z=S.parentNode;var X=V[0];if(Z&&(!Z[X]||!S.nodeIndex)){var W=1;for(var T=Z.firstChild;T;T=T.nextSibling){if(T.nodeType==1){T.nodeIndex=W++}}Z[X]=W-1}if(Y=="first"){return S.nodeIndex==1}else{if(Y=="last"){return S.nodeIndex==Z[X]}else{if(Y=="only"){return Z[X]==1}else{if(Y=="nth"){var ab=false,U=V[2],aa=V[3];if(U==1&&aa==0){return true}if(U==0){if(S.nodeIndex==aa){ab=true}}else{if((S.nodeIndex-aa)%U==0&&(S.nodeIndex-aa)/U>=0){ab=true}}return ab}}}}},PSEUDO:function(Y,U,V,Z){var T=U[1],W=H.filters[T];if(W){return W(Y,V,U,Z)}else{if(T==="contains"){return(Y.textContent||Y.innerText||"").indexOf(U[3])>=0}else{if(T==="not"){var X=U[3];for(var V=0,S=X.length;V=0:V==="~="?(" "+X+" ").indexOf(T)>=0:!U[4]?S:V==="!="?X!=T:V==="^="?X.indexOf(T)===0:V==="$="?X.substr(X.length-T.length)===T:V==="|="?X===T||X.substr(0,T.length+1)===T+"-":false},POS:function(W,T,U,X){var S=T[2],V=H.setFilters[S];if(V){return V(W,U,T,X)}}}};var L=H.match.POS;for(var N in H.match){H.match[N]=RegExp(H.match[N].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(T,S){T=Array.prototype.slice.call(T);if(S){S.push.apply(S,T);return S}return T};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(M){E=function(W,V){var T=V||[];if(G.call(W)==="[object Array]"){Array.prototype.push.apply(T,W)}else{if(typeof W.length==="number"){for(var U=0,S=W.length;U";var S=document.documentElement;S.insertBefore(T,S.firstChild);if(!!document.getElementById(U)){H.find.ID=function(W,X,Y){if(typeof X.getElementById!=="undefined"&&!Y){var V=X.getElementById(W[1]);return V?V.id===W[1]||typeof V.getAttributeNode!=="undefined"&&V.getAttributeNode("id").nodeValue===W[1]?[V]:g:[]}};H.filter.ID=function(X,V){var W=typeof X.getAttributeNode!=="undefined"&&X.getAttributeNode("id");return X.nodeType===1&&W&&W.nodeValue===V}}S.removeChild(T)})();(function(){var S=document.createElement("div");S.appendChild(document.createComment(""));if(S.getElementsByTagName("*").length>0){H.find.TAG=function(T,X){var W=X.getElementsByTagName(T[1]);if(T[1]==="*"){var V=[];for(var U=0;W[U];U++){if(W[U].nodeType===1){V.push(W[U])}}W=V}return W}}S.innerHTML="";if(S.firstChild&&S.firstChild.getAttribute("href")!=="#"){H.attrHandle.href=function(T){return T.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var S=F,T=document.createElement("div");T.innerHTML="

";if(T.querySelectorAll&&T.querySelectorAll(".TEST").length===0){return}F=function(X,W,U,V){W=W||document;if(!V&&W.nodeType===9&&!P(W)){try{return E(W.querySelectorAll(X),U)}catch(Y){}}return S(X,W,U,V)};F.find=S.find;F.filter=S.filter;F.selectors=S.selectors;F.matches=S.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){H.order.splice(1,0,"CLASS");H.find.CLASS=function(S,T){return T.getElementsByClassName(S[1])}}function O(T,Z,Y,ac,aa,ab){for(var W=0,U=ac.length;W0){W=S;break}}}S=S[T]}ab[V]=W}}}var J=document.compareDocumentPosition?function(T,S){return T.compareDocumentPosition(S)&16}:function(T,S){return T!==S&&(T.contains?T.contains(S):true)};var P=function(S){return S.nodeType===9&&S.documentElement.nodeName!=="HTML"||!!S.ownerDocument&&P(S.ownerDocument)};var I=function(S,Z){var V=[],W="",X,U=Z.nodeType?[Z]:Z;while((X=H.match.PSEUDO.exec(S))){W+=X[0];S=S.replace(H.match.PSEUDO,"")}S=H.relative[S]?S+"*":S;for(var Y=0,T=U.length;Y=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}this[H].style.display=o.data(this[H],"olddisplay",K)}}return this}},hide:function(H,I){if(H){return this.animate(t("hide",3),H,I)}else{for(var G=0,F=this.length;G=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)==1){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(H,F){var E=H?"Left":"Top",G=H?"Right":"Bottom";o.fn["inner"+F]=function(){return this[F.toLowerCase()]()+j(this,"padding"+E)+j(this,"padding"+G)};o.fn["outer"+F]=function(J){return this["inner"+F]()+j(this,"border"+E+"Width")+j(this,"border"+G+"Width")+(J?j(this,"margin"+E)+j(this,"margin"+G):0)};var I=F.toLowerCase();o.fn[I]=function(J){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+F]||document.body["client"+F]:this[0]==document?Math.max(document.documentElement["client"+F],document.body["scroll"+F],document.documentElement["scroll"+F],document.body["offset"+F],document.documentElement["offset"+F]):J===g?(this.length?o.css(this[0],I):null):this.css(I,typeof J==="string"?J:J+"px")}})})(); -------------------------------------------------------------------------------- /tests/test_site/jquery-1.4.1.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v1.4.1 3 | * http://jquery.com/ 4 | * 5 | * Copyright 2010, John Resig 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://jquery.org/license 8 | * 9 | * Includes Sizzle.js 10 | * http://sizzlejs.com/ 11 | * Copyright 2010, The Dojo Foundation 12 | * Released under the MIT, BSD, and GPL Licenses. 13 | * 14 | * Date: Mon Jan 25 19:43:33 2010 -0500 15 | */ 16 | (function(z,v){function la(){if(!c.isReady){try{r.documentElement.doScroll("left")}catch(a){setTimeout(la,1);return}c.ready()}}function Ma(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,i){var j=a.length;if(typeof b==="object"){for(var n in b)X(a,n,b[n],f,e,d);return a}if(d!==v){f=!i&&f&&c.isFunction(d);for(n=0;n-1){i=j.data;i.beforeFilter&&i.beforeFilter[a.type]&&!i.beforeFilter[a.type](a)||f.push(j.selector)}else delete x[o]}i=c(a.target).closest(f, 18 | a.currentTarget);m=0;for(s=i.length;m)[^>]*$|^#([\w-]+)$/,Qa=/^.[^:#\[\.,]*$/,Ra=/\S/,Sa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Ta=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,O=navigator.userAgent, 21 | va=false,P=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,Q=Array.prototype.slice,wa=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(typeof a==="string")if((d=Pa.exec(a))&&(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:r;if(a=Ta.exec(a))if(c.isPlainObject(b)){a=[r.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=ra([d[1]], 22 | [f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}}else{if(b=r.getElementById(d[2])){if(b.id!==d[2])return S.find(a);this.length=1;this[0]=b}this.context=r;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=r;a=r.getElementsByTagName(a)}else return!b||b.jquery?(b||S).find(a):c(b).find(a);else if(c.isFunction(a))return S.ready(a);if(a.selector!==v){this.selector=a.selector;this.context=a.context}return c.isArray(a)?this.setArray(a):c.makeArray(a, 23 | this)},selector:"",jquery:"1.4.1",length:0,size:function(){return this.length},toArray:function(){return Q.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){a=c(a||null);a.prevObject=this;a.context=this.context;if(b==="find")a.selector=this.selector+(this.selector?" ":"")+d;else if(b)a.selector=this.selector+"."+b+"("+d+")";return a},setArray:function(a){this.length=0;ba.apply(this,a);return this},each:function(a,b){return c.each(this, 24 | a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(r,c);else P&&P.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(Q.apply(this,arguments),"slice",Q.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice}; 25 | c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,i,j,n;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a";var e=d.getElementsByTagName("*"),i=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!i)){c.support= 34 | {leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(i.getAttribute("style")),hrefNormalized:i.getAttribute("href")==="/a",opacity:/^0.55$/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:r.createElement("select").appendChild(r.createElement("option")).selected,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null}; 35 | b.type="text/javascript";try{b.appendChild(r.createTextNode("window."+f+"=1;"))}catch(j){}a.insertBefore(b,a.firstChild);if(z[f]){c.support.scriptEval=true;delete z[f]}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function n(){c.support.noCloneEvent=false;d.detachEvent("onclick",n)});d.cloneNode(true).fireEvent("onclick")}d=r.createElement("div");d.innerHTML="";a=r.createDocumentFragment();a.appendChild(d.firstChild); 36 | c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var n=r.createElement("div");n.style.width=n.style.paddingLeft="1px";r.body.appendChild(n);c.boxModel=c.support.boxModel=n.offsetWidth===2;r.body.removeChild(n).style.display="none"});a=function(n){var o=r.createElement("div");n="on"+n;var m=n in o;if(!m){o.setAttribute(n,"return;");m=typeof o[n]==="function"}return m};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=i=null}})();c.props= 37 | {"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ua=0,xa={},Va={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var f=a[G],e=c.cache;if(!b&&!f)return null;f||(f=++Ua);if(typeof b==="object"){a[G]=f;e=e[f]=c.extend(true, 38 | {},b)}else e=e[f]?e[f]:typeof d==="undefined"?Va:(e[f]={});if(d!==v){a[G]=f;e[b]=d}return typeof b==="string"?e[b]:e}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==z?xa:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{try{delete a[G]}catch(i){a.removeAttribute&&a.removeAttribute(G)}delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this, 39 | a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===v){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===v&&this.length)f=c.data(this[0],a);return f===v&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d); 40 | return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===v)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]|| 41 | a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var ya=/[\n\t]/g,ca=/\s+/,Wa=/\r/g,Xa=/href|src|style/,Ya=/(button|input)/i,Za=/(button|input|object|select|textarea)/i,$a=/^(a|area)$/i,za=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(o){var m= 42 | c(this);m.addClass(a.call(this,o,m.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===v){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value|| 45 | {}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var i=b?d:0;for(d=b?d+1:e.length;i=0;else if(c.nodeName(this,"select")){var x=c.makeArray(s);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),x)>=0});if(!x.length)this.selectedIndex=-1}else this.value=s}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return v;if(f&&b in c.attrFn)return c(a)[b](d); 47 | f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==v;b=f&&c.props[b]||b;if(a.nodeType===1){var i=Xa.test(b);if(b in a&&f&&!i){if(e){b==="type"&&Ya.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:Za.test(a.nodeName)||$a.test(a.nodeName)&&a.href?0:v;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText= 48 | ""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&i?a.getAttribute(b,2):a.getAttribute(b);return a===null?v:a}return c.style(a,b,d)}});var ab=function(a){return a.replace(/[^\w\s\.\|`]/g,function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==z&&!a.frameElement)a=z;if(!d.guid)d.guid=c.guid++;if(f!==v){d=c.proxy(d);d.data=f}var e=c.data(a,"events")||c.data(a,"events",{}),i=c.data(a,"handle"),j;if(!i){j= 49 | function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(j.elem,arguments):v};i=c.data(a,"handle",j)}if(i){i.elem=a;b=b.split(/\s+/);for(var n,o=0;n=b[o++];){var m=n.split(".");n=m.shift();if(o>1){d=c.proxy(d);if(f!==v)d.data=f}d.type=m.slice(0).sort().join(".");var s=e[n],x=this.special[n]||{};if(!s){s=e[n]={};if(!x.setup||x.setup.call(a,f,m,d)===false)if(a.addEventListener)a.addEventListener(n,i,false);else a.attachEvent&&a.attachEvent("on"+n,i)}if(x.add)if((m=x.add.call(a, 50 | d,f,m,s))&&c.isFunction(m)){m.guid=m.guid||d.guid;m.data=m.data||d.data;m.type=m.type||d.type;d=m}s[d.guid]=d;this.global[n]=true}a=null}}},global:{},remove:function(a,b,d){if(!(a.nodeType===3||a.nodeType===8)){var f=c.data(a,"events"),e,i,j;if(f){if(b===v||typeof b==="string"&&b.charAt(0)===".")for(i in f)this.remove(a,i+(b||""));else{if(b.type){d=b.handler;b=b.type}b=b.split(/\s+/);for(var n=0;i=b[n++];){var o=i.split(".");i=o.shift();var m=!o.length,s=c.map(o.slice(0).sort(),ab);s=new RegExp("(^|\\.)"+ 51 | s.join("\\.(?:.*\\.)?")+"(\\.|$)");var x=this.special[i]||{};if(f[i]){if(d){j=f[i][d.guid];delete f[i][d.guid]}else for(var A in f[i])if(m||s.test(f[i][A].type))delete f[i][A];x.remove&&x.remove.call(a,o,j);for(e in f[i])break;if(!e){if(!x.teardown||x.teardown.call(a,o)===false)if(a.removeEventListener)a.removeEventListener(i,c.data(a,"handle"),false);else a.detachEvent&&a.detachEvent("on"+i,c.data(a,"handle"));e=null;delete f[i]}}}}for(e in f)break;if(!e){if(A=c.data(a,"handle"))A.elem=null;c.removeData(a, 52 | "events");c.removeData(a,"handle")}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();this.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return v;a.result=v;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d, 53 | b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(i){}if(!a.isPropagationStopped()&&f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){d=a.target;var j;if(!(c.nodeName(d,"a")&&e==="click")&&!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()])){try{if(d[e]){if(j=d["on"+e])d["on"+e]=null;this.triggered=true;d[e]()}}catch(n){}if(j)d["on"+e]=j;this.triggered=false}}},handle:function(a){var b, 54 | d;a=arguments[0]=c.event.fix(a||z.event);a.currentTarget=this;d=a.type.split(".");a.type=d.shift();b=!d.length&&!a.exclusive;var f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)");d=(c.data(this,"events")||{})[a.type];for(var e in d){var i=d[e];if(b||f.test(i.type)){a.handler=i;a.data=i.data;i=i.apply(this,arguments);if(i!==v){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), 55 | fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||r;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=r.documentElement;d=r.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop|| 56 | d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==v)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a,b){c.extend(a,b||{});a.guid+=b.selector+b.live;b.liveProxy=a;c.event.add(this,b.live,na,b)},remove:function(a){if(a.length){var b= 57 | 0,d=new RegExp("(^|\\.)"+a[0]+"(\\.|$)");c.each(c.data(this,"events").live||{},function(){d.test(this.type)&&b++});b<1&&c.event.remove(this,a[0],na)}},special:{}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true}; 58 | c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y};var Aa=function(a){for(var b= 59 | a.relatedTarget;b&&b!==this;)try{b=b.parentNode}catch(d){break}if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}},Ba=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ba:Aa,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ba:Aa)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(a,b,d){if(this.nodeName.toLowerCase()!== 60 | "form"){c.event.add(this,"click.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="submit"||i==="image")&&c(e).closest("form").length)return ma("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit."+d.guid,function(f){var e=f.target,i=e.type;if((i==="text"||i==="password")&&c(e).closest("form").length&&f.keyCode===13)return ma("submit",this,arguments)})}else return false},remove:function(a,b){c.event.remove(this,"click.specialSubmit"+(b?"."+b.guid:""));c.event.remove(this, 61 | "keypress.specialSubmit"+(b?"."+b.guid:""))}};if(!c.support.changeBubbles){var da=/textarea|input|select/i;function Ca(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d}function ea(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Ca(d);if(a.type!=="focusout"|| 62 | d.type!=="radio")c.data(d,"_change_data",e);if(!(f===v||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}}c.event.special.change={filters:{focusout:ea,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return ea.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return ea.call(this,a)},beforeactivate:function(a){a= 63 | a.target;a.nodeName.toLowerCase()==="input"&&a.type==="radio"&&c.data(a,"_change_data",Ca(a))}},setup:function(a,b,d){for(var f in T)c.event.add(this,f+".specialChange."+d.guid,T[f]);return da.test(this.nodeName)},remove:function(a,b){for(var d in T)c.event.remove(this,d+".specialChange"+(b?"."+b.guid:""),T[d]);return da.test(this.nodeName)}};var T=c.event.special.change.filters}r.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this, 64 | f)}c.event.special[b]={setup:function(){this.addEventListener(a,d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var i in d)this[b](i,f,d[i],e);return this}if(c.isFunction(f)){e=f;f=v}var j=b==="one"?c.proxy(e,function(n){c(this).unbind(n,j);return e.apply(this,arguments)}):e;return d==="unload"&&b!=="one"?this.one(d,f,e):this.each(function(){c.event.add(this,d,j,f)})}});c.fn.extend({unbind:function(a, 65 | b){if(typeof a==="object"&&!a.preventDefault){for(var d in a)this.unbind(d,a[d]);return this}return this.each(function(){c.event.remove(this,a,b)})},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},toggle:function(a){for(var b=arguments,d=1;d0){y=t;break}}t=t[g]}l[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,i=Object.prototype.toString,j=false,n=true;[0,0].sort(function(){n=false;return 0});var o=function(g,h,k,l){k=k||[];var q=h=h||r;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g|| 70 | typeof g!=="string")return k;for(var p=[],u,t,y,R,H=true,M=w(h),I=g;(f.exec(""),u=f.exec(I))!==null;){I=u[3];p.push(u[1]);if(u[2]){R=u[3];break}}if(p.length>1&&s.exec(g))if(p.length===2&&m.relative[p[0]])t=fa(p[0]+p[1],h);else for(t=m.relative[p[0]]?[h]:o(p.shift(),h);p.length;){g=p.shift();if(m.relative[g])g+=p.shift();t=fa(g,t)}else{if(!l&&p.length>1&&h.nodeType===9&&!M&&m.match.ID.test(p[0])&&!m.match.ID.test(p[p.length-1])){u=o.find(p.shift(),h,M);h=u.expr?o.filter(u.expr,u.set)[0]:u.set[0]}if(h){u= 71 | l?{expr:p.pop(),set:A(l)}:o.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=u.expr?o.filter(u.expr,u.set):u.set;if(p.length>0)y=A(t);else H=false;for(;p.length;){var D=p.pop();u=D;if(m.relative[D])u=p.pop();else D="";if(u==null)u=h;m.relative[D](y,u,M)}}else y=[]}y||(y=t);y||o.error(D||g);if(i.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))k.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&& 72 | y[g].nodeType===1&&k.push(t[g]);else k.push.apply(k,y);else A(y,k);if(R){o(R,q,k,l);o.uniqueSort(k)}return k};o.uniqueSort=function(g){if(C){j=n;g.sort(C);if(j)for(var h=1;h":function(g,h){var k=typeof h==="string";if(k&&!/\W/.test(h)){h=h.toLowerCase();for(var l=0,q=g.length;l=0))k||l.push(u);else if(k)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&& 79 | "2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,k,l,q,p){h=g[1].replace(/\\/g,"");if(!p&&m.attrMap[h])g[1]=m.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,k,l,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=o(g[3],null,null,h);else{g=o.filter(g[3],h,k,true^q);k||l.push.apply(l,g);return false}else if(m.match.POS.test(g[0])||m.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true); 80 | return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,k){return!!o(k[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"=== 81 | g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,h){return h===0},last:function(g,h,k,l){return h===l.length-1},even:function(g,h){return h%2=== 82 | 0},odd:function(g,h){return h%2===1},lt:function(g,h,k){return hk[3]-0},nth:function(g,h,k){return k[3]-0===h},eq:function(g,h,k){return k[3]-0===h}},filter:{PSEUDO:function(g,h,k,l){var q=h[1],p=m.filters[q];if(p)return p(g,k,h,l);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=h[3];k=0;for(l=h.length;k= 84 | 0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var k=h[1];g=m.attrHandle[k]?m.attrHandle[k](g):g[k]!=null?g[k]:g.getAttribute(k);k=g+"";var l=h[2];h=h[4];return g==null?l==="!=":l==="="?k===h:l==="*="?k.indexOf(h)>=0:l==="~="?(" "+k+" ").indexOf(h)>=0:!h?k&&g!==false:l==="!="?k!==h:l==="^="? 85 | k.indexOf(h)===0:l==="$="?k.substr(k.length-h.length)===h:l==="|="?k===h||k.substr(0,h.length+1)===h+"-":false},POS:function(g,h,k,l){var q=m.setFilters[h[2]];if(q)return q(g,k,h,l)}}},s=m.match.POS;for(var x in m.match){m.match[x]=new RegExp(m.match[x].source+/(?![^\[]*\])(?![^\(]*\))/.source);m.leftMatch[x]=new RegExp(/(^(?:.|\r|\n)*?)/.source+m.match[x].source.replace(/\\(\d+)/g,function(g,h){return"\\"+(h-0+1)}))}var A=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g}; 86 | try{Array.prototype.slice.call(r.documentElement.childNodes,0)}catch(B){A=function(g,h){h=h||[];if(i.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var k=0,l=g.length;k";var k=r.documentElement;k.insertBefore(g,k.firstChild);if(r.getElementById(h)){m.find.ID=function(l,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(l[1]))?q.id===l[1]||typeof q.getAttributeNode!=="undefined"&&q.getAttributeNode("id").nodeValue===l[1]?[q]:v:[]};m.filter.ID=function(l,q){var p=typeof l.getAttributeNode!=="undefined"&&l.getAttributeNode("id"); 89 | return l.nodeType===1&&p&&p.nodeValue===q}}k.removeChild(g);k=g=null})();(function(){var g=r.createElement("div");g.appendChild(r.createComment(""));if(g.getElementsByTagName("*").length>0)m.find.TAG=function(h,k){k=k.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var l=0;k[l];l++)k[l].nodeType===1&&h.push(k[l]);k=h}return k};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")m.attrHandle.href=function(h){return h.getAttribute("href", 90 | 2)};g=null})();r.querySelectorAll&&function(){var g=o,h=r.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){o=function(l,q,p,u){q=q||r;if(!u&&q.nodeType===9&&!w(q))try{return A(q.querySelectorAll(l),p)}catch(t){}return g(l,q,p,u)};for(var k in g)o[k]=g[k];h=null}}();(function(){var g=r.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length=== 91 | 0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){m.order.splice(1,0,"CLASS");m.find.CLASS=function(h,k,l){if(typeof k.getElementsByClassName!=="undefined"&&!l)return k.getElementsByClassName(h[1])};g=null}}})();var E=r.compareDocumentPosition?function(g,h){return g.compareDocumentPosition(h)&16}:function(g,h){return g!==h&&(g.contains?g.contains(h):true)},w=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},fa=function(g,h){var k=[], 92 | l="",q;for(h=h.nodeType?[h]:h;q=m.match.PSEUDO.exec(g);){l+=q[0];g=g.replace(m.match.PSEUDO,"")}g=m.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var i=d;i0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,i={},j;if(f&&a.length){e=0;for(var n=a.length;e 95 | -1:c(f).is(e)){d.push({selector:j,elem:f});delete i[j]}}f=f.parentNode}}return d}var o=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(m,s){for(;s&&s.ownerDocument&&s!==b;){if(o?o.index(s)>-1:c(s).is(a))return s;s=s.parentNode}return null})},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(), 96 | a);return this.pushStack(pa(a[0])||pa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")}, 97 | nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);bb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e): 98 | e;if((this.length>1||db.test(f))&&cb.test(a))e=e.reverse();return this.pushStack(e,a,Q.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===v||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!== 99 | b&&d.push(a);return d}});var Fa=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ga=/(<([\w:]+)[^>]*?)\/>/g,eb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,Ha=/<([\w:]+)/,fb=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"], 100 | col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==v)return this.empty().append((this[0]&&this[0].ownerDocument||r).createTextNode(a));return c.getText(this)}, 101 | wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length? 102 | d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments, 103 | false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&& 104 | !c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Fa,"").replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){qa(this,b);qa(this.find("*"),b.find("*"))}return b},html:function(a){if(a===v)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Fa,""):null;else if(typeof a==="string"&&!/