diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..5d609ac7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @chriskacerguis diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..34ee3d3c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Please provide either a cleanly formatted code snippet or a link to repo / gist with code that I can use to reproduce: + +```php + public function set_response($data = null, $http_code = null) + { + $this->response($data, $http_code, true); + } +``` + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots / Error Messages** +If applicable, add screenshots and/or error messages to help explain your problem. + +**Environment (please complete the following information):** + - PHP Version: [e.g. 7.2.1] + - CodeIgniter Version [e.g. 4.0.1] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a761a8b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +vendor +.idea \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a482d6ee..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -Changelog: -=========== - -### 2.7.0 - -* Added Blacklist IP option -* Added controller based access controls -* Added support for OPTIONS, PATCH, and HEAD (from boh1996) -* Added logging of the time it takes for a request (rtime column in DB) -* Changed DB schemas to use InnoDB, not MyISAM -* Updated Readme to reflect new developer (Chris Kacerguis) - -### 2.6.2 - -* Update CodeIgniter files to 2.1.3 -* Fixed issue #165 - -### 2.6.1 - -* Update CodeIgniter files to 2.1.2 -* Log Table support for IPv6 & NULL parameters -* Abstract out the processes of firing a controller method within _remap() to an separate method -* Moved GET, POST, PUT, and DELETE parsing to separate methods, allowing them to be overridden as needed -* Small bugfix for a PHP 5.3 strlen error -* Fixed some PHP 5.4 warnings -* Fix for bug in Format.php's to_html() which failed to detect if $data was really a multidimensional array. -* Fix for empty node on XML output format, for false = 0, true = 1. - -### 2.6.0 - -* Added loads of PHPDoc comments. -* Response where method doesn't exist is now "HTTP 405 Method Not Allowed", not "HTTP 404 Not Found". -* Compatible with PHP 5.4. -* Added support for gzip compression. -* Fix the apache\_request\_header function with CGI. -* Fixed up correctly .foo extensions to work when get arguments provided. -* Allows method emulation via X-HTTP-Method-Override -* Support for Backbone.emulateHTTP improved. -* Combine both URI segment and GET params instead of using one or the other -* Separate each piece of the WWW-Authenticate header for digest requests with a comma. -* Added IP whitelist option. - -### 2.5 - -* Instead of just seeing item, item, item, the singular version of the basenode will be used if possible. [Example](http://d.pr/RS46). -* Re-factored to use the Format library, which will soon be merged with CodeIgniter. -* Fixed Limit bug (limit of 5 would allow 6 requests). -* Added logging for invalid API key requests. -* Changed serialize to serialized. diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/README.md b/README.md index ccd236d9..59776fe9 100644 --- a/README.md +++ b/README.md @@ -1,196 +1,164 @@ -# CodeIgniter Rest Server +# CodeIgniter RestServer -A fully RESTful server implementation for CodeIgniter using one library, one -config file and one controller. +A fully RESTful server implementation for CodeIgniter 3 using one library, one config file and one controller. -## Requirements +> [!IMPORTANT] +> I have published the first "beta" of codeigniter-restserver 4. See the "development" branch. Please be sure to note the system requirments. -1. PHP 5.4 or greater -2. CodeIgniter 3.0+ +## Requirements -_Note: for 1.7.x support download v2.2 from Downloads tab_ +- PHP 7.2 or greater +- CodeIgniter 3.1.11+ ## Installation -Drag and drop the **application/libraries/Format.php** and **application/libraries/REST_Controller.php** files into your application's directories. To use `require_once` it at the top of your controllers to load it into the scope. Additionally, copy the **rest.php** file from **application/config** in your application's configuration directory. - -## Handling Requests - -When your controller extends from `REST_Controller`, the method names will be appended with the HTTP method used to access the request. If you're making an HTTP `GET` call to `/books`, for instance, it would call a `Books#index_get()` method. - -This allows you to implement a RESTful interface easily: - -```php -class Books extends REST_Controller -{ - public function index_get() - { - // Display all books - } - - public function index_post() - { - // Create a new book - } -} -``` - -`REST_Controller` also supports `PUT` and `DELETE` methods, allowing you to support a truly RESTful interface. - - -Accessing parameters is also easy. Simply use the name of the HTTP verb as a method: - -```php -$this->get('blah'); // GET param -$this->post('blah'); // POST param -$this->put('blah'); // PUT param -``` - -The HTTP spec for DELETE requests precludes the use of parameters. For delete requests, you can add items to the URL - -```php -public function index_delete($id) -{ - $this->response([ - 'returned from delete:' => $id, - ]); -} +```sh +composer require chriskacerguis/codeigniter-restserver ``` -If query parameters are passed via the URL, regardless of whether it's a GET request, can be obtained by the query method: +## Usage -```php -$this->query('blah'); // Query param -``` - -## Content Types - -`REST_Controller` supports a bunch of different request/response formats, including XML, JSON and serialised PHP. By default, the class will check the URL and look for a format either as an extension or as a separate segment. +CodeIgniter Rest Server is available on [Packagist](https://packagist.org/packages/chriskacerguis/codeigniter-restserver) (using semantic versioning), and installation via composer is the recommended way to install Codeigniter Rest Server. Just add this line to your `composer.json` file: -This means your URLs can look like this: -``` -http://example.com/books.json -http://example.com/books?format=json +```json +"chriskacerguis/codeigniter-restserver": "^3.1" ``` -This can be flaky with URI segments, so the recommend approach is using the HTTP `Accept` header: +or run -```bash -$ curl -H "Accept: application/json" http://example.com +```sh +composer require chriskacerguis/codeigniter-restserver ``` -Any responses you make from the class (see [responses](#responses) for more on this) will be serialised in the designated format. - -## Responses - -The class provides a `response()` method that allows you to return data in the user's requested response format. +Note that you will need to copy `rest.php` to your `config` directory (e.g. `application/config`) -Returning any object / array / string / whatever is easy: +Step 1: Add this to your controller (should be before any of your code) ```php -public function index_get() -{ - $this->response($this->db->get('books')->result()); -} +use chriskacerguis\RestServer\RestController; ``` -This will automatically return an `HTTP 200 OK` response. You can specify the status code in the second parameter: +Step 2: Extend your controller ```php -public function index_post() - { - // ...create new book - $this->response($book, 201); // Send an HTTP 201 Created - } +class Example extends RestController ``` -If you don't specify a response code, and the data you respond with `== FALSE` (an empty array or string, for instance), the response code will automatically be set to `404 Not Found`: +## Basic GET example -```php -$this->response([]); // HTTP 404 Not Found -``` +Here is a basic example. This controller, which should be saved as `Api.php`, can be called in two ways: -## Multilingual Support - -If your application uses language files to support multiple locales, `REST_Controller` will automatically parse the HTTP `Accept-Language` header and provide the language(s) in your actions. This information can be found in the `$this->response->lang` object: +* `http://domain/api/users/` will return the list of all users +* `http://domain/api/users/id/1` will only return information about the user with id = 1 ```php -public function __construct() -{ - parent::__construct(); - - if (is_array($this->response->lang)) - { - $this->load->language('application', $this->response->lang[0]); - } - else - { - $this->load->language('application', $this->response->lang); - } + 0, 'name' => 'John', 'email' => 'john@example.com'], + ['id' => 1, 'name' => 'Jim', 'email' => 'jim@example.com'], + ]; + + $id = $this->get( 'id' ); + + if ( $id === null ) + { + // Check if the users data store contains users + if ( $users ) + { + // Set the response and exit + $this->response( $users, 200 ); + } + else + { + // Set the response and exit + $this->response( [ + 'status' => false, + 'message' => 'No users were found' + ], 404 ); + } + } + else + { + if ( array_key_exists( $id, $users ) ) + { + $this->response( $users[$id], 200 ); + } + else + { + $this->response( [ + 'status' => false, + 'message' => 'No such user found' + ], 404 ); + } + } + } } ``` -## Authentication - -This class also provides rudimentary support for HTTP basic authentication and/or the securer HTTP digest access authentication. +## Extending supported formats -You can enable basic authentication by setting the `$config['rest_auth']` to `'basic'`. The `$config['rest_valid_logins']` directive can then be used to set the usernames and passwords able to log in to your system. The class will automatically send all the correct headers to trigger the authentication dialogue: +If you need to be able to support more formats for replies, you can extend the +`Format` class to add the required `to_...` methods +1. Extend the `RestController` class (in `libraries/MY_REST_Controller.php`) ```php -$config['rest_valid_logins'] = ['username' => 'password', 'other_person' => 'secure123']; -``` - -Enabling digest auth is similarly easy. Configure your desired logins in the config file like above, and set `$config['rest_auth']` to `'digest'`. The class will automatically send out the headers to enable digest auth. - -If you're tying this library into an AJAX endpoint where clients authenticate using PHP sessions then you may not like either of the digest nor basic authentication methods. In that case, you can tell the REST Library what PHP session variable to check for. If the variable exists, then the user is authorized. It will be up to your application to set that variable. You can define the variable in ``$config['auth_source']``. Then tell the library to use a php session variable by setting ``$config['rest_auth']`` to ``session``. - -All three methods of authentication can be secured further by using an IP whitelist. If you enable `$config['rest_ip_whitelist_enabled']` in your config file, you can then set a list of allowed IPs. +format = new Format(); + } +} ``` -Your localhost IPs (`127.0.0.1` and `0.0.0.0`) are allowed by default. - -## API Keys - -In addition to the authentication methods above, the `REST_Controller` class also supports the use of API keys. Enabling API keys is easy. Turn it on in your **config/rest.php** file: +2. Extend the `Format` class (can be created as a CodeIgniter library in `libraries/Format.php`). +Following is an example to add support for PDF output ```php -$config['rest_enable_keys'] = TRUE; -``` +_data; + } + + if (is_array($data) || substr($data, 0, 4) != '%PDF') { + $html = $this->to_html($data); + + // Use your PDF lib of choice. For example mpdf + $mpdf = new \Mpdf\Mpdf(); + $mpdf->WriteHTML($html); + return $mpdf->Output('', 'S'); + } + + return $data; + } +} ``` - -## Other Documentation / Tutorials - -* [NetTuts: Working with RESTful Services in CodeIgniter](http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/) - -## Contributions - -This project was originally written by Phil Sturgeon, however his involvement has shifted -as he is no longer using it. As of 2013/11/20 further developement and support will be done by Chris Kacerguis. - -Pull Requests are the best way to fix bugs or add features. I know loads of you use this, so please -contribute if you have improvements to be made and I'll keep releasing versions over time. diff --git a/application/config/routes.php b/application/config/routes.php deleted file mode 100644 index 5cbeb064..00000000 --- a/application/config/routes.php +++ /dev/null @@ -1,62 +0,0 @@ - my_controller/index -| my-controller/my-method -> my_controller/my_method -*/ -$route['default_controller'] = 'welcome'; -$route['404_override'] = ''; -$route['translate_uri_dashes'] = TRUE; - -/* -| ------------------------------------------------------------------------- -| Sample REST API Routes -| ------------------------------------------------------------------------- -*/ -$route['api/example/users/(:num)'] = 'api/example/users/id/$1'; // Example 4 -$route['api/example/users/(:num)(\.)([a-zA-Z0-9_-]+)(.*)'] = 'api/example/users/id/$1/format/$3$4'; // Example 8 diff --git a/application/controllers/Rest_server.php b/application/controllers/Rest_server.php deleted file mode 100644 index 5d44f921..00000000 --- a/application/controllers/Rest_server.php +++ /dev/null @@ -1,13 +0,0 @@ -load->helper('url'); - - $this->load->view('rest_server'); - } -} diff --git a/application/controllers/Welcome.php b/application/controllers/Welcome.php deleted file mode 100644 index d16a4dee..00000000 --- a/application/controllers/Welcome.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @see http://codeigniter.com/user_guide/general/urls.html - */ - public function index() - { - $this->load->helper('url'); - - $this->load->view('welcome_message'); - } -} diff --git a/application/controllers/api/Example.php b/application/controllers/api/Example.php deleted file mode 100644 index 2cfecaaf..00000000 --- a/application/controllers/api/Example.php +++ /dev/null @@ -1,137 +0,0 @@ -methods['user_get']['limit'] = 500; // 500 requests per hour per user/key - $this->methods['user_post']['limit'] = 100; // 100 requests per hour per user/key - $this->methods['user_delete']['limit'] = 50; // 50 requests per hour per user/key - } - - public function users_get() - { - // Users from a data store e.g. database - $users = [ - ['id' => 1, 'name' => 'John', 'email' => 'john@example.com', 'fact' => 'Loves coding'], - ['id' => 2, 'name' => 'Jim', 'email' => 'jim@example.com', 'fact' => 'Developed on CodeIgniter'], - ['id' => 3, 'name' => 'Jane', 'email' => 'jane@example.com', 'fact' => 'Lives in the USA', ['hobbies' => ['guitar', 'cycling']]], - ]; - - $id = $this->get('id'); - - // If the id parameter doesn't exist return all the users - - if ($id === NULL) - { - // Check if the users data store contains users (in case the database result returns NULL) - if ($users) - { - // Set the response and exit - $this->response($users, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code - } - else - { - // Set the response and exit - $this->response([ - 'status' => FALSE, - 'message' => 'No users were found' - ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code - } - } - - // Find and return a single record for a particular user. - - $id = (int) $id; - - // Validate the id. - if ($id <= 0) - { - // Invalid id, set the response and exit. - $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // Get the user from the array, using the id as key for retreival. - // Usually a model is to be used for this. - - $user = NULL; - - if (!empty($users)) - { - foreach ($users as $key => $value) - { - if (isset($value['id']) && $value['id'] === $id) - { - $user = $value; - } - } - } - - if (!empty($user)) - { - $this->set_response($user, REST_Controller::HTTP_OK); // OK (200) being the HTTP response code - } - else - { - $this->set_response([ - 'status' => FALSE, - 'message' => 'User could not be found' - ], REST_Controller::HTTP_NOT_FOUND); // NOT_FOUND (404) being the HTTP response code - } - } - - public function users_post() - { - // $this->some_model->update_user( ... ); - $message = [ - 'id' => 100, // Automatically generated by the model - 'name' => $this->post('name'), - 'email' => $this->post('email'), - 'message' => 'Added a resource' - ]; - - $this->set_response($message, REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code - } - - public function users_delete() - { - $id = (int) $this->get('id'); - - // Validate the id. - if ($id <= 0) - { - // Set the response and exit - $this->response(NULL, REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // $this->some_model->delete_something($id); - $message = [ - 'id' => $id, - 'message' => 'Deleted the resource' - ]; - - $this->set_response($message, REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code - } - -} diff --git a/application/controllers/api/Key.php b/application/controllers/api/Key.php deleted file mode 100644 index 1823c814..00000000 --- a/application/controllers/api/Key.php +++ /dev/null @@ -1,272 +0,0 @@ - ['level' => 10, 'limit' => 10], - 'index_delete' => ['level' => 10], - 'level_post' => ['level' => 10], - 'regenerate_post' => ['level' => 10], - ]; - - /** - * Insert a key into the database - * - * @access public - * @return void - */ - public function index_put() - { - // Build a new key - $key = $this->_generate_key(); - - // If no key level provided, provide a generic key - $level = $this->put('level') ? $this->put('level') : 1; - $ignore_limits = ctype_digit($this->put('ignore_limits')) ? (int) $this->put('ignore_limits') : 1; - - // Insert the new key - if ($this->_insert_key($key, ['level' => $level, 'ignore_limits' => $ignore_limits])) - { - $this->response([ - 'status' => TRUE, - 'key' => $key - ], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code - } - else - { - $this->response([ - 'status' => FALSE, - 'message' => 'Could not save the key' - ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code - } - } - - /** - * Remove a key from the database to stop it working - * - * @access public - * @return void - */ - public function index_delete() - { - $key = $this->delete('key'); - - // Does this key exist? - if (!$this->_key_exists($key)) - { - // It doesn't appear the key exists - $this->response([ - 'status' => FALSE, - 'message' => 'Invalid API key' - ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // Destroy it - $this->_delete_key($key); - - // Respond that the key was destroyed - $this->response([ - 'status' => TRUE, - 'message' => 'API key was deleted' - ], REST_Controller::HTTP_NO_CONTENT); // NO_CONTENT (204) being the HTTP response code - } - - /** - * Change the level - * - * @access public - * @return void - */ - public function level_post() - { - $key = $this->post('key'); - $new_level = $this->post('level'); - - // Does this key exist? - if (!$this->_key_exists($key)) - { - // It doesn't appear the key exists - $this->response([ - 'status' => FALSE, - 'message' => 'Invalid API key' - ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // Update the key level - if ($this->_update_key($key, ['level' => $new_level])) - { - $this->response([ - 'status' => TRUE, - 'message' => 'API key was updated' - ], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code - } - else - { - $this->response([ - 'status' => FALSE, - 'message' => 'Could not update the key level' - ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code - } - } - - /** - * Suspend a key - * - * @access public - * @return void - */ - public function suspend_post() - { - $key = $this->post('key'); - - // Does this key exist? - if (!$this->_key_exists($key)) - { - // It doesn't appear the key exists - $this->response([ - 'status' => FALSE, - 'message' => 'Invalid API key' - ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // Update the key level - if ($this->_update_key($key, ['level' => 0])) - { - $this->response([ - 'status' => TRUE, - 'message' => 'Key was suspended' - ], REST_Controller::HTTP_OK); // OK (200) being the HTTP response code - } - else - { - $this->response([ - 'status' => FALSE, - 'message' => 'Could not suspend the user' - ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code - } - } - - /** - * Regenerate a key - * - * @access public - * @return void - */ - public function regenerate_post() - { - $old_key = $this->post('key'); - $key_details = $this->_get_key($old_key); - - // Does this key exist? - if (!$key_details) - { - // It doesn't appear the key exists - $this->response([ - 'status' => FALSE, - 'message' => 'Invalid API key' - ], REST_Controller::HTTP_BAD_REQUEST); // BAD_REQUEST (400) being the HTTP response code - } - - // Build a new key - $new_key = $this->_generate_key(); - - // Insert the new key - if ($this->_insert_key($new_key, ['level' => $key_details->level, 'ignore_limits' => $key_details->ignore_limits])) - { - // Suspend old key - $this->_update_key($old_key, ['level' => 0]); - - $this->response([ - 'status' => TRUE, - 'key' => $new_key - ], REST_Controller::HTTP_CREATED); // CREATED (201) being the HTTP response code - } - else - { - $this->response([ - 'status' => FALSE, - 'message' => 'Could not save the key' - ], REST_Controller::HTTP_INTERNAL_SERVER_ERROR); // INTERNAL_SERVER_ERROR (500) being the HTTP response code - } - } - - /* Helper Methods */ - - private function _generate_key() - { - do - { - // Generate a random salt - $salt = $this->security->get_random_bytes(64); - - // If an error occurred, then fall back to the previous method - if ($salt === FALSE) - { - $salt = hash('sha256', time() . mt_rand()); - } - - $new_key = substr($salt, 0, config_item('rest_key_length')); - } - while ($this->_key_exists($new_key)); - - return $new_key; - } - - /* Private Data Methods */ - - private function _get_key($key) - { - return $this->db - ->where(config_item('rest_key_column'), $key) - ->get(config_item('rest_keys_table')) - ->row(); - } - - private function _key_exists($key) - { - return $this->db - ->where(config_item('rest_key_column'), $key) - ->count_all_results(config_item('rest_keys_table')) > 0; - } - - private function _insert_key($key, $data) - { - $data[config_item('rest_key_column')] = $key; - $data['date_created'] = function_exists('now') ? now() : time(); - - return $this->db - ->set($data) - ->insert(config_item('rest_keys_table')); - } - - private function _update_key($key, $data) - { - return $this->db - ->where(config_item('rest_key_column'), $key) - ->update(config_item('rest_keys_table'), $data); - } - - private function _delete_key($key) - { - return $this->db - ->where(config_item('rest_key_column'), $key) - ->delete(config_item('rest_keys_table')); - } - -} diff --git a/application/libraries/REST_Controller.php b/application/libraries/REST_Controller.php deleted file mode 100644 index 2c50884f..00000000 --- a/application/libraries/REST_Controller.php +++ /dev/null @@ -1,2110 +0,0 @@ - 'application/json', - 'array' => 'application/json', - 'csv' => 'application/csv', - 'html' => 'text/html', - 'jsonp' => 'application/javascript', - 'php' => 'text/plain', - 'serialized' => 'application/vnd.php.serialized', - 'xml' => 'application/xml' - ]; - - /** - * Information about the current API user - * - * @var object - */ - protected $_apiuser; - - /** - * Enable XSS flag - * Determines whether the XSS filter is always active when - * GET, OPTIONS, HEAD, POST, PUT, DELETE and PATCH data is encountered. - * Set automatically based on config setting. - * - * @var bool - */ - protected $_enable_xss = FALSE; - - /** - * HTTP status codes and their respective description - * Note: Only the widely used HTTP status codes are used - * - * @var array - * @link http://www.restapitutorial.com/httpstatuscodes.html - */ - protected $http_status_codes = [ - self::HTTP_OK => 'OK', - self::HTTP_CREATED => 'CREATED', - self::HTTP_NO_CONTENT => 'NO CONTENT', - self::HTTP_NOT_MODIFIED => 'NOT MODIFIED', - self::HTTP_BAD_REQUEST => 'BAD REQUEST', - self::HTTP_UNAUTHORIZED => 'UNAUTHORIZED', - self::HTTP_FORBIDDEN => 'FORBIDDEN', - self::HTTP_NOT_FOUND => 'NOT FOUND', - self::HTTP_METHOD_NOT_ALLOWED => 'METHOD NOT ALLOWED', - self::HTTP_NOT_ACCEPTABLE => 'NOT ACCEPTABLE', - self::HTTP_CONFLICT => 'CONFLICT', - self::HTTP_INTERNAL_SERVER_ERROR => 'INTERNAL SERVER ERROR', - self::HTTP_NOT_IMPLEMENTED => 'NOT IMPLEMENTED' - ]; - - /** - * Extend this function to apply additional checking early on in the process - * - * @access protected - * @return void - */ - protected function early_checks() - { - } - - /** - * Constructor for the REST API - * - * @access public - * @param string $config Configuration filename minus the file extension - * e.g: my_rest.php is passed as 'my_rest' - * @return void - */ - public function __construct($config = 'rest') - { - parent::__construct(); - - // Disable XML Entity (security vulnerability) - libxml_disable_entity_loader(TRUE); - - // Check to see if PHP is equal to or greater than 5.4.x - if (is_php('5.4') === FALSE) - { - // CodeIgniter 3 is recommended for v5.4 or above - exit('Using PHP v' . PHP_VERSION . ', though PHP v5.4 or greater is required'); - } - - // Check to see if this is CI 3.x - if (explode('.', CI_VERSION, 2)[0] < 3) - { - exit('REST Server requires CodeIgniter 3.x'); - } - - // Set the default value of global xss filtering. Same approach as CodeIgniter 3 - $this->_enable_xss = ($this->config->item('global_xss_filtering') === TRUE); - - // Don't try to parse template variables like {elapsed_time} and {memory_usage} - // when output is displayed for not damaging data accidentally - $this->output->parse_exec_vars = FALSE; - - // Start the timer for how long the request takes - $this->_start_rtime = microtime(TRUE); - - // Load the rest.php configuration file - $this->load->config($config); - - // At present the library is bundled with REST_Controller 2.5+, but will eventually be part of CodeIgniter (no citation) - $this->load->library('format'); - - // Get the language - $language = $this->config->item('rest_language'); - if ($language === NULL) - { - $language = 'english'; - } - - // Load the language file - $this->lang->load('rest_controller', $language); - - // Initialise the response, request and rest objects - $this->request = new stdClass(); - $this->response = new stdClass(); - $this->rest = new stdClass(); - - // Check to see if the current IP address is blacklisted - if ($this->config->item('rest_ip_blacklist_enabled') === TRUE) - { - $this->_check_blacklist_auth(); - } - - // Determine whether the connection is HTTPS - $this->request->ssl = is_https(); - - // How is this request being made? GET, POST, PATCH, DELETE, INSERT, PUT, HEAD or OPTIONS - $this->request->method = $this->_detect_method(); - - // Create an argument container if it doesn't exist e.g. _get_args - if (isset($this->{'_' . $this->request->method . '_args'}) === FALSE) - { - $this->{'_' . $this->request->method . '_args'} = []; - } - - // Set up the query parameters - $this->_parse_query(); - - // Set up the GET variables - $this->_get_args = array_merge($this->_get_args, $this->uri->ruri_to_assoc()); - - // Try to find a format for the request (means we have a request body) - $this->request->format = $this->_detect_input_format(); - - // Not all methods have a body attached with them - $this->request->body = NULL; - - $this->{'_parse_' . $this->request->method}(); - - // Now we know all about our request, let's try and parse the body if it exists - if ($this->request->format && $this->request->body) - { - $this->request->body = $this->format->factory($this->request->body, $this->request->format)->to_array(); - // Assign payload arguments to proper method container - $this->{'_' . $this->request->method . '_args'} = $this->request->body; - } - - // Merge both for one mega-args variable - $this->_args = array_merge( - $this->_get_args, - $this->_options_args, - $this->_patch_args, - $this->_head_args, - $this->_put_args, - $this->_post_args, - $this->_delete_args, - $this->{'_' . $this->request->method . '_args'} - ); - - // Which format should the data be returned in? - $this->response->format = $this->_detect_output_format(); - - // Which language should the data be returned in? - $this->response->lang = $this->_detect_lang(); - - // Extend this function to apply additional checking early on in the process - $this->early_checks(); - - // Load DB if its enabled - if ($this->config->item('rest_database_group') && ($this->config->item('rest_enable_keys') || $this->config->item('rest_enable_logging'))) - { - $this->rest->db = $this->load->database($this->config->item('rest_database_group'), TRUE); - } - - // Use whatever database is in use (isset returns FALSE) - elseif (property_exists($this, 'db')) - { - $this->rest->db = $this->db; - } - - // Check if there is a specific auth type for the current class/method - // _auth_override_check could exit so we need $this->rest->db initialized before - $this->auth_override = $this->_auth_override_check(); - - // Checking for keys? GET TO WorK! - // Skip keys test for $config['auth_override_class_method']['class'['method'] = 'none' - if ($this->config->item('rest_enable_keys') && $this->auth_override !== TRUE) - { - $this->_allow = $this->_detect_api_key(); - } - - // Only allow ajax requests - if ($this->input->is_ajax_request() === FALSE && $this->config->item('rest_ajax_only')) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ajax_only') - ], self::HTTP_NOT_ACCEPTABLE); - } - - // When there is no specific override for the current class/method, use the default auth value set in the config - if ($this->auth_override === FALSE && !($this->config->item('rest_enable_keys') && $this->_allow === TRUE)) - { - $rest_auth = strtolower($this->config->item('rest_auth')); - switch ($rest_auth) - { - case 'basic': - $this->_prepare_basic_auth(); - break; - case 'digest': - $this->_prepare_digest_auth(); - break; - case 'session': - $this->_check_php_session(); - break; - } - if ($this->config->item('rest_ip_whitelist_enabled') === TRUE) - { - $this->_check_whitelist_auth(); - } - } - } - - /** - * Deconstructor - * - * @author Chris Kacerguis - * @access public - * @return void - */ - public function __destruct() - { - // Get the current timestamp - $this->_end_rtime = microtime(TRUE); - - // Log the loading time to the log table - if ($this->config->item('rest_enable_logging') === TRUE) - { - $this->_log_access_time(); - } - } - - /** - * Requests are not made to methods directly, the request will be for - * an "object". This simply maps the object and method to the correct - * Controller method. - * - * @access public - * @param string $object_called - * @param array $arguments The arguments passed to the controller method. - */ - public function _remap($object_called, $arguments) - { - // Should we answer if not over SSL? - if ($this->config->item('force_https') && $this->request->ssl === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unsupported') - ], self::HTTP_FORBIDDEN); - } - - // Remove the supported format from the function name e.g. index.json => index - $object_called = preg_replace('/^(.*)\.(?:' . implode('|', array_keys($this->_supported_formats)) . ')$/', '$1', $object_called); - - $controller_method = $object_called . '_' . $this->request->method; - - // Do we want to log this method (if allowed by config)? - $log_method = !(isset($this->methods[$controller_method]['log']) && $this->methods[$controller_method]['log'] === FALSE); - - // Use keys for this method? - $use_key = !(isset($this->methods[$controller_method]['key']) && $this->methods[$controller_method]['key'] === FALSE); - - // They provided a key, but it wasn't valid, so get them out of here. - if ($this->config->item('rest_enable_keys') && $use_key && $this->_allow === FALSE) - { - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request(); - } - - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => sprintf($this->lang->line('text_rest_invalid_api_key'), $this->rest->key) - ], self::HTTP_FORBIDDEN); - } - - // Check to see if this key has access to the requested controller. - if ($this->config->item('rest_enable_keys') && $use_key && empty($this->rest->key) === FALSE && $this->_check_access() === FALSE) - { - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request(); - } - - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - - // Sure it exists, but can they do anything with it? - if (method_exists($this, $controller_method) === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unknown_method') - ], self::HTTP_NOT_FOUND); - } - - // Doing key related stuff? Can only do it if they have a key right? - if ($this->config->item('rest_enable_keys') && empty($this->rest->key) === FALSE) - { - // Check the limit - if ($this->config->item('rest_enable_limits') && $this->_check_limit($controller_method) === FALSE) - { - $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_time_limit')]; - $this->response($response, self::HTTP_UNAUTHORIZED); - } - - // If no level is set use 0, they probably aren't using permissions - $level = isset($this->methods[$controller_method]['level']) ? $this->methods[$controller_method]['level'] : 0; - - // If no level is set, or it is lower than/equal to the key's level - $authorized = $level <= $this->rest->level; - - // IM TELLIN! - if ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request($authorized); - } - - // They don't have good enough perms - $response = [$this->config->item('rest_status_field_name') => FALSE, $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_api_key_permissions')]; - $authorized || $this->response($response, self::HTTP_UNAUTHORIZED); - } - - // No key stuff, but record that stuff is happening - elseif ($this->config->item('rest_enable_logging') && $log_method) - { - $this->_log_request($authorized = TRUE); - } - - // Call the controller method and passed arguments - try - { - call_user_func_array([$this, $controller_method], $arguments); - } - catch (Exception $ex) - { - // If the method doesn't exist, then the error will be caught and an error response shown - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => [ - 'classname' => get_class($ex), - 'message' => $ex->getMessage() - ] - ], self::HTTP_INTERNAL_SERVER_ERROR); - } - } - - /** - * Takes mixed data and optionally a status code, then creates the response - * - * @access public - * @param array|NULL $data Data to output to the user - * @param int|NULL $http_code HTTP status code - * @param bool $continue TRUE to flush the response to the client and continue - * running the script; otherwise, exit - */ - public function response($data = NULL, $http_code = NULL, $continue = FALSE) - { - // If the HTTP status is not NULL, then cast as an integer - if ($http_code !== NULL) - { - // So as to be safe later on in the process - $http_code = (int) $http_code; - } - - // Set the output as NULL by default - $output = NULL; - - // If data is NULL and no HTTP status code provided, then display, error and exit - if ($data === NULL && $http_code === NULL) - { - $http_code = self::HTTP_NOT_FOUND; - } - - // If data is not NULL and a HTTP status code provided, then continue - elseif ($data !== NULL) - { - // If the format method exists, call and return the output in that format - if (method_exists($this->format, 'to_' . $this->response->format)) - { - // Set the format header - $this->output->set_content_type($this->_supported_formats[$this->response->format], strtolower($this->config->item('charset'))); - $output = $this->format->factory($data)->{'to_' . $this->response->format}(); - - // An array must be parsed as a string, so as not to cause an array to string error. - // Json is the most appropriate form for such a datatype - if ($this->response->format === 'array') - { - $output = $this->format->factory($output)->{'to_json'}(); - } - } - else - { - // If an array or object, then parse as a json, so as to be a 'string' - if (is_array($data) || is_object($data)) - { - $data = $this->format->factory($data)->{'to_json'}(); - } - - // Format is not supported, so output the raw data as a string - $output = $data; - } - } - - // If not greater than zero, then set the HTTP status code as 200 by default - // Though perhaps 500 should be set instead, for the developer not passing a - // correct HTTP status code - $http_code > 0 || $http_code = self::HTTP_OK; - - $this->output->set_status_header($http_code); - - // JC: Log response code only if rest logging enabled - if ($this->config->item('rest_enable_logging') === TRUE) - { - $this->_log_response_code($http_code); - } - - // Output the data - $this->output->set_output($output); - - if ($continue === FALSE) - { - // Display the data and exit execution - $this->output->_display(); - exit; - } - - // Otherwise dump the output automatically - } - - /** - * Takes mixed data and optionally a status code, then creates the response - * within the buffers of the Output class. The response is sent to the client - * lately by the framework, after the current controller's method termination. - * All the hooks after the controller's method termination are executable. - * - * @access public - * @param array|NULL $data Data to output to the user - * @param int|NULL $http_code HTTP status code - */ - public function set_response($data = NULL, $http_code = NULL) - { - $this->response($data, $http_code, TRUE); - } - - /** - * Get the input format e.g. json or xml - * - * @access protected - * @return string|NULL Supported input format; otherwise, NULL - */ - protected function _detect_input_format() - { - // Get the CONTENT-TYPE value from the SERVER variable - $content_type = $this->input->server('CONTENT_TYPE'); - - if (empty($content_type) === FALSE) - { - // Check all formats against the HTTP_ACCEPT header - foreach ($this->_supported_formats as $key => $value) - { - // $key = format e.g. csv - // $value = mime type e.g. application/csv - - // If a semi-colon exists in the string, then explode by ; and get the value of where - // the current array pointer resides. This will generally be the first element of the array - $content_type = (strpos($content_type, ';') !== FALSE ? current(explode(';', $content_type)) : $content_type); - - // If both the mime types match, then return the format - if ($content_type === $value) - { - return $key; - } - } - } - - return NULL; - } - - /** - * Detect which format should be used to output the data - * - * @access protected - * @return mixed|NULL|string Output format - */ - protected function _detect_output_format() - { - // Concatenate formats to a regex pattern e.g. \.(csv|json|xml) - $pattern = '/\.(' . implode('|', array_keys($this->_supported_formats)) . ')($|\/)/'; - $matches = []; - - // Check if a file extension is used e.g. http://example.com/api/index.json?param1=param2 - if (preg_match($pattern, $this->uri->uri_string(), $matches)) - { - return $matches[1]; - } - - // Get the format parameter named as 'format' - if (isset($this->_get_args['format'])) - { - $format = strtolower($this->_get_args['format']); - - if (isset($this->_supported_formats[$format]) === TRUE) - { - return $format; - } - } - - // Get the HTTP_ACCEPT server variable - $http_accept = $this->input->server('HTTP_ACCEPT'); - - // Otherwise, check the HTTP_ACCEPT server variable - if ($this->config->item('rest_ignore_http_accept') === FALSE && $http_accept !== NULL) - { - // Check all formats against the HTTP_ACCEPT header - foreach (array_keys($this->_supported_formats) as $format) - { - // Has this format been requested? - if (strpos($http_accept, $format) !== FALSE) - { - if ($format !== 'html' && $format !== 'xml') - { - // If not HTML or XML assume it's correct - return $format; - } - elseif ($format === 'html' && strpos($http_accept, 'xml') === FALSE) - { - // HTML or XML have shown up as a match - // If it is truly HTML, it wont want any XML - return $format; - } - else if ($format === 'xml' && strpos($http_accept, 'html') === FALSE) - { - // If it is truly XML, it wont want any HTML - return $format; - } - } - } - } - - // Check if the controller has a default format - if (empty($this->rest_format) === FALSE) - { - return $this->rest_format; - } - - // Obtain the default format from the configuration - return $this->config->item('rest_default_format'); - } - - /** - * Get the HTTP request string e.g. get or post - * - * @access protected - * @return string|NULL Supported request method as a lowercase string; otherwise, NULL if not supported - */ - protected function _detect_method() - { - // Declare a variable to store the method - $method = NULL; - - // Determine whether the 'enable_emulate_request' setting is enabled - if ($this->config->item('enable_emulate_request') === TRUE) - { - $method = $this->input->post('_method'); - if ($method === NULL) - { - $method = $this->input->server('HTTP_X_HTTP_METHOD_OVERRIDE'); - } - - $method = strtolower($method); - } - - if (empty($method)) - { - // Get the request method as a lowercase string. - $method = $this->input->method(); - } - - return in_array($method, $this->allowed_http_methods) && method_exists($this, '_parse_' . $method) ? $method : 'get'; - } - - /** - * See if the user has provided an API key - * - * @access protected - * @return bool - */ - protected function _detect_api_key() - { - // Get the api key name variable set in the rest config file - $api_key_variable = $this->config->item('rest_key_name'); - - // Work out the name of the SERVER entry based on config - $key_name = 'HTTP_' . strtoupper(str_replace('-', '_', $api_key_variable)); - - $this->rest->key = NULL; - $this->rest->level = NULL; - $this->rest->user_id = NULL; - $this->rest->ignore_limits = FALSE; - - // Find the key from server or arguments - if (($key = isset($this->_args[$api_key_variable]) ? $this->_args[$api_key_variable] : $this->input->server($key_name))) - { - if (!($row = $this->rest->db->where($this->config->item('rest_key_column'), $key)->get($this->config->item('rest_keys_table'))->row())) - { - return FALSE; - } - - $this->rest->key = $row->{$this->config->item('rest_key_column')}; - - isset($row->user_id) && $this->rest->user_id = $row->user_id; - isset($row->level) && $this->rest->level = $row->level; - isset($row->ignore_limits) && $this->rest->ignore_limits = $row->ignore_limits; - - $this->_apiuser = $row; - - /* - * If "is private key" is enabled, compare the ip address with the list - * of valid ip addresses stored in the database. - */ - if (empty($row->is_private_key) === FALSE) - { - // Check for a list of valid ip addresses - if (isset($row->ip_addresses)) - { - // multiple ip addresses must be separated using a comma, explode and loop - $list_ip_addresses = explode(',', $row->ip_addresses); - $found_address = FALSE; - - foreach ($list_ip_addresses as $ip_address) - { - if ($this->input->ip_address() === trim($ip_address)) - { - // there is a match, set the the value to TRUE and break out of the loop - $found_address = TRUE; - break; - } - } - - return $found_address; - } - else - { - // There should be at least one IP address for this private key. - return FALSE; - } - } - - return TRUE; - } - - // No key has been sent - return FALSE; - } - - /** - * Preferred return language - * - * @access protected - * @return string|NULL The language code - */ - protected function _detect_lang() - { - $lang = $this->input->server('HTTP_ACCEPT_LANGUAGE'); - if ($lang === NULL) - { - return NULL; - } - - // It appears more than one language has been sent using a comma delimiter - if (strpos($lang, ',') !== FALSE) - { - $langs = explode(',', $lang); - - $return_langs = []; - foreach ($langs as $lang) - { - // Remove weight and trim leading and trailing whitespace - list($lang) = explode(';', $lang); - $return_langs[] = trim($lang); - } - - return $return_langs; - } - - // Otherwise simply return as a string - return $lang; - } - - /** - * Add the request to the log table - * - * @access protected - * @param bool $authorized TRUE the user is authorized; otherwise, FALSE - * @return bool TRUE the data was inserted; otherwise, FALSE - */ - protected function _log_request($authorized = FALSE) - { - // Insert the request into the log table - $is_inserted = $this->rest->db - ->insert( - $this->config->item('rest_logs_table'), [ - 'uri' => $this->uri->uri_string(), - 'method' => $this->request->method, - 'params' => $this->_args ? ($this->config->item('rest_logs_json_params') === TRUE ? json_encode($this->_args) : serialize($this->_args)) : NULL, - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'ip_address' => $this->input->ip_address(), - 'time' => now(), // Used to be: function_exists('now') ? now() : time() - 'authorized' => $authorized - ]); - - // Get the last insert id to update at a later stage of the request - $this->_insert_id = $this->rest->db->insert_id(); - - return $is_inserted; - } - - /** - * Check if the requests to a controller method exceed a limit - * - * @access protected - * @param string $controller_method The method being called - * @return bool TRUE the call limit is below the threshold; otherwise, FALSE - */ - protected function _check_limit($controller_method) - { - // They are special, or it might not even have a limit - if (empty($this->rest->ignore_limits) === FALSE) - { - // Everything is fine - return TRUE; - } - - switch ($this->config->item('rest_limits_method')) - { - case 'API_KEY': - $limited_uri = 'api-key:' . (isset($this->rest->key) ? $this->rest->key : ''); - $limited_method_name = isset($this->rest->key) ? $this->rest->key : ''; - break; - - case 'METHOD_NAME': - $limited_uri = 'method-name:' . $controller_method; - $limited_method_name = $controller_method; - break; - - case 'ROUTED_URL': - default: - $limited_uri = $this->uri->ruri_string(); - if (strpos(strrev($limited_uri), strrev($this->response->format)) === 0) - { - $limited_uri = substr($limited_uri,0, -strlen($this->response->format) - 1); - } - $limited_uri = 'uri:' . $limited_uri . ':' . $this->request->method; // It's good to differentiate GET from PUT - $limited_method_name = $controller_method; - break; - } - - if (isset($this->methods[$limited_method_name]['limit']) === FALSE ) - { - // Everything is fine - return TRUE; - } - - // How many times can you get to this method in a defined time_limit (default: 1 hour)? - $limit = $this->methods[$limited_method_name]['limit']; - - $time_limit = (isset($this->methods[$limited_method_name]['time']) ? $this->methods[$limited_method_name]['time'] : 3600); // 3600 = 60 * 60 - - // Get data about a keys' usage and limit to one row - $result = $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', $this->rest->key) - ->get($this->config->item('rest_limits_table')) - ->row(); - - // No calls have been made for this key - if ($result === NULL) - { - // Create a new row for the following key - $this->rest->db->insert($this->config->item('rest_limits_table'), [ - 'uri' => $limited_uri, - 'api_key' => isset($this->rest->key) ? $this->rest->key : '', - 'count' => 1, - 'hour_started' => time() - ]); - } - - // Been a time limit (or by default an hour) since they called - elseif ($result->hour_started < (time() - $time_limit)) - { - // Reset the started period and count - $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', isset($this->rest->key) ? $this->rest->key : '') - ->set('hour_started', time()) - ->set('count', 1) - ->update($this->config->item('rest_limits_table')); - } - - // They have called within the hour, so lets update - else - { - // The limit has been exceeded - if ($result->count >= $limit) - { - return FALSE; - } - - // Increase the count by one - $this->rest->db - ->where('uri', $limited_uri) - ->where('api_key', $this->rest->key) - ->set('count', 'count + 1', FALSE) - ->update($this->config->item('rest_limits_table')); - } - - return TRUE; - } - - /** - * Check if there is a specific auth type set for the current class/method/HTTP-method being called - * - * @access protected - * @return bool - */ - protected function _auth_override_check() - { - // Assign the class/method auth type override array from the config - $auth_override_class_method = $this->config->item('auth_override_class_method'); - - // Check to see if the override array is even populated - if (!empty($auth_override_class_method)) - { - // check for wildcard flag for rules for classes - if (!empty($auth_override_class_method[$this->router->class]['*'])) // Check for class overrides - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method[$this->router->class]['*'] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method[$this->router->class]['*'] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method[$this->router->class]['*'] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method[$this->router->class]['*'] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - - // Check to see if there's an override value set for the current class/method being called - if (!empty($auth_override_class_method[$this->router->class][$this->router->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method[$this->router->class][$this->router->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - } - - // Assign the class/method/HTTP-method auth type override array from the config - $auth_override_class_method_http = $this->config->item('auth_override_class_method_http'); - - // Check to see if the override array is even populated - if (!empty($auth_override_class_method_http)) - { - // check for wildcard flag for rules for classes - if(!empty($auth_override_class_method_http[$this->router->class]['*'][$this->request->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method_http[$this->router->class]['*'][$this->request->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - - // Check to see if there's an override value set for the current class/method/HTTP-method being called - if(!empty($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method])) - { - // None auth override found, prepare nothing but send back a TRUE override flag - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'none') - { - return TRUE; - } - - // Basic auth override found, prepare basic - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'basic') - { - $this->_prepare_basic_auth(); - - return TRUE; - } - - // Digest auth override found, prepare digest - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'digest') - { - $this->_prepare_digest_auth(); - - return TRUE; - } - - // Whitelist auth override found, check client's ip against config whitelist - if ($auth_override_class_method_http[$this->router->class][$this->router->method][$this->request->method] === 'whitelist') - { - $this->_check_whitelist_auth(); - - return TRUE; - } - } - } - return FALSE; - } - - /** - * Parse the GET request arguments - * - * @access protected - * @return void - */ - protected function _parse_get() - { - // Merge both the URI segments and query parameters - $this->_get_args = array_merge($this->_get_args, $this->_query_args); - } - - /** - * Parse the POST request arguments - * - * @access protected - * @return void - */ - protected function _parse_post() - { - $this->_post_args = $_POST; - - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - } - - /** - * Parse the PUT request arguments - * - * @access protected - * @return void - */ - protected function _parse_put() - { - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - else - { - // If no filetype is provided, then there are probably just arguments - if ($this->input->method() === 'put') - { - $this->_put_args = $this->input->input_stream(); - } - } - } - - /** - * Parse the HEAD request arguments - * - * @access protected - * @return void - */ - protected function _parse_head() - { - // Parse the HEAD variables - parse_str(parse_url(http://github.com/$this-%3Einput-%3Eserver('REQUEST_URI'), PHP_URL_QUERY), $head); - - // Merge both the URI segments and HEAD params - $this->_head_args = array_merge($this->_head_args, $head); - } - - /** - * Parse the OPTIONS request arguments - * - * @access protected - * @return void - */ - protected function _parse_options() - { - // Parse the OPTIONS variables - parse_str(parse_url(http://github.com/$this-%3Einput-%3Eserver('REQUEST_URI'), PHP_URL_QUERY), $options); - - // Merge both the URI segments and OPTIONS params - $this->_options_args = array_merge($this->_options_args, $options); - } - - /** - * Parse the PATCH request arguments - * - * @access protected - * @return void - */ - protected function _parse_patch() - { - // It might be a HTTP body - if ($this->request->format) - { - $this->request->body = $this->input->raw_input_stream; - } - else - { - // If no filetype is provided, then there are probably just arguments - if ($this->input->method() === 'patch') - { - $this->_patch_args = $this->input->input_stream(); - } - } - } - - /** - * Parse the DELETE request arguments - * - * @access protected - * @return void - */ - protected function _parse_delete() - { - // These should exist if a DELETE request - if ($this->input->method() === 'delete') - { - $this->_delete_args = $this->input->input_stream(); - } - } - - /** - * Parse the query parameters - * - * @access protected - * @return void - */ - protected function _parse_query() - { - // Declare a variable that will hold the REQUEST_URI - $request_uri = NULL; - - // If using the commandline version - if (is_cli()) - { - $args = $this->input->server('argv'); - unset($args[0]); - - // Combine the arguments using '/' as the delimiter - $request_uri = '/' . implode('/', $args) . '/'; - - // Set the following server variables (perhaps not required anymore?) - $_SERVER['REQUEST_URI'] = $request_uri; - $_SERVER['PATH_INFO'] = $request_uri; - $_SERVER['QUERY_STRING'] = $request_uri; - } - else - { - $request_uri = $this->input->server('REQUEST_URI'); - } - - // Parse the query parameters from the query string - parse_str(parse_url(http://github.com/$request_uri,%20PHP_URL_QUERY), $this->_query_args); - } - - // INPUT FUNCTION -------------------------------------------------------------- - - /** - * Retrieve a value from a GET request - * - * @access public - * @param NULL $key Key to retrieve from the GET request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the GET request; otherwise, NULL - */ - public function get($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_get_args; - } - - return isset($this->_get_args[$key]) ? $this->_xss_clean($this->_get_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a OPTIONS request - * - * @access public - * @param NULL $key Key to retrieve from the OPTIONS request. - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the OPTIONS request; otherwise, NULL - */ - public function options($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_options_args; - } - - return isset($this->_options_args[$key]) ? $this->_xss_clean($this->_options_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a HEAD request - * - * @access public - * @param NULL $key Key to retrieve from the HEAD request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the HEAD request; otherwise, NULL - */ - public function head($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->head_args; - } - - return isset($this->head_args[$key]) ? $this->_xss_clean($this->head_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a POST request - * - * @access public - * @param NULL $key Key to retrieve from the POST request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the POST request; otherwise, NULL - */ - public function post($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_post_args; - } - - return isset($this->_post_args[$key]) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a PUT request - * - * @access public - * @param NULL $key Key to retrieve from the PUT request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the PUT request; otherwise, NULL - */ - public function put($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_put_args; - } - - return isset($this->_put_args[$key]) ? $this->_xss_clean($this->_put_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a DELETE request - * - * @access public - * @param NULL $key Key to retrieve from the DELETE request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the DELETE request; otherwise, NULL - */ - public function delete($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_delete_args; - } - - return isset($this->_delete_args[$key]) ? $this->_xss_clean($this->_delete_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from a PATCH request - * - * @access public - * @param NULL $key Key to retrieve from the PATCH request - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the PATCH request; otherwise, NULL - */ - public function patch($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_patch_args; - } - - return isset($this->_patch_args[$key]) ? $this->_xss_clean($this->_patch_args[$key], $xss_clean) : NULL; - } - - /** - * Retrieve a value from the query parameters - * - * @access public - * @param NULL $key Key to retrieve from the query parameters - * If NULL an array of arguments is returned - * @param NULL $xss_clean Whether to apply XSS filtering - * @return array|string|NULL Value from the query parameters; otherwise, NULL - */ - public function query($key = NULL, $xss_clean = NULL) - { - if ($key === NULL) - { - return $this->_query_args; - } - - return isset($this->_query_args[$key]) ? $this->_xss_clean($this->_query_args[$key], $xss_clean) : NULL; - } - - /** - * Sanitizes data so that Cross Site Scripting Hacks can be - * prevented. - * - * @access protected - * @param string $value Input data - * @param bool $xss_clean Whether to apply XSS filtering - * @return string - */ - protected function _xss_clean($value, $xss_clean) - { - is_bool($xss_clean) || $xss_clean = $this->_enable_xss; - - return $xss_clean === TRUE ? $this->security->xss_clean($value) : $value; - } - - /** - * Retrieve the validation errors - * - * @access public - * @return array - */ - public function validation_errors() - { - $string = strip_tags($this->form_validation->error_string()); - - return explode(PHP_EOL, trim($string, PHP_EOL)); - } - - // SECURITY FUNCTIONS --------------------------------------------------------- - - /** - * Perform LDAP Authentication - * - * @access protected - * @param string $username The username to validate - * @param string $password The password to validate - * @return bool - */ - protected function _perform_ldap_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('debug', 'LDAP Auth: failure, empty username'); - return FALSE; - } - - log_message('debug', 'LDAP Auth: Loading configuration'); - - $this->config->load('ldap.php', TRUE); - - $ldap = [ - 'timeout' => $this->config->item('timeout', 'ldap'), - 'host' => $this->config->item('server', 'ldap'), - 'port' => $this->config->item('port', 'ldap'), - 'rdn' => $this->config->item('binduser', 'ldap'), - 'pass' => $this->config->item('bindpw', 'ldap'), - 'basedn' => $this->config->item('basedn', 'ldap'), - ]; - - log_message('debug', 'LDAP Auth: Connect to ' . (isset($ldaphost) ? $ldaphost : '[ldap not configured]')); - - // Connect to the ldap server - $ldapconn = ldap_connect($ldap['host'], $ldap['port']); - if ($ldapconn) - { - log_message('debug', 'Setting timeout to ' . $ldap['timeout'] . ' seconds'); - - ldap_set_option($ldapconn, LDAP_OPT_NETWORK_TIMEOUT, $ldap['timeout']); - - log_message('debug', 'LDAP Auth: Binding to ' . $ldap['host'] . ' with dn ' . $ldap['rdn']); - - // Binding to the ldap server - $ldapbind = ldap_bind($ldapconn, $ldap['rdn'], $ldap['pass']); - - // Verify the binding - if ($ldapbind === FALSE) - { - log_message('error', 'LDAP Auth: bind was unsuccessful'); - return FALSE; - } - - log_message('debug', 'LDAP Auth: bind successful'); - } - - // Search for user - if (($res_id = ldap_search($ldapconn, $ldap['basedn'], "uid=$username")) === FALSE) - { - log_message('error', 'LDAP Auth: User ' . $username . ' not found in search'); - return FALSE; - } - - if (ldap_count_entries($ldapconn, $res_id) !== 1) - { - log_message('error', 'LDAP Auth: Failure, username ' . $username . 'found more than once'); - return FALSE; - } - - if (($entry_id = ldap_first_entry($ldapconn, $res_id)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, entry of search result could not be fetched'); - return FALSE; - } - - if (($user_dn = ldap_get_dn($ldapconn, $entry_id)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, user-dn could not be fetched'); - return FALSE; - } - - // User found, could not authenticate as user - if (($link_id = ldap_bind($ldapconn, $user_dn, $password)) === FALSE) - { - log_message('error', 'LDAP Auth: Failure, username/password did not match: ' . $user_dn); - return FALSE; - } - - log_message('debug', 'LDAP Auth: Success ' . $user_dn . ' authenticated successfully'); - - $this->_user_ldap_dn = $user_dn; - - ldap_close($ldapconn); - - return TRUE; - } - - /** - * Perform Library Authentication - Override this function to change the way the library is called - * - * @access protected - * @param string $username The username to validate - * @param string $password The password to validate - * @return bool - */ - protected function _perform_library_auth($username = '', $password = NULL) - { - if (empty($username)) - { - log_message('error', 'Library Auth: Failure, empty username'); - return FALSE; - } - - $auth_library_class = strtolower($this->config->item('auth_library_class')); - $auth_library_function = strtolower($this->config->item('auth_library_function')); - - if (empty($auth_library_class)) - { - log_message('debug', 'Library Auth: Failure, empty auth_library_class'); - return FALSE; - } - - if (empty($auth_library_function)) - { - log_message('debug', 'Library Auth: Failure, empty auth_library_function'); - return FALSE; - } - - if (is_callable([$auth_library_class, $auth_library_function]) === FALSE) - { - $this->load->library($auth_library_class); - } - - return $this->{$auth_library_class}->$auth_library_function($username, $password); - } - - /** - * Check if the user is logged in - * - * @access protected - * @param string $username The user's name - * @param bool|string $password The user's password - * @return bool - */ - protected function _check_login($username = NULL, $password = FALSE) - { - if (empty($username)) - { - return FALSE; - } - - $auth_source = strtolower($this->config->item('auth_source')); - $rest_auth = strtolower($this->config->item('rest_auth')); - $valid_logins = $this->config->item('rest_valid_logins'); - - if (!$this->config->item('auth_source') && $rest_auth === 'digest') - { - // For digest we do not have a password passed as argument - return md5($username . ':' . $this->config->item('rest_realm') . ':' . (isset($valid_logins[$username]) ? $valid_logins[$username] : '')); - } - - if ($password === FALSE) - { - return FALSE; - } - - if ($auth_source === 'ldap') - { - log_message('debug', "Performing LDAP authentication for $username"); - - return $this->_perform_ldap_auth($username, $password); - } - - if ($auth_source === 'library') - { - log_message('debug', "Performing Library authentication for $username"); - - return $this->_perform_library_auth($username, $password); - } - - if (array_key_exists($username, $valid_logins) === FALSE) - { - return FALSE; - } - - if ($valid_logins[$username] !== $password) - { - return FALSE; - } - - return TRUE; - } - - /** - * Check to see if the user is logged in with a PHP session key - * - * @access protected - * @return void - */ - protected function _check_php_session() - { - // Get the auth_source config item - $key = $this->config->item('auth_source'); - - // If falsy, then the user isn't logged in - if (!$this->session->userdata($key)) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Prepares for basic authentication - * - * @access protected - * @return void - */ - protected function _prepare_basic_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - // Returns NULL if the SERVER variables PHP_AUTH_USER and HTTP_AUTHENTICATION don't exist - $username = $this->input->server('PHP_AUTH_USER'); - $http_auth = $this->input->server('HTTP_AUTHENTICATION'); - - $password = NULL; - if ($username !== NULL) - { - $password = $this->input->server('PHP_AUTH_PW'); - } - elseif ($http_auth !== NULL) - { - // If the authentication header is set as basic, then extract the username and password from - // HTTP_AUTHORIZATION e.g. my_username:my_password. This is passed in the .htaccess file - if (strpos(strtolower($http_auth), 'basic') === 0) - { - // Search online for HTTP_AUTHORIZATION workaround to explain what this is doing - list($username, $password) = explode(':', base64_decode(substr($this->input->server('HTTP_AUTHORIZATION'), 6))); - } - } - - // Check if the user is logged into the system - if ($this->_check_login($username, $password) === FALSE) - { - $this->_force_login(); - } - } - - /** - * Prepares for digest authentication - * - * @access protected - * @return void - */ - protected function _prepare_digest_auth() - { - // If whitelist is enabled it has the first chance to kick them out - if ($this->config->item('rest_ip_whitelist_enabled')) - { - $this->_check_whitelist_auth(); - } - - // We need to test which server authentication variable to use, - // because the PHP ISAPI module in IIS acts different from CGI - $digest_string = $this->input->server('PHP_AUTH_DIGEST'); - if ($digest_string === NULL) - { - $digest_string = $this->input->server('HTTP_AUTHORIZATION'); - } - - $unique_id = uniqid(); - - // The $_SESSION['error_prompted'] variable is used to ask the password - // again if none given or if the user enters wrong auth information - if (empty($digest_string)) - { - $this->_force_login($unique_id); - } - - // We need to retrieve authentication data from the $digest_string variable - $matches = []; - preg_match_all('@(username|nonce|uri|nc|cnonce|qop|response)=[\'"]?([^\'",]+)@', $digest_string, $matches); - $digest = (empty($matches[1]) || empty($matches[2])) ? [] : array_combine($matches[1], $matches[2]); - - // For digest authentication the library function should return already stored md5(username:restrealm:password) for that username @see rest.php::auth_library_function config - $username = $this->_check_login($digest['username'], TRUE); - if (array_key_exists('username', $digest) === FALSE || $username === FALSE) - { - $this->_force_login($unique_id); - } - - $md5 = md5(strtoupper($this->request->method) . ':' . $digest['uri']); - $valid_response = md5($username . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $md5); - - // Check if the string don't compare (case-insensitive) - if (strcasecmp($digest['response'], $valid_response) !== 0) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_invalid_credentials') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Checks if the client's ip is in the 'rest_ip_blacklist' config and generates a 401 response - * - * @access protected - * @return void - */ - protected function _check_blacklist_auth() - { - // Match an ip address in a blacklist e.g. 127.0.0.0, 0.0.0.0 - $pattern = sprintf('/(?:,\s*|^)\Q%s\E(?=,\s*|$)/m', $this->input->ip_address()); - - // Returns 1, 0 or FALSE (on error only). Therefore implicitly convert 1 to TRUE - if (preg_match($pattern, $this->config->item('rest_ip_blacklist'))) - { - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_denied') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Check if the client's ip is in the 'rest_ip_whitelist' config and generates a 401 response - * - * @access protected - * @return void - */ - protected function _check_whitelist_auth() - { - $whitelist = explode(',', $this->config->item('rest_ip_whitelist')); - - array_push($whitelist, '127.0.0.1', '0.0.0.0'); - - foreach ($whitelist as &$ip) - { - // As $ip is a reference, trim leading and trailing whitespace, then store the new value - // using the reference - $ip = trim($ip); - } - - if (in_array($this->input->ip_address(), $whitelist) === FALSE) - { - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_ip_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - } - - /** - * Force logging in by setting the WWW-Authenticate header - * - * @access protected - * @param string $nonce A server-specified data string which should be uniquely generated - * each time - * @return void - */ - protected function _force_login($nonce = '') - { - $rest_auth = $this->config->item('rest_auth'); - $rest_realm = $this->config->item('rest_realm'); - if (strtolower($rest_auth) === 'basic') - { - // See http://tools.ietf.org/html/rfc2617#page-5 - header('WWW-Authenticate: Basic realm="' . $rest_realm . '"'); - } - elseif (strtolower($rest_auth) === 'digest') - { - // See http://tools.ietf.org/html/rfc2617#page-18 - header( - 'WWW-Authenticate: Digest realm="' . $rest_realm - . '", qop="auth", nonce="' . $nonce - . '", opaque="' . md5($rest_realm) . '"'); - } - - // Display an error response - $this->response([ - $this->config->item('rest_status_field_name') => FALSE, - $this->config->item('rest_message_field_name') => $this->lang->line('text_rest_unauthorized') - ], self::HTTP_UNAUTHORIZED); - } - - /** - * Updates the log table with the total access time - * - * @access protected - * @author Chris Kacerguis - * @return bool TRUE log table updated; otherwise, FALSE - */ - protected function _log_access_time() - { - $payload['rtime'] = $this->_end_rtime - $this->_start_rtime; - - return $this->rest->db->update( - $this->config->item('rest_logs_table'), $payload, [ - 'id' => $this->_insert_id - ]); - } - - /** - * Updates the log table with HTTP response code - * - * @access protected - * @author Justin Chen - * @param $http_code int HTTP status code - * @return bool TRUE log table updated; otherwise, FALSE - */ - protected function _log_response_code($http_code) - { - $payload['response_code'] = $http_code; - - return $this->rest->db->update( - $this->config->item('rest_logs_table'), $payload, [ - 'id' => $this->_insert_id - ]); - } - - /** - * Check to see if the API key has access to the controller and methods - * - * @access protected - * @return bool TRUE the API key has access; otherwise, FALSE - */ - protected function _check_access() - { - // If we don't want to check access, just return TRUE - if ($this->config->item('rest_enable_access') === FALSE) - { - return TRUE; - } - - // Fetch controller based on path and controller name - $controller = implode( - '/', [ - $this->router->directory, - $this->router->class - ]); - - // Remove any double slashes for safety - $controller = str_replace('//', '/', $controller); - - // Query the access table and get the number of results - return $this->rest->db - ->where('key', $this->rest->key) - ->where('controller', $controller) - ->get($this->config->item('rest_access_table')) - ->num_rows() > 0; - } - -} diff --git a/application/views/rest_server.php b/application/views/rest_server.php deleted file mode 100644 index b5e378db..00000000 --- a/application/views/rest_server.php +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - REST Server Tests - - - - - -
-

REST Server Tests

- -
- -

Home

- -

- See the article - - http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/ - -

- -

- The master project repository is - - https://github.com/chriskacerguis/codeigniter-restserver - -

- -

- Click on the links to check whether the REST server is working. -

- -
    -
  1. Users - defaulting to JSON
  2. -
  3. Users - get it in CSV
  4. -
  5. User #1 - defaulting to JSON (users/id/1)
  6. -
  7. User #1 - defaulting to JSON (users/1)
  8. -
  9. User #1 - get it in XML (users/id/1.xml)
  10. -
  11. User #1 - get it in XML (users/id/1/format/xml)
  12. -
  13. User #1 - get it in XML (users/id/1?format=xml)
  14. -
  15. User #1 - get it in XML (users/1.xml)
  16. -
  17. Users - get it in JSON (AJAX request)
  18. -
  19. Users - get it in HTML (users.html)
  20. -
  21. Users - get it in HTML (users/format/html)
  22. -
  23. Users - get it in HTML (users?format=html)
  24. -
- -
- - -
- - - - - - - diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php deleted file mode 100644 index 1870d556..00000000 --- a/application/views/welcome_message.php +++ /dev/null @@ -1,98 +0,0 @@ - - - - - Welcome to CodeIgniter - - - - - -
-

Welcome to CodeIgniter!

- -
- -

REST Server Tests

- - -

REST Server Documentation

- - -

The page you are looking at is being generated dynamically by CodeIgniter.

- -

If you would like to edit this page you'll find it located at:

- application/views/welcome_message.php - -

The corresponding controller for this page is found at:

- application/controllers/Welcome.php - - -

If you are exploring CodeIgniter for the very first time, you should start by reading the User Guide.

- -
- - -
- - - diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..329d3b46 --- /dev/null +++ b/composer.json @@ -0,0 +1,17 @@ +{ + "name": "chriskacerguis/codeigniter-restserver", + "description": "CI Rest Server", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Chris Kacerguis", + "email": "chriskacerguis@gmail.com" + } + ], + "minimum-stability": "dev", + "autoload": { + "psr-4": {"chriskacerguis\\RestServer\\": "src/"} + }, + "require": {} +} diff --git a/documentation/404.html b/documentation/404.html deleted file mode 100644 index 27b3bc36..00000000 --- a/documentation/404.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - Page not found - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-Example.html b/documentation/class-Example.html deleted file mode 100644 index e8af6477..00000000 --- a/documentation/class-Example.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - Class Example - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-Format.html b/documentation/class-Format.html deleted file mode 100644 index 07d2a5df..00000000 --- a/documentation/class-Format.html +++ /dev/null @@ -1,983 +0,0 @@ - - - - - - Class Format - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-Key.html b/documentation/class-Key.html deleted file mode 100644 index 540d7d3e..00000000 --- a/documentation/class-Key.html +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - Class Key - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-REST_Controller.html b/documentation/class-REST_Controller.html deleted file mode 100644 index fc94cfdb..00000000 --- a/documentation/class-REST_Controller.html +++ /dev/null @@ -1,3863 +0,0 @@ - - - - - - Class REST_Controller - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-Rest_server.html b/documentation/class-Rest_server.html deleted file mode 100644 index 20eae472..00000000 --- a/documentation/class-Rest_server.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - Class Rest_server - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/class-Welcome.html b/documentation/class-Welcome.html deleted file mode 100644 index 2ba710ee..00000000 --- a/documentation/class-Welcome.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - Class Welcome - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/elementlist.js b/documentation/elementlist.js deleted file mode 100644 index b52ccbf6..00000000 --- a/documentation/elementlist.js +++ /dev/null @@ -1,3 +0,0 @@ - -var ApiGen = ApiGen || {}; -ApiGen.elements = [["c","Example"],["c","Format"],["c","Key"],["c","REST_Controller"],["c","Rest_server"],["c","Welcome"]]; diff --git a/documentation/index.html b/documentation/index.html deleted file mode 100644 index e2e1801a..00000000 --- a/documentation/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - Overview - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/package-CodeIgniter.Libraries.html b/documentation/package-CodeIgniter.Libraries.html deleted file mode 100644 index 2e0fe2ed..00000000 --- a/documentation/package-CodeIgniter.Libraries.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - Package CodeIgniter\Libraries - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/package-CodeIgniter.Rest.html b/documentation/package-CodeIgniter.Rest.html deleted file mode 100644 index f6d41a7d..00000000 --- a/documentation/package-CodeIgniter.Rest.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - Package CodeIgniter\Rest - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/package-CodeIgniter.html b/documentation/package-CodeIgniter.html deleted file mode 100644 index e9351dfc..00000000 --- a/documentation/package-CodeIgniter.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - Package CodeIgniter - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/package-None.html b/documentation/package-None.html deleted file mode 100644 index 08b3ad5c..00000000 --- a/documentation/package-None.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - No package - - - - - - -
- -
- -
- - - - - - diff --git a/documentation/resources/collapsed.png b/documentation/resources/collapsed.png deleted file mode 100644 index 56e73239..00000000 Binary files a/documentation/resources/collapsed.png and /dev/null differ diff --git a/documentation/resources/combined.js b/documentation/resources/combined.js deleted file mode 100644 index 84bde60a..00000000 --- a/documentation/resources/combined.js +++ /dev/null @@ -1,1315 +0,0 @@ - -var ApiGen = ApiGen || {}; -ApiGen.config = {"options":{"elementDetailsCollapsed":true,"elementsOrder":"natural"},"name":"ApiGen theme","templatesPath":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src","resources":{"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/resources":"resources"},"templates":{"overview":{"filename":"index.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/overview.latte"},"combined":{"filename":"resources\/combined.js","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/combined.js.latte"},"elementlist":{"filename":"elementlist.js","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/elementlist.js.latte"},"404":{"filename":"404.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/404.latte"},"package":{"filename":"package-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/package.latte"},"namespace":{"filename":"namespace-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/namespace.latte"},"class":{"filename":"class-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/class.latte"},"constant":{"filename":"constant-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/constant.latte"},"function":{"filename":"function-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/function.latte"},"annotationGroup":{"filename":"annotation-group-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/annotation-group.latte"},"source":{"filename":"source-%s.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/source.latte"},"tree":{"filename":"tree.html","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/tree.latte"},"sitemap":{"filename":"sitemap.xml","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/sitemap.xml.latte"},"opensearch":{"filename":"opensearch.xml","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/opensearch.xml.latte"},"robots":{"filename":"robots.txt","template":"phar:\/\/C:\/Users\/softwarespot\/Documents\/GitHub\/codeigniter-restserver\/apigen.phar\/bin\/..\/\/vendor\/apigen\/theme-default\/src\/robots.txt.latte"}}}; - - - /*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license -*/ -(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t -}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); -u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("