From c471a7aa3f59952141c4dc4d35afe44050bbed80 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 29 Jul 2009 01:16:49 +0000 Subject: [PATCH] Updates. git-svn-id: http://svn.cleancode.org/svn/pickles@144 4d10bc64-7434-11dc-a737-d2d0f8310089 --- classes/Controller.php | 258 +- classes/DB.php | 25 +- classes/Security.php | 149 +- classes/WebService/AuthorizeNet/AIM.php | 2 + classes/WebService/PayPal/Common.php | 75 + classes/WebService/PayPal/IPN.php | 38 + classes/WebService/PayPal/WPS.php | 64 + common/static/js/jquery-1.3.2.js | 4376 ----------------------- common/static/js/jquery-1.3.2.min.js | 19 + common/static/js/jquery.js | 2 +- pickles.php | 28 +- 11 files changed, 456 insertions(+), 4580 deletions(-) create mode 100644 classes/WebService/PayPal/Common.php create mode 100644 classes/WebService/PayPal/IPN.php create mode 100644 classes/WebService/PayPal/WPS.php delete mode 100644 common/static/js/jquery-1.3.2.js create mode 100644 common/static/js/jquery-1.3.2.min.js diff --git a/classes/Controller.php b/classes/Controller.php index 88edc72..c09cd73 100644 --- a/classes/Controller.php +++ b/classes/Controller.php @@ -128,144 +128,144 @@ class Controller extends Object { } } - /** - * @todo Maybe the logout shouldn't be an internal thing, what if - * the user wanted to call the logout page something else? or - * better yet, they want to next it, like /users/logout? - * @todo May want to make it work from /store/admin/logout and not - * just from / - */ - if ($module['requested']['name'] == 'logout') { - $security = new Security($config, $db); - $security->logout(); + // Loads the requested module's information + $module['requested']['filename'] = strtr($module['requested']['name'], '-', '_'); + $module['requested']['php_file'] = '../modules/' . $module['requested']['filename'] . '.php'; + $module['requested']['class_name'] = strtr($module['requested']['filename'], '/', '_'); + + // Establishes the shared module information + $module['shared']['name'] = $config->getSharedModule($module['requested']['name']); + $module['shared']['filename'] = strtr($module['shared']['name'], '-', '_'); + $module['shared']['php_file'] = PICKLES_PATH . 'common/modules/' . $module['shared']['filename'] . '.php'; + $module['shared']['class_name'] = strtr($module['shared']['filename'], '/', '_'); + + // Tries to load the site level module + if (file_exists($module['requested']['php_file'])) { + require_once $module['requested']['php_file']; + + if (class_exists($module['requested']['class_name'])) { + $module['object'] = new $module['requested']['class_name']($config, $db, $mailer, $error); + } } + // Tries to load the shared module + else if (file_exists($module['shared']['php_file']) && $module['shared']['name'] != false) { + require_once $module['shared']['php_file']; + + if (class_exists($module['shared']['class_name'])) { + $module['object'] = new $module['shared']['class_name']($config, $db, $mailer, $error); + } + } + // Loads the stock module else { - // Loads the requested module's information - $module['requested']['filename'] = strtr($module['requested']['name'], '-', '_'); - $module['requested']['php_file'] = '../modules/' . $module['requested']['filename'] . '.php'; - $module['requested']['class_name'] = strtr($module['requested']['filename'], '/', '_'); + $module['object'] = new Module($config, $db, $mailer, $error); + } - // Establishes the shared module information - $module['shared']['name'] = $config->getSharedModule($module['requested']['name']); - $module['shared']['filename'] = strtr($module['shared']['name'], '-', '_'); - $module['shared']['php_file'] = PICKLES_PATH . 'common/modules/' . $module['shared']['filename'] . '.php'; - $module['shared']['class_name'] = strtr($module['shared']['filename'], '/', '_'); + // Checks if we loaded a module file and no class was present + if ($module['object'] != null) { - // Tries to load the site level module - if (file_exists($module['requested']['php_file'])) { - require_once $module['requested']['php_file']; - - if (class_exists($module['requested']['class_name'])) { - $module['object'] = new $module['requested']['class_name']($config, $db, $mailer, $error); + // Potentially starts the session if it's not started already + if ($module['object']->getSession() === true) { + if (ini_get('session.auto_start') == 0) { + session_start(); } - } - // Tries to load the shared module - else if (file_exists($module['shared']['php_file']) && $module['shared']['name'] != false) { - require_once $module['shared']['php_file']; - - if (class_exists($module['shared']['class_name'])) { - $module['object'] = new $module['shared']['class_name']($config, $db, $mailer, $error); - } - } - // Loads the stock module - else { - $module['object'] = new Module($config, $db, $mailer, $error); - } - - // Checks if we loaded a module file and no class was present - if ($module['object'] != null) { - - // Potentially starts the session if it's not started already - if ($module['object']->getSession() === true) { - if (ini_get('session.auto_start') == 0) { - session_start(); - } - } - - // Potentially requests use authentication - if ($module['object']->getAuthentication() === true) { - if (!isset($security)) { - $security = new Security($config, $db); - } - $security->authenticate(); - } - - // Checks if the display type was passed in - if (!isset($display_type)) { - $display_type = $module['object']->getDisplay(); - } - - // Creates a new viewer object - $display_class = 'Display_' . $display_type; - $display = new $display_class($config, $error); - - // Potentially establishes caching - $caching = $module['object']->getCaching(); - if ($caching) { - $display->caching = $caching; - if ($display_type == DISPLAY_SMARTY) { - $module['object']->setSmartyObject($display->getSmartyObject()); - } - } - - $display->prepare(); - - // Potentially executes the module's logic - if (method_exists($module['object'], '__default')) { - $module['object']->__default(); - - if ($module['object']->getCacheID()) { - $display->cache_id = $module['object']->getCacheID(); - } - } - - // Overrides the name and filename with the passed name - if ($module['object']->name != null && $module['requested']['filename'] != $module['object']->name) { - $module['requested']['filename'] = $module['object']->name; - $module['requested']['name'] = $module['object']->name; - } - - // Overrides the filename with the passed template - if ($module['object']->template != null) { - $module['requested']['filename'] = $module['object']->template; - } - - // Overrides the shared template information with the passed shared template - if ($module['object']->shared_template != null) { - $module['shared']['class_name'] = $module['object']->shared_template; - $module['shared']['filename'] = strtr($module['shared']['class_name'], '_', '/'); - $module['shared']['php_file'] = PICKLES_PATH . 'common/modules/' . $module['shared']['filename'] . '.php'; - $module['shared']['name'] = $module['shared']['filename']; - } - - // Sets the display's properties - $display->module_name = $module['requested']['name']; - $display->module_filename = $module['requested']['filename']; - $display->shared_module_name = $module['shared']['name']; - $display->shared_module_filename = $module['shared']['filename']; - - if ($this->execute_tests == true) { - var_dump($module); - exit('caught test'); - } - - // Loads the module data into the display to be rendered + + // Performs a logout if requested /** - * @todo perhaps make this a passed variable + * @todo Maybe the logout shouldn't be an internal thing, what if + * the user wanted to call the logout page something else? or + * better yet, they want to next it, like /users/logout? + * @todo May want to make it work from /store/admin/logout and not + * just from / */ - $display->data = $module['object']->public; - - // Runs the requested rendering function - $display->render($module); - - // Do some cleanup - if (isset($security)) { - unset($security); + if ($module['requested']['name'] == 'logout') { + $security = new Security($config, $db); + $security->logout(); } - - unset($module, $viewer); - unset($db, $mailer, $config, $error); } + + // Potentially requests use authentication + if ($module['object']->getAuthentication() === true) { + if (!isset($security)) { + $security = new Security($config, $db); + } + $security->authenticate(); + } + + // Checks if the display type was passed in + if (!isset($display_type)) { + $display_type = $module['object']->getDisplay(); + } + + // Creates a new viewer object + $display_class = 'Display_' . $display_type; + $display = new $display_class($config, $error); + + // Potentially establishes caching + $caching = $module['object']->getCaching(); + if ($caching) { + $display->caching = $caching; + if ($display_type == DISPLAY_SMARTY) { + $module['object']->setSmartyObject($display->getSmartyObject()); + } + } + + $display->prepare(); + + // Potentially executes the module's logic + if (method_exists($module['object'], '__default')) { + $module['object']->__default(); + + if ($module['object']->getCacheID()) { + $display->cache_id = $module['object']->getCacheID(); + } + } + + // Overrides the name and filename with the passed name + if ($module['object']->name != null && $module['requested']['filename'] != $module['object']->name) { + $module['requested']['filename'] = $module['object']->name; + $module['requested']['name'] = $module['object']->name; + } + + // Overrides the filename with the passed template + if ($module['object']->template != null) { + $module['requested']['filename'] = $module['object']->template; + } + + // Overrides the shared template information with the passed shared template + if ($module['object']->shared_template != null) { + $module['shared']['class_name'] = $module['object']->shared_template; + $module['shared']['filename'] = strtr($module['shared']['class_name'], '_', '/'); + $module['shared']['php_file'] = PICKLES_PATH . 'common/modules/' . $module['shared']['filename'] . '.php'; + $module['shared']['name'] = $module['shared']['filename']; + } + + // Sets the display's properties + $display->module_name = $module['requested']['name']; + $display->module_filename = $module['requested']['filename']; + $display->shared_module_name = $module['shared']['name']; + $display->shared_module_filename = $module['shared']['filename']; + + if ($this->execute_tests == true) { + var_dump($module); + exit('caught test'); + } + + // Loads the module data into the display to be rendered + /** + * @todo perhaps make this a passed variable + */ + $display->data = $module['object']->public; + + // Runs the requested rendering function + $display->render($module); + + // Do some cleanup + if (isset($security)) { + unset($security); + } + + unset($module, $viewer); + unset($db, $mailer, $config, $error); } } } diff --git a/classes/DB.php b/classes/DB.php index 4584a08..3960473 100755 --- a/classes/DB.php +++ b/classes/DB.php @@ -334,8 +334,13 @@ class DB extends Object { " . implode($values, ", ") . " ); "); - - return mysql_insert_id($this->connection); + + if ($this->error->isError()) { + return false; + } + else { + return mysql_insert_id($this->connection); + } } else { $this->error->addError('No data was specified'); @@ -370,7 +375,21 @@ class DB extends Object { $fields = $where = null; if (is_array($values)) { foreach ($values as $key => $value) { - $fields .= ($fields ? ', ' : null) . $key . " = '" . mysql_real_escape_string(stripslashes($value), $this->connection) . "'"; + switch ($value) { + case null: + $value = 'NULL'; + break; + + case 'NOW()': + $value = 'NOW()'; + break; + + default: + $value = "'" . mysql_real_escape_string(stripslashes($value), $this->connection) . "'"; + break; + } + + $fields .= ($fields ? ', ' : null) . $key . " = " . $value; } if (is_array($conditions)) { diff --git a/classes/Security.php b/classes/Security.php index 65085fc..652b141 100644 --- a/classes/Security.php +++ b/classes/Security.php @@ -28,19 +28,14 @@ * Security Class * * Handles authenticating a user via an Apache login box. - * - * @todo Make the SQL less specific, right now you have to use a table - * named users, and use the email as the username. I will need - * to move this to the configuration and allow the user to - * specify which table to authenticate against, and what column - * names to use for the username and password. */ -class Security extends Object { - +class Security extends Object +{ private $config; private $db; - public function __construct(Config $config, DB $db) { + public function __construct(Config $config, DB $db) + { parent::__construct(); $this->config = $config; $this->db = $db; @@ -54,69 +49,89 @@ class Security extends Object { * the user cannot successfully they will be presented with a 401 * Unauthorized page. * - * @todo I'm sure someone will find the access denied message offensive, - * so this will need to be made more generic. May also want to - * add in the ability for someone to add a custom message and/or - * landing page in the configuration as well. + * @todo May also want to add in the ability for someone to add a custom + * message and/or landing page in the configuration as well. */ - public function authenticate() { - - if (isset($this->config->admin, $this->config->admin->username, $this->config->admin->password)) { - - $_SESSION['user_id'] = null; - - if (isset($_SERVER['PHP_AUTH_USER'])) { - if ( - $_SERVER['PHP_AUTH_USER'] == $this->config->admin->username - && $this->encrypt($this->config->admin->salt, $_SERVER['PHP_AUTH_PW']) == $this->config->admin->password - ) { - $_SESSION['user_id'] = 1; + public function authenticate() + { + if (!isset($_SESSION['user_id'])) + { + if (isset($this->config->admin, $this->config->admin->username, $this->config->admin->password)) + { + $_SESSION['user_id'] = null; + + if (isset($_SERVER['PHP_AUTH_USER'])) + { + if ($_SERVER['PHP_AUTH_USER'] == $this->config->admin->username && Security::doubleMD5($_SERVER['PHP_AUTH_PW'], $this->config->admin->salt) == $this->config->admin->password) + { + $_SESSION['user_id'] = 1; + } } } - } - else { + else + { + $table = array( + 'name' => 'users', + 'fields' => array( + 'id' => 'id', + 'username' => 'username', + 'password' => 'password' + ) + ); - $table = array( - 'name' => 'users', - 'fields' => array( - 'id' => 'id', - 'username' => 'username', - 'password' => 'password' - ) - ); + $table = $this->config->getTableMapping('users', $table); - $table = $this->config->getTableMapping('users', $table); + if (isset($_SERVER['PHP_AUTH_USER'])) + { + $from = ' + FROM ' . $table['name'] . ' + WHERE ' . $table['fields']['username'] . ' = "' . $_SERVER['PHP_AUTH_USER'] . '" + AND ' . $table['fields']['password'] . ' = "' . md5($_SERVER['PHP_AUTH_PW']) . '"; + '; - if (isset($_SERVER['PHP_AUTH_USER'])) { - $from = ' - FROM ' . $table['name'] . ' - WHERE ' . $table['fields']['username'] . ' = "' . $_SERVER['PHP_AUTH_USER'] . '" - AND ' . $table['fields']['password'] . ' = "' . md5($_SERVER['PHP_AUTH_PW']) . '"; - '; - - $this->db->execute('SELECT COUNT(' . $table['fields']['id'] . ') ' . $from); - if ($this->db->getField() != 0) { - $this->db->execute('SELECT ' . $table['fields']['id'] . ' ' . $from); - $_SESSION['user_id'] = $this->db->getField(); - } - else { - $_SESSION['user_id'] = null; + $this->db->execute('SELECT COUNT(' . $table['fields']['id'] . ') ' . $from); + if ($this->db->getField() != 0) + { + $this->db->execute('SELECT ' . $table['fields']['id'] . ' ' . $from); + $_SESSION['user_id'] = $this->db->getField(); + } + else + { + $_SESSION['user_id'] = null; + } } } } - if (!isset($_SESSION['user_id'])) { - header('WWW-Authenticate: Basic realm="' . $_SERVER['SERVER_NAME'] . ' Secured Page"'); - header('HTTP/1.0 401 Unauthorized'); - exit('Invalid login credentials, access denied.'); + if (!isset($_SESSION['user_id'])) + { + if ($this->config->modules->{'pre-login'}) + { + header('Location: /' . $this->config->modules->{'pre-login'}); + exit(); + } + else + { + header('WWW-Authenticate: Basic realm="' . $_SERVER['SERVER_NAME'] . ' Secured Page"'); + header('HTTP/1.0 401 Unauthorized'); + exit('Invalid login credentials, access denied.'); + } } - else { - /** - * @todo add logic to allow the site owner to force a redirect when a user logs in - */ - //header('Location: /'); - //exit(); + /* + else + { + if ($this->config->modules->{'post-login'}) + { + //header('Location: /' . $this->config->modules->{'post-login'}); + //exit(); + } + else + { + //header('Location: /'); + //exit(); + } } + */ } /** @@ -124,13 +139,21 @@ class Security extends Object { * * Destroys the session, and redirects the user to the root of the site. */ - public function logout() { + public function logout() + { session_destroy(); header('Location: /'); } - public function encrypt($salt, $string) { - return md5($salt . md5($salt . $string)); + public static function doubleMD5($string, $salt1 = null, $salt2 = null) + { + if (!isset($salt2)) + { + $salt2 = $salt1; + } + + + return md5($salt2 . md5($salt1 . $string)); } } diff --git a/classes/WebService/AuthorizeNet/AIM.php b/classes/WebService/AuthorizeNet/AIM.php index 9abfd6c..524e674 100644 --- a/classes/WebService/AuthorizeNet/AIM.php +++ b/classes/WebService/AuthorizeNet/AIM.php @@ -190,6 +190,8 @@ class WebService_AuthorizeNet_AIM extends WebService_Common { $response_values = explode('|', $response, 41); $response = array_combine($this->response_variables, $response_values); + file_put_contents('/tmp/authnet.log', print_r($response, true), FILE_APPEND); + // Trims all of the variables up // @todo Replace this with a user defined trim() and use array_walk() foreach ($response as $key => $value) { diff --git a/classes/WebService/PayPal/Common.php b/classes/WebService/PayPal/Common.php new file mode 100644 index 0000000..c1c7ae3 --- /dev/null +++ b/classes/WebService/PayPal/Common.php @@ -0,0 +1,75 @@ +. + * + * @author Joshua John Sherman + * @copyright Copyright 2009 Joshua John Sherman + * @link http://phpwithpickles.org + * @license http://www.gnu.org/copyleft/lesser.html + * @package PICKLES + */ + +/** + * Common PayPal Web Service Class + * + * This is the class that each PayPal gateway class should be extending from. + */ +abstract class WebService_PayPal_Common extends WebService_Common { + + private $test_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; + private $prod_url = 'https://www.paypal.com/cgi-bin/webscr'; + + protected $url = null; + + /** + * Constructor + * + * Runs the parent's constructor and adds the module to the object. + */ + public function __construct(Config $config, Error $error) { + parent::__construct($config, $error); + + $this->config = $config; + $this->error = $error; + + $this->url = $this->test_url; + + // @todo there is a test flag for paypal "test_ipn = 1" + + // Loads the API keys based on what URL is being loaded + // if (preg_match("/{$this->config->webservices->authorizenet_aim->domain}/", $_SERVER['HTTP_HOST'])) { + // $url = $this->prod_url; + // $login = $this->config->webservices->authorizenet_aim->login; + // $transaction_key = $this->config->webservices->authorizenet_aim->transaction_key; + // $test_request = 'FALSE'; + // } + // else { + // $url = $this->test_url; + // $login = $this->test_login; + // $transaction_key = $this->test_transaction_key; + // $test_request = 'TRUE'; + // } + } + + /** + * Abstract processing function that is overloaded within the loaded gateway + */ + //public abstract function process(); +} + +?> diff --git a/classes/WebService/PayPal/IPN.php b/classes/WebService/PayPal/IPN.php new file mode 100644 index 0000000..fdd6518 --- /dev/null +++ b/classes/WebService/PayPal/IPN.php @@ -0,0 +1,38 @@ +. + * + * @author Joshua John Sherman + * @copyright Copyright 2009 Joshua John Sherman + * @link http://phpwithpickles.org + * @license http://www.gnu.org/copyleft/lesser.html + * @package PICKLES + */ + +/** + * PayPal Instant Payment Notification (IPN) Web Service + */ +class WebService_PayPal_IPN extends WebService_PayPal_Common { + + public function process() { + + return $response; + } +} + +?> diff --git a/classes/WebService/PayPal/WPS.php b/classes/WebService/PayPal/WPS.php new file mode 100644 index 0000000..11dd3d8 --- /dev/null +++ b/classes/WebService/PayPal/WPS.php @@ -0,0 +1,64 @@ +. + * + * @author Joshua John Sherman + * @copyright Copyright 2009 Joshua John Sherman + * @link http://phpwithpickles.org + * @license http://www.gnu.org/copyleft/lesser.html + * @package PICKLES + */ + +/** + * PayPal Web Payments Standard (WPS) Web Service + */ +class WebService_PayPal_WPS extends WebService_PayPal_Common { + + private $variables = array( + 'rm' => 2, // 2 == POST + 'cmd' => '_xclick', // _xclick-subscriptions + ); + + public function set($variable, $value) { + $this->variables[$variable] = $value; + } + + // @todo ENCRYPT FORM VIA PAYPAL ENCRYPTED WEBSITE PAYMENTS + public function process() { + + $form = ' +
+

Please wait while you are redirected to PayPal.

+ If you are not redirected to PayPal within 5 seconds... + '; + + // Adds all the variables to the form + foreach ($this->variables as $variable => $value) { + $form .= '' . "\n"; + } + + $form .= ' + +
+ '; + + return $form; + } +} + +?> diff --git a/common/static/js/jquery-1.3.2.js b/common/static/js/jquery-1.3.2.js deleted file mode 100644 index 9263574..0000000 --- a/common/static/js/jquery-1.3.2.js +++ /dev/null @@ -1,4376 +0,0 @@ -/*! - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){ - -var - // Will speed up references to window, and allows munging its name. - window = this, - // Will speed up references to undefined, and allows munging its name. - undefined, - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - // Map over the $ in case of overwrite - _$ = window.$, - - jQuery = window.jQuery = window.$ = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); - }, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this[0] = selector; - this.length = 1; - this.context = selector; - return this; - } - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); - - // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); - - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem && elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - var ret = jQuery( elem || [] ); - ret.context = document; - ret.selector = selector; - return ret; - } - - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else - return jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document ).ready( selector ); - - // Make sure that old selector state is passed along - if ( selector.selector && selector.context ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return this.setArray(jQuery.isArray( selector ) ? - selector : - jQuery.makeArray(selector)); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.3.2", - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num === undefined ? - - // Return a 'clean' array - Array.prototype.slice.call( this ) : - - // Return just the object - this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = jQuery( elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) - ret.selector = this.selector + (this.selector ? " " : "") + selector; - else if ( name ) - ret.selector = this.selector + "." + name + "(" + selector + ")"; - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - Array.prototype.push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem && elem.jquery ? elem[0] : elem - , this ); - }, - - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( typeof name === "string" ) - if ( value === undefined ) - return this[0] && jQuery[ type || "attr" ]( this[0], name ); - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text !== "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).clone(); - - if ( this[0].parentNode ) - wrap.insertBefore( this[0] ); - - wrap.map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }).append(this); - } - - return this; - }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); - }, - - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); - }, - - append: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery( [] ); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: [].push, - sort: [].sort, - splice: [].splice, - - find: function( selector ) { - if ( this.length === 1 ) { - var ret = this.pushStack( [], "find", selector ); - ret.length = 0; - jQuery.find( selector, this[0], ret ); - return ret; - } else { - return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - })), "find", selector ); - } - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var html = this.outerHTML; - if ( !html ) { - var div = this.ownerDocument.createElement("div"); - div.appendChild( this.cloneNode(true) ); - html = div.innerHTML; - } - - return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; - } else - return this.cloneNode(true); - }); - - // Copy the events from the original to the clone - if ( events === true ) { - var orig = this.find("*").andSelf(), i = 0; - - ret.find("*").andSelf().each(function(){ - if ( this.nodeName !== orig[i].nodeName ) - return; - - var events = jQuery.data( orig[i], "events" ); - - for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); - } - } - - i++; - }); - } - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ - return elem.nodeType === 1; - }) ), "filter", selector ); - }, - - closest: function( selector ) { - var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, - closer = 0; - - return this.map(function(){ - var cur = this; - while ( cur && cur.ownerDocument ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { - jQuery.data(cur, "closest", closer); - return cur; - } - cur = cur.parentNode; - closer++; - } - }); - }, - - not: function( selector ) { - if ( typeof selector === "string" ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return this.pushStack( jQuery.unique( jQuery.merge( - this.get(), - typeof selector === "string" ? - jQuery( selector ) : - jQuery.makeArray( selector ) - ))); - }, - - is: function( selector ) { - return !!selector && jQuery.multiFilter( selector, this ).length > 0; - }, - - hasClass: function( selector ) { - return !!selector && this.is( "." + selector ); - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if( jQuery.nodeName( elem, 'option' ) ) - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Everything else, we just grab the value - return (elem.value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - if ( typeof value === "number" ) - value += ''; - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(value); - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value === undefined ? - (this[0] ? - this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, +i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ), - "slice", Array.prototype.slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function( args, table, callback ) { - if ( this[0] ) { - var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), - scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild; - - if ( first ) - for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), this.length > 1 || i > 0 ? - fragment.cloneNode(true) : fragment ); - - if ( scripts ) - jQuery.each( scripts, evalScript ); - } - - return this; - - function root( elem, cur ) { - return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? - (elem.getElementsByTagName("tbody")[0] || - elem.appendChild(elem.ownerDocument.createElement("tbody"))) : - elem; - } - } -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -function now(){ - return +new Date; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) - target = {}; - - // extend jQuery itself if only one argument is passed - if ( length == i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) - // Extend the base object - for ( var name in options ) { - var src = target[ name ], copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) - continue; - - // Recurse if we're merging object values - if ( deep && copy && typeof copy === "object" && !copy.nodeType ) - target[ name ] = jQuery.extend( deep, - // Never move original objects, clone them - src || ( copy.length != null ? [ ] : { } ) - , copy ); - - // Don't bring in undefined values - else if ( copy !== undefined ) - target[ name ] = copy; - - } - - // Return the modified object - return target; -}; - -// exclude the following css properties to add px -var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, - // cache defaultView - defaultView = document.defaultView || {}, - toString = Object.prototype.toString; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) - window.jQuery = _jQuery; - - return jQuery; - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || - !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); - }, - - // Evalulates a script in a global context - globalEval: function( data ) { - if ( data && /\S/.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - if ( jQuery.support.scriptEval ) - script.appendChild( document.createTextNode( data ) ); - else - script.text = data; - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, length = object.length; - - if ( args ) { - if ( length === undefined ) { - for ( name in object ) - if ( callback.apply( object[ name ], args ) === false ) - break; - } else - for ( ; i < length; ) - if ( callback.apply( object[ i++ ], args ) === false ) - break; - - // A special, fast, case for the most common use of each - } else { - if ( length === undefined ) { - for ( name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) - break; - } else - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} - } - - return object; - }, - - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames !== undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use hasClass("class") - has: function( elem, className ) { - return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force, extra ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - - if ( extra === "border" ) - return; - - jQuery.each( which, function() { - if ( !extra ) - val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - if ( extra === "margin" ) - val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; - else - val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - } - - if ( elem.offsetWidth !== 0 ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, Math.round(val)); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret, style = elem.style; - - // We need to handle opacity special in IE - if ( name == "opacity" && !jQuery.support.opacity ) { - ret = jQuery.attr( style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && style && style[ name ] ) - ret = style[ name ]; - - else if ( defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var computedStyle = defaultView.getComputedStyle( elem, null ); - - if ( computedStyle ) - ret = computedStyle.getPropertyValue( name ); - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var left = style.left, rsLeft = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - style.left = ret || 0; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - elem.runtimeStyle.left = rsLeft; - } - } - - return ret; - }, - - clean: function( elems, context, fragment ) { - context = context || document; - - // !context.createElement fails in IE with an error but returns typeof 'object' - if ( typeof context.createElement === "undefined" ) - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { - var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); - if ( match ) - return [ context.createElement( match[1] ) ]; - } - - var ret = [], scripts = [], div = context.createElement("div"); - - jQuery.each(elems, function(i, elem){ - if ( typeof elem === "number" ) - elem += ''; - - if ( !elem ) - return; - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); - - var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
" ] || - - !tags.indexOf("", "" ] || - - // matched above - (!tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - // IE can't serialize and