Version Notes
Supports Magento v1.6 and later
Download this release
Release Info
Developer | Aric Watson |
Extension | HE_TwoFactorAuth |
Version | 1.0.4 |
Comparing to | |
See all releases |
Version 1.0.4
- app/code/community/HE/TwoFactorAuth/Block/Adminhtml/System/Config/Fieldset/Hint.php +29 -0
- app/code/community/HE/TwoFactorAuth/Block/Validate.php +9 -0
- app/code/community/HE/TwoFactorAuth/Helper/Data.php +99 -0
- app/code/community/HE/TwoFactorAuth/Model/Observer.php +236 -0
- app/code/community/HE/TwoFactorAuth/Model/Resource/Mysql4/Setup.php +5 -0
- app/code/community/HE/TwoFactorAuth/Model/Sysconfig/Appkey.php +46 -0
- app/code/community/HE/TwoFactorAuth/Model/Sysconfig/Provider.php +32 -0
- app/code/community/HE/TwoFactorAuth/Model/Validate.php +35 -0
- app/code/community/HE/TwoFactorAuth/Model/Validate/Duo.php +103 -0
- app/code/community/HE/TwoFactorAuth/Model/Validate/Duo/Request.php +265 -0
- app/code/community/HE/TwoFactorAuth/Model/Validate/Google.php +124 -0
- app/code/community/HE/TwoFactorAuth/controllers/Adminhtml/TwofactorController.php +302 -0
- app/code/community/HE/TwoFactorAuth/etc/adminhtml.xml +23 -0
- app/code/community/HE/TwoFactorAuth/etc/config.xml +163 -0
- app/code/community/HE/TwoFactorAuth/etc/system.xml +203 -0
- app/code/community/HE/TwoFactorAuth/readme.txt +47 -0
- app/code/community/HE/TwoFactorAuth/sql/he_twofactorauth/mysql4-install-1.0.2.php +19 -0
- app/design/adminhtml/default/default/layout/he_twofactor/auth.xml +33 -0
- app/design/adminhtml/default/default/template/he_twofactor/duo/auth.phtml +57 -0
- app/design/adminhtml/default/default/template/he_twofactor/google/auth.phtml +121 -0
- app/design/adminhtml/default/default/template/he_twofactor/system/config/fieldset/hint.phtml +48 -0
- app/etc/modules/HE_TwoFactorAuth.xml +9 -0
- js/he_twofactor/Duo-Web-v1.bundled.js +6573 -0
- js/he_twofactor/Duo-Web-v1.bundled.min.js +32 -0
- js/he_twofactor/Duo-Web-v1.js +325 -0
- js/he_twofactor/adminpanel.js +100 -0
- js/he_twofactor/readme.txt +1 -0
- lib/Duo/duo_web.php +88 -0
- lib/GoogleAuthenticator/PHPGangsta/GoogleAuthenticator.php +205 -0
- package.xml +2 -0
app/code/community/HE/TwoFactorAuth/Block/Adminhtml/System/Config/Fieldset/Hint.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Renderer for Hint Banner in System Configuration
|
5 |
+
*/
|
6 |
+
class HE_TwoFactorAuth_Block_Adminhtml_System_Config_Fieldset_Hint
|
7 |
+
extends Mage_Adminhtml_Block_Abstract
|
8 |
+
implements Varien_Data_Form_Element_Renderer_Interface
|
9 |
+
{
|
10 |
+
protected $_template = 'he_twofactor/system/config/fieldset/hint.phtml';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Render fieldset html
|
14 |
+
*
|
15 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
16 |
+
*
|
17 |
+
* @return string
|
18 |
+
*/
|
19 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
20 |
+
{
|
21 |
+
return $this->toHtml();
|
22 |
+
}
|
23 |
+
|
24 |
+
public function getTwoFactorAuthVersion()
|
25 |
+
{
|
26 |
+
return (string)Mage::getConfig()->getNode('modules/HE_TwoFactorAuth/version');
|
27 |
+
}
|
28 |
+
|
29 |
+
}
|
app/code/community/HE/TwoFactorAuth/Block/Validate.php
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class HE_TwoFactorAuth_Block_Validate extends Mage_Core_Block_Template
|
4 |
+
{
|
5 |
+
public function getSaveUrl()
|
6 |
+
{
|
7 |
+
return $this->getUrl('twofactorauth/interstitial/verify');
|
8 |
+
}
|
9 |
+
}
|
app/code/community/HE/TwoFactorAuth/Helper/Data.php
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* For more information on Duo security's API, please see -
|
10 |
+
* https://www.duosecurity.com
|
11 |
+
*/
|
12 |
+
|
13 |
+
class HE_TwoFactorAuth_Helper_Data extends Mage_Core_Helper_Abstract
|
14 |
+
{
|
15 |
+
|
16 |
+
public function __construct()
|
17 |
+
{
|
18 |
+
$this->_provider = Mage::getStoreConfig('he2faconfig/control/provider');
|
19 |
+
$this->_logging = Mage::getStoreConfig('he2faconfig/control/logging');
|
20 |
+
$this->_logAccess = Mage::getStoreConfig('he2faconfig/control/logaccess');
|
21 |
+
$this->_ipWhitelist = $this->getIPWhitelist();
|
22 |
+
}
|
23 |
+
|
24 |
+
public function isDisabled()
|
25 |
+
{
|
26 |
+
$tfaFlag = Mage::getBaseDir('base') . '/tfaoff.flag';
|
27 |
+
|
28 |
+
if (file_exists($tfaFlag)) {
|
29 |
+
if ($this->shouldLog()) {
|
30 |
+
Mage::log("isDisabled - Found tfaoff.flag, TFA disabled.", 0, "two_factor_auth.log");
|
31 |
+
}
|
32 |
+
|
33 |
+
return true;
|
34 |
+
}
|
35 |
+
|
36 |
+
if (!$this->_provider || $this->_provider == 'disabled') {
|
37 |
+
return true;
|
38 |
+
}
|
39 |
+
|
40 |
+
$method = Mage::getSingleton('he_twofactorauth/validate_' . $this->_provider);
|
41 |
+
|
42 |
+
if (!$method) {
|
43 |
+
return true;
|
44 |
+
}
|
45 |
+
|
46 |
+
return !$method->isValid();
|
47 |
+
}
|
48 |
+
|
49 |
+
public function getProvider()
|
50 |
+
{
|
51 |
+
return $this->_provider;
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
public function shouldLog()
|
56 |
+
{
|
57 |
+
return $this->_logging;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function shouldLogAccess()
|
61 |
+
{
|
62 |
+
return $this->_logAccess;
|
63 |
+
}
|
64 |
+
|
65 |
+
public function disable2FA()
|
66 |
+
{
|
67 |
+
Mage::getModel('core/config')->saveConfig('he2faconfig/control/provider', 'disabled');
|
68 |
+
Mage::app()->getStore()->resetConfig();
|
69 |
+
}
|
70 |
+
|
71 |
+
private function getIPWhitelist()
|
72 |
+
{
|
73 |
+
$return = [];
|
74 |
+
$ips = preg_split("/\r\n|\n|\r/", trim(Mage::getStoreConfig('he2faconfig/control/ipwhitelist')));
|
75 |
+
foreach ($ips as $ip) {
|
76 |
+
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
77 |
+
$return[] = trim($ip);
|
78 |
+
}
|
79 |
+
}
|
80 |
+
return $return;
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
public function inWhitelist($ip)
|
85 |
+
{
|
86 |
+
if (count($this->_ipWhitelist) == 0) { return false; }
|
87 |
+
|
88 |
+
if (in_array( $ip, $this->_ipWhitelist )) {
|
89 |
+
if ( $this->shouldLogAccess() ) {
|
90 |
+
Mage::log("TFA bypassed for IP $ip - whitelisted", 0, "two_factor_auth.log");
|
91 |
+
}
|
92 |
+
return true;
|
93 |
+
}
|
94 |
+
else {
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Observer.php
ADDED
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* Observer watches login attempts (currently admin only) and will enforce multi-factor
|
10 |
+
* authentication if not disabled.
|
11 |
+
*
|
12 |
+
* For more information on Duo security's API, please see -
|
13 |
+
* https://www.duosecurity.com
|
14 |
+
*/
|
15 |
+
|
16 |
+
class HE_TwoFactorAuth_Model_Observer
|
17 |
+
{
|
18 |
+
protected $_allowedActions = array('login', 'forgotpassword');
|
19 |
+
|
20 |
+
public function __construct()
|
21 |
+
{
|
22 |
+
$this->_shouldLog = Mage::helper('he_twofactorauth')->shouldLog();
|
23 |
+
}
|
24 |
+
|
25 |
+
public function admin_user_authenticate_after($observer)
|
26 |
+
{
|
27 |
+
if (Mage::helper('he_twofactorauth')->isDisabled()) {
|
28 |
+
return;
|
29 |
+
}
|
30 |
+
|
31 |
+
// check ip-whitelist
|
32 |
+
if (Mage::helper('he_twofactorauth')->inWhitelist( Mage::helper('core/http')->getRemoteAddr() )) {
|
33 |
+
Mage::getSingleton('admin/session')->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE);
|
34 |
+
}
|
35 |
+
|
36 |
+
if (Mage::getSingleton('admin/session')->get2faState() != HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE) {
|
37 |
+
|
38 |
+
if ($this->_shouldLog) {
|
39 |
+
Mage::log("authenticate_after - get2faState is not active", 0, "two_factor_auth.log");
|
40 |
+
}
|
41 |
+
|
42 |
+
// set we are processing 2f login
|
43 |
+
Mage::getSingleton('admin/session')->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_PROCESSING);
|
44 |
+
|
45 |
+
$provider = Mage::helper('he_twofactorauth/data')->getProvider();
|
46 |
+
|
47 |
+
//redirect to the 2f login page
|
48 |
+
$twoFactAuthPage = Mage::helper("adminhtml")->getUrl("adminhtml/twofactor/$provider");
|
49 |
+
|
50 |
+
if ($this->_shouldLog) {
|
51 |
+
Mage::log("authenticate_after - redirect to $twoFactAuthPage", 0, "two_factor_auth.log");
|
52 |
+
}
|
53 |
+
|
54 |
+
Mage::app()->getResponse()
|
55 |
+
->setRedirect($twoFactAuthPage)
|
56 |
+
->sendResponse();
|
57 |
+
exit();
|
58 |
+
} else {
|
59 |
+
if ($this->_shouldLog) {
|
60 |
+
Mage::log("authenticate_after - getValid2Fa is true", 0, "two_factor_auth.log");
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
/***
|
66 |
+
* controller to check for valid 2fa
|
67 |
+
* admin states
|
68 |
+
*
|
69 |
+
* @param $observer
|
70 |
+
*/
|
71 |
+
|
72 |
+
public function check_twofactor_active($observer)
|
73 |
+
{
|
74 |
+
if (Mage::helper('he_twofactorauth')->isDisabled()) {
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
|
78 |
+
$request = $observer->getControllerAction()->getRequest();
|
79 |
+
$tfaState = Mage::getSingleton('admin/session')->get2faState();
|
80 |
+
$action = Mage::app()->getRequest()->getActionName();
|
81 |
+
|
82 |
+
switch ($tfaState) {
|
83 |
+
case HE_TwoFactorAuth_Model_Validate::TFA_STATE_NONE:
|
84 |
+
if ($this->_shouldLog) {
|
85 |
+
Mage::log("check_twofactor_active - tfa state none", 0, "two_factor_auth.log");
|
86 |
+
}
|
87 |
+
break;
|
88 |
+
case HE_TwoFactorAuth_Model_Validate::TFA_STATE_PROCESSING:
|
89 |
+
if ($this->_shouldLog) {
|
90 |
+
Mage::log("check_twofactor_active - tfa state processing", 0, "two_factor_auth.log");
|
91 |
+
}
|
92 |
+
break;
|
93 |
+
case HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE:
|
94 |
+
if ($this->_shouldLog) {
|
95 |
+
Mage::log("check_twofactor_active - tfa state active", 0, "two_factor_auth.log");
|
96 |
+
}
|
97 |
+
break;
|
98 |
+
default:
|
99 |
+
if ($this->_shouldLog) {
|
100 |
+
Mage::log("check_twofactor_active - tfa state unknown - " . $tfaState, 0, "two_factor_auth.log");
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
if ($action == 'logout') {
|
105 |
+
if ($this->_shouldLog) {
|
106 |
+
Mage::log("check_twofactor_active - logout", 0, "two_factor_auth.log");
|
107 |
+
}
|
108 |
+
Mage::getSingleton('admin/session')->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_NONE);
|
109 |
+
|
110 |
+
return $this;
|
111 |
+
}
|
112 |
+
|
113 |
+
if (in_array($action, $this->_allowedActions)) {
|
114 |
+
return $this;
|
115 |
+
}
|
116 |
+
|
117 |
+
if ($request->getControllerName() == 'twofactor'
|
118 |
+
|| $tfaState == HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE
|
119 |
+
) {
|
120 |
+
if ($this->_shouldLog) {
|
121 |
+
Mage::log(
|
122 |
+
"check_twofactor_active - return controller twofactor or is active", 0, "two_factor_auth.log"
|
123 |
+
);
|
124 |
+
}
|
125 |
+
|
126 |
+
return $this;
|
127 |
+
}
|
128 |
+
|
129 |
+
if (Mage::getSingleton('admin/session')->get2faState() != HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE) {
|
130 |
+
|
131 |
+
if ($this->_shouldLog) {
|
132 |
+
Mage::log("check_twofactor_active - not active, try again", 0, "two_factor_auth.log");
|
133 |
+
}
|
134 |
+
|
135 |
+
$msg = Mage::helper('he_twofactorauth')->__(
|
136 |
+
'You must complete Two Factor Authentication before accessing Magento administration'
|
137 |
+
);
|
138 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
139 |
+
|
140 |
+
// set we are processing 2f login
|
141 |
+
Mage::getSingleton('admin/session')->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_PROCESSING);
|
142 |
+
|
143 |
+
$provider = Mage::helper('he_twofactorauth')->getProvider();
|
144 |
+
$twoFactAuthPage = Mage::helper("adminhtml")->getUrl("adminhtml/twofactor/$provider");
|
145 |
+
|
146 |
+
//disable the dispatch for now
|
147 |
+
$request = Mage::app()->getRequest();
|
148 |
+
$action = $request->getActionName();
|
149 |
+
Mage::app()->getFrontController()
|
150 |
+
->getAction()
|
151 |
+
->setFlag($action, Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH, true);
|
152 |
+
|
153 |
+
$response = Mage::app()->getResponse();
|
154 |
+
|
155 |
+
if ($this->_shouldLog) {
|
156 |
+
Mage::log("check_twofactor_active - redirect to $twoFactAuthPage", 0, "two_factor_auth.log");
|
157 |
+
}
|
158 |
+
|
159 |
+
$response->setRedirect($twoFactAuthPage)->sendResponse();
|
160 |
+
exit();
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
/*
|
165 |
+
* Add a fieldset and field to the admin edit user form
|
166 |
+
* in order to allow selective clearing of a users shared secret (google)
|
167 |
+
*/
|
168 |
+
|
169 |
+
public function googleClearSecretCheck(Varien_Event_Observer $observer)
|
170 |
+
{
|
171 |
+
$block = $observer->getEvent()->getBlock();
|
172 |
+
|
173 |
+
if (!isset($block)) {
|
174 |
+
return $this;
|
175 |
+
}
|
176 |
+
|
177 |
+
if ($block->getType() == 'adminhtml/permissions_user_edit_form') {
|
178 |
+
|
179 |
+
// check that google is set for twofactor authentication
|
180 |
+
if (Mage::helper('he_twofactorauth')->getProvider() == 'google') {
|
181 |
+
//create new custom fieldset 'website'
|
182 |
+
$form = $block->getForm();
|
183 |
+
$fieldset = $form->addFieldset(
|
184 |
+
'website_field', array(
|
185 |
+
'legend' => 'Google Authenticator',
|
186 |
+
'class' => 'fieldset-wide'
|
187 |
+
)
|
188 |
+
);
|
189 |
+
|
190 |
+
$fieldset->addField(
|
191 |
+
'checkbox', 'checkbox', array(
|
192 |
+
'label' => Mage::helper('he_twofactorauth')->__(
|
193 |
+
'Reset Google Authenticator'
|
194 |
+
),
|
195 |
+
'name' => 'clear_google_secret',
|
196 |
+
'checked' => false,
|
197 |
+
'onclick' => "",
|
198 |
+
'onchange' => "",
|
199 |
+
'value' => '1',
|
200 |
+
'disabled' => false,
|
201 |
+
'after_element_html' => '<small>Check this and save to reset this user\'s Google Authenticator.<br />They will need to use the QR code to reconnect their device after their next successful login.</small>',
|
202 |
+
'tabindex' => 1
|
203 |
+
)
|
204 |
+
);
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
|
210 |
+
/*
|
211 |
+
* Clear a user's google secret field if request
|
212 |
+
*
|
213 |
+
*/
|
214 |
+
public function googleSaveClear(Varien_Event_Observer $observer)
|
215 |
+
{
|
216 |
+
// check that a user record has been saved
|
217 |
+
|
218 |
+
// if google is turned and 2fa active...
|
219 |
+
if ((Mage::helper('he_twofactorauth')->getProvider() == 'google')
|
220 |
+
&& (!Mage::helper('he_twofactorauth')->isDisabled())
|
221 |
+
) {
|
222 |
+
$params = Mage::app()->getRequest()->getParams();
|
223 |
+
if (isset($params['clear_google_secret'])) {
|
224 |
+
if ($params['clear_google_secret'] == 1) {
|
225 |
+
$object = $observer->getEvent()->getObject();
|
226 |
+
$object->setTwofactorGoogleSecret(''); // just clear the secret
|
227 |
+
|
228 |
+
Mage::log(
|
229 |
+
"Clearing google secret for admin user (" . $object->getUsername() . ")", 0,
|
230 |
+
"two_factor_auth.log"
|
231 |
+
);
|
232 |
+
}
|
233 |
+
}
|
234 |
+
}
|
235 |
+
}
|
236 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Resource/Mysql4/Setup.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class HE_TwoFactorAuth_Model_Resource_Mysql4_Setup extends Mage_Core_Model_Resource_Setup {
|
4 |
+
|
5 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Sysconfig/Appkey.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
* Author : Greg Croasdill
|
4 |
+
* Human Element, Inc http://www.human-element.com
|
5 |
+
*
|
6 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
7 |
+
*
|
8 |
+
* For more information on Duo security's API, please see -
|
9 |
+
* https://www.duosecurity.com
|
10 |
+
*/
|
11 |
+
|
12 |
+
|
13 |
+
class HE_TwoFactorAuth_Model_Sysconfig_Appkey extends Mage_Adminhtml_Model_System_Config_Backend_Encrypted
|
14 |
+
{
|
15 |
+
public function save()
|
16 |
+
{
|
17 |
+
// TODO - check to see if Duo is enabled
|
18 |
+
$appkey = $this->getValue(); //get the value from our config
|
19 |
+
|
20 |
+
if(strlen($appkey) < 40) //exit if we're less than 50 characters
|
21 |
+
{
|
22 |
+
Mage::throwException("The Duo application key needs to be at least 40 characters long.");
|
23 |
+
}
|
24 |
+
return parent::save(); //call original save method so whatever happened
|
25 |
+
}
|
26 |
+
|
27 |
+
protected function _afterLoad()
|
28 |
+
{
|
29 |
+
$value = (string)$this->getValue();
|
30 |
+
if (empty($value)) {
|
31 |
+
$key = $this->generateKey(40);
|
32 |
+
$this->setValue($key);
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
function generateKey($length=40)
|
37 |
+
{
|
38 |
+
$charset='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
39 |
+
$count = strlen($charset);
|
40 |
+
$str = '';
|
41 |
+
while ($length--) {
|
42 |
+
$str .= $charset[mt_rand(0, $count-1)];
|
43 |
+
}
|
44 |
+
return $str;
|
45 |
+
}
|
46 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Sysconfig/Provider.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* For more information on Duo security's API, please see -
|
10 |
+
* https://www.duosecurity.com
|
11 |
+
*/
|
12 |
+
|
13 |
+
class HE_TwoFactorAuth_Model_Sysconfig_Provider
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* Options getter - creates a list of options from a list of providers in config.xml
|
17 |
+
*/
|
18 |
+
public function toOptionArray()
|
19 |
+
{
|
20 |
+
// get the list of providers from the validator class
|
21 |
+
|
22 |
+
$providersXML = Mage::getStoreConfig('he2faconfig/providers'); //set in config.xml
|
23 |
+
|
24 |
+
$providers=array();
|
25 |
+
|
26 |
+
foreach($providersXML as $provider => $node) {
|
27 |
+
$providers[]=(array('value' => $provider , 'label' => $node['title']));
|
28 |
+
}
|
29 |
+
|
30 |
+
return $providers;
|
31 |
+
}
|
32 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Validate.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* For more information on Duo security's API, please see -
|
10 |
+
* https://www.duosecurity.com
|
11 |
+
*/
|
12 |
+
|
13 |
+
|
14 |
+
class HE_TwoFactorAuth_Model_Validate extends Mage_Core_Model_Abstract
|
15 |
+
{
|
16 |
+
const TFA_STATE_NONE = 0;
|
17 |
+
const TFA_STATE_PROCESSING = 1;
|
18 |
+
const TFA_STATE_ACTIVE = 2;
|
19 |
+
|
20 |
+
const TFA_CHECK_FAIL = 0;
|
21 |
+
const TFA_CHECK_SUCCESS = 1;
|
22 |
+
|
23 |
+
public function signRequest($user)
|
24 |
+
{
|
25 |
+
}
|
26 |
+
|
27 |
+
public function verifyResponse($response)
|
28 |
+
{
|
29 |
+
}
|
30 |
+
|
31 |
+
public function isValid()
|
32 |
+
{
|
33 |
+
return $this::TFA_CHECK_FAIL;
|
34 |
+
}
|
35 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Validate/Duo.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
* Author : Greg Croasdill
|
4 |
+
* Human Element, Inc http://www.human-element.com
|
5 |
+
*
|
6 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
7 |
+
*
|
8 |
+
* For more information on Duo security's API, please see -
|
9 |
+
* https://www.duosecurity.com
|
10 |
+
*/
|
11 |
+
|
12 |
+
include_once Mage::getBaseDir('lib') . DS . 'Duo' . DS . 'duo_web.php';
|
13 |
+
|
14 |
+
class HE_TwoFactorAuth_Model_Validate_Duo extends HE_TwoFactorAuth_Model_Validate
|
15 |
+
{
|
16 |
+
public function __construct()
|
17 |
+
{
|
18 |
+
$this->_helper = Mage::helper('he_twofactorauth');
|
19 |
+
|
20 |
+
$this->_host = Mage::getStoreConfig('he2faconfig/duo/host');
|
21 |
+
$this->_ikey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/ikey'));
|
22 |
+
$this->_skey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/skey'));
|
23 |
+
$this->_akey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/akey'));
|
24 |
+
|
25 |
+
if (!($this->_host && $this->_ikey && $this->_skey && $this->_akey)) {
|
26 |
+
$this->_helper->disable2FA();
|
27 |
+
$msg = $this->_helper->__(
|
28 |
+
'Duo Twofactor Authentication is missing one or more settings. Please configure HE Two Factor Authentication.'
|
29 |
+
);
|
30 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
31 |
+
}
|
32 |
+
|
33 |
+
$this->_shouldLog = Mage::helper('he_twofactorauth')->shouldLog();
|
34 |
+
}
|
35 |
+
|
36 |
+
public function signRequest($user)
|
37 |
+
{
|
38 |
+
if ($this->_shouldLog) {
|
39 |
+
Mage::log("in signRequest with $user", 0, "two_factor_auth.log");
|
40 |
+
}
|
41 |
+
$sig_request = Duo::signRequest($this->_ikey, $this->_skey, $this->_akey, $user);
|
42 |
+
|
43 |
+
return $sig_request;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function verifyResponse($response)
|
47 |
+
{
|
48 |
+
$verified = Duo::verifyResponse($this->_ikey, $this->_skey, $this->_akey, $response);
|
49 |
+
|
50 |
+
return ($verified != null);
|
51 |
+
}
|
52 |
+
|
53 |
+
public function getHost()
|
54 |
+
{
|
55 |
+
return $this->_host;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function check()
|
59 |
+
{
|
60 |
+
return Mage::getModel('he_twofactorauth/validate_duo_request')->check();
|
61 |
+
}
|
62 |
+
|
63 |
+
public function isValid()
|
64 |
+
{
|
65 |
+
|
66 |
+
$status = HE_TwoFactorAuth_Model_Validate::TFA_CHECK_FAIL;
|
67 |
+
|
68 |
+
//TODO - Use provider based checks instead of hardcoding for Duo
|
69 |
+
if (!Mage::getModel('he_twofactorauth/validate_duo_request')->ping()) {
|
70 |
+
$msg = $this->_helper->__('Can not connect to specified Duo API server - TFA settings not validated');
|
71 |
+
} elseif (!$this->check()) {
|
72 |
+
$msg = $this->_helper->__(
|
73 |
+
'Credentials for Duo API server not accepted, please check - TFA settings not validated'
|
74 |
+
);
|
75 |
+
} else {
|
76 |
+
$status = HE_TwoFactorAuth_Model_Validate::TFA_CHECK_SUCCESS;
|
77 |
+
$msg = $this->_helper->__('Credentials for Duo API server accepted - TFA settings validated');
|
78 |
+
}
|
79 |
+
|
80 |
+
//let the user know the status
|
81 |
+
if ($status == HE_TwoFactorAuth_Model_Validate::TFA_CHECK_SUCCESS) {
|
82 |
+
//Mage::getSingleton('adminhtml/session')->addSuccess($msg);
|
83 |
+
if ($this->_shouldLog) {
|
84 |
+
Mage::log("isValid - $msg.", Zend_Log::ERR, "two_factor_auth.log");
|
85 |
+
}
|
86 |
+
$newMode = $this->_helper->__('VALID');
|
87 |
+
} else {
|
88 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
89 |
+
if ($this->_shouldLog) {
|
90 |
+
Mage::log("isValid - $msg.", Zend_Log::INFO, "two_factor_auth.log");
|
91 |
+
}
|
92 |
+
$newMode = $this->_helper->__('NOT VALID');
|
93 |
+
}
|
94 |
+
|
95 |
+
//if mode changed, update config
|
96 |
+
if ($newMode <> Mage::getStoreConfig('he2faconfig/duo/validated')) {
|
97 |
+
Mage::getModel('core/config')->saveConfig('he2faconfig/duo/validated', $newMode);
|
98 |
+
Mage::app()->getStore()->resetConfig();
|
99 |
+
}
|
100 |
+
|
101 |
+
return $status;
|
102 |
+
}
|
103 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Validate/Duo/Request.php
ADDED
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* This class implements the Duo Security's V2 REST API. Currently only 3 functions are supported
|
10 |
+
* - ping
|
11 |
+
* - check
|
12 |
+
* - logo
|
13 |
+
* Ping and Check are basic functions used to validate the integration settings. Logo can be used to
|
14 |
+
* customize the login screen.
|
15 |
+
*
|
16 |
+
* If ping or check fail, then the integration is disabled. This allows users to access the Magento admin
|
17 |
+
* even if the Duo integration settings are incorrect.
|
18 |
+
*
|
19 |
+
* For more information on Duo security's API, please see -
|
20 |
+
* https://www.duosecurity.com
|
21 |
+
*/
|
22 |
+
|
23 |
+
class HE_TwoFactorAuth_Model_Validate_Duo_Request extends Mage_Core_Model_Abstract
|
24 |
+
{
|
25 |
+
var $_path; //rest request path
|
26 |
+
var $_params; //call parameters
|
27 |
+
var $_method; //call method (GET|POST)
|
28 |
+
var $_date; //current time, formatted as RFC 2822.
|
29 |
+
|
30 |
+
// these values are supplied on the DUO integration control panel
|
31 |
+
var $_host; //DUO API host
|
32 |
+
var $_ikey;
|
33 |
+
var $_skey;
|
34 |
+
|
35 |
+
// a unique key for your application, set in the Magento Admin
|
36 |
+
var $_akey;
|
37 |
+
|
38 |
+
// a file to keep the logo associated with the DUO integration
|
39 |
+
var $_logoFile;
|
40 |
+
|
41 |
+
/***
|
42 |
+
* Initialize the request environment
|
43 |
+
*/
|
44 |
+
public function _construct()
|
45 |
+
{
|
46 |
+
$this->_params = array();
|
47 |
+
$this->_path = "";
|
48 |
+
$this->_method = "GET";
|
49 |
+
$this->_date = date("r");
|
50 |
+
|
51 |
+
$this->_host = Mage::getStoreConfig('he2faconfig/duo/host');
|
52 |
+
$this->_ikey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/ikey'));
|
53 |
+
$this->_skey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/skey'));
|
54 |
+
$this->_akey = Mage::helper('core')->decrypt(Mage::getStoreConfig('he2faconfig/duo/akey'));
|
55 |
+
|
56 |
+
$this->_logoFile = Mage::getBaseDir('media') . "/duo_logo.png";
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
/***
|
61 |
+
* create the request cannon string to be used in the password hash. Data must
|
62 |
+
* match the request parameters
|
63 |
+
*
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
protected function _makeCannon()
|
67 |
+
{
|
68 |
+
$cannon = array(
|
69 |
+
"date" => $this->_date,
|
70 |
+
"method" => $this->_method,
|
71 |
+
"host" => $this->_host,
|
72 |
+
"path" => $this->_path,
|
73 |
+
);
|
74 |
+
|
75 |
+
if (count($this->_params)) {
|
76 |
+
$cannon["params"] = ksort($this->_params);
|
77 |
+
} else {
|
78 |
+
$cannon["params"] = "";
|
79 |
+
}
|
80 |
+
|
81 |
+
return implode("\n", $cannon);
|
82 |
+
}
|
83 |
+
|
84 |
+
/***
|
85 |
+
* TBD
|
86 |
+
*/
|
87 |
+
protected function _getPostfields()
|
88 |
+
{
|
89 |
+
|
90 |
+
}
|
91 |
+
|
92 |
+
/***
|
93 |
+
* Create a hash string to be used as the password for the request
|
94 |
+
* Must be sha1 encrypted
|
95 |
+
*
|
96 |
+
* @return string
|
97 |
+
*/
|
98 |
+
protected function _makeHash()
|
99 |
+
{
|
100 |
+
return hash_hmac('sha1', $this->_makeCannon(), $this->_skey);
|
101 |
+
}
|
102 |
+
|
103 |
+
/***
|
104 |
+
* Add the date to the request headers, required for validation of authentication request
|
105 |
+
*
|
106 |
+
* @return array
|
107 |
+
*/
|
108 |
+
protected function _getHeaders()
|
109 |
+
{
|
110 |
+
return array("Date: $this->_date");
|
111 |
+
}
|
112 |
+
|
113 |
+
/***
|
114 |
+
* Format parameters for GET calls and return full URL with query string
|
115 |
+
*
|
116 |
+
* @param string $url
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
protected function _addUrlParams($url)
|
121 |
+
{
|
122 |
+
if (count($this->_params) > 0) {
|
123 |
+
return $url . "?" . http_build_query(ksort($this->_params));
|
124 |
+
} else {
|
125 |
+
return $url;
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
/***
|
130 |
+
* Call the DUO rest service and return the results
|
131 |
+
*
|
132 |
+
* @param bool $raw
|
133 |
+
* @param bool $debug
|
134 |
+
*
|
135 |
+
* @return array|bool|mixed
|
136 |
+
*/
|
137 |
+
|
138 |
+
protected function _doRequest($raw = false, $debug = false)
|
139 |
+
{
|
140 |
+
if ($this->_path == "") {
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
|
144 |
+
$url = "https://" . $this->_host . $this->_path;
|
145 |
+
|
146 |
+
$headers = $this->_getHeaders();
|
147 |
+
|
148 |
+
$curl = curl_init();
|
149 |
+
if ($this->_method == "POST") {
|
150 |
+
curl_setopt($curl, CURLOPT_POST, 1); //defaults to get
|
151 |
+
curl_setopt($curl, CURLOPT_POSTFIELDS, $this->_getPostfields());
|
152 |
+
curl_setopt($curl, CURLOPT_URL, $url);
|
153 |
+
} else {
|
154 |
+
$url = $this->_addUrlParams($url);
|
155 |
+
curl_setopt($curl, CURLOPT_URL, $url);
|
156 |
+
}
|
157 |
+
|
158 |
+
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
159 |
+
curl_setopt($curl, CURLOPT_USERPWD, $this->_ikey . ":" . $this->_makeHash());
|
160 |
+
curl_setopt($curl, CURLOPT_FRESH_CONNECT, true);
|
161 |
+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
162 |
+
|
163 |
+
curl_setopt($curl, CURLOPT_VERBOSE, $debug);
|
164 |
+
curl_setopt($curl, CURLOPT_BINARYTRANSFER, $raw);
|
165 |
+
|
166 |
+
if (!$result = curl_exec($curl)) {
|
167 |
+
$error = curl_error($curl);
|
168 |
+
} else {
|
169 |
+
$error = false;
|
170 |
+
}
|
171 |
+
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
172 |
+
|
173 |
+
curl_close($curl);
|
174 |
+
|
175 |
+
if ($error || $responseCode <> '200') {
|
176 |
+
//todo - put in error logging
|
177 |
+
return false;
|
178 |
+
} else {
|
179 |
+
if ($raw) {
|
180 |
+
return $result;
|
181 |
+
} else {
|
182 |
+
return json_decode($result, true);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
/***
|
188 |
+
* Check to see if the DUO service is reachable
|
189 |
+
*
|
190 |
+
* @return bool
|
191 |
+
*/
|
192 |
+
|
193 |
+
public function ping()
|
194 |
+
{
|
195 |
+
$this->_path = "/auth/v2/ping";
|
196 |
+
$result = $this->_doRequest();
|
197 |
+
|
198 |
+
if (!$result || $result['stat'] <> "OK") {
|
199 |
+
return false;
|
200 |
+
} else {
|
201 |
+
return true;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
/***
|
206 |
+
* Check to see if the integration settings are valid
|
207 |
+
*
|
208 |
+
* @return bool
|
209 |
+
*/
|
210 |
+
|
211 |
+
public function check()
|
212 |
+
{
|
213 |
+
$this->_path = "/auth/v2/check";
|
214 |
+
$result = $this->_doRequest();
|
215 |
+
|
216 |
+
if (!$result || $result['stat'] <> "OK") {
|
217 |
+
return false;
|
218 |
+
} else {
|
219 |
+
return true;
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
/***
|
224 |
+
* Get the logo, if one is registered with DUO, for the integration
|
225 |
+
*
|
226 |
+
* @return bool
|
227 |
+
*/
|
228 |
+
public function logo()
|
229 |
+
{
|
230 |
+
$this->_path = "/auth/v2/logo";
|
231 |
+
$result = $this->_doRequest(true);
|
232 |
+
|
233 |
+
if (!$result) {
|
234 |
+
return false;
|
235 |
+
} else {
|
236 |
+
if (file_put_contents($this->_logoFile, $result)) {
|
237 |
+
return $this->_logoFile;
|
238 |
+
} else {
|
239 |
+
return false;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
// TODO - the remainder of the DUO protocol will be filled out later if needed
|
245 |
+
|
246 |
+
public function enroll()
|
247 |
+
{
|
248 |
+
}
|
249 |
+
|
250 |
+
public function enroll_status()
|
251 |
+
{
|
252 |
+
}
|
253 |
+
|
254 |
+
public function preauth()
|
255 |
+
{
|
256 |
+
}
|
257 |
+
|
258 |
+
public function auth()
|
259 |
+
{
|
260 |
+
}
|
261 |
+
|
262 |
+
public function auth_status()
|
263 |
+
{
|
264 |
+
}
|
265 |
+
}
|
app/code/community/HE/TwoFactorAuth/Model/Validate/Google.php
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
* Author : Aric Watson
|
4 |
+
* Nexcess.net - https://www.nexcess.net
|
5 |
+
*
|
6 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
7 |
+
*
|
8 |
+
* For more information on Google Authenticator, please see -
|
9 |
+
* https://github.com/google/google-authenticator/wiki
|
10 |
+
*
|
11 |
+
* Some code based on previous work by Michael Kliewe/PHPGangsta
|
12 |
+
* https://github.com/PHPGangsta/GoogleAuthenticator
|
13 |
+
* http://www.phpgangsta.de/
|
14 |
+
*/
|
15 |
+
|
16 |
+
require_once(Mage::getBaseDir('lib') . DS . 'GoogleAuthenticator' . DS . 'PHPGangsta' . DS . 'GoogleAuthenticator.php');
|
17 |
+
|
18 |
+
class HE_TwoFactorAuth_Model_Validate_Google extends HE_TwoFactorAuth_Model_Validate
|
19 |
+
{
|
20 |
+
|
21 |
+
public function __construct()
|
22 |
+
{
|
23 |
+
$this->_shouldLog = Mage::helper('he_twofactorauth')->shouldLog();
|
24 |
+
}
|
25 |
+
|
26 |
+
/*
|
27 |
+
* HOTP - counter based
|
28 |
+
* TOTP - time based
|
29 |
+
*/
|
30 |
+
public function getToken($username, $tokentype = "TOTP")
|
31 |
+
{
|
32 |
+
$token = $this->setUser($username, $tokentype);
|
33 |
+
if ($this->_shouldLog) {
|
34 |
+
Mage::log("token = " . var_export($token, true));
|
35 |
+
}
|
36 |
+
|
37 |
+
$user = Mage::getModel('admin/user')->loadByUsername($username);
|
38 |
+
$user->setTwofactorauthToken($token);
|
39 |
+
//$user->save(); //password gets messed up after saving?!
|
40 |
+
}
|
41 |
+
|
42 |
+
|
43 |
+
public function isValid()
|
44 |
+
{
|
45 |
+
return true;
|
46 |
+
}
|
47 |
+
|
48 |
+
/*
|
49 |
+
* generates and returns a new shared secret
|
50 |
+
*/
|
51 |
+
public function generateSecret()
|
52 |
+
{
|
53 |
+
$ga = new PHPGangsta_GoogleAuthenticator();
|
54 |
+
$secret = $ga->createSecret();
|
55 |
+
|
56 |
+
return $secret;
|
57 |
+
}
|
58 |
+
|
59 |
+
|
60 |
+
/*
|
61 |
+
* generates and returns QR code URL from google
|
62 |
+
*/
|
63 |
+
public function generateQRCodeUrl($secret, $username)
|
64 |
+
{
|
65 |
+
if ((empty($secret)) || (empty($username))) {
|
66 |
+
return;
|
67 |
+
}
|
68 |
+
|
69 |
+
$ga = new PHPGangsta_GoogleAuthenticator();
|
70 |
+
$url = $ga->getQRCodeGoogleUrl($username, $secret);
|
71 |
+
|
72 |
+
return $url;
|
73 |
+
}
|
74 |
+
|
75 |
+
|
76 |
+
/*
|
77 |
+
* verifies the code using TOTP
|
78 |
+
*/
|
79 |
+
|
80 |
+
public function validateCode($code)
|
81 |
+
{
|
82 |
+
if (empty($code)) {
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
Mage::log("Google - validateCode: " . $code, 0, "two_factor_auth.log");
|
86 |
+
|
87 |
+
// get user's shared secret
|
88 |
+
$user = Mage::getSingleton('admin/session')->getUser();
|
89 |
+
$admin_user = Mage::getModel('admin/user')->load($user->getId());
|
90 |
+
|
91 |
+
$ga = new PHPGangsta_GoogleAuthenticator();
|
92 |
+
$secret = Mage::helper('core')->decrypt($admin_user->getTwofactorGoogleSecret());
|
93 |
+
|
94 |
+
return $ga->verifyCode($secret, $code, 1);
|
95 |
+
}
|
96 |
+
|
97 |
+
|
98 |
+
/*
|
99 |
+
* abstract function in GoogleAuthenticator, needs to be defined here TODO
|
100 |
+
*/
|
101 |
+
function getDataBad($username, $index = null) // this was causing problems, not sure why...
|
102 |
+
{
|
103 |
+
$user = Mage::getModel('admin/user')->loadByUsername($username);
|
104 |
+
|
105 |
+
return $user->getTwofactorauthToken() == null ? false : $user->getTwofactorauthToken();
|
106 |
+
}
|
107 |
+
|
108 |
+
/*
|
109 |
+
* abstract function in GoogleAuthenticator, needs to be defined here
|
110 |
+
*/
|
111 |
+
function putData($username, $data)
|
112 |
+
{
|
113 |
+
$user = Mage::getModel('admin/user')->loadByUsername($username);
|
114 |
+
$user->setTwofactorauthToken("test");
|
115 |
+
$user->save();
|
116 |
+
}
|
117 |
+
|
118 |
+
/*
|
119 |
+
* abstract function in GoogleAuthenticator, needs to be defined here
|
120 |
+
*/
|
121 |
+
function getUsers()
|
122 |
+
{
|
123 |
+
}
|
124 |
+
}
|
app/code/community/HE/TwoFactorAuth/controllers/Adminhtml/TwofactorController.php
ADDED
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* Author : Greg Croasdill
|
5 |
+
* Human Element, Inc http://www.human-element.com
|
6 |
+
*
|
7 |
+
* License : GPL -- https://www.gnu.org/copyleft/gpl.html
|
8 |
+
*
|
9 |
+
* For more information on Duo security's API, please see -
|
10 |
+
* https://www.duosecurity.com
|
11 |
+
*
|
12 |
+
* For more information on Google Authenticator, please see -
|
13 |
+
* https://github.com/google/google-authenticator/wiki
|
14 |
+
*
|
15 |
+
* Some code based on previous work by Jonathan Day jonathan@aligent.com.au
|
16 |
+
* https://github.com/magento-hackathon/Magento-Two-factor-Authentication
|
17 |
+
*
|
18 |
+
*/
|
19 |
+
|
20 |
+
class HE_TwoFactorAuth_Adminhtml_TwofactorController extends Mage_Adminhtml_Controller_Action
|
21 |
+
{
|
22 |
+
|
23 |
+
public function _construct()
|
24 |
+
{
|
25 |
+
$this->_shouldLog = Mage::helper('he_twofactorauth')->shouldLog();
|
26 |
+
$this->_shouldLogAccess = Mage::helper('he_twofactorauth')->shouldLogAccess();
|
27 |
+
parent::_construct();
|
28 |
+
}
|
29 |
+
|
30 |
+
//need an action per provider so that we can load the correct 2fa form
|
31 |
+
|
32 |
+
public function duoAction()
|
33 |
+
{
|
34 |
+
if ($this->_shouldLog) {
|
35 |
+
Mage::log("duoAction start", 0, "two_factor_auth.log");
|
36 |
+
}
|
37 |
+
$msg = Mage::helper('he_twofactorauth')->__('Please complete the DUO two factor authentication');
|
38 |
+
Mage::getSingleton('adminhtml/session')->addNotice($msg);
|
39 |
+
|
40 |
+
$this->loadLayout();
|
41 |
+
$this->renderLayout();
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
public function googleAction()
|
46 |
+
{
|
47 |
+
if ($this->_shouldLog) {
|
48 |
+
Mage::log("googleAction start", 0, "two_factor_auth.log");
|
49 |
+
}
|
50 |
+
$this->loadLayout();
|
51 |
+
$this->renderLayout();
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
/***
|
56 |
+
* verify is a generic action, looks at the current config to get provider, then dispatches correct verify method
|
57 |
+
*
|
58 |
+
* @return $this
|
59 |
+
*/
|
60 |
+
public function verifyAction()
|
61 |
+
{
|
62 |
+
if ($this->_shouldLog) {
|
63 |
+
Mage::log("verifyAction start", 0, "two_factor_auth.log");
|
64 |
+
}
|
65 |
+
|
66 |
+
if ($this->_shouldLogAccess) {
|
67 |
+
$ipAddress = Mage::helper('core/http')->getRemoteAddr();
|
68 |
+
$adminName = Mage::getSingleton('admin/session')->getUser()->getUsername();
|
69 |
+
|
70 |
+
Mage::log("TFA Verify attempt for admin account $adminName from IP $ipAddress", 0, "two_factor_auth.log");
|
71 |
+
}
|
72 |
+
|
73 |
+
$provider = Mage::helper('he_twofactorauth')->getProvider();
|
74 |
+
|
75 |
+
$verifyProcess = '_verify' . ucfirst($provider);
|
76 |
+
|
77 |
+
if (method_exists($this, $verifyProcess)) {
|
78 |
+
$this->$verifyProcess();
|
79 |
+
} else {
|
80 |
+
Mage::helper('he_twofactorauth')->disable2FA();
|
81 |
+
if ($this->_shouldLog) {
|
82 |
+
Mage::log(
|
83 |
+
"verifyAction - Unsupported provider $provider. Two factor Authentication is disabled", 0,
|
84 |
+
"two_factor_auth.log"
|
85 |
+
);
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
return $this;
|
90 |
+
}
|
91 |
+
|
92 |
+
private function _verifyDuo()
|
93 |
+
{
|
94 |
+
$duoSigResp = Mage::app()->getRequest()->getPost('sig_response', null);
|
95 |
+
|
96 |
+
$validate = Mage::getModel('he_twofactorauth/validate_duo');
|
97 |
+
|
98 |
+
if ($validate->verifyResponse($duoSigResp) === false) {
|
99 |
+
if ($this->_shouldLog) {
|
100 |
+
Mage::log("verifyDuo - failed verify", 0, "two_factor_auth.log");
|
101 |
+
}
|
102 |
+
|
103 |
+
if ($this->_shouldLogAccess) {
|
104 |
+
$ipAddress = Mage::helper('core/http')->getRemoteAddr();
|
105 |
+
$adminName = Mage::getSingleton('admin/session')->getUser()->getUsername();
|
106 |
+
|
107 |
+
Mage::log(
|
108 |
+
"verifyDuo - TFA Verify attempt FAILED for admin account $adminName from IP $ipAddress", 0,
|
109 |
+
"two_factor_auth.log"
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
//TODO - make status message area on template
|
114 |
+
$msg = Mage::helper('he_twofactorauth')->__(
|
115 |
+
'verifyDuo - Two Factor Authentication has failed. Please try again or contact an administrator.'
|
116 |
+
);
|
117 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
118 |
+
|
119 |
+
$this->_redirect('adminhtml/twofactor/duo');
|
120 |
+
|
121 |
+
return $this;
|
122 |
+
}
|
123 |
+
|
124 |
+
if ($this->_shouldLog) {
|
125 |
+
Mage::log("verifyDuo - Duo Validated", 0, "two_factor_auth.log");
|
126 |
+
}
|
127 |
+
if ($this->_shouldLogAccess) {
|
128 |
+
$ipAddress = Mage::helper('core/http')->getRemoteAddr();
|
129 |
+
$adminName = Mage::getSingleton('admin/session')->getUser()->getUsername();
|
130 |
+
|
131 |
+
Mage::log(
|
132 |
+
"verifyDuo - TFA Verify attempt SUCCEEDED for admin account $adminName from IP $ipAddress", 0,
|
133 |
+
"two_factor_auth.log"
|
134 |
+
);
|
135 |
+
}
|
136 |
+
|
137 |
+
|
138 |
+
Mage::getSingleton('admin/session')
|
139 |
+
->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE);
|
140 |
+
$this->_redirect('*');
|
141 |
+
|
142 |
+
return $this;
|
143 |
+
}
|
144 |
+
|
145 |
+
private function _verifyGoogle()
|
146 |
+
{
|
147 |
+
if ($this->_shouldLog) {
|
148 |
+
Mage::log("verifyAction - start Google validate", 0, "two_factor_auth.log");
|
149 |
+
}
|
150 |
+
$params = $this->getRequest()->getParams();
|
151 |
+
|
152 |
+
$ipAddress = Mage::helper('core/http')->getRemoteAddr();
|
153 |
+
$adminName = Mage::getSingleton('admin/session')->getUser()->getUsername();
|
154 |
+
|
155 |
+
// save the user's shared secret
|
156 |
+
if ((!empty($params['google_secret'])) && (strlen($params['google_secret']) == 16)) {
|
157 |
+
$user = Mage::getSingleton('admin/session')->getUser();
|
158 |
+
$admin_user = Mage::getModel('admin/user')->load($user->getId());
|
159 |
+
$admin_user->setTwofactorGoogleSecret(Mage::helper('core')->encrypt($params['google_secret']));
|
160 |
+
$admin_user->save();
|
161 |
+
if (($this->_shouldLog) || ($this->_shouldLogAccess)) {
|
162 |
+
Mage::log(
|
163 |
+
"verifyGoogle - new google secret saved for admin account $adminName from IP $ipAddress", 0,
|
164 |
+
"two_factor_auth.log"
|
165 |
+
);
|
166 |
+
}
|
167 |
+
|
168 |
+
// redirect back to login, now they'll need to enter the code.
|
169 |
+
$msg = Mage::helper('he_twofactorauth')->__("Please enter your input code.");
|
170 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
171 |
+
$this->_redirect('adminhtml/twofactor/google');
|
172 |
+
|
173 |
+
return $this;
|
174 |
+
} else {
|
175 |
+
// check the key
|
176 |
+
// Test to make sure the parameter exists and remove any spaces
|
177 |
+
if (array_key_exists('input_code', $params)) {
|
178 |
+
$gcode = str_replace(' ', '', $params['input_code']);
|
179 |
+
} else {
|
180 |
+
$gcode = '';
|
181 |
+
}
|
182 |
+
|
183 |
+
// TODO add better error checking and flow!
|
184 |
+
if ((strlen($gcode) == 6) && (is_numeric($gcode))) {
|
185 |
+
if ($this->_shouldLog) {
|
186 |
+
Mage::log("verifyGoogle - checking input code '" . $gcode . "'", 0, "two_factor_auth.log");
|
187 |
+
}
|
188 |
+
$g2fa = Mage::getModel("he_twofactorauth/validate_google");
|
189 |
+
$goodCode = $g2fa->validateCode($gcode);
|
190 |
+
if ($goodCode) {
|
191 |
+
if ($this->_shouldLogAccess) {
|
192 |
+
|
193 |
+
Mage::log(
|
194 |
+
"verifyGoogle - TFA Verify attempt SUCCESSFUL for admin account $adminName from IP $ipAddress",
|
195 |
+
0, "two_factor_auth.log"
|
196 |
+
);
|
197 |
+
}
|
198 |
+
|
199 |
+
$msg = Mage::helper('he_twofactorauth')->__("Valid code entered");
|
200 |
+
Mage::getSingleton('adminhtml/session')->addSuccess($msg);
|
201 |
+
Mage::getSingleton('admin/session')->set2faState(HE_TwoFactorAuth_Model_Validate::TFA_STATE_ACTIVE);
|
202 |
+
$this->_redirect('*');
|
203 |
+
|
204 |
+
return $this;
|
205 |
+
} else {
|
206 |
+
if ($this->_shouldLogAccess) {
|
207 |
+
Mage::log(
|
208 |
+
"verifyGoogle - TFA Verify attempt FAILED for admin account $adminName from IP $ipAddress",
|
209 |
+
0, "two_factor_auth.log"
|
210 |
+
);
|
211 |
+
}
|
212 |
+
$msg = Mage::helper('he_twofactorauth')->__("Invalid code entered");
|
213 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
214 |
+
$this->_redirect('adminhtml/twofactor/google');
|
215 |
+
|
216 |
+
return $this;
|
217 |
+
}
|
218 |
+
} else {
|
219 |
+
if ($this->_shouldLogAccess) {
|
220 |
+
Mage::log(
|
221 |
+
"verifyGoogle - TFA Verify attempt FAILED for admin account $adminName from IP $ipAddress", 0,
|
222 |
+
"two_factor_auth.log"
|
223 |
+
);
|
224 |
+
}
|
225 |
+
$msg = Mage::helper('he_twofactorauth')->__("Invalid code entered");
|
226 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
227 |
+
$this->_redirect('adminhtml/twofactor/google');
|
228 |
+
|
229 |
+
return $this;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
/***
|
235 |
+
* verify is a generic action, looks at the current config to get provider, then dispatches correct verify method
|
236 |
+
*
|
237 |
+
* @return $this
|
238 |
+
*/
|
239 |
+
public function validateAction()
|
240 |
+
{
|
241 |
+
if ($this->_shouldLog) {
|
242 |
+
Mage::log("validateAction start", 0, "two_factor_auth.log");
|
243 |
+
}
|
244 |
+
$provider = Mage::helper('he_twofactorauth')->getProvider();
|
245 |
+
|
246 |
+
$validateProcess = '_validate' . ucfirst($provider);
|
247 |
+
|
248 |
+
if (method_exists($this, $validateProcess)) {
|
249 |
+
$this->$validateProcess();
|
250 |
+
} else {
|
251 |
+
Mage::helper('he_twofactorauth')->disable2FA();
|
252 |
+
if ($this->_shouldLog) {
|
253 |
+
Mage::log(
|
254 |
+
"validateAction - Unsupported provider $provider. Two factor Authentication is disabled", 0,
|
255 |
+
"two_factor_auth.log"
|
256 |
+
);
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
return $this;
|
261 |
+
}
|
262 |
+
|
263 |
+
private function _validateDuo()
|
264 |
+
{
|
265 |
+
if ($this->_shouldLog) {
|
266 |
+
Mage::log("validateAction starting", 0, "two_factor_auth.log");
|
267 |
+
}
|
268 |
+
|
269 |
+
$validate = Mage::getModel('he_twofactorauth/validate_duo_request');
|
270 |
+
|
271 |
+
if ($validate->ping() == false) {
|
272 |
+
if ($this->_shouldLog) {
|
273 |
+
Mage::log(
|
274 |
+
"validateDuo - ValidateAction ping fail - can not communicate with Duo auth server", 0,
|
275 |
+
"two_factor_auth.log"
|
276 |
+
);
|
277 |
+
}
|
278 |
+
|
279 |
+
$msg = Mage::helper('he_twofactorauth')->__(
|
280 |
+
'Can not connect to authentication server. Two Factor Authentication has been disabled.'
|
281 |
+
);
|
282 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
283 |
+
|
284 |
+
} elseif ($validate->check() == false) {
|
285 |
+
if ($this->_shouldLog) {
|
286 |
+
Mage::log(
|
287 |
+
"validateDuo - ValidateAction check fail - can not communicate with Duo auth server", 0,
|
288 |
+
"two_factor_auth.log"
|
289 |
+
);
|
290 |
+
}
|
291 |
+
|
292 |
+
$msg = Mage::helper('he_twofactorauth')->__(
|
293 |
+
'Can not connect to authentication server. Two Factor Authentication has been disabled.'
|
294 |
+
);
|
295 |
+
Mage::getSingleton('adminhtml/session')->addError($msg);
|
296 |
+
}
|
297 |
+
|
298 |
+
$this->_redirect('*');
|
299 |
+
|
300 |
+
return $this;
|
301 |
+
}
|
302 |
+
}
|
app/code/community/HE/TwoFactorAuth/etc/adminhtml.xml
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<acl>
|
4 |
+
<resources>
|
5 |
+
<admin>
|
6 |
+
<children>
|
7 |
+
<system>
|
8 |
+
<children>
|
9 |
+
<config>
|
10 |
+
<children>
|
11 |
+
<he2faconfig translate="title" module="he_twofactorauth">
|
12 |
+
<title>Configure Two Factor Security</title>
|
13 |
+
<sort_order>0</sort_order>
|
14 |
+
</he2faconfig>
|
15 |
+
</children>
|
16 |
+
</config>
|
17 |
+
</children>
|
18 |
+
</system>
|
19 |
+
</children>
|
20 |
+
</admin>
|
21 |
+
</resources>
|
22 |
+
</acl>
|
23 |
+
</config>
|
app/code/community/HE/TwoFactorAuth/etc/config.xml
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<HE_TwoFactorAuth>
|
5 |
+
<version>1.0.4</version>
|
6 |
+
</HE_TwoFactorAuth>
|
7 |
+
</modules>
|
8 |
+
<global>
|
9 |
+
<models>
|
10 |
+
<he_twofactorauth>
|
11 |
+
<class>HE_TwoFactorAuth_Model</class>
|
12 |
+
<resourceModel>he_twofactorauth_resource</resourceModel>
|
13 |
+
</he_twofactorauth>
|
14 |
+
<he_twofactorauth_resource>
|
15 |
+
<class>HE_TwoFactorAuth_Model_Resource</class>
|
16 |
+
</he_twofactorauth_resource>
|
17 |
+
</models>
|
18 |
+
<blocks>
|
19 |
+
<he_twofactorauth>
|
20 |
+
<class>HE_TwoFactorAuth_Block</class>
|
21 |
+
</he_twofactorauth>
|
22 |
+
</blocks>
|
23 |
+
<helpers>
|
24 |
+
<he_twofactorauth>
|
25 |
+
<class>HE_TwoFactorAuth_Helper</class>
|
26 |
+
</he_twofactorauth>
|
27 |
+
</helpers>
|
28 |
+
|
29 |
+
<events>
|
30 |
+
<controller_action_postdispatch_adminhtml>
|
31 |
+
<observers>
|
32 |
+
<he_twofactorauth_observer_check>
|
33 |
+
<type>singleton</type>
|
34 |
+
<class>he_twofactorauth/observer</class>
|
35 |
+
<method>check_twofactor_active</method>
|
36 |
+
</he_twofactorauth_observer_check>
|
37 |
+
</observers>
|
38 |
+
</controller_action_postdispatch_adminhtml>
|
39 |
+
</events>
|
40 |
+
|
41 |
+
<resources>
|
42 |
+
<he_twofactorauth>
|
43 |
+
<setup>
|
44 |
+
<module>HE_TwoFactorAuth</module>
|
45 |
+
<class>HE_TwoFactorAuth_Model_Resource_Mysql4_Setup</class>
|
46 |
+
</setup>
|
47 |
+
<connection>
|
48 |
+
<use>core_setup</use>
|
49 |
+
</connection>
|
50 |
+
</he_twofactorauth>
|
51 |
+
</resources>
|
52 |
+
|
53 |
+
</global>
|
54 |
+
|
55 |
+
<admin>
|
56 |
+
<routers>
|
57 |
+
<adminhtml>
|
58 |
+
<args>
|
59 |
+
<modules>
|
60 |
+
<HE_TwoFactorAuth before="Mage_Adminhtml">HE_TwoFactorAuth_Adminhtml</HE_TwoFactorAuth>
|
61 |
+
</modules>
|
62 |
+
</args>
|
63 |
+
</adminhtml>
|
64 |
+
</routers>
|
65 |
+
</admin>
|
66 |
+
|
67 |
+
<adminhtml>
|
68 |
+
<events>
|
69 |
+
<admin_session_user_login_success>
|
70 |
+
<observers>
|
71 |
+
<he_twofactorauth_observer>
|
72 |
+
<type>singleton</type>
|
73 |
+
<class>he_twofactorauth/observer</class>
|
74 |
+
<method>admin_user_authenticate_after</method>
|
75 |
+
</he_twofactorauth_observer>
|
76 |
+
</observers>
|
77 |
+
</admin_session_user_login_success>
|
78 |
+
|
79 |
+
<adminhtml_block_html_before>
|
80 |
+
<observers>
|
81 |
+
<he_twofactorauth_observer>
|
82 |
+
<type>model</type>
|
83 |
+
<class>he_twofactorauth/observer</class>
|
84 |
+
<method>googleClearSecretCheck</method>
|
85 |
+
</he_twofactorauth_observer>
|
86 |
+
</observers>
|
87 |
+
</adminhtml_block_html_before>
|
88 |
+
|
89 |
+
<model_save_before>
|
90 |
+
<observers>
|
91 |
+
<he_twofactorauth_observer>
|
92 |
+
<type>model</type>
|
93 |
+
<class>he_twofactorauth/observer</class>
|
94 |
+
<method>googleSaveClear</method>
|
95 |
+
</he_twofactorauth_observer>
|
96 |
+
</observers>
|
97 |
+
</model_save_before>
|
98 |
+
|
99 |
+
</events>
|
100 |
+
|
101 |
+
<layout>
|
102 |
+
<updates>
|
103 |
+
<he_twofactor>
|
104 |
+
<file>he_twofactor/auth.xml</file>
|
105 |
+
</he_twofactor>
|
106 |
+
</updates>
|
107 |
+
</layout>
|
108 |
+
|
109 |
+
<translate>
|
110 |
+
<modules>
|
111 |
+
<HE_TwoFactorAuth>
|
112 |
+
<files>
|
113 |
+
<default>HE_TwoFactorAuth.csv</default>
|
114 |
+
</files>
|
115 |
+
</HE_TwoFactorAuth>
|
116 |
+
</modules>
|
117 |
+
</translate>
|
118 |
+
|
119 |
+
</adminhtml>
|
120 |
+
<frontend>
|
121 |
+
<translate>
|
122 |
+
<modules>
|
123 |
+
<HE_TwoFactorAuth>
|
124 |
+
<files>
|
125 |
+
<default>HE_TwoFactorAuth.csv</default>
|
126 |
+
</files>
|
127 |
+
</HE_TwoFactorAuth>
|
128 |
+
</modules>
|
129 |
+
</translate>
|
130 |
+
</frontend>
|
131 |
+
<default>
|
132 |
+
<he2falinks>
|
133 |
+
<human-element-link>http://www.human-element.com</human-element-link>
|
134 |
+
<nexcess-link>https://www.nexcess.net</nexcess-link>
|
135 |
+
<docs-link>http://www.human-element.com/sentry-two-factor-authentication-documentation</docs-link>
|
136 |
+
<submit-bug-link>http://www.human-element.com/sentry-two-factor-authentication-documentation#bugs</submit-bug-link>
|
137 |
+
<multi-auth-link>http://en.wikipedia.org/wiki/Multi-factor_authentication</multi-auth-link>
|
138 |
+
<mage-support-link>http://www.human-element.com/magento-support-page</mage-support-link>
|
139 |
+
<contact-link>http://www.human-element.com/contact/#contact-form</contact-link>
|
140 |
+
</he2falinks>
|
141 |
+
<he2faconfig>
|
142 |
+
<providers>
|
143 |
+
<disabled>
|
144 |
+
<title>Disable multi-factor authentication</title>
|
145 |
+
</disabled>
|
146 |
+
<duo>
|
147 |
+
<title>Duo Security</title>
|
148 |
+
<link>https://www.duosecurity.com/</link>
|
149 |
+
</duo>
|
150 |
+
<google>
|
151 |
+
<title>Google Authenticator</title>
|
152 |
+
<link>https://support.google.com/accounts/answer/1066447?hl=en</link>
|
153 |
+
</google>
|
154 |
+
</providers>
|
155 |
+
<duo>
|
156 |
+
<validated>NOT VALID</validated>
|
157 |
+
</duo>
|
158 |
+
<control>
|
159 |
+
<logaccess>1</logaccess>
|
160 |
+
</control>
|
161 |
+
</he2faconfig>
|
162 |
+
</default>
|
163 |
+
</config>
|
app/code/community/HE/TwoFactorAuth/etc/system.xml
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" ?>
|
2 |
+
<config>
|
3 |
+
<tabs>
|
4 |
+
<he2faconfig translate="label" module="he_twofactorauth">
|
5 |
+
<label>Human Element - Sentry</label>
|
6 |
+
<class>sentry-section</class>
|
7 |
+
<sort_order>99999</sort_order>
|
8 |
+
</he2faconfig>
|
9 |
+
</tabs>
|
10 |
+
<sections>
|
11 |
+
<he2faconfig translate="label" module="he_twofactorauth">
|
12 |
+
<label>Two-Factor Auth Settings</label>
|
13 |
+
<class>sentry-item sentry-settings</class>
|
14 |
+
<tab>he2faconfig</tab>
|
15 |
+
<!-- frontend_type>text</frontend_type -->
|
16 |
+
<sort_order>20</sort_order>
|
17 |
+
<show_in_default>1</show_in_default>
|
18 |
+
<show_in_website>1</show_in_website>
|
19 |
+
<show_in_store>0</show_in_store>
|
20 |
+
<groups>
|
21 |
+
<hint>
|
22 |
+
<frontend_model>he_twofactorauth/adminhtml_system_config_fieldset_hint</frontend_model>
|
23 |
+
<sort_order>0</sort_order>
|
24 |
+
<show_in_default>1</show_in_default>
|
25 |
+
<show_in_website>1</show_in_website>
|
26 |
+
<show_in_store>0</show_in_store>
|
27 |
+
</hint>
|
28 |
+
<control>
|
29 |
+
<logging translate="label"/>
|
30 |
+
<label>Provider Selection</label>
|
31 |
+
<sort_order>100</sort_order>
|
32 |
+
<show_in_default>1</show_in_default>
|
33 |
+
<show_in_website>1</show_in_website>
|
34 |
+
<comment>
|
35 |
+
<![CDATA[Choose an authentication provider or disable multi-factor authentication]]>
|
36 |
+
</comment>
|
37 |
+
<fields>
|
38 |
+
<provider>
|
39 |
+
<label>Choose Two Factor Provider</label>
|
40 |
+
<frontend_type>select</frontend_type>
|
41 |
+
<source_model>he_twofactorauth/sysconfig_provider</source_model>
|
42 |
+
<sort_order>10</sort_order>
|
43 |
+
<show_in_default>1</show_in_default>
|
44 |
+
<show_in_website>1</show_in_website>
|
45 |
+
<comment>
|
46 |
+
<![CDATA[Select the security provider for authentication]]>
|
47 |
+
</comment>
|
48 |
+
</provider>
|
49 |
+
|
50 |
+
<ipwhitelist>
|
51 |
+
<label>Whitelisted IP Addresses</label>
|
52 |
+
<frontend_type>textarea</frontend_type>
|
53 |
+
<sort_order>15</sort_order>
|
54 |
+
<show_in_default>1</show_in_default>
|
55 |
+
<show_in_website>1</show_in_website>
|
56 |
+
<comment>
|
57 |
+
<![CDATA[
|
58 |
+
You may whitelist IP addresses here (one per line). Anyone logging in from a whitelisted IP will not be required to perform two-factor authentication.
|
59 |
+
]]>
|
60 |
+
</comment>
|
61 |
+
</ipwhitelist>
|
62 |
+
|
63 |
+
<logaccess>
|
64 |
+
<label>Enable access logging</label>
|
65 |
+
<frontend_type>select</frontend_type>
|
66 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
67 |
+
<sort_order>20</sort_order>
|
68 |
+
<show_in_default>1</show_in_default>
|
69 |
+
<show_in_website>1</show_in_website>
|
70 |
+
<comment>
|
71 |
+
<![CDATA[When enabled, the module will log all access attempts and failures to a file named
|
72 |
+
two_factor_auth.log in Magento's log directory]]>
|
73 |
+
</comment>
|
74 |
+
</logaccess>
|
75 |
+
|
76 |
+
<logging>
|
77 |
+
<label>Enable debug logging</label>
|
78 |
+
<frontend_type>select</frontend_type>
|
79 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
80 |
+
<sort_order>30</sort_order>
|
81 |
+
<show_in_default>1</show_in_default>
|
82 |
+
<show_in_website>1</show_in_website>
|
83 |
+
<comment>
|
84 |
+
<![CDATA[When enabled, the module will write debugging information to a file named
|
85 |
+
two_factor_auth.log in Magento's log directory]]>
|
86 |
+
</comment>
|
87 |
+
</logging>
|
88 |
+
|
89 |
+
|
90 |
+
</fields>
|
91 |
+
</control>
|
92 |
+
<duo translate="label">
|
93 |
+
<label>Duo Security</label>
|
94 |
+
<frontend_type>text</frontend_type>
|
95 |
+
<sort_order>200</sort_order>
|
96 |
+
<show_in_default>1</show_in_default>
|
97 |
+
<show_in_website>1</show_in_website>
|
98 |
+
<show_in_store>0</show_in_store>
|
99 |
+
<comment>
|
100 |
+
<![CDATA[<p><strong>About Duo Security</strong><br/>Duo Security is a leading provider of online
|
101 |
+
Two Factor authentication. For more information, please visit their
|
102 |
+
<a href="https://www.duosecurity.com/" target="_new">website</a>.</p>
|
103 |
+
<p><strong>SETUP</strong> For information on setting up a new account or adding an integration please see one
|
104 |
+
of the following:
|
105 |
+
<ul style="padding-left:15px">
|
106 |
+
<li>To create a Duo account, go to the <a href="https://signup.duosecurity.com/" target="_new">signup</a> page.
|
107 |
+
<li>To get your Integration settings, please visit the
|
108 |
+
<a href="https://admin-41c88c1d.duosecurity.com/integrations" target="_new">Integrations Control Panel</a> in your Duo account.
|
109 |
+
<li>For information on setting up a new integration, please read
|
110 |
+
<a href="https://www.duosecurity.com/docs/getting_started">Getting Started with Duo Security</a>
|
111 |
+
</ul></p>
|
112 |
+
<p>When setting up a new integration select either <strong>"Magento"</strong> or <strong>"Web SDK"</strong> as the type of integration.</p>
|
113 |
+
<p><strong>NOTE!</strong> You must have a DUO account before enabling this module.<br/><p>
|
114 |
+
]]>
|
115 |
+
</comment>
|
116 |
+
<fields>
|
117 |
+
<ikey>
|
118 |
+
<label>Integration Key</label>
|
119 |
+
<frontend_type>obscure</frontend_type>
|
120 |
+
<backend_model>adminhtml/system_config_backend_encrypted</backend_model>
|
121 |
+
<sort_order>10</sort_order>
|
122 |
+
<show_in_default>1</show_in_default>
|
123 |
+
<show_in_website>1</show_in_website>
|
124 |
+
<show_in_store>0</show_in_store>
|
125 |
+
<comment>
|
126 |
+
<![CDATA[The Integration Key from the Duo Security admin dashboard integrations page]]>
|
127 |
+
</comment>
|
128 |
+
</ikey>
|
129 |
+
<skey>
|
130 |
+
<label>Secret Key</label>
|
131 |
+
<frontend_type>obscure</frontend_type>
|
132 |
+
<backend_model>adminhtml/system_config_backend_encrypted</backend_model>
|
133 |
+
<sort_order>20</sort_order>
|
134 |
+
<show_in_default>1</show_in_default>
|
135 |
+
<show_in_website>1</show_in_website>
|
136 |
+
<show_in_store>0</show_in_store>
|
137 |
+
<comment>
|
138 |
+
<![CDATA[The Secret Key from the Duo Security admin dashboard integrations page]]>
|
139 |
+
</comment>
|
140 |
+
</skey>
|
141 |
+
<host>
|
142 |
+
<label>Duo Security API hostname</label>
|
143 |
+
<frontend_type>text</frontend_type>
|
144 |
+
<sort_order>30</sort_order>
|
145 |
+
<show_in_default>1</show_in_default>
|
146 |
+
<show_in_website>1</show_in_website>
|
147 |
+
<show_in_store>0</show_in_store>
|
148 |
+
<comment>
|
149 |
+
<![CDATA[Supply the API hostname from the Duo Security admin dashboard integrations page]]>
|
150 |
+
</comment>
|
151 |
+
</host>
|
152 |
+
<akey>
|
153 |
+
<label>Application Key</label>
|
154 |
+
<frontend_type>text</frontend_type>
|
155 |
+
<backend_model>he_twofactorauth/sysconfig_appkey</backend_model>
|
156 |
+
<sort_order>40</sort_order>
|
157 |
+
<show_in_default>1</show_in_default>
|
158 |
+
<show_in_website>1</show_in_website>
|
159 |
+
<show_in_store>0</show_in_store>
|
160 |
+
<comment><![CDATA[The Application Key is a key you create that is unique for your application.
|
161 |
+
Must be at least 40 characters long. Use the autogenerated key above,
|
162 |
+
or create one of your own.]]>
|
163 |
+
</comment>
|
164 |
+
</akey>
|
165 |
+
<validated>
|
166 |
+
<label>Settings Validated</label>
|
167 |
+
<frontend_type>label</frontend_type>
|
168 |
+
<sort_order>1000</sort_order>
|
169 |
+
<show_in_default>1</show_in_default>
|
170 |
+
<show_in_website>1</show_in_website>
|
171 |
+
<show_in_store>0</show_in_store>
|
172 |
+
<comment><![CDATA[The authentication settings validation status. Duo authentication can not be used
|
173 |
+
until this field says "VALID"]]>
|
174 |
+
</comment>
|
175 |
+
</validated>
|
176 |
+
</fields>
|
177 |
+
</duo>
|
178 |
+
<google translate="label">
|
179 |
+
<label>Google Authenticate</label>
|
180 |
+
<frontend_type>text</frontend_type>
|
181 |
+
<sort_order>300</sort_order>
|
182 |
+
<show_in_default>1</show_in_default>
|
183 |
+
<show_in_website>1</show_in_website>
|
184 |
+
<show_in_store>0</show_in_store>
|
185 |
+
<comment>
|
186 |
+
<![CDATA[<p><strong>About Google Authenticate</strong><br/>
|
187 |
+
Google Authenticate is a popular two-factor authentication solution. It is free to use, but requires use of a compatible app (freely avaialble from Google). </p>
|
188 |
+
|
189 |
+
<p>After enabling this option, the first time a user successfully logs in they will be required scan a QR code with their Google Authenticator app in order to setup their Google Authenticator secret code.</p>
|
190 |
+
|
191 |
+
<p><strong>To login with Google Authenticator, users must use the Google Authenticator app:</strong></p>
|
192 |
+
<ul>
|
193 |
+
<li><a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">For iOS</a></li>
|
194 |
+
<li><a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">For Android</a></li>
|
195 |
+
</ul>
|
196 |
+
|
197 |
+
]]>
|
198 |
+
</comment>
|
199 |
+
</google>
|
200 |
+
</groups>
|
201 |
+
</he2faconfig>
|
202 |
+
</sections>
|
203 |
+
</config>
|
app/code/community/HE/TwoFactorAuth/readme.txt
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Sentry
|
2 |
+
## Magento Two Factor Authentication Module
|
3 |
+
|
4 |
+
### Authors
|
5 |
+
- Greg Croasdill, Human Element, Inc http://www.human-element.com
|
6 |
+
- Gregg Milligan, Human Element, Inc http://www.human-element.com
|
7 |
+
- Aric Watson, Nexcess.net https://www.nexcess.net/
|
8 |
+
|
9 |
+
### License
|
10 |
+
- GPL -- https://www.gnu.org/copyleft/gpl.html
|
11 |
+
|
12 |
+
### Purpose
|
13 |
+
Sentry Two-Factor Authentication will protect your Magento store and customer data by adding an extra check to authenticate
|
14 |
+
your Admin users before allowing them access. Developed as a partnership between the Human Element Magento Development team
|
15 |
+
and Nexcess Hosting, Sentry Two-Factor Authentication for Magento is easy to setup and admin users can quickly login.
|
16 |
+
|
17 |
+
### Supported Providers (more to come)
|
18 |
+
The following __Two Factor Authentication__ providers are supported at this time.
|
19 |
+
|
20 |
+
#### Duo Security
|
21 |
+
For more information on Duo security's API, please see -
|
22 |
+
- https://www.duosecurity.com
|
23 |
+
|
24 |
+
#### Google Authenticator
|
25 |
+
For more information on Google Authenticator, please see -
|
26 |
+
- https://github.com/google/google-authenticator/wiki
|
27 |
+
- https://support.google.com/accounts/answer/180744?hl=en&ref_topic=1099588
|
28 |
+
|
29 |
+
### Referanced work
|
30 |
+
|
31 |
+
Some code based on previous work by Jonathan Day jonathan@aligent.com.au
|
32 |
+
- https://github.com/magento-hackathon/Magento-Two-factor-Authentication
|
33 |
+
|
34 |
+
Some code based on previous work by Michael Kliewe/PHPGangsta
|
35 |
+
- https://github.com/PHPGangsta/GoogleAuthenticator
|
36 |
+
- http://www.phpgangsta.de/
|
37 |
+
|
38 |
+
----
|
39 |
+
### Notes -
|
40 |
+
1. Installing this module will update the AdminUser table in the Magento database to add a twofactor_google_secret
|
41 |
+
field for storing the local GA key. It is safe to remove this field once the module is removed.
|
42 |
+
|
43 |
+
2. If you get locked out of admin because of a settings issue, loss of your provider account or other software related issue, you can *temporarily disable* the second factor authentication -
|
44 |
+
- Place a file named __tfaoff.flag__ in the root directory of your Magento installation.
|
45 |
+
- Login to Magento's Admin area without the second factor.
|
46 |
+
- Update settings or disable Sentry
|
47 |
+
- Remove the tfaoff.flag file to re-enable two factor authentication.
|
app/code/community/HE/TwoFactorAuth/sql/he_twofactorauth/mysql4-install-1.0.2.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// create the twofactor_google_secret table on admin_user
|
3 |
+
|
4 |
+
$installer = $this;
|
5 |
+
$installer->startSetup();
|
6 |
+
$installer->getConnection()
|
7 |
+
->addColumn($installer->getTable('admin/user'),
|
8 |
+
'twofactor_google_secret',
|
9 |
+
array(
|
10 |
+
'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
|
11 |
+
'nullable' => true,
|
12 |
+
'length' => 255,
|
13 |
+
'default' => null,
|
14 |
+
'comment' => 'Google Secret'
|
15 |
+
)
|
16 |
+
);
|
17 |
+
$installer->endSetup();
|
18 |
+
|
19 |
+
// TODO add an uninstall script for users who remove module - apparently no automatic way to do this
|
app/design/adminhtml/default/default/layout/he_twofactor/auth.xml
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<layout>
|
3 |
+
|
4 |
+
<default>
|
5 |
+
<reference name="head">
|
6 |
+
<action method="addJs"><script>he_twofactor/adminpanel.js</script></action>
|
7 |
+
<action method="addItem"><type>skin_css</type><name>he_twofactor/css/admin.css</name></action>
|
8 |
+
</reference>
|
9 |
+
</default>
|
10 |
+
|
11 |
+
<adminhtml_twofactor_duo>
|
12 |
+
<block type="core/text_list" name="root" output="toHtml">
|
13 |
+
<block type="adminhtml/template" name="content" template="he_twofactor/duo/auth.phtml" />
|
14 |
+
</block>
|
15 |
+
</adminhtml_twofactor_duo>
|
16 |
+
|
17 |
+
<adminhtml_twofactor_google>
|
18 |
+
<block type="core/text_list" name="root" output="toHtml">
|
19 |
+
<block type="adminhtml/template" name="content" template="he_twofactor/google/auth.phtml">
|
20 |
+
<block type="core/text_list" name="form.additional.info" />
|
21 |
+
</block>
|
22 |
+
</block>
|
23 |
+
</adminhtml_twofactor_google>
|
24 |
+
|
25 |
+
<adminhtml_twofactor_authy>
|
26 |
+
<block type="core/text_list" name="root" output="toHtml">
|
27 |
+
<block type="adminhtml/template" name="content" template="he_twofactor/authy/auth.phtml">
|
28 |
+
<block type="core/text_list" name="form.additional.info" />
|
29 |
+
</block>
|
30 |
+
</block>
|
31 |
+
</adminhtml_twofactor_authy>
|
32 |
+
|
33 |
+
</layout>
|
app/design/adminhtml/default/default/template/he_twofactor/duo/auth.phtml
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$user = Mage::getSingleton('admin/session')->getUser()->getUsername();
|
4 |
+
$sign_request = Mage::getModel("he_twofactorauth/validate_duo")->signRequest($user);
|
5 |
+
$auth_host = Mage::getModel("he_twofactorauth/validate_duo")->getHost();
|
6 |
+
|
7 |
+
$ready = ($user && $sign_request && $auth_host); // all have to be set
|
8 |
+
|
9 |
+
?>
|
10 |
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
11 |
+
<html lang="en">
|
12 |
+
<head>
|
13 |
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
14 |
+
<title><?php echo Mage::helper('adminhtml')->__('Duo Two Factor Authentication'); ?></title>
|
15 |
+
<link type="text/css" rel="stylesheet" href="<?php echo $this->getSkinUrl('reset.css'); ?>" media="all" />
|
16 |
+
<link type="text/css" rel="stylesheet" href="<?php echo $this->getSkinUrl('boxes.css'); ?>" media="all" />
|
17 |
+
<link rel="icon" href="<?php echo $this->getSkinUrl('favicon.ico'); ?>" type="image/x-icon" />
|
18 |
+
<link rel="shortcut icon" href="<?php echo $this->getSkinUrl('favicon.ico'); ?>" type="image/x-icon" />
|
19 |
+
|
20 |
+
<script type="text/javascript" src="<?php echo $this->getJsUrl('he_twofactor/Duo-Web-v1.bundled.min.js'); ?>"></script>
|
21 |
+
|
22 |
+
<!--[if IE]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('iestyles.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
23 |
+
<!--[if lt IE 7]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('below_ie7.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
24 |
+
<!--[if IE 7]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('ie7.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
25 |
+
</head>
|
26 |
+
<body id="page-login">
|
27 |
+
<div class="login-container" style="margin: 100px auto;">
|
28 |
+
|
29 |
+
<div class="login-box">
|
30 |
+
<div class="login-form" style="background-size: 100% 100%;">
|
31 |
+
<div id="messages">
|
32 |
+
<?php echo $this->getMessagesBlock()->getGroupedHtml() ?>
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<?php if ($ready) { ?>
|
36 |
+
<script>
|
37 |
+
Duo.init({
|
38 |
+
'host': <?php echo "'" . $auth_host . "'"; ?>,
|
39 |
+
'post_action':'<?php echo $this->getUrl('adminhtml/twofactor/verify') ?>',
|
40 |
+
'sig_request':<?php echo "'" . $sign_request . "'"; ?>,
|
41 |
+
'form_key':'<?php echo Mage::getSingleton('core/session')->getFormKey()?>'
|
42 |
+
});
|
43 |
+
</script>
|
44 |
+
|
45 |
+
<iframe id="duo_iframe" width="468" height="330" frameborder="0" allowtransparency="true" style="background: transparent;"></iframe>
|
46 |
+
<?php } else { ?>
|
47 |
+
The Duo Security Two-Factor Authentication is not configured correctly and has been disabled. <br/>
|
48 |
+
Please use the link below to login and review the settings.<br/>
|
49 |
+
<?php } ?>
|
50 |
+
<a href="<?php echo $this->getUrl('adminhtml/index/logout') ?>" class="link-logout"><?php echo $this->__('Return to the Login screen') ?></a>
|
51 |
+
</div>
|
52 |
+
<div class="bottom"></div>
|
53 |
+
</div>
|
54 |
+
</div>
|
55 |
+
|
56 |
+
</body>
|
57 |
+
</html>
|
app/design/adminhtml/default/default/template/he_twofactor/google/auth.phtml
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Magento
|
4 |
+
*
|
5 |
+
* NOTICE OF LICENSE
|
6 |
+
*
|
7 |
+
* This source file is subject to the Academic Free License (AFL 3.0)
|
8 |
+
* that is bundled with this package in the file LICENSE_AFL.txt.
|
9 |
+
* It is also available through the world-wide-web at this URL:
|
10 |
+
* http://opensource.org/licenses/afl-3.0.php
|
11 |
+
* If you did not receive a copy of the license and are unable to
|
12 |
+
* obtain it through the world-wide-web, please send an email
|
13 |
+
* to license@magentocommerce.com so we can send you a copy immediately.
|
14 |
+
*
|
15 |
+
* DISCLAIMER
|
16 |
+
*
|
17 |
+
* Do not edit or add to this file if you wish to upgrade Magento to newer
|
18 |
+
* versions in the future. If you wish to customize Magento for your
|
19 |
+
* needs please refer to http://www.magentocommerce.com for more information.
|
20 |
+
*
|
21 |
+
* @category design
|
22 |
+
* @package default_default
|
23 |
+
* @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com)
|
24 |
+
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
25 |
+
*/
|
26 |
+
?>
|
27 |
+
<?php
|
28 |
+
|
29 |
+
// check to see if the user has a secret shared, force QR code if not
|
30 |
+
$user = Mage::getSingleton('admin/session')->getUser();
|
31 |
+
$admin_user = Mage::getModel('admin/user')->load($user->getId());
|
32 |
+
$secret = $admin_user->getTwofactorGoogleSecret();
|
33 |
+
|
34 |
+
if (!empty($secret)) {
|
35 |
+
// $msg = Mage::helper('he_twofactorauth')->__('Please complete the Google two factor authentication');
|
36 |
+
// Mage::getSingleton('adminhtml/session')->addNotice($msg);
|
37 |
+
}
|
38 |
+
else {
|
39 |
+
$g2fa = Mage::getModel("he_twofactorauth/validate_google");
|
40 |
+
$secret = $g2fa->generateSecret();
|
41 |
+
$qr_url = $g2fa->generateQRCodeUrl($secret, $user->getUsername());
|
42 |
+
$msg = Mage::helper('he_twofactorauth')->__('You must set up your Google Authenticator app using the QR code below.');
|
43 |
+
Mage::getSingleton('adminhtml/session')->addNotice($msg);
|
44 |
+
// TODO figure out message weirdness
|
45 |
+
}
|
46 |
+
|
47 |
+
?>
|
48 |
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
49 |
+
<html lang="en">
|
50 |
+
<head>
|
51 |
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
52 |
+
<title><?php echo Mage::helper('adminhtml')->__('Google Two Factor Authentication'); ?></title>
|
53 |
+
<link type="text/css" rel="stylesheet" href="<?php echo $this->getSkinUrl('reset.css'); ?>" media="all" />
|
54 |
+
<link type="text/css" rel="stylesheet" href="<?php echo $this->getSkinUrl('boxes.css'); ?>" media="all" />
|
55 |
+
<link rel="icon" href="<?php echo $this->getSkinUrl('favicon.ico'); ?>" type="image/x-icon" />
|
56 |
+
<link rel="shortcut icon" href="<?php echo $this->getSkinUrl('favicon.ico'); ?>" type="image/x-icon" />
|
57 |
+
|
58 |
+
<script type="text/javascript" src="<?php echo $this->getJsUrl(); ?>index.php/x.js?f=prototype/prototype.js,prototype/validation.js,mage/adminhtml/events.js,mage/adminhtml/form.js,scriptaculous/effects.js"></script>
|
59 |
+
<script type="text/javascript" src="<?php echo $this->getJsUrl('mage/captcha.js') ?>"></script>
|
60 |
+
|
61 |
+
<!--[if IE]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('iestyles.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
62 |
+
<!--[if lt IE 7]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('below_ie7.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
63 |
+
<!--[if IE 7]> <link rel="stylesheet" href="<?php echo $this->getSkinUrl('ie7.css'); ?>" type="text/css" media="all" /> <![endif]-->
|
64 |
+
</head>
|
65 |
+
<body id="page-login">
|
66 |
+
<div class="login-container">
|
67 |
+
<div class="login-box">
|
68 |
+
<form method="post" action="<?php echo $this->getUrl('adminhtml/twofactor/verify') ?>" id="loginForm">
|
69 |
+
<fieldset class="login-form">
|
70 |
+
<input name="form_key" type="hidden" value="<?php echo $this->getFormKey(); ?>" />
|
71 |
+
<h2><?php echo Mage::helper('adminhtml')->__('Please enter your One Time Password'); ?></h2>
|
72 |
+
<div id="messages">
|
73 |
+
<?php echo $this->getMessagesBlock()->getGroupedHtml(); ?>
|
74 |
+
</div>
|
75 |
+
|
76 |
+
<?php if (!empty($qr_url)) { // SHOW the QR code so they can set up their authenticator ?>
|
77 |
+
|
78 |
+
<p align='center'><img src="<?php echo $qr_url; ?>" /></p>
|
79 |
+
<input type="hidden" name="google_secret" value="<?php echo $secret; ?>" />
|
80 |
+
<p><strong>To use this QR code in Google Authenticator</strong>:</p>
|
81 |
+
<ol>
|
82 |
+
<li>Start the app</li>
|
83 |
+
<li>Choose 'set up account' from the menu (top right corner)</li>
|
84 |
+
<li>Choose 'scan a barcode'</li>
|
85 |
+
<li>Point the camera at the QR code above</li>
|
86 |
+
<li>When the app tells you the secret has been saved, press the 'Continue' button below.</li>
|
87 |
+
</ol>
|
88 |
+
|
89 |
+
<?php } else { // ... otherwise, show the input code field ?>
|
90 |
+
<div class="input-box forgot-password"><label for="input_code"><?php echo Mage::helper('adminhtml')->__('Security Code'); ?></label><br />
|
91 |
+
<input type="text" id="input_code" name="input_code" value="" class="required-entry input-text" style="width:461px;" />
|
92 |
+
</div>
|
93 |
+
<?php } ?>
|
94 |
+
|
95 |
+
<?php echo $this->getChildHtml('form.additional.info'); ?>
|
96 |
+
|
97 |
+
<div class="clear"></div>
|
98 |
+
<div class="form-buttons">
|
99 |
+
<a class="left" href="<?php echo $this->getUrl('adminhtml', array('_nosecret' => true)); ?>">« <?php echo Mage::helper('adminhtml')->__('Back to Login'); ?></a>
|
100 |
+
|
101 |
+
<?php if (!empty($qr_url)) { ?>
|
102 |
+
<button class="forgot-password" onclick="loginForm.submit()" type="button"><span><span><span><?php echo Mage::helper('adminhtml')->__('Continue'); // TODO need better/clearer wording here? ?></span></span></span></button>
|
103 |
+
|
104 |
+
<?php } else { ?>
|
105 |
+
|
106 |
+
<button class="forgot-password" onclick="loginForm.submit()" type="button"><span><span><span><?php echo Mage::helper('adminhtml')->__('Submit Code'); ?></span></span></span></button>
|
107 |
+
|
108 |
+
<?php } ?>
|
109 |
+
</div>
|
110 |
+
</fieldset>
|
111 |
+
<p class="legal"><?php echo Mage::helper('adminhtml')->__('Magento is a trademark of Magento Inc. Copyright © %s Magento Inc.', date('Y')); ?></p>
|
112 |
+
</form>
|
113 |
+
<div class="bottom"></div>
|
114 |
+
<script type="text/javascript">
|
115 |
+
var loginForm = new varienForm('loginForm');
|
116 |
+
</script>
|
117 |
+
</div>
|
118 |
+
</div>
|
119 |
+
</body>
|
120 |
+
</html>
|
121 |
+
|
app/design/adminhtml/default/default/template/he_twofactor/system/config/fieldset/hint.phtml
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
//gets module version from config.xml
|
3 |
+
$_moduleVersion=$this->getTwoFactorAuthVersion();
|
4 |
+
//config init
|
5 |
+
$_configLinks='default/he2falinks/'; $_config=Mage::getConfig();
|
6 |
+
//links from translate CSV file
|
7 |
+
$_heLink=$_config->getNode($_configLinks.'human-element-link');
|
8 |
+
$_nexcessLink=$_config->getNode($_configLinks.'nexcess-link');
|
9 |
+
$_docsLink=$_config->getNode($_configLinks.'docs-link');
|
10 |
+
$_submitBugLink=$_config->getNode($_configLinks.'submit-bug-link');
|
11 |
+
//$_multiAuthLink=$_config->getNode($_configLinks.'multi-auth-link');
|
12 |
+
$_mageSupportLink=$_config->getNode($_configLinks.'mage-support-link');
|
13 |
+
$_contactLink=$_config->getNode($_configLinks.'contact-link');
|
14 |
+
|
15 |
+
//remove http... from start of link so it can be used as a label in the paragraph
|
16 |
+
function simplifyLinkLabel($link){
|
17 |
+
$link=str_replace('https://www.', '', $link);
|
18 |
+
$link=str_replace('http://www.', '', $link);
|
19 |
+
return $link;
|
20 |
+
}
|
21 |
+
?>
|
22 |
+
|
23 |
+
<div class="sentry-intro">
|
24 |
+
<div class="line">
|
25 |
+
<?php echo $this->__('Sentry Two-Factor Authentication for Magento v%s', $_moduleVersion); ?>
|
26 |
+
</div>
|
27 |
+
<div class="line">
|
28 |
+
<?php echo $this->__('Developed as a Partnership between <a href="%s" target="_blank">Human Element</a> and <a href="%s" target="_blank">Nexcess</a> Hosting',$_heLink,$_nexcessLink); ?>
|
29 |
+
</div>
|
30 |
+
</div>
|
31 |
+
<div class="sentry-banner">
|
32 |
+
<div class="sentry-ribbon">
|
33 |
+
<?php echo $this->__('<strong>Sentry</strong> Two-factor Authentication for Magento'); ?>
|
34 |
+
<div class="btns">
|
35 |
+
<a class="btn bug" href="<?php echo $_submitBugLink; ?>" target="_blank"><?php echo $this->__('Submit a Bug'); ?></a>
|
36 |
+
<a class="btn readme" href="<?php echo $_docsLink; ?>" target="_blank"><?php echo $this->__('Read Me'); ?></a>
|
37 |
+
</div>
|
38 |
+
</div>
|
39 |
+
<p>Multifactor authentication provides an additional check when logging into your Magento administration system.
|
40 |
+
Once configured, an additional challenge screen is added to the admin login process. This screen will interact
|
41 |
+
with an additional method of contact, usually a cellphone or email, that will prove that the user attempting login is the real user.</p>
|
42 |
+
<p>NOTE - when using multifactor authentication it is highly advised that each admin user have their own account.</p>
|
43 |
+
<p>To find out more about Human Element and <a href="<?php echo $_mageSupportLink ?>" target="_blank">Magento Support Services</a>, please visit our website at
|
44 |
+
<a href="<?php echo $_heLink ?>" target="_blank"><?php echo simplifyLinkLabel($_heLink); ?></a>
|
45 |
+
or fill in our <a href="<?php echo $_contactLink; ?>" target="_blank">contact form</a>.
|
46 |
+
To find out more about Nexcess Hosting, visit their website at
|
47 |
+
<a href="<?php echo $_nexcessLink; ?>" target="_blank"><?php echo simplifyLinkLabel($_nexcessLink); ?></a>. </p>
|
48 |
+
</div>
|
app/etc/modules/HE_TwoFactorAuth.xml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<HE_TwoFactorAuth>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>community</codePool>
|
7 |
+
</HE_TwoFactorAuth>
|
8 |
+
</modules>
|
9 |
+
</config>
|
js/he_twofactor/Duo-Web-v1.bundled.js
ADDED
@@ -0,0 +1,6573 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* jQuery JavaScript Library v1.4.2
|
3 |
+
* http://jquery.com/
|
4 |
+
*
|
5 |
+
* Copyright 2010, John Resig
|
6 |
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
7 |
+
* http://jquery.org/license
|
8 |
+
*
|
9 |
+
* Includes Sizzle.js
|
10 |
+
* http://sizzlejs.com/
|
11 |
+
* Copyright 2010, The Dojo Foundation
|
12 |
+
* Released under the MIT, BSD, and GPL Licenses.
|
13 |
+
*
|
14 |
+
* Date: Sat Feb 13 22:33:48 2010 -0500
|
15 |
+
*/
|
16 |
+
(function( window, undefined ) {
|
17 |
+
|
18 |
+
// Define a local copy of jQuery
|
19 |
+
var jQuery = function( selector, context ) {
|
20 |
+
// The jQuery object is actually just the init constructor 'enhanced'
|
21 |
+
return new jQuery.fn.init( selector, context );
|
22 |
+
},
|
23 |
+
|
24 |
+
// Map over jQuery in case of overwrite
|
25 |
+
_jQuery = window.jQuery,
|
26 |
+
|
27 |
+
// Map over the $ in case of overwrite
|
28 |
+
_$ = window.$,
|
29 |
+
|
30 |
+
// Use the correct document accordingly with window argument (sandbox)
|
31 |
+
document = window.document,
|
32 |
+
|
33 |
+
// A central reference to the root jQuery(document)
|
34 |
+
rootjQuery,
|
35 |
+
|
36 |
+
// A simple way to check for HTML strings or ID strings
|
37 |
+
// (both of which we optimize for)
|
38 |
+
quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
|
39 |
+
|
40 |
+
// Is it a simple selector
|
41 |
+
isSimple = /^.[^:#\[\.,]*$/,
|
42 |
+
|
43 |
+
// Check if a string has a non-whitespace character in it
|
44 |
+
rnotwhite = /\S/,
|
45 |
+
|
46 |
+
// Used for trimming whitespace
|
47 |
+
rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
|
48 |
+
|
49 |
+
// Match a standalone tag
|
50 |
+
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
|
51 |
+
|
52 |
+
// Keep a UserAgent string for use with jQuery.browser
|
53 |
+
userAgent = navigator.userAgent,
|
54 |
+
|
55 |
+
// For matching the engine and version of the browser
|
56 |
+
browserMatch,
|
57 |
+
|
58 |
+
// Has the ready events already been bound?
|
59 |
+
readyBound = false,
|
60 |
+
|
61 |
+
// The functions to execute on DOM ready
|
62 |
+
readyList = [],
|
63 |
+
|
64 |
+
// The ready event handler
|
65 |
+
DOMContentLoaded,
|
66 |
+
|
67 |
+
// Save a reference to some core methods
|
68 |
+
toString = Object.prototype.toString,
|
69 |
+
hasOwnProperty = Object.prototype.hasOwnProperty,
|
70 |
+
push = Array.prototype.push,
|
71 |
+
slice = Array.prototype.slice,
|
72 |
+
indexOf = Array.prototype.indexOf;
|
73 |
+
|
74 |
+
jQuery.fn = jQuery.prototype = {
|
75 |
+
init: function( selector, context ) {
|
76 |
+
var match, elem, ret, doc;
|
77 |
+
|
78 |
+
// Handle $(""), $(null), or $(undefined)
|
79 |
+
if ( !selector ) {
|
80 |
+
return this;
|
81 |
+
}
|
82 |
+
|
83 |
+
// Handle $(DOMElement)
|
84 |
+
if ( selector.nodeType ) {
|
85 |
+
this.context = this[0] = selector;
|
86 |
+
this.length = 1;
|
87 |
+
return this;
|
88 |
+
}
|
89 |
+
|
90 |
+
// The body element only exists once, optimize finding it
|
91 |
+
if ( selector === "body" && !context ) {
|
92 |
+
this.context = document;
|
93 |
+
this[0] = document.body;
|
94 |
+
this.selector = "body";
|
95 |
+
this.length = 1;
|
96 |
+
return this;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Handle HTML strings
|
100 |
+
if ( typeof selector === "string" ) {
|
101 |
+
// Are we dealing with HTML string or an ID?
|
102 |
+
match = quickExpr.exec( selector );
|
103 |
+
|
104 |
+
// Verify a match, and that no context was specified for #id
|
105 |
+
if ( match && (match[1] || !context) ) {
|
106 |
+
|
107 |
+
// HANDLE: $(html) -> $(array)
|
108 |
+
if ( match[1] ) {
|
109 |
+
doc = (context ? context.ownerDocument || context : document);
|
110 |
+
|
111 |
+
// If a single string is passed in and it's a single tag
|
112 |
+
// just do a createElement and skip the rest
|
113 |
+
ret = rsingleTag.exec( selector );
|
114 |
+
|
115 |
+
if ( ret ) {
|
116 |
+
if ( jQuery.isPlainObject( context ) ) {
|
117 |
+
selector = [ document.createElement( ret[1] ) ];
|
118 |
+
jQuery.fn.attr.call( selector, context, true );
|
119 |
+
|
120 |
+
} else {
|
121 |
+
selector = [ doc.createElement( ret[1] ) ];
|
122 |
+
}
|
123 |
+
|
124 |
+
} else {
|
125 |
+
ret = buildFragment( [ match[1] ], [ doc ] );
|
126 |
+
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
|
127 |
+
}
|
128 |
+
|
129 |
+
return jQuery.merge( this, selector );
|
130 |
+
|
131 |
+
// HANDLE: $("#id")
|
132 |
+
} else {
|
133 |
+
elem = document.getElementById( match[2] );
|
134 |
+
|
135 |
+
if ( elem ) {
|
136 |
+
// Handle the case where IE and Opera return items
|
137 |
+
// by name instead of ID
|
138 |
+
if ( elem.id !== match[2] ) {
|
139 |
+
return rootjQuery.find( selector );
|
140 |
+
}
|
141 |
+
|
142 |
+
// Otherwise, we inject the element directly into the jQuery object
|
143 |
+
this.length = 1;
|
144 |
+
this[0] = elem;
|
145 |
+
}
|
146 |
+
|
147 |
+
this.context = document;
|
148 |
+
this.selector = selector;
|
149 |
+
return this;
|
150 |
+
}
|
151 |
+
|
152 |
+
// HANDLE: $("TAG")
|
153 |
+
} else if ( !context && /^\w+$/.test( selector ) ) {
|
154 |
+
this.selector = selector;
|
155 |
+
this.context = document;
|
156 |
+
selector = document.getElementsByTagName( selector );
|
157 |
+
return jQuery.merge( this, selector );
|
158 |
+
|
159 |
+
// HANDLE: $(expr, $(...))
|
160 |
+
} else if ( !context || context.jquery ) {
|
161 |
+
return (context || rootjQuery).find( selector );
|
162 |
+
|
163 |
+
// HANDLE: $(expr, context)
|
164 |
+
// (which is just equivalent to: $(context).find(expr)
|
165 |
+
} else {
|
166 |
+
return jQuery( context ).find( selector );
|
167 |
+
}
|
168 |
+
|
169 |
+
// HANDLE: $(function)
|
170 |
+
// Shortcut for document ready
|
171 |
+
} else if ( jQuery.isFunction( selector ) ) {
|
172 |
+
return rootjQuery.ready( selector );
|
173 |
+
}
|
174 |
+
|
175 |
+
if (selector.selector !== undefined) {
|
176 |
+
this.selector = selector.selector;
|
177 |
+
this.context = selector.context;
|
178 |
+
}
|
179 |
+
|
180 |
+
return jQuery.makeArray( selector, this );
|
181 |
+
},
|
182 |
+
|
183 |
+
// Start with an empty selector
|
184 |
+
selector: "",
|
185 |
+
|
186 |
+
// The current version of jQuery being used
|
187 |
+
jquery: "1.4.2",
|
188 |
+
|
189 |
+
// The default length of a jQuery object is 0
|
190 |
+
length: 0,
|
191 |
+
|
192 |
+
// The number of elements contained in the matched element set
|
193 |
+
size: function() {
|
194 |
+
return this.length;
|
195 |
+
},
|
196 |
+
|
197 |
+
toArray: function() {
|
198 |
+
return slice.call( this, 0 );
|
199 |
+
},
|
200 |
+
|
201 |
+
// Get the Nth element in the matched element set OR
|
202 |
+
// Get the whole matched element set as a clean array
|
203 |
+
get: function( num ) {
|
204 |
+
return num == null ?
|
205 |
+
|
206 |
+
// Return a 'clean' array
|
207 |
+
this.toArray() :
|
208 |
+
|
209 |
+
// Return just the object
|
210 |
+
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
|
211 |
+
},
|
212 |
+
|
213 |
+
// Take an array of elements and push it onto the stack
|
214 |
+
// (returning the new matched element set)
|
215 |
+
pushStack: function( elems, name, selector ) {
|
216 |
+
// Build a new jQuery matched element set
|
217 |
+
var ret = jQuery();
|
218 |
+
|
219 |
+
if ( jQuery.isArray( elems ) ) {
|
220 |
+
push.apply( ret, elems );
|
221 |
+
|
222 |
+
} else {
|
223 |
+
jQuery.merge( ret, elems );
|
224 |
+
}
|
225 |
+
|
226 |
+
// Add the old object onto the stack (as a reference)
|
227 |
+
ret.prevObject = this;
|
228 |
+
|
229 |
+
ret.context = this.context;
|
230 |
+
|
231 |
+
if ( name === "find" ) {
|
232 |
+
ret.selector = this.selector + (this.selector ? " " : "") + selector;
|
233 |
+
} else if ( name ) {
|
234 |
+
ret.selector = this.selector + "." + name + "(" + selector + ")";
|
235 |
+
}
|
236 |
+
|
237 |
+
// Return the newly-formed element set
|
238 |
+
return ret;
|
239 |
+
},
|
240 |
+
|
241 |
+
// Execute a callback for every element in the matched set.
|
242 |
+
// (You can seed the arguments with an array of args, but this is
|
243 |
+
// only used internally.)
|
244 |
+
each: function( callback, args ) {
|
245 |
+
return jQuery.each( this, callback, args );
|
246 |
+
},
|
247 |
+
|
248 |
+
ready: function( fn ) {
|
249 |
+
// Attach the listeners
|
250 |
+
jQuery.bindReady();
|
251 |
+
|
252 |
+
// If the DOM is already ready
|
253 |
+
if ( jQuery.isReady ) {
|
254 |
+
// Execute the function immediately
|
255 |
+
fn.call( document, jQuery );
|
256 |
+
|
257 |
+
// Otherwise, remember the function for later
|
258 |
+
} else if ( readyList ) {
|
259 |
+
// Add the function to the wait list
|
260 |
+
readyList.push( fn );
|
261 |
+
}
|
262 |
+
|
263 |
+
return this;
|
264 |
+
},
|
265 |
+
|
266 |
+
eq: function( i ) {
|
267 |
+
return i === -1 ?
|
268 |
+
this.slice( i ) :
|
269 |
+
this.slice( i, +i + 1 );
|
270 |
+
},
|
271 |
+
|
272 |
+
first: function() {
|
273 |
+
return this.eq( 0 );
|
274 |
+
},
|
275 |
+
|
276 |
+
last: function() {
|
277 |
+
return this.eq( -1 );
|
278 |
+
},
|
279 |
+
|
280 |
+
slice: function() {
|
281 |
+
return this.pushStack( slice.apply( this, arguments ),
|
282 |
+
"slice", slice.call(arguments).join(",") );
|
283 |
+
},
|
284 |
+
|
285 |
+
map: function( callback ) {
|
286 |
+
return this.pushStack( jQuery.map(this, function( elem, i ) {
|
287 |
+
return callback.call( elem, i, elem );
|
288 |
+
}));
|
289 |
+
},
|
290 |
+
|
291 |
+
end: function() {
|
292 |
+
return this.prevObject || jQuery(null);
|
293 |
+
},
|
294 |
+
|
295 |
+
// For internal use only.
|
296 |
+
// Behaves like an Array's method, not like a jQuery method.
|
297 |
+
push: push,
|
298 |
+
sort: [].sort,
|
299 |
+
splice: [].splice
|
300 |
+
};
|
301 |
+
|
302 |
+
// Give the init function the jQuery prototype for later instantiation
|
303 |
+
jQuery.fn.init.prototype = jQuery.fn;
|
304 |
+
|
305 |
+
jQuery.extend = jQuery.fn.extend = function() {
|
306 |
+
// copy reference to target object
|
307 |
+
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
|
308 |
+
|
309 |
+
// Handle a deep copy situation
|
310 |
+
if ( typeof target === "boolean" ) {
|
311 |
+
deep = target;
|
312 |
+
target = arguments[1] || {};
|
313 |
+
// skip the boolean and the target
|
314 |
+
i = 2;
|
315 |
+
}
|
316 |
+
|
317 |
+
// Handle case when target is a string or something (possible in deep copy)
|
318 |
+
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
|
319 |
+
target = {};
|
320 |
+
}
|
321 |
+
|
322 |
+
// extend jQuery itself if only one argument is passed
|
323 |
+
if ( length === i ) {
|
324 |
+
target = this;
|
325 |
+
--i;
|
326 |
+
}
|
327 |
+
|
328 |
+
for ( ; i < length; i++ ) {
|
329 |
+
// Only deal with non-null/undefined values
|
330 |
+
if ( (options = arguments[ i ]) != null ) {
|
331 |
+
// Extend the base object
|
332 |
+
for ( name in options ) {
|
333 |
+
src = target[ name ];
|
334 |
+
copy = options[ name ];
|
335 |
+
|
336 |
+
// Prevent never-ending loop
|
337 |
+
if ( target === copy ) {
|
338 |
+
continue;
|
339 |
+
}
|
340 |
+
|
341 |
+
// Recurse if we're merging object literal values or arrays
|
342 |
+
if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
|
343 |
+
var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
|
344 |
+
: jQuery.isArray(copy) ? [] : {};
|
345 |
+
|
346 |
+
// Never move original objects, clone them
|
347 |
+
target[ name ] = jQuery.extend( deep, clone, copy );
|
348 |
+
|
349 |
+
// Don't bring in undefined values
|
350 |
+
} else if ( copy !== undefined ) {
|
351 |
+
target[ name ] = copy;
|
352 |
+
}
|
353 |
+
}
|
354 |
+
}
|
355 |
+
}
|
356 |
+
|
357 |
+
// Return the modified object
|
358 |
+
return target;
|
359 |
+
};
|
360 |
+
|
361 |
+
jQuery.extend({
|
362 |
+
noConflict: function( deep ) {
|
363 |
+
window.$ = _$;
|
364 |
+
|
365 |
+
if ( deep ) {
|
366 |
+
window.jQuery = _jQuery;
|
367 |
+
}
|
368 |
+
|
369 |
+
return jQuery;
|
370 |
+
},
|
371 |
+
|
372 |
+
// Is the DOM ready to be used? Set to true once it occurs.
|
373 |
+
isReady: false,
|
374 |
+
|
375 |
+
// Handle when the DOM is ready
|
376 |
+
ready: function() {
|
377 |
+
// Make sure that the DOM is not already loaded
|
378 |
+
if ( !jQuery.isReady ) {
|
379 |
+
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
|
380 |
+
if ( !document.body ) {
|
381 |
+
return setTimeout( jQuery.ready, 13 );
|
382 |
+
}
|
383 |
+
|
384 |
+
// Remember that the DOM is ready
|
385 |
+
jQuery.isReady = true;
|
386 |
+
|
387 |
+
// If there are functions bound, to execute
|
388 |
+
if ( readyList ) {
|
389 |
+
// Execute all of them
|
390 |
+
var fn, i = 0;
|
391 |
+
while ( (fn = readyList[ i++ ]) ) {
|
392 |
+
fn.call( document, jQuery );
|
393 |
+
}
|
394 |
+
|
395 |
+
// Reset the list of functions
|
396 |
+
readyList = null;
|
397 |
+
}
|
398 |
+
|
399 |
+
// Trigger any bound ready events
|
400 |
+
if ( jQuery.fn.triggerHandler ) {
|
401 |
+
jQuery( document ).triggerHandler( "ready" );
|
402 |
+
}
|
403 |
+
}
|
404 |
+
},
|
405 |
+
|
406 |
+
bindReady: function() {
|
407 |
+
if ( readyBound ) {
|
408 |
+
return;
|
409 |
+
}
|
410 |
+
|
411 |
+
readyBound = true;
|
412 |
+
|
413 |
+
// Catch cases where $(document).ready() is called after the
|
414 |
+
// browser event has already occurred.
|
415 |
+
if ( document.readyState === "complete" ) {
|
416 |
+
return jQuery.ready();
|
417 |
+
}
|
418 |
+
|
419 |
+
// Mozilla, Opera and webkit nightlies currently support this event
|
420 |
+
if ( document.addEventListener ) {
|
421 |
+
// Use the handy event callback
|
422 |
+
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
|
423 |
+
|
424 |
+
// A fallback to window.onload, that will always work
|
425 |
+
window.addEventListener( "load", jQuery.ready, false );
|
426 |
+
|
427 |
+
// If IE event model is used
|
428 |
+
} else if ( document.attachEvent ) {
|
429 |
+
// ensure firing before onload,
|
430 |
+
// maybe late but safe also for iframes
|
431 |
+
document.attachEvent("onreadystatechange", DOMContentLoaded);
|
432 |
+
|
433 |
+
// A fallback to window.onload, that will always work
|
434 |
+
window.attachEvent( "onload", jQuery.ready );
|
435 |
+
|
436 |
+
// If IE and not a frame
|
437 |
+
// continually check to see if the document is ready
|
438 |
+
var toplevel = false;
|
439 |
+
|
440 |
+
try {
|
441 |
+
toplevel = window.frameElement == null;
|
442 |
+
} catch(e) {}
|
443 |
+
|
444 |
+
if ( document.documentElement.doScroll && toplevel ) {
|
445 |
+
doScrollCheck();
|
446 |
+
}
|
447 |
+
}
|
448 |
+
},
|
449 |
+
|
450 |
+
// See test/unit/core.js for details concerning isFunction.
|
451 |
+
// Since version 1.3, DOM methods and functions like alert
|
452 |
+
// aren't supported. They return false on IE (#2968).
|
453 |
+
isFunction: function( obj ) {
|
454 |
+
return toString.call(obj) === "[object Function]";
|
455 |
+
},
|
456 |
+
|
457 |
+
isArray: function( obj ) {
|
458 |
+
return toString.call(obj) === "[object Array]";
|
459 |
+
},
|
460 |
+
|
461 |
+
isPlainObject: function( obj ) {
|
462 |
+
// Must be an Object.
|
463 |
+
// Because of IE, we also have to check the presence of the constructor property.
|
464 |
+
// Make sure that DOM nodes and window objects don't pass through, as well
|
465 |
+
if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
|
466 |
+
return false;
|
467 |
+
}
|
468 |
+
|
469 |
+
// Not own constructor property must be Object
|
470 |
+
if ( obj.constructor
|
471 |
+
&& !hasOwnProperty.call(obj, "constructor")
|
472 |
+
&& !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
|
473 |
+
return false;
|
474 |
+
}
|
475 |
+
|
476 |
+
// Own properties are enumerated firstly, so to speed up,
|
477 |
+
// if last one is own, then all properties are own.
|
478 |
+
|
479 |
+
var key;
|
480 |
+
for ( key in obj ) {}
|
481 |
+
|
482 |
+
return key === undefined || hasOwnProperty.call( obj, key );
|
483 |
+
},
|
484 |
+
|
485 |
+
isEmptyObject: function( obj ) {
|
486 |
+
for ( var name in obj ) {
|
487 |
+
return false;
|
488 |
+
}
|
489 |
+
return true;
|
490 |
+
},
|
491 |
+
|
492 |
+
error: function( msg ) {
|
493 |
+
throw msg;
|
494 |
+
},
|
495 |
+
|
496 |
+
parseJSON: function( data ) {
|
497 |
+
if ( typeof data !== "string" || !data ) {
|
498 |
+
return null;
|
499 |
+
}
|
500 |
+
|
501 |
+
// Make sure leading/trailing whitespace is removed (IE can't handle it)
|
502 |
+
data = jQuery.trim( data );
|
503 |
+
|
504 |
+
// Make sure the incoming data is actual JSON
|
505 |
+
// Logic borrowed from http://json.org/json2.js
|
506 |
+
if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
|
507 |
+
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
|
508 |
+
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
|
509 |
+
|
510 |
+
// Try to use the native JSON parser first
|
511 |
+
return window.JSON && window.JSON.parse ?
|
512 |
+
window.JSON.parse( data ) :
|
513 |
+
(new Function("return " + data))();
|
514 |
+
|
515 |
+
} else {
|
516 |
+
jQuery.error( "Invalid JSON: " + data );
|
517 |
+
}
|
518 |
+
},
|
519 |
+
|
520 |
+
noop: function() {},
|
521 |
+
|
522 |
+
// Evalulates a script in a global context
|
523 |
+
globalEval: function( data ) {
|
524 |
+
if ( data && rnotwhite.test(data) ) {
|
525 |
+
// Inspired by code by Andrea Giammarchi
|
526 |
+
// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
|
527 |
+
var head = document.getElementsByTagName("head")[0] || document.documentElement,
|
528 |
+
script = document.createElement("script");
|
529 |
+
|
530 |
+
script.type = "text/javascript";
|
531 |
+
|
532 |
+
if ( jQuery.support.scriptEval ) {
|
533 |
+
script.appendChild( document.createTextNode( data ) );
|
534 |
+
} else {
|
535 |
+
script.text = data;
|
536 |
+
}
|
537 |
+
|
538 |
+
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
|
539 |
+
// This arises when a base node is used (#2709).
|
540 |
+
head.insertBefore( script, head.firstChild );
|
541 |
+
head.removeChild( script );
|
542 |
+
}
|
543 |
+
},
|
544 |
+
|
545 |
+
nodeName: function( elem, name ) {
|
546 |
+
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
|
547 |
+
},
|
548 |
+
|
549 |
+
// args is for internal usage only
|
550 |
+
each: function( object, callback, args ) {
|
551 |
+
var name, i = 0,
|
552 |
+
length = object.length,
|
553 |
+
isObj = length === undefined || jQuery.isFunction(object);
|
554 |
+
|
555 |
+
if ( args ) {
|
556 |
+
if ( isObj ) {
|
557 |
+
for ( name in object ) {
|
558 |
+
if ( callback.apply( object[ name ], args ) === false ) {
|
559 |
+
break;
|
560 |
+
}
|
561 |
+
}
|
562 |
+
} else {
|
563 |
+
for ( ; i < length; ) {
|
564 |
+
if ( callback.apply( object[ i++ ], args ) === false ) {
|
565 |
+
break;
|
566 |
+
}
|
567 |
+
}
|
568 |
+
}
|
569 |
+
|
570 |
+
// A special, fast, case for the most common use of each
|
571 |
+
} else {
|
572 |
+
if ( isObj ) {
|
573 |
+
for ( name in object ) {
|
574 |
+
if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
|
575 |
+
break;
|
576 |
+
}
|
577 |
+
}
|
578 |
+
} else {
|
579 |
+
for ( var value = object[0];
|
580 |
+
i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
|
581 |
+
}
|
582 |
+
}
|
583 |
+
|
584 |
+
return object;
|
585 |
+
},
|
586 |
+
|
587 |
+
trim: function( text ) {
|
588 |
+
return (text || "").replace( rtrim, "" );
|
589 |
+
},
|
590 |
+
|
591 |
+
// results is for internal usage only
|
592 |
+
makeArray: function( array, results ) {
|
593 |
+
var ret = results || [];
|
594 |
+
|
595 |
+
if ( array != null ) {
|
596 |
+
// The window, strings (and functions) also have 'length'
|
597 |
+
// The extra typeof function check is to prevent crashes
|
598 |
+
// in Safari 2 (See: #3039)
|
599 |
+
if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
|
600 |
+
push.call( ret, array );
|
601 |
+
} else {
|
602 |
+
jQuery.merge( ret, array );
|
603 |
+
}
|
604 |
+
}
|
605 |
+
|
606 |
+
return ret;
|
607 |
+
},
|
608 |
+
|
609 |
+
inArray: function( elem, array ) {
|
610 |
+
if ( array.indexOf ) {
|
611 |
+
return array.indexOf( elem );
|
612 |
+
}
|
613 |
+
|
614 |
+
for ( var i = 0, length = array.length; i < length; i++ ) {
|
615 |
+
if ( array[ i ] === elem ) {
|
616 |
+
return i;
|
617 |
+
}
|
618 |
+
}
|
619 |
+
|
620 |
+
return -1;
|
621 |
+
},
|
622 |
+
|
623 |
+
merge: function( first, second ) {
|
624 |
+
var i = first.length, j = 0;
|
625 |
+
|
626 |
+
if ( typeof second.length === "number" ) {
|
627 |
+
for ( var l = second.length; j < l; j++ ) {
|
628 |
+
first[ i++ ] = second[ j ];
|
629 |
+
}
|
630 |
+
|
631 |
+
} else {
|
632 |
+
while ( second[j] !== undefined ) {
|
633 |
+
first[ i++ ] = second[ j++ ];
|
634 |
+
}
|
635 |
+
}
|
636 |
+
|
637 |
+
first.length = i;
|
638 |
+
|
639 |
+
return first;
|
640 |
+
},
|
641 |
+
|
642 |
+
grep: function( elems, callback, inv ) {
|
643 |
+
var ret = [];
|
644 |
+
|
645 |
+
// Go through the array, only saving the items
|
646 |
+
// that pass the validator function
|
647 |
+
for ( var i = 0, length = elems.length; i < length; i++ ) {
|
648 |
+
if ( !inv !== !callback( elems[ i ], i ) ) {
|
649 |
+
ret.push( elems[ i ] );
|
650 |
+
}
|
651 |
+
}
|
652 |
+
|
653 |
+
return ret;
|
654 |
+
},
|
655 |
+
|
656 |
+
// arg is for internal usage only
|
657 |
+
map: function( elems, callback, arg ) {
|
658 |
+
var ret = [], value;
|
659 |
+
|
660 |
+
// Go through the array, translating each of the items to their
|
661 |
+
// new value (or values).
|
662 |
+
for ( var i = 0, length = elems.length; i < length; i++ ) {
|
663 |
+
value = callback( elems[ i ], i, arg );
|
664 |
+
|
665 |
+
if ( value != null ) {
|
666 |
+
ret[ ret.length ] = value;
|
667 |
+
}
|
668 |
+
}
|
669 |
+
|
670 |
+
return ret.concat.apply( [], ret );
|
671 |
+
},
|
672 |
+
|
673 |
+
// A global GUID counter for objects
|
674 |
+
guid: 1,
|
675 |
+
|
676 |
+
proxy: function( fn, proxy, thisObject ) {
|
677 |
+
if ( arguments.length === 2 ) {
|
678 |
+
if ( typeof proxy === "string" ) {
|
679 |
+
thisObject = fn;
|
680 |
+
fn = thisObject[ proxy ];
|
681 |
+
proxy = undefined;
|
682 |
+
|
683 |
+
} else if ( proxy && !jQuery.isFunction( proxy ) ) {
|
684 |
+
thisObject = proxy;
|
685 |
+
proxy = undefined;
|
686 |
+
}
|
687 |
+
}
|
688 |
+
|
689 |
+
if ( !proxy && fn ) {
|
690 |
+
proxy = function() {
|
691 |
+
return fn.apply( thisObject || this, arguments );
|
692 |
+
};
|
693 |
+
}
|
694 |
+
|
695 |
+
// Set the guid of unique handler to the same of original handler, so it can be removed
|
696 |
+
if ( fn ) {
|
697 |
+
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
|
698 |
+
}
|
699 |
+
|
700 |
+
// So proxy can be declared as an argument
|
701 |
+
return proxy;
|
702 |
+
},
|
703 |
+
|
704 |
+
// Use of jQuery.browser is frowned upon.
|
705 |
+
// More details: http://docs.jquery.com/Utilities/jQuery.browser
|
706 |
+
uaMatch: function( ua ) {
|
707 |
+
ua = ua.toLowerCase();
|
708 |
+
|
709 |
+
var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
|
710 |
+
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
|
711 |
+
/(msie) ([\w.]+)/.exec( ua ) ||
|
712 |
+
!/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
|
713 |
+
[];
|
714 |
+
|
715 |
+
return { browser: match[1] || "", version: match[2] || "0" };
|
716 |
+
},
|
717 |
+
|
718 |
+
browser: {}
|
719 |
+
});
|
720 |
+
|
721 |
+
browserMatch = jQuery.uaMatch( userAgent );
|
722 |
+
if ( browserMatch.browser ) {
|
723 |
+
jQuery.browser[ browserMatch.browser ] = true;
|
724 |
+
jQuery.browser.version = browserMatch.version;
|
725 |
+
}
|
726 |
+
|
727 |
+
// Deprecated, use jQuery.browser.webkit instead
|
728 |
+
if ( jQuery.browser.webkit ) {
|
729 |
+
jQuery.browser.safari = true;
|
730 |
+
}
|
731 |
+
|
732 |
+
if ( indexOf ) {
|
733 |
+
jQuery.inArray = function( elem, array ) {
|
734 |
+
return indexOf.call( array, elem );
|
735 |
+
};
|
736 |
+
}
|
737 |
+
|
738 |
+
// All jQuery objects should point back to these
|
739 |
+
rootjQuery = jQuery(document);
|
740 |
+
|
741 |
+
// Cleanup functions for the document ready method
|
742 |
+
if ( document.addEventListener ) {
|
743 |
+
DOMContentLoaded = function() {
|
744 |
+
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
|
745 |
+
jQuery.ready();
|
746 |
+
};
|
747 |
+
|
748 |
+
} else if ( document.attachEvent ) {
|
749 |
+
DOMContentLoaded = function() {
|
750 |
+
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
|
751 |
+
if ( document.readyState === "complete" ) {
|
752 |
+
document.detachEvent( "onreadystatechange", DOMContentLoaded );
|
753 |
+
jQuery.ready();
|
754 |
+
}
|
755 |
+
};
|
756 |
+
}
|
757 |
+
|
758 |
+
// The DOM ready check for Internet Explorer
|
759 |
+
function doScrollCheck() {
|
760 |
+
if ( jQuery.isReady ) {
|
761 |
+
return;
|
762 |
+
}
|
763 |
+
|
764 |
+
try {
|
765 |
+
// If IE is used, use the trick by Diego Perini
|
766 |
+
// http://javascript.nwbox.com/IEContentLoaded/
|
767 |
+
document.documentElement.doScroll("left");
|
768 |
+
} catch( error ) {
|
769 |
+
setTimeout( doScrollCheck, 1 );
|
770 |
+
return;
|
771 |
+
}
|
772 |
+
|
773 |
+
// and execute any waiting functions
|
774 |
+
jQuery.ready();
|
775 |
+
}
|
776 |
+
|
777 |
+
function evalScript( i, elem ) {
|
778 |
+
if ( elem.src ) {
|
779 |
+
jQuery.ajax({
|
780 |
+
url: elem.src,
|
781 |
+
async: false,
|
782 |
+
dataType: "script"
|
783 |
+
});
|
784 |
+
} else {
|
785 |
+
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
|
786 |
+
}
|
787 |
+
|
788 |
+
if ( elem.parentNode ) {
|
789 |
+
elem.parentNode.removeChild( elem );
|
790 |
+
}
|
791 |
+
}
|
792 |
+
|
793 |
+
// Mutifunctional method to get and set values to a collection
|
794 |
+
// The value/s can be optionally by executed if its a function
|
795 |
+
function access( elems, key, value, exec, fn, pass ) {
|
796 |
+
var length = elems.length;
|
797 |
+
|
798 |
+
// Setting many attributes
|
799 |
+
if ( typeof key === "object" ) {
|
800 |
+
for ( var k in key ) {
|
801 |
+
access( elems, k, key[k], exec, fn, value );
|
802 |
+
}
|
803 |
+
return elems;
|
804 |
+
}
|
805 |
+
|
806 |
+
// Setting one attribute
|
807 |
+
if ( value !== undefined ) {
|
808 |
+
// Optionally, function values get executed if exec is true
|
809 |
+
exec = !pass && exec && jQuery.isFunction(value);
|
810 |
+
|
811 |
+
for ( var i = 0; i < length; i++ ) {
|
812 |
+
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
|
813 |
+
}
|
814 |
+
|
815 |
+
return elems;
|
816 |
+
}
|
817 |
+
|
818 |
+
// Getting an attribute
|
819 |
+
return length ? fn( elems[0], key ) : undefined;
|
820 |
+
}
|
821 |
+
|
822 |
+
function now() {
|
823 |
+
return (new Date).getTime();
|
824 |
+
}
|
825 |
+
(function() {
|
826 |
+
|
827 |
+
jQuery.support = {};
|
828 |
+
|
829 |
+
var root = document.documentElement,
|
830 |
+
script = document.createElement("script"),
|
831 |
+
div = document.createElement("div"),
|
832 |
+
id = "script" + now();
|
833 |
+
|
834 |
+
div.style.display = "none";
|
835 |
+
div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
836 |
+
|
837 |
+
var all = div.getElementsByTagName("*"),
|
838 |
+
a = div.getElementsByTagName("a")[0];
|
839 |
+
|
840 |
+
// Can't get basic test support
|
841 |
+
if ( !all || !all.length || !a ) {
|
842 |
+
return;
|
843 |
+
}
|
844 |
+
|
845 |
+
jQuery.support = {
|
846 |
+
// IE strips leading whitespace when .innerHTML is used
|
847 |
+
leadingWhitespace: div.firstChild.nodeType === 3,
|
848 |
+
|
849 |
+
// Make sure that tbody elements aren't automatically inserted
|
850 |
+
// IE will insert them into empty tables
|
851 |
+
tbody: !div.getElementsByTagName("tbody").length,
|
852 |
+
|
853 |
+
// Make sure that link elements get serialized correctly by innerHTML
|
854 |
+
// This requires a wrapper element in IE
|
855 |
+
htmlSerialize: !!div.getElementsByTagName("link").length,
|
856 |
+
|
857 |
+
// Get the style information from getAttribute
|
858 |
+
// (IE uses .cssText insted)
|
859 |
+
style: /red/.test( a.getAttribute("style") ),
|
860 |
+
|
861 |
+
// Make sure that URLs aren't manipulated
|
862 |
+
// (IE normalizes it by default)
|
863 |
+
hrefNormalized: a.getAttribute("href") === "/a",
|
864 |
+
|
865 |
+
// Make sure that element opacity exists
|
866 |
+
// (IE uses filter instead)
|
867 |
+
// Use a regex to work around a WebKit issue. See #5145
|
868 |
+
opacity: /^0.55$/.test( a.style.opacity ),
|
869 |
+
|
870 |
+
// Verify style float existence
|
871 |
+
// (IE uses styleFloat instead of cssFloat)
|
872 |
+
cssFloat: !!a.style.cssFloat,
|
873 |
+
|
874 |
+
// Make sure that if no value is specified for a checkbox
|
875 |
+
// that it defaults to "on".
|
876 |
+
// (WebKit defaults to "" instead)
|
877 |
+
checkOn: div.getElementsByTagName("input")[0].value === "on",
|
878 |
+
|
879 |
+
// Make sure that a selected-by-default option has a working selected property.
|
880 |
+
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
|
881 |
+
optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
|
882 |
+
|
883 |
+
parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
|
884 |
+
|
885 |
+
// Will be defined later
|
886 |
+
deleteExpando: true,
|
887 |
+
checkClone: false,
|
888 |
+
scriptEval: false,
|
889 |
+
noCloneEvent: true,
|
890 |
+
boxModel: null
|
891 |
+
};
|
892 |
+
|
893 |
+
script.type = "text/javascript";
|
894 |
+
try {
|
895 |
+
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
|
896 |
+
} catch(e) {}
|
897 |
+
|
898 |
+
root.insertBefore( script, root.firstChild );
|
899 |
+
|
900 |
+
// Make sure that the execution of code works by injecting a script
|
901 |
+
// tag with appendChild/createTextNode
|
902 |
+
// (IE doesn't support this, fails, and uses .text instead)
|
903 |
+
if ( window[ id ] ) {
|
904 |
+
jQuery.support.scriptEval = true;
|
905 |
+
delete window[ id ];
|
906 |
+
}
|
907 |
+
|
908 |
+
// Test to see if it's possible to delete an expando from an element
|
909 |
+
// Fails in Internet Explorer
|
910 |
+
try {
|
911 |
+
delete script.test;
|
912 |
+
|
913 |
+
} catch(e) {
|
914 |
+
jQuery.support.deleteExpando = false;
|
915 |
+
}
|
916 |
+
|
917 |
+
root.removeChild( script );
|
918 |
+
|
919 |
+
if ( div.attachEvent && div.fireEvent ) {
|
920 |
+
div.attachEvent("onclick", function click() {
|
921 |
+
// Cloning a node shouldn't copy over any
|
922 |
+
// bound event handlers (IE does this)
|
923 |
+
jQuery.support.noCloneEvent = false;
|
924 |
+
div.detachEvent("onclick", click);
|
925 |
+
});
|
926 |
+
div.cloneNode(true).fireEvent("onclick");
|
927 |
+
}
|
928 |
+
|
929 |
+
div = document.createElement("div");
|
930 |
+
div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
|
931 |
+
|
932 |
+
var fragment = document.createDocumentFragment();
|
933 |
+
fragment.appendChild( div.firstChild );
|
934 |
+
|
935 |
+
// WebKit doesn't clone checked state correctly in fragments
|
936 |
+
jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
|
937 |
+
|
938 |
+
// Figure out if the W3C box model works as expected
|
939 |
+
// document.body must exist before we can do this
|
940 |
+
jQuery(function() {
|
941 |
+
var div = document.createElement("div");
|
942 |
+
div.style.width = div.style.paddingLeft = "1px";
|
943 |
+
|
944 |
+
document.body.appendChild( div );
|
945 |
+
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
|
946 |
+
document.body.removeChild( div ).style.display = 'none';
|
947 |
+
|
948 |
+
div = null;
|
949 |
+
});
|
950 |
+
|
951 |
+
// Technique from Juriy Zaytsev
|
952 |
+
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
|
953 |
+
var eventSupported = function( eventName ) {
|
954 |
+
var el = document.createElement("div");
|
955 |
+
eventName = "on" + eventName;
|
956 |
+
|
957 |
+
var isSupported = (eventName in el);
|
958 |
+
if ( !isSupported ) {
|
959 |
+
el.setAttribute(eventName, "return;");
|
960 |
+
isSupported = typeof el[eventName] === "function";
|
961 |
+
}
|
962 |
+
el = null;
|
963 |
+
|
964 |
+
return isSupported;
|
965 |
+
};
|
966 |
+
|
967 |
+
jQuery.support.submitBubbles = eventSupported("submit");
|
968 |
+
jQuery.support.changeBubbles = eventSupported("change");
|
969 |
+
|
970 |
+
// release memory in IE
|
971 |
+
root = script = div = all = a = null;
|
972 |
+
})();
|
973 |
+
|
974 |
+
jQuery.props = {
|
975 |
+
"for": "htmlFor",
|
976 |
+
"class": "className",
|
977 |
+
readonly: "readOnly",
|
978 |
+
maxlength: "maxLength",
|
979 |
+
cellspacing: "cellSpacing",
|
980 |
+
rowspan: "rowSpan",
|
981 |
+
colspan: "colSpan",
|
982 |
+
tabindex: "tabIndex",
|
983 |
+
usemap: "useMap",
|
984 |
+
frameborder: "frameBorder"
|
985 |
+
};
|
986 |
+
var expando = "jQuery" + now(), uuid = 0, windowData = {};
|
987 |
+
|
988 |
+
jQuery.extend({
|
989 |
+
cache: {},
|
990 |
+
|
991 |
+
expando:expando,
|
992 |
+
|
993 |
+
// The following elements throw uncatchable exceptions if you
|
994 |
+
// attempt to add expando properties to them.
|
995 |
+
noData: {
|
996 |
+
"embed": true,
|
997 |
+
"object": true,
|
998 |
+
"applet": true
|
999 |
+
},
|
1000 |
+
|
1001 |
+
data: function( elem, name, data ) {
|
1002 |
+
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
|
1003 |
+
return;
|
1004 |
+
}
|
1005 |
+
|
1006 |
+
elem = elem == window ?
|
1007 |
+
windowData :
|
1008 |
+
elem;
|
1009 |
+
|
1010 |
+
var id = elem[ expando ], cache = jQuery.cache, thisCache;
|
1011 |
+
|
1012 |
+
if ( !id && typeof name === "string" && data === undefined ) {
|
1013 |
+
return null;
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
// Compute a unique ID for the element
|
1017 |
+
if ( !id ) {
|
1018 |
+
id = ++uuid;
|
1019 |
+
}
|
1020 |
+
|
1021 |
+
// Avoid generating a new cache unless none exists and we
|
1022 |
+
// want to manipulate it.
|
1023 |
+
if ( typeof name === "object" ) {
|
1024 |
+
elem[ expando ] = id;
|
1025 |
+
thisCache = cache[ id ] = jQuery.extend(true, {}, name);
|
1026 |
+
|
1027 |
+
} else if ( !cache[ id ] ) {
|
1028 |
+
elem[ expando ] = id;
|
1029 |
+
cache[ id ] = {};
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
thisCache = cache[ id ];
|
1033 |
+
|
1034 |
+
// Prevent overriding the named cache with undefined values
|
1035 |
+
if ( data !== undefined ) {
|
1036 |
+
thisCache[ name ] = data;
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
return typeof name === "string" ? thisCache[ name ] : thisCache;
|
1040 |
+
},
|
1041 |
+
|
1042 |
+
removeData: function( elem, name ) {
|
1043 |
+
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
|
1044 |
+
return;
|
1045 |
+
}
|
1046 |
+
|
1047 |
+
elem = elem == window ?
|
1048 |
+
windowData :
|
1049 |
+
elem;
|
1050 |
+
|
1051 |
+
var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
|
1052 |
+
|
1053 |
+
// If we want to remove a specific section of the element's data
|
1054 |
+
if ( name ) {
|
1055 |
+
if ( thisCache ) {
|
1056 |
+
// Remove the section of cache data
|
1057 |
+
delete thisCache[ name ];
|
1058 |
+
|
1059 |
+
// If we've removed all the data, remove the element's cache
|
1060 |
+
if ( jQuery.isEmptyObject(thisCache) ) {
|
1061 |
+
jQuery.removeData( elem );
|
1062 |
+
}
|
1063 |
+
}
|
1064 |
+
|
1065 |
+
// Otherwise, we want to remove all of the element's data
|
1066 |
+
} else {
|
1067 |
+
if ( jQuery.support.deleteExpando ) {
|
1068 |
+
delete elem[ jQuery.expando ];
|
1069 |
+
|
1070 |
+
} else if ( elem.removeAttribute ) {
|
1071 |
+
elem.removeAttribute( jQuery.expando );
|
1072 |
+
}
|
1073 |
+
|
1074 |
+
// Completely remove the data cache
|
1075 |
+
delete cache[ id ];
|
1076 |
+
}
|
1077 |
+
}
|
1078 |
+
});
|
1079 |
+
|
1080 |
+
jQuery.fn.extend({
|
1081 |
+
data: function( key, value ) {
|
1082 |
+
if ( typeof key === "undefined" && this.length ) {
|
1083 |
+
return jQuery.data( this[0] );
|
1084 |
+
|
1085 |
+
} else if ( typeof key === "object" ) {
|
1086 |
+
return this.each(function() {
|
1087 |
+
jQuery.data( this, key );
|
1088 |
+
});
|
1089 |
+
}
|
1090 |
+
|
1091 |
+
var parts = key.split(".");
|
1092 |
+
parts[1] = parts[1] ? "." + parts[1] : "";
|
1093 |
+
|
1094 |
+
if ( value === undefined ) {
|
1095 |
+
var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
|
1096 |
+
|
1097 |
+
if ( data === undefined && this.length ) {
|
1098 |
+
data = jQuery.data( this[0], key );
|
1099 |
+
}
|
1100 |
+
return data === undefined && parts[1] ?
|
1101 |
+
this.data( parts[0] ) :
|
1102 |
+
data;
|
1103 |
+
} else {
|
1104 |
+
return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
|
1105 |
+
jQuery.data( this, key, value );
|
1106 |
+
});
|
1107 |
+
}
|
1108 |
+
},
|
1109 |
+
|
1110 |
+
removeData: function( key ) {
|
1111 |
+
return this.each(function() {
|
1112 |
+
jQuery.removeData( this, key );
|
1113 |
+
});
|
1114 |
+
}
|
1115 |
+
});
|
1116 |
+
jQuery.extend({
|
1117 |
+
queue: function( elem, type, data ) {
|
1118 |
+
if ( !elem ) {
|
1119 |
+
return;
|
1120 |
+
}
|
1121 |
+
|
1122 |
+
type = (type || "fx") + "queue";
|
1123 |
+
var q = jQuery.data( elem, type );
|
1124 |
+
|
1125 |
+
// Speed up dequeue by getting out quickly if this is just a lookup
|
1126 |
+
if ( !data ) {
|
1127 |
+
return q || [];
|
1128 |
+
}
|
1129 |
+
|
1130 |
+
if ( !q || jQuery.isArray(data) ) {
|
1131 |
+
q = jQuery.data( elem, type, jQuery.makeArray(data) );
|
1132 |
+
|
1133 |
+
} else {
|
1134 |
+
q.push( data );
|
1135 |
+
}
|
1136 |
+
|
1137 |
+
return q;
|
1138 |
+
},
|
1139 |
+
|
1140 |
+
dequeue: function( elem, type ) {
|
1141 |
+
type = type || "fx";
|
1142 |
+
|
1143 |
+
var queue = jQuery.queue( elem, type ), fn = queue.shift();
|
1144 |
+
|
1145 |
+
// If the fx queue is dequeued, always remove the progress sentinel
|
1146 |
+
if ( fn === "inprogress" ) {
|
1147 |
+
fn = queue.shift();
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
if ( fn ) {
|
1151 |
+
// Add a progress sentinel to prevent the fx queue from being
|
1152 |
+
// automatically dequeued
|
1153 |
+
if ( type === "fx" ) {
|
1154 |
+
queue.unshift("inprogress");
|
1155 |
+
}
|
1156 |
+
|
1157 |
+
fn.call(elem, function() {
|
1158 |
+
jQuery.dequeue(elem, type);
|
1159 |
+
});
|
1160 |
+
}
|
1161 |
+
}
|
1162 |
+
});
|
1163 |
+
|
1164 |
+
jQuery.fn.extend({
|
1165 |
+
queue: function( type, data ) {
|
1166 |
+
if ( typeof type !== "string" ) {
|
1167 |
+
data = type;
|
1168 |
+
type = "fx";
|
1169 |
+
}
|
1170 |
+
|
1171 |
+
if ( data === undefined ) {
|
1172 |
+
return jQuery.queue( this[0], type );
|
1173 |
+
}
|
1174 |
+
return this.each(function( i, elem ) {
|
1175 |
+
var queue = jQuery.queue( this, type, data );
|
1176 |
+
|
1177 |
+
if ( type === "fx" && queue[0] !== "inprogress" ) {
|
1178 |
+
jQuery.dequeue( this, type );
|
1179 |
+
}
|
1180 |
+
});
|
1181 |
+
},
|
1182 |
+
dequeue: function( type ) {
|
1183 |
+
return this.each(function() {
|
1184 |
+
jQuery.dequeue( this, type );
|
1185 |
+
});
|
1186 |
+
},
|
1187 |
+
|
1188 |
+
// Based off of the plugin by Clint Helfers, with permission.
|
1189 |
+
// http://blindsignals.com/index.php/2009/07/jquery-delay/
|
1190 |
+
delay: function( time, type ) {
|
1191 |
+
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
|
1192 |
+
type = type || "fx";
|
1193 |
+
|
1194 |
+
return this.queue( type, function() {
|
1195 |
+
var elem = this;
|
1196 |
+
setTimeout(function() {
|
1197 |
+
jQuery.dequeue( elem, type );
|
1198 |
+
}, time );
|
1199 |
+
});
|
1200 |
+
},
|
1201 |
+
|
1202 |
+
clearQueue: function( type ) {
|
1203 |
+
return this.queue( type || "fx", [] );
|
1204 |
+
}
|
1205 |
+
});
|
1206 |
+
var rclass = /[\n\t]/g,
|
1207 |
+
rspace = /\s+/,
|
1208 |
+
rreturn = /\r/g,
|
1209 |
+
rspecialurl = /href|src|style/,
|
1210 |
+
rtype = /(button|input)/i,
|
1211 |
+
rfocusable = /(button|input|object|select|textarea)/i,
|
1212 |
+
rclickable = /^(a|area)$/i,
|
1213 |
+
rradiocheck = /radio|checkbox/;
|
1214 |
+
|
1215 |
+
jQuery.fn.extend({
|
1216 |
+
attr: function( name, value ) {
|
1217 |
+
return access( this, name, value, true, jQuery.attr );
|
1218 |
+
},
|
1219 |
+
|
1220 |
+
removeAttr: function( name, fn ) {
|
1221 |
+
return this.each(function(){
|
1222 |
+
jQuery.attr( this, name, "" );
|
1223 |
+
if ( this.nodeType === 1 ) {
|
1224 |
+
this.removeAttribute( name );
|
1225 |
+
}
|
1226 |
+
});
|
1227 |
+
},
|
1228 |
+
|
1229 |
+
addClass: function( value ) {
|
1230 |
+
if ( jQuery.isFunction(value) ) {
|
1231 |
+
return this.each(function(i) {
|
1232 |
+
var self = jQuery(this);
|
1233 |
+
self.addClass( value.call(this, i, self.attr("class")) );
|
1234 |
+
});
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
if ( value && typeof value === "string" ) {
|
1238 |
+
var classNames = (value || "").split( rspace );
|
1239 |
+
|
1240 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
1241 |
+
var elem = this[i];
|
1242 |
+
|
1243 |
+
if ( elem.nodeType === 1 ) {
|
1244 |
+
if ( !elem.className ) {
|
1245 |
+
elem.className = value;
|
1246 |
+
|
1247 |
+
} else {
|
1248 |
+
var className = " " + elem.className + " ", setClass = elem.className;
|
1249 |
+
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
|
1250 |
+
if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
|
1251 |
+
setClass += " " + classNames[c];
|
1252 |
+
}
|
1253 |
+
}
|
1254 |
+
elem.className = jQuery.trim( setClass );
|
1255 |
+
}
|
1256 |
+
}
|
1257 |
+
}
|
1258 |
+
}
|
1259 |
+
|
1260 |
+
return this;
|
1261 |
+
},
|
1262 |
+
|
1263 |
+
removeClass: function( value ) {
|
1264 |
+
if ( jQuery.isFunction(value) ) {
|
1265 |
+
return this.each(function(i) {
|
1266 |
+
var self = jQuery(this);
|
1267 |
+
self.removeClass( value.call(this, i, self.attr("class")) );
|
1268 |
+
});
|
1269 |
+
}
|
1270 |
+
|
1271 |
+
if ( (value && typeof value === "string") || value === undefined ) {
|
1272 |
+
var classNames = (value || "").split(rspace);
|
1273 |
+
|
1274 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
1275 |
+
var elem = this[i];
|
1276 |
+
|
1277 |
+
if ( elem.nodeType === 1 && elem.className ) {
|
1278 |
+
if ( value ) {
|
1279 |
+
var className = (" " + elem.className + " ").replace(rclass, " ");
|
1280 |
+
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
|
1281 |
+
className = className.replace(" " + classNames[c] + " ", " ");
|
1282 |
+
}
|
1283 |
+
elem.className = jQuery.trim( className );
|
1284 |
+
|
1285 |
+
} else {
|
1286 |
+
elem.className = "";
|
1287 |
+
}
|
1288 |
+
}
|
1289 |
+
}
|
1290 |
+
}
|
1291 |
+
|
1292 |
+
return this;
|
1293 |
+
},
|
1294 |
+
|
1295 |
+
toggleClass: function( value, stateVal ) {
|
1296 |
+
var type = typeof value, isBool = typeof stateVal === "boolean";
|
1297 |
+
|
1298 |
+
if ( jQuery.isFunction( value ) ) {
|
1299 |
+
return this.each(function(i) {
|
1300 |
+
var self = jQuery(this);
|
1301 |
+
self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
|
1302 |
+
});
|
1303 |
+
}
|
1304 |
+
|
1305 |
+
return this.each(function() {
|
1306 |
+
if ( type === "string" ) {
|
1307 |
+
// toggle individual class names
|
1308 |
+
var className, i = 0, self = jQuery(this),
|
1309 |
+
state = stateVal,
|
1310 |
+
classNames = value.split( rspace );
|
1311 |
+
|
1312 |
+
while ( (className = classNames[ i++ ]) ) {
|
1313 |
+
// check each className given, space seperated list
|
1314 |
+
state = isBool ? state : !self.hasClass( className );
|
1315 |
+
self[ state ? "addClass" : "removeClass" ]( className );
|
1316 |
+
}
|
1317 |
+
|
1318 |
+
} else if ( type === "undefined" || type === "boolean" ) {
|
1319 |
+
if ( this.className ) {
|
1320 |
+
// store className if set
|
1321 |
+
jQuery.data( this, "__className__", this.className );
|
1322 |
+
}
|
1323 |
+
|
1324 |
+
// toggle whole className
|
1325 |
+
this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
|
1326 |
+
}
|
1327 |
+
});
|
1328 |
+
},
|
1329 |
+
|
1330 |
+
hasClass: function( selector ) {
|
1331 |
+
var className = " " + selector + " ";
|
1332 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
1333 |
+
if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
|
1334 |
+
return true;
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
return false;
|
1339 |
+
},
|
1340 |
+
|
1341 |
+
val: function( value ) {
|
1342 |
+
if ( value === undefined ) {
|
1343 |
+
var elem = this[0];
|
1344 |
+
|
1345 |
+
if ( elem ) {
|
1346 |
+
if ( jQuery.nodeName( elem, "option" ) ) {
|
1347 |
+
return (elem.attributes.value || {}).specified ? elem.value : elem.text;
|
1348 |
+
}
|
1349 |
+
|
1350 |
+
// We need to handle select boxes special
|
1351 |
+
if ( jQuery.nodeName( elem, "select" ) ) {
|
1352 |
+
var index = elem.selectedIndex,
|
1353 |
+
values = [],
|
1354 |
+
options = elem.options,
|
1355 |
+
one = elem.type === "select-one";
|
1356 |
+
|
1357 |
+
// Nothing was selected
|
1358 |
+
if ( index < 0 ) {
|
1359 |
+
return null;
|
1360 |
+
}
|
1361 |
+
|
1362 |
+
// Loop through all the selected options
|
1363 |
+
for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
|
1364 |
+
var option = options[ i ];
|
1365 |
+
|
1366 |
+
if ( option.selected ) {
|
1367 |
+
// Get the specifc value for the option
|
1368 |
+
value = jQuery(option).val();
|
1369 |
+
|
1370 |
+
// We don't need an array for one selects
|
1371 |
+
if ( one ) {
|
1372 |
+
return value;
|
1373 |
+
}
|
1374 |
+
|
1375 |
+
// Multi-Selects return an array
|
1376 |
+
values.push( value );
|
1377 |
+
}
|
1378 |
+
}
|
1379 |
+
|
1380 |
+
return values;
|
1381 |
+
}
|
1382 |
+
|
1383 |
+
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
|
1384 |
+
if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
|
1385 |
+
return elem.getAttribute("value") === null ? "on" : elem.value;
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
|
1389 |
+
// Everything else, we just grab the value
|
1390 |
+
return (elem.value || "").replace(rreturn, "");
|
1391 |
+
|
1392 |
+
}
|
1393 |
+
|
1394 |
+
return undefined;
|
1395 |
+
}
|
1396 |
+
|
1397 |
+
var isFunction = jQuery.isFunction(value);
|
1398 |
+
|
1399 |
+
return this.each(function(i) {
|
1400 |
+
var self = jQuery(this), val = value;
|
1401 |
+
|
1402 |
+
if ( this.nodeType !== 1 ) {
|
1403 |
+
return;
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
if ( isFunction ) {
|
1407 |
+
val = value.call(this, i, self.val());
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
// Typecast each time if the value is a Function and the appended
|
1411 |
+
// value is therefore different each time.
|
1412 |
+
if ( typeof val === "number" ) {
|
1413 |
+
val += "";
|
1414 |
+
}
|
1415 |
+
|
1416 |
+
if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
|
1417 |
+
this.checked = jQuery.inArray( self.val(), val ) >= 0;
|
1418 |
+
|
1419 |
+
} else if ( jQuery.nodeName( this, "select" ) ) {
|
1420 |
+
var values = jQuery.makeArray(val);
|
1421 |
+
|
1422 |
+
jQuery( "option", this ).each(function() {
|
1423 |
+
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
|
1424 |
+
});
|
1425 |
+
|
1426 |
+
if ( !values.length ) {
|
1427 |
+
this.selectedIndex = -1;
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
} else {
|
1431 |
+
this.value = val;
|
1432 |
+
}
|
1433 |
+
});
|
1434 |
+
}
|
1435 |
+
});
|
1436 |
+
|
1437 |
+
jQuery.extend({
|
1438 |
+
attrFn: {
|
1439 |
+
val: true,
|
1440 |
+
css: true,
|
1441 |
+
html: true,
|
1442 |
+
text: true,
|
1443 |
+
data: true,
|
1444 |
+
width: true,
|
1445 |
+
height: true,
|
1446 |
+
offset: true
|
1447 |
+
},
|
1448 |
+
|
1449 |
+
attr: function( elem, name, value, pass ) {
|
1450 |
+
// don't set attributes on text and comment nodes
|
1451 |
+
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
|
1452 |
+
return undefined;
|
1453 |
+
}
|
1454 |
+
|
1455 |
+
if ( pass && name in jQuery.attrFn ) {
|
1456 |
+
return jQuery(elem)[name](value);
|
1457 |
+
}
|
1458 |
+
|
1459 |
+
var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
|
1460 |
+
// Whether we are setting (or getting)
|
1461 |
+
set = value !== undefined;
|
1462 |
+
|
1463 |
+
// Try to normalize/fix the name
|
1464 |
+
name = notxml && jQuery.props[ name ] || name;
|
1465 |
+
|
1466 |
+
// Only do all the following if this is a node (faster for style)
|
1467 |
+
if ( elem.nodeType === 1 ) {
|
1468 |
+
// These attributes require special treatment
|
1469 |
+
var special = rspecialurl.test( name );
|
1470 |
+
|
1471 |
+
// Safari mis-reports the default selected property of an option
|
1472 |
+
// Accessing the parent's selectedIndex property fixes it
|
1473 |
+
if ( name === "selected" && !jQuery.support.optSelected ) {
|
1474 |
+
var parent = elem.parentNode;
|
1475 |
+
if ( parent ) {
|
1476 |
+
parent.selectedIndex;
|
1477 |
+
|
1478 |
+
// Make sure that it also works with optgroups, see #5701
|
1479 |
+
if ( parent.parentNode ) {
|
1480 |
+
parent.parentNode.selectedIndex;
|
1481 |
+
}
|
1482 |
+
}
|
1483 |
+
}
|
1484 |
+
|
1485 |
+
// If applicable, access the attribute via the DOM 0 way
|
1486 |
+
if ( name in elem && notxml && !special ) {
|
1487 |
+
if ( set ) {
|
1488 |
+
// We can't allow the type property to be changed (since it causes problems in IE)
|
1489 |
+
if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
|
1490 |
+
jQuery.error( "type property can't be changed" );
|
1491 |
+
}
|
1492 |
+
|
1493 |
+
elem[ name ] = value;
|
1494 |
+
}
|
1495 |
+
|
1496 |
+
// browsers index elements by id/name on forms, give priority to attributes.
|
1497 |
+
if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
|
1498 |
+
return elem.getAttributeNode( name ).nodeValue;
|
1499 |
+
}
|
1500 |
+
|
1501 |
+
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
|
1502 |
+
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
|
1503 |
+
if ( name === "tabIndex" ) {
|
1504 |
+
var attributeNode = elem.getAttributeNode( "tabIndex" );
|
1505 |
+
|
1506 |
+
return attributeNode && attributeNode.specified ?
|
1507 |
+
attributeNode.value :
|
1508 |
+
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
|
1509 |
+
0 :
|
1510 |
+
undefined;
|
1511 |
+
}
|
1512 |
+
|
1513 |
+
return elem[ name ];
|
1514 |
+
}
|
1515 |
+
|
1516 |
+
if ( !jQuery.support.style && notxml && name === "style" ) {
|
1517 |
+
if ( set ) {
|
1518 |
+
elem.style.cssText = "" + value;
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
return elem.style.cssText;
|
1522 |
+
}
|
1523 |
+
|
1524 |
+
if ( set ) {
|
1525 |
+
// convert the value to a string (all browsers do this but IE) see #1070
|
1526 |
+
elem.setAttribute( name, "" + value );
|
1527 |
+
}
|
1528 |
+
|
1529 |
+
var attr = !jQuery.support.hrefNormalized && notxml && special ?
|
1530 |
+
// Some attributes require a special call on IE
|
1531 |
+
elem.getAttribute( name, 2 ) :
|
1532 |
+
elem.getAttribute( name );
|
1533 |
+
|
1534 |
+
// Non-existent attributes return null, we normalize to undefined
|
1535 |
+
return attr === null ? undefined : attr;
|
1536 |
+
}
|
1537 |
+
|
1538 |
+
// elem is actually elem.style ... set the style
|
1539 |
+
// Using attr for specific style information is now deprecated. Use style instead.
|
1540 |
+
return jQuery.style( elem, name, value );
|
1541 |
+
}
|
1542 |
+
});
|
1543 |
+
var rnamespaces = /\.(.*)$/,
|
1544 |
+
fcleanup = function( nm ) {
|
1545 |
+
return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
|
1546 |
+
return "\\" + ch;
|
1547 |
+
});
|
1548 |
+
};
|
1549 |
+
|
1550 |
+
/*
|
1551 |
+
* A number of helper functions used for managing events.
|
1552 |
+
* Many of the ideas behind this code originated from
|
1553 |
+
* Dean Edwards' addEvent library.
|
1554 |
+
*/
|
1555 |
+
jQuery.event = {
|
1556 |
+
|
1557 |
+
// Bind an event to an element
|
1558 |
+
// Original by Dean Edwards
|
1559 |
+
add: function( elem, types, handler, data ) {
|
1560 |
+
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
|
1561 |
+
return;
|
1562 |
+
}
|
1563 |
+
|
1564 |
+
// For whatever reason, IE has trouble passing the window object
|
1565 |
+
// around, causing it to be cloned in the process
|
1566 |
+
if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
|
1567 |
+
elem = window;
|
1568 |
+
}
|
1569 |
+
|
1570 |
+
var handleObjIn, handleObj;
|
1571 |
+
|
1572 |
+
if ( handler.handler ) {
|
1573 |
+
handleObjIn = handler;
|
1574 |
+
handler = handleObjIn.handler;
|
1575 |
+
}
|
1576 |
+
|
1577 |
+
// Make sure that the function being executed has a unique ID
|
1578 |
+
if ( !handler.guid ) {
|
1579 |
+
handler.guid = jQuery.guid++;
|
1580 |
+
}
|
1581 |
+
|
1582 |
+
// Init the element's event structure
|
1583 |
+
var elemData = jQuery.data( elem );
|
1584 |
+
|
1585 |
+
// If no elemData is found then we must be trying to bind to one of the
|
1586 |
+
// banned noData elements
|
1587 |
+
if ( !elemData ) {
|
1588 |
+
return;
|
1589 |
+
}
|
1590 |
+
|
1591 |
+
var events = elemData.events = elemData.events || {},
|
1592 |
+
eventHandle = elemData.handle, eventHandle;
|
1593 |
+
|
1594 |
+
if ( !eventHandle ) {
|
1595 |
+
elemData.handle = eventHandle = function() {
|
1596 |
+
// Handle the second event of a trigger and when
|
1597 |
+
// an event is called after a page has unloaded
|
1598 |
+
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
|
1599 |
+
jQuery.event.handle.apply( eventHandle.elem, arguments ) :
|
1600 |
+
undefined;
|
1601 |
+
};
|
1602 |
+
}
|
1603 |
+
|
1604 |
+
// Add elem as a property of the handle function
|
1605 |
+
// This is to prevent a memory leak with non-native events in IE.
|
1606 |
+
eventHandle.elem = elem;
|
1607 |
+
|
1608 |
+
// Handle multiple events separated by a space
|
1609 |
+
// jQuery(...).bind("mouseover mouseout", fn);
|
1610 |
+
types = types.split(" ");
|
1611 |
+
|
1612 |
+
var type, i = 0, namespaces;
|
1613 |
+
|
1614 |
+
while ( (type = types[ i++ ]) ) {
|
1615 |
+
handleObj = handleObjIn ?
|
1616 |
+
jQuery.extend({}, handleObjIn) :
|
1617 |
+
{ handler: handler, data: data };
|
1618 |
+
|
1619 |
+
// Namespaced event handlers
|
1620 |
+
if ( type.indexOf(".") > -1 ) {
|
1621 |
+
namespaces = type.split(".");
|
1622 |
+
type = namespaces.shift();
|
1623 |
+
handleObj.namespace = namespaces.slice(0).sort().join(".");
|
1624 |
+
|
1625 |
+
} else {
|
1626 |
+
namespaces = [];
|
1627 |
+
handleObj.namespace = "";
|
1628 |
+
}
|
1629 |
+
|
1630 |
+
handleObj.type = type;
|
1631 |
+
handleObj.guid = handler.guid;
|
1632 |
+
|
1633 |
+
// Get the current list of functions bound to this event
|
1634 |
+
var handlers = events[ type ],
|
1635 |
+
special = jQuery.event.special[ type ] || {};
|
1636 |
+
|
1637 |
+
// Init the event handler queue
|
1638 |
+
if ( !handlers ) {
|
1639 |
+
handlers = events[ type ] = [];
|
1640 |
+
|
1641 |
+
// Check for a special event handler
|
1642 |
+
// Only use addEventListener/attachEvent if the special
|
1643 |
+
// events handler returns false
|
1644 |
+
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
|
1645 |
+
// Bind the global event handler to the element
|
1646 |
+
if ( elem.addEventListener ) {
|
1647 |
+
elem.addEventListener( type, eventHandle, false );
|
1648 |
+
|
1649 |
+
} else if ( elem.attachEvent ) {
|
1650 |
+
elem.attachEvent( "on" + type, eventHandle );
|
1651 |
+
}
|
1652 |
+
}
|
1653 |
+
}
|
1654 |
+
|
1655 |
+
if ( special.add ) {
|
1656 |
+
special.add.call( elem, handleObj );
|
1657 |
+
|
1658 |
+
if ( !handleObj.handler.guid ) {
|
1659 |
+
handleObj.handler.guid = handler.guid;
|
1660 |
+
}
|
1661 |
+
}
|
1662 |
+
|
1663 |
+
// Add the function to the element's handler list
|
1664 |
+
handlers.push( handleObj );
|
1665 |
+
|
1666 |
+
// Keep track of which events have been used, for global triggering
|
1667 |
+
jQuery.event.global[ type ] = true;
|
1668 |
+
}
|
1669 |
+
|
1670 |
+
// Nullify elem to prevent memory leaks in IE
|
1671 |
+
elem = null;
|
1672 |
+
},
|
1673 |
+
|
1674 |
+
global: {},
|
1675 |
+
|
1676 |
+
// Detach an event or set of events from an element
|
1677 |
+
remove: function( elem, types, handler, pos ) {
|
1678 |
+
// don't do events on text and comment nodes
|
1679 |
+
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
|
1680 |
+
return;
|
1681 |
+
}
|
1682 |
+
|
1683 |
+
var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
|
1684 |
+
elemData = jQuery.data( elem ),
|
1685 |
+
events = elemData && elemData.events;
|
1686 |
+
|
1687 |
+
if ( !elemData || !events ) {
|
1688 |
+
return;
|
1689 |
+
}
|
1690 |
+
|
1691 |
+
// types is actually an event object here
|
1692 |
+
if ( types && types.type ) {
|
1693 |
+
handler = types.handler;
|
1694 |
+
types = types.type;
|
1695 |
+
}
|
1696 |
+
|
1697 |
+
// Unbind all events for the element
|
1698 |
+
if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
|
1699 |
+
types = types || "";
|
1700 |
+
|
1701 |
+
for ( type in events ) {
|
1702 |
+
jQuery.event.remove( elem, type + types );
|
1703 |
+
}
|
1704 |
+
|
1705 |
+
return;
|
1706 |
+
}
|
1707 |
+
|
1708 |
+
// Handle multiple events separated by a space
|
1709 |
+
// jQuery(...).unbind("mouseover mouseout", fn);
|
1710 |
+
types = types.split(" ");
|
1711 |
+
|
1712 |
+
while ( (type = types[ i++ ]) ) {
|
1713 |
+
origType = type;
|
1714 |
+
handleObj = null;
|
1715 |
+
all = type.indexOf(".") < 0;
|
1716 |
+
namespaces = [];
|
1717 |
+
|
1718 |
+
if ( !all ) {
|
1719 |
+
// Namespaced event handlers
|
1720 |
+
namespaces = type.split(".");
|
1721 |
+
type = namespaces.shift();
|
1722 |
+
|
1723 |
+
namespace = new RegExp("(^|\\.)" +
|
1724 |
+
jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
|
1725 |
+
}
|
1726 |
+
|
1727 |
+
eventType = events[ type ];
|
1728 |
+
|
1729 |
+
if ( !eventType ) {
|
1730 |
+
continue;
|
1731 |
+
}
|
1732 |
+
|
1733 |
+
if ( !handler ) {
|
1734 |
+
for ( var j = 0; j < eventType.length; j++ ) {
|
1735 |
+
handleObj = eventType[ j ];
|
1736 |
+
|
1737 |
+
if ( all || namespace.test( handleObj.namespace ) ) {
|
1738 |
+
jQuery.event.remove( elem, origType, handleObj.handler, j );
|
1739 |
+
eventType.splice( j--, 1 );
|
1740 |
+
}
|
1741 |
+
}
|
1742 |
+
|
1743 |
+
continue;
|
1744 |
+
}
|
1745 |
+
|
1746 |
+
special = jQuery.event.special[ type ] || {};
|
1747 |
+
|
1748 |
+
for ( var j = pos || 0; j < eventType.length; j++ ) {
|
1749 |
+
handleObj = eventType[ j ];
|
1750 |
+
|
1751 |
+
if ( handler.guid === handleObj.guid ) {
|
1752 |
+
// remove the given handler for the given type
|
1753 |
+
if ( all || namespace.test( handleObj.namespace ) ) {
|
1754 |
+
if ( pos == null ) {
|
1755 |
+
eventType.splice( j--, 1 );
|
1756 |
+
}
|
1757 |
+
|
1758 |
+
if ( special.remove ) {
|
1759 |
+
special.remove.call( elem, handleObj );
|
1760 |
+
}
|
1761 |
+
}
|
1762 |
+
|
1763 |
+
if ( pos != null ) {
|
1764 |
+
break;
|
1765 |
+
}
|
1766 |
+
}
|
1767 |
+
}
|
1768 |
+
|
1769 |
+
// remove generic event handler if no more handlers exist
|
1770 |
+
if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
|
1771 |
+
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
|
1772 |
+
removeEvent( elem, type, elemData.handle );
|
1773 |
+
}
|
1774 |
+
|
1775 |
+
ret = null;
|
1776 |
+
delete events[ type ];
|
1777 |
+
}
|
1778 |
+
}
|
1779 |
+
|
1780 |
+
// Remove the expando if it's no longer used
|
1781 |
+
if ( jQuery.isEmptyObject( events ) ) {
|
1782 |
+
var handle = elemData.handle;
|
1783 |
+
if ( handle ) {
|
1784 |
+
handle.elem = null;
|
1785 |
+
}
|
1786 |
+
|
1787 |
+
delete elemData.events;
|
1788 |
+
delete elemData.handle;
|
1789 |
+
|
1790 |
+
if ( jQuery.isEmptyObject( elemData ) ) {
|
1791 |
+
jQuery.removeData( elem );
|
1792 |
+
}
|
1793 |
+
}
|
1794 |
+
},
|
1795 |
+
|
1796 |
+
// bubbling is internal
|
1797 |
+
trigger: function( event, data, elem /*, bubbling */ ) {
|
1798 |
+
// Event object or event type
|
1799 |
+
var type = event.type || event,
|
1800 |
+
bubbling = arguments[3];
|
1801 |
+
|
1802 |
+
if ( !bubbling ) {
|
1803 |
+
event = typeof event === "object" ?
|
1804 |
+
// jQuery.Event object
|
1805 |
+
event[expando] ? event :
|
1806 |
+
// Object literal
|
1807 |
+
jQuery.extend( jQuery.Event(type), event ) :
|
1808 |
+
// Just the event type (string)
|
1809 |
+
jQuery.Event(type);
|
1810 |
+
|
1811 |
+
if ( type.indexOf("!") >= 0 ) {
|
1812 |
+
event.type = type = type.slice(0, -1);
|
1813 |
+
event.exclusive = true;
|
1814 |
+
}
|
1815 |
+
|
1816 |
+
// Handle a global trigger
|
1817 |
+
if ( !elem ) {
|
1818 |
+
// Don't bubble custom events when global (to avoid too much overhead)
|
1819 |
+
event.stopPropagation();
|
1820 |
+
|
1821 |
+
// Only trigger if we've ever bound an event for it
|
1822 |
+
if ( jQuery.event.global[ type ] ) {
|
1823 |
+
jQuery.each( jQuery.cache, function() {
|
1824 |
+
if ( this.events && this.events[type] ) {
|
1825 |
+
jQuery.event.trigger( event, data, this.handle.elem );
|
1826 |
+
}
|
1827 |
+
});
|
1828 |
+
}
|
1829 |
+
}
|
1830 |
+
|
1831 |
+
// Handle triggering a single element
|
1832 |
+
|
1833 |
+
// don't do events on text and comment nodes
|
1834 |
+
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
|
1835 |
+
return undefined;
|
1836 |
+
}
|
1837 |
+
|
1838 |
+
// Clean up in case it is reused
|
1839 |
+
event.result = undefined;
|
1840 |
+
event.target = elem;
|
1841 |
+
|
1842 |
+
// Clone the incoming data, if any
|
1843 |
+
data = jQuery.makeArray( data );
|
1844 |
+
data.unshift( event );
|
1845 |
+
}
|
1846 |
+
|
1847 |
+
event.currentTarget = elem;
|
1848 |
+
|
1849 |
+
// Trigger the event, it is assumed that "handle" is a function
|
1850 |
+
var handle = jQuery.data( elem, "handle" );
|
1851 |
+
if ( handle ) {
|
1852 |
+
handle.apply( elem, data );
|
1853 |
+
}
|
1854 |
+
|
1855 |
+
var parent = elem.parentNode || elem.ownerDocument;
|
1856 |
+
|
1857 |
+
// Trigger an inline bound script
|
1858 |
+
try {
|
1859 |
+
if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
|
1860 |
+
if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
|
1861 |
+
event.result = false;
|
1862 |
+
}
|
1863 |
+
}
|
1864 |
+
|
1865 |
+
// prevent IE from throwing an error for some elements with some event types, see #3533
|
1866 |
+
} catch (e) {}
|
1867 |
+
|
1868 |
+
if ( !event.isPropagationStopped() && parent ) {
|
1869 |
+
jQuery.event.trigger( event, data, parent, true );
|
1870 |
+
|
1871 |
+
} else if ( !event.isDefaultPrevented() ) {
|
1872 |
+
var target = event.target, old,
|
1873 |
+
isClick = jQuery.nodeName(target, "a") && type === "click",
|
1874 |
+
special = jQuery.event.special[ type ] || {};
|
1875 |
+
|
1876 |
+
if ( (!special._default || special._default.call( elem, event ) === false) &&
|
1877 |
+
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
|
1878 |
+
|
1879 |
+
try {
|
1880 |
+
if ( target[ type ] ) {
|
1881 |
+
// Make sure that we don't accidentally re-trigger the onFOO events
|
1882 |
+
old = target[ "on" + type ];
|
1883 |
+
|
1884 |
+
if ( old ) {
|
1885 |
+
target[ "on" + type ] = null;
|
1886 |
+
}
|
1887 |
+
|
1888 |
+
jQuery.event.triggered = true;
|
1889 |
+
target[ type ]();
|
1890 |
+
}
|
1891 |
+
|
1892 |
+
// prevent IE from throwing an error for some elements with some event types, see #3533
|
1893 |
+
} catch (e) {}
|
1894 |
+
|
1895 |
+
if ( old ) {
|
1896 |
+
target[ "on" + type ] = old;
|
1897 |
+
}
|
1898 |
+
|
1899 |
+
jQuery.event.triggered = false;
|
1900 |
+
}
|
1901 |
+
}
|
1902 |
+
},
|
1903 |
+
|
1904 |
+
handle: function( event ) {
|
1905 |
+
var all, handlers, namespaces, namespace, events;
|
1906 |
+
|
1907 |
+
event = arguments[0] = jQuery.event.fix( event || window.event );
|
1908 |
+
event.currentTarget = this;
|
1909 |
+
|
1910 |
+
// Namespaced event handlers
|
1911 |
+
all = event.type.indexOf(".") < 0 && !event.exclusive;
|
1912 |
+
|
1913 |
+
if ( !all ) {
|
1914 |
+
namespaces = event.type.split(".");
|
1915 |
+
event.type = namespaces.shift();
|
1916 |
+
namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
|
1917 |
+
}
|
1918 |
+
|
1919 |
+
var events = jQuery.data(this, "events"), handlers = events[ event.type ];
|
1920 |
+
|
1921 |
+
if ( events && handlers ) {
|
1922 |
+
// Clone the handlers to prevent manipulation
|
1923 |
+
handlers = handlers.slice(0);
|
1924 |
+
|
1925 |
+
for ( var j = 0, l = handlers.length; j < l; j++ ) {
|
1926 |
+
var handleObj = handlers[ j ];
|
1927 |
+
|
1928 |
+
// Filter the functions by class
|
1929 |
+
if ( all || namespace.test( handleObj.namespace ) ) {
|
1930 |
+
// Pass in a reference to the handler function itself
|
1931 |
+
// So that we can later remove it
|
1932 |
+
event.handler = handleObj.handler;
|
1933 |
+
event.data = handleObj.data;
|
1934 |
+
event.handleObj = handleObj;
|
1935 |
+
|
1936 |
+
var ret = handleObj.handler.apply( this, arguments );
|
1937 |
+
|
1938 |
+
if ( ret !== undefined ) {
|
1939 |
+
event.result = ret;
|
1940 |
+
if ( ret === false ) {
|
1941 |
+
event.preventDefault();
|
1942 |
+
event.stopPropagation();
|
1943 |
+
}
|
1944 |
+
}
|
1945 |
+
|
1946 |
+
if ( event.isImmediatePropagationStopped() ) {
|
1947 |
+
break;
|
1948 |
+
}
|
1949 |
+
}
|
1950 |
+
}
|
1951 |
+
}
|
1952 |
+
|
1953 |
+
return event.result;
|
1954 |
+
},
|
1955 |
+
|
1956 |
+
props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
1957 |
+
|
1958 |
+
fix: function( event ) {
|
1959 |
+
if ( event[ expando ] ) {
|
1960 |
+
return event;
|
1961 |
+
}
|
1962 |
+
|
1963 |
+
// store a copy of the original event object
|
1964 |
+
// and "clone" to set read-only properties
|
1965 |
+
var originalEvent = event;
|
1966 |
+
event = jQuery.Event( originalEvent );
|
1967 |
+
|
1968 |
+
for ( var i = this.props.length, prop; i; ) {
|
1969 |
+
prop = this.props[ --i ];
|
1970 |
+
event[ prop ] = originalEvent[ prop ];
|
1971 |
+
}
|
1972 |
+
|
1973 |
+
// Fix target property, if necessary
|
1974 |
+
if ( !event.target ) {
|
1975 |
+
event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
|
1976 |
+
}
|
1977 |
+
|
1978 |
+
// check if target is a textnode (safari)
|
1979 |
+
if ( event.target.nodeType === 3 ) {
|
1980 |
+
event.target = event.target.parentNode;
|
1981 |
+
}
|
1982 |
+
|
1983 |
+
// Add relatedTarget, if necessary
|
1984 |
+
if ( !event.relatedTarget && event.fromElement ) {
|
1985 |
+
event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
|
1986 |
+
}
|
1987 |
+
|
1988 |
+
// Calculate pageX/Y if missing and clientX/Y available
|
1989 |
+
if ( event.pageX == null && event.clientX != null ) {
|
1990 |
+
var doc = document.documentElement, body = document.body;
|
1991 |
+
event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
|
1992 |
+
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
|
1993 |
+
}
|
1994 |
+
|
1995 |
+
// Add which for key events
|
1996 |
+
if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
|
1997 |
+
event.which = event.charCode || event.keyCode;
|
1998 |
+
}
|
1999 |
+
|
2000 |
+
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
|
2001 |
+
if ( !event.metaKey && event.ctrlKey ) {
|
2002 |
+
event.metaKey = event.ctrlKey;
|
2003 |
+
}
|
2004 |
+
|
2005 |
+
// Add which for click: 1 === left; 2 === middle; 3 === right
|
2006 |
+
// Note: button is not normalized, so don't use it
|
2007 |
+
if ( !event.which && event.button !== undefined ) {
|
2008 |
+
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
|
2009 |
+
}
|
2010 |
+
|
2011 |
+
return event;
|
2012 |
+
},
|
2013 |
+
|
2014 |
+
// Deprecated, use jQuery.guid instead
|
2015 |
+
guid: 1E8,
|
2016 |
+
|
2017 |
+
// Deprecated, use jQuery.proxy instead
|
2018 |
+
proxy: jQuery.proxy,
|
2019 |
+
|
2020 |
+
special: {
|
2021 |
+
ready: {
|
2022 |
+
// Make sure the ready event is setup
|
2023 |
+
setup: jQuery.bindReady,
|
2024 |
+
teardown: jQuery.noop
|
2025 |
+
},
|
2026 |
+
|
2027 |
+
live: {
|
2028 |
+
add: function( handleObj ) {
|
2029 |
+
jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
|
2030 |
+
},
|
2031 |
+
|
2032 |
+
remove: function( handleObj ) {
|
2033 |
+
var remove = true,
|
2034 |
+
type = handleObj.origType.replace(rnamespaces, "");
|
2035 |
+
|
2036 |
+
jQuery.each( jQuery.data(this, "events").live || [], function() {
|
2037 |
+
if ( type === this.origType.replace(rnamespaces, "") ) {
|
2038 |
+
remove = false;
|
2039 |
+
return false;
|
2040 |
+
}
|
2041 |
+
});
|
2042 |
+
|
2043 |
+
if ( remove ) {
|
2044 |
+
jQuery.event.remove( this, handleObj.origType, liveHandler );
|
2045 |
+
}
|
2046 |
+
}
|
2047 |
+
|
2048 |
+
},
|
2049 |
+
|
2050 |
+
beforeunload: {
|
2051 |
+
setup: function( data, namespaces, eventHandle ) {
|
2052 |
+
// We only want to do this special case on windows
|
2053 |
+
if ( this.setInterval ) {
|
2054 |
+
this.onbeforeunload = eventHandle;
|
2055 |
+
}
|
2056 |
+
|
2057 |
+
return false;
|
2058 |
+
},
|
2059 |
+
teardown: function( namespaces, eventHandle ) {
|
2060 |
+
if ( this.onbeforeunload === eventHandle ) {
|
2061 |
+
this.onbeforeunload = null;
|
2062 |
+
}
|
2063 |
+
}
|
2064 |
+
}
|
2065 |
+
}
|
2066 |
+
};
|
2067 |
+
|
2068 |
+
var removeEvent = document.removeEventListener ?
|
2069 |
+
function( elem, type, handle ) {
|
2070 |
+
elem.removeEventListener( type, handle, false );
|
2071 |
+
} :
|
2072 |
+
function( elem, type, handle ) {
|
2073 |
+
elem.detachEvent( "on" + type, handle );
|
2074 |
+
};
|
2075 |
+
|
2076 |
+
jQuery.Event = function( src ) {
|
2077 |
+
// Allow instantiation without the 'new' keyword
|
2078 |
+
if ( !this.preventDefault ) {
|
2079 |
+
return new jQuery.Event( src );
|
2080 |
+
}
|
2081 |
+
|
2082 |
+
// Event object
|
2083 |
+
if ( src && src.type ) {
|
2084 |
+
this.originalEvent = src;
|
2085 |
+
this.type = src.type;
|
2086 |
+
// Event type
|
2087 |
+
} else {
|
2088 |
+
this.type = src;
|
2089 |
+
}
|
2090 |
+
|
2091 |
+
// timeStamp is buggy for some events on Firefox(#3843)
|
2092 |
+
// So we won't rely on the native value
|
2093 |
+
this.timeStamp = now();
|
2094 |
+
|
2095 |
+
// Mark it as fixed
|
2096 |
+
this[ expando ] = true;
|
2097 |
+
};
|
2098 |
+
|
2099 |
+
function returnFalse() {
|
2100 |
+
return false;
|
2101 |
+
}
|
2102 |
+
function returnTrue() {
|
2103 |
+
return true;
|
2104 |
+
}
|
2105 |
+
|
2106 |
+
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
|
2107 |
+
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
2108 |
+
jQuery.Event.prototype = {
|
2109 |
+
preventDefault: function() {
|
2110 |
+
this.isDefaultPrevented = returnTrue;
|
2111 |
+
|
2112 |
+
var e = this.originalEvent;
|
2113 |
+
if ( !e ) {
|
2114 |
+
return;
|
2115 |
+
}
|
2116 |
+
|
2117 |
+
// if preventDefault exists run it on the original event
|
2118 |
+
if ( e.preventDefault ) {
|
2119 |
+
e.preventDefault();
|
2120 |
+
}
|
2121 |
+
// otherwise set the returnValue property of the original event to false (IE)
|
2122 |
+
e.returnValue = false;
|
2123 |
+
},
|
2124 |
+
stopPropagation: function() {
|
2125 |
+
this.isPropagationStopped = returnTrue;
|
2126 |
+
|
2127 |
+
var e = this.originalEvent;
|
2128 |
+
if ( !e ) {
|
2129 |
+
return;
|
2130 |
+
}
|
2131 |
+
// if stopPropagation exists run it on the original event
|
2132 |
+
if ( e.stopPropagation ) {
|
2133 |
+
e.stopPropagation();
|
2134 |
+
}
|
2135 |
+
// otherwise set the cancelBubble property of the original event to true (IE)
|
2136 |
+
e.cancelBubble = true;
|
2137 |
+
},
|
2138 |
+
stopImmediatePropagation: function() {
|
2139 |
+
this.isImmediatePropagationStopped = returnTrue;
|
2140 |
+
this.stopPropagation();
|
2141 |
+
},
|
2142 |
+
isDefaultPrevented: returnFalse,
|
2143 |
+
isPropagationStopped: returnFalse,
|
2144 |
+
isImmediatePropagationStopped: returnFalse
|
2145 |
+
};
|
2146 |
+
|
2147 |
+
// Checks if an event happened on an element within another element
|
2148 |
+
// Used in jQuery.event.special.mouseenter and mouseleave handlers
|
2149 |
+
var withinElement = function( event ) {
|
2150 |
+
// Check if mouse(over|out) are still within the same parent element
|
2151 |
+
var parent = event.relatedTarget;
|
2152 |
+
|
2153 |
+
// Firefox sometimes assigns relatedTarget a XUL element
|
2154 |
+
// which we cannot access the parentNode property of
|
2155 |
+
try {
|
2156 |
+
// Traverse up the tree
|
2157 |
+
while ( parent && parent !== this ) {
|
2158 |
+
parent = parent.parentNode;
|
2159 |
+
}
|
2160 |
+
|
2161 |
+
if ( parent !== this ) {
|
2162 |
+
// set the correct event type
|
2163 |
+
event.type = event.data;
|
2164 |
+
|
2165 |
+
// handle event if we actually just moused on to a non sub-element
|
2166 |
+
jQuery.event.handle.apply( this, arguments );
|
2167 |
+
}
|
2168 |
+
|
2169 |
+
// assuming we've left the element since we most likely mousedover a xul element
|
2170 |
+
} catch(e) { }
|
2171 |
+
},
|
2172 |
+
|
2173 |
+
// In case of event delegation, we only need to rename the event.type,
|
2174 |
+
// liveHandler will take care of the rest.
|
2175 |
+
delegate = function( event ) {
|
2176 |
+
event.type = event.data;
|
2177 |
+
jQuery.event.handle.apply( this, arguments );
|
2178 |
+
};
|
2179 |
+
|
2180 |
+
// Create mouseenter and mouseleave events
|
2181 |
+
jQuery.each({
|
2182 |
+
mouseenter: "mouseover",
|
2183 |
+
mouseleave: "mouseout"
|
2184 |
+
}, function( orig, fix ) {
|
2185 |
+
jQuery.event.special[ orig ] = {
|
2186 |
+
setup: function( data ) {
|
2187 |
+
jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
|
2188 |
+
},
|
2189 |
+
teardown: function( data ) {
|
2190 |
+
jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
|
2191 |
+
}
|
2192 |
+
};
|
2193 |
+
});
|
2194 |
+
|
2195 |
+
// submit delegation
|
2196 |
+
if ( !jQuery.support.submitBubbles ) {
|
2197 |
+
|
2198 |
+
jQuery.event.special.submit = {
|
2199 |
+
setup: function( data, namespaces ) {
|
2200 |
+
if ( this.nodeName.toLowerCase() !== "form" ) {
|
2201 |
+
jQuery.event.add(this, "click.specialSubmit", function( e ) {
|
2202 |
+
var elem = e.target, type = elem.type;
|
2203 |
+
|
2204 |
+
if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
|
2205 |
+
return trigger( "submit", this, arguments );
|
2206 |
+
}
|
2207 |
+
});
|
2208 |
+
|
2209 |
+
jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
|
2210 |
+
var elem = e.target, type = elem.type;
|
2211 |
+
|
2212 |
+
if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
|
2213 |
+
return trigger( "submit", this, arguments );
|
2214 |
+
}
|
2215 |
+
});
|
2216 |
+
|
2217 |
+
} else {
|
2218 |
+
return false;
|
2219 |
+
}
|
2220 |
+
},
|
2221 |
+
|
2222 |
+
teardown: function( namespaces ) {
|
2223 |
+
jQuery.event.remove( this, ".specialSubmit" );
|
2224 |
+
}
|
2225 |
+
};
|
2226 |
+
|
2227 |
+
}
|
2228 |
+
|
2229 |
+
// change delegation, happens here so we have bind.
|
2230 |
+
if ( !jQuery.support.changeBubbles ) {
|
2231 |
+
|
2232 |
+
var formElems = /textarea|input|select/i,
|
2233 |
+
|
2234 |
+
changeFilters,
|
2235 |
+
|
2236 |
+
getVal = function( elem ) {
|
2237 |
+
var type = elem.type, val = elem.value;
|
2238 |
+
|
2239 |
+
if ( type === "radio" || type === "checkbox" ) {
|
2240 |
+
val = elem.checked;
|
2241 |
+
|
2242 |
+
} else if ( type === "select-multiple" ) {
|
2243 |
+
val = elem.selectedIndex > -1 ?
|
2244 |
+
jQuery.map( elem.options, function( elem ) {
|
2245 |
+
return elem.selected;
|
2246 |
+
}).join("-") :
|
2247 |
+
"";
|
2248 |
+
|
2249 |
+
} else if ( elem.nodeName.toLowerCase() === "select" ) {
|
2250 |
+
val = elem.selectedIndex;
|
2251 |
+
}
|
2252 |
+
|
2253 |
+
return val;
|
2254 |
+
},
|
2255 |
+
|
2256 |
+
testChange = function testChange( e ) {
|
2257 |
+
var elem = e.target, data, val;
|
2258 |
+
|
2259 |
+
if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
|
2260 |
+
return;
|
2261 |
+
}
|
2262 |
+
|
2263 |
+
data = jQuery.data( elem, "_change_data" );
|
2264 |
+
val = getVal(elem);
|
2265 |
+
|
2266 |
+
// the current data will be also retrieved by beforeactivate
|
2267 |
+
if ( e.type !== "focusout" || elem.type !== "radio" ) {
|
2268 |
+
jQuery.data( elem, "_change_data", val );
|
2269 |
+
}
|
2270 |
+
|
2271 |
+
if ( data === undefined || val === data ) {
|
2272 |
+
return;
|
2273 |
+
}
|
2274 |
+
|
2275 |
+
if ( data != null || val ) {
|
2276 |
+
e.type = "change";
|
2277 |
+
return jQuery.event.trigger( e, arguments[1], elem );
|
2278 |
+
}
|
2279 |
+
};
|
2280 |
+
|
2281 |
+
jQuery.event.special.change = {
|
2282 |
+
filters: {
|
2283 |
+
focusout: testChange,
|
2284 |
+
|
2285 |
+
click: function( e ) {
|
2286 |
+
var elem = e.target, type = elem.type;
|
2287 |
+
|
2288 |
+
if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
|
2289 |
+
return testChange.call( this, e );
|
2290 |
+
}
|
2291 |
+
},
|
2292 |
+
|
2293 |
+
// Change has to be called before submit
|
2294 |
+
// Keydown will be called before keypress, which is used in submit-event delegation
|
2295 |
+
keydown: function( e ) {
|
2296 |
+
var elem = e.target, type = elem.type;
|
2297 |
+
|
2298 |
+
if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
|
2299 |
+
(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
|
2300 |
+
type === "select-multiple" ) {
|
2301 |
+
return testChange.call( this, e );
|
2302 |
+
}
|
2303 |
+
},
|
2304 |
+
|
2305 |
+
// Beforeactivate happens also before the previous element is blurred
|
2306 |
+
// with this event you can't trigger a change event, but you can store
|
2307 |
+
// information/focus[in] is not needed anymore
|
2308 |
+
beforeactivate: function( e ) {
|
2309 |
+
var elem = e.target;
|
2310 |
+
jQuery.data( elem, "_change_data", getVal(elem) );
|
2311 |
+
}
|
2312 |
+
},
|
2313 |
+
|
2314 |
+
setup: function( data, namespaces ) {
|
2315 |
+
if ( this.type === "file" ) {
|
2316 |
+
return false;
|
2317 |
+
}
|
2318 |
+
|
2319 |
+
for ( var type in changeFilters ) {
|
2320 |
+
jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
|
2321 |
+
}
|
2322 |
+
|
2323 |
+
return formElems.test( this.nodeName );
|
2324 |
+
},
|
2325 |
+
|
2326 |
+
teardown: function( namespaces ) {
|
2327 |
+
jQuery.event.remove( this, ".specialChange" );
|
2328 |
+
|
2329 |
+
return formElems.test( this.nodeName );
|
2330 |
+
}
|
2331 |
+
};
|
2332 |
+
|
2333 |
+
changeFilters = jQuery.event.special.change.filters;
|
2334 |
+
}
|
2335 |
+
|
2336 |
+
function trigger( type, elem, args ) {
|
2337 |
+
args[0].type = type;
|
2338 |
+
return jQuery.event.handle.apply( elem, args );
|
2339 |
+
}
|
2340 |
+
|
2341 |
+
// Create "bubbling" focus and blur events
|
2342 |
+
if ( document.addEventListener ) {
|
2343 |
+
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
|
2344 |
+
jQuery.event.special[ fix ] = {
|
2345 |
+
setup: function() {
|
2346 |
+
this.addEventListener( orig, handler, true );
|
2347 |
+
},
|
2348 |
+
teardown: function() {
|
2349 |
+
this.removeEventListener( orig, handler, true );
|
2350 |
+
}
|
2351 |
+
};
|
2352 |
+
|
2353 |
+
function handler( e ) {
|
2354 |
+
e = jQuery.event.fix( e );
|
2355 |
+
e.type = fix;
|
2356 |
+
return jQuery.event.handle.call( this, e );
|
2357 |
+
}
|
2358 |
+
});
|
2359 |
+
}
|
2360 |
+
|
2361 |
+
jQuery.each(["bind", "one"], function( i, name ) {
|
2362 |
+
jQuery.fn[ name ] = function( type, data, fn ) {
|
2363 |
+
// Handle object literals
|
2364 |
+
if ( typeof type === "object" ) {
|
2365 |
+
for ( var key in type ) {
|
2366 |
+
this[ name ](key, data, type[key], fn);
|
2367 |
+
}
|
2368 |
+
return this;
|
2369 |
+
}
|
2370 |
+
|
2371 |
+
if ( jQuery.isFunction( data ) ) {
|
2372 |
+
fn = data;
|
2373 |
+
data = undefined;
|
2374 |
+
}
|
2375 |
+
|
2376 |
+
var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
|
2377 |
+
jQuery( this ).unbind( event, handler );
|
2378 |
+
return fn.apply( this, arguments );
|
2379 |
+
}) : fn;
|
2380 |
+
|
2381 |
+
if ( type === "unload" && name !== "one" ) {
|
2382 |
+
this.one( type, data, fn );
|
2383 |
+
|
2384 |
+
} else {
|
2385 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
2386 |
+
jQuery.event.add( this[i], type, handler, data );
|
2387 |
+
}
|
2388 |
+
}
|
2389 |
+
|
2390 |
+
return this;
|
2391 |
+
};
|
2392 |
+
});
|
2393 |
+
|
2394 |
+
jQuery.fn.extend({
|
2395 |
+
unbind: function( type, fn ) {
|
2396 |
+
// Handle object literals
|
2397 |
+
if ( typeof type === "object" && !type.preventDefault ) {
|
2398 |
+
for ( var key in type ) {
|
2399 |
+
this.unbind(key, type[key]);
|
2400 |
+
}
|
2401 |
+
|
2402 |
+
} else {
|
2403 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
2404 |
+
jQuery.event.remove( this[i], type, fn );
|
2405 |
+
}
|
2406 |
+
}
|
2407 |
+
|
2408 |
+
return this;
|
2409 |
+
},
|
2410 |
+
|
2411 |
+
delegate: function( selector, types, data, fn ) {
|
2412 |
+
return this.live( types, data, fn, selector );
|
2413 |
+
},
|
2414 |
+
|
2415 |
+
undelegate: function( selector, types, fn ) {
|
2416 |
+
if ( arguments.length === 0 ) {
|
2417 |
+
return this.unbind( "live" );
|
2418 |
+
|
2419 |
+
} else {
|
2420 |
+
return this.die( types, null, fn, selector );
|
2421 |
+
}
|
2422 |
+
},
|
2423 |
+
|
2424 |
+
trigger: function( type, data ) {
|
2425 |
+
return this.each(function() {
|
2426 |
+
jQuery.event.trigger( type, data, this );
|
2427 |
+
});
|
2428 |
+
},
|
2429 |
+
|
2430 |
+
triggerHandler: function( type, data ) {
|
2431 |
+
if ( this[0] ) {
|
2432 |
+
var event = jQuery.Event( type );
|
2433 |
+
event.preventDefault();
|
2434 |
+
event.stopPropagation();
|
2435 |
+
jQuery.event.trigger( event, data, this[0] );
|
2436 |
+
return event.result;
|
2437 |
+
}
|
2438 |
+
},
|
2439 |
+
|
2440 |
+
toggle: function( fn ) {
|
2441 |
+
// Save reference to arguments for access in closure
|
2442 |
+
var args = arguments, i = 1;
|
2443 |
+
|
2444 |
+
// link all the functions, so any of them can unbind this click handler
|
2445 |
+
while ( i < args.length ) {
|
2446 |
+
jQuery.proxy( fn, args[ i++ ] );
|
2447 |
+
}
|
2448 |
+
|
2449 |
+
return this.click( jQuery.proxy( fn, function( event ) {
|
2450 |
+
// Figure out which function to execute
|
2451 |
+
var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
|
2452 |
+
jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
|
2453 |
+
|
2454 |
+
// Make sure that clicks stop
|
2455 |
+
event.preventDefault();
|
2456 |
+
|
2457 |
+
// and execute the function
|
2458 |
+
return args[ lastToggle ].apply( this, arguments ) || false;
|
2459 |
+
}));
|
2460 |
+
},
|
2461 |
+
|
2462 |
+
hover: function( fnOver, fnOut ) {
|
2463 |
+
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
|
2464 |
+
}
|
2465 |
+
});
|
2466 |
+
|
2467 |
+
var liveMap = {
|
2468 |
+
focus: "focusin",
|
2469 |
+
blur: "focusout",
|
2470 |
+
mouseenter: "mouseover",
|
2471 |
+
mouseleave: "mouseout"
|
2472 |
+
};
|
2473 |
+
|
2474 |
+
jQuery.each(["live", "die"], function( i, name ) {
|
2475 |
+
jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
|
2476 |
+
var type, i = 0, match, namespaces, preType,
|
2477 |
+
selector = origSelector || this.selector,
|
2478 |
+
context = origSelector ? this : jQuery( this.context );
|
2479 |
+
|
2480 |
+
if ( jQuery.isFunction( data ) ) {
|
2481 |
+
fn = data;
|
2482 |
+
data = undefined;
|
2483 |
+
}
|
2484 |
+
|
2485 |
+
types = (types || "").split(" ");
|
2486 |
+
|
2487 |
+
while ( (type = types[ i++ ]) != null ) {
|
2488 |
+
match = rnamespaces.exec( type );
|
2489 |
+
namespaces = "";
|
2490 |
+
|
2491 |
+
if ( match ) {
|
2492 |
+
namespaces = match[0];
|
2493 |
+
type = type.replace( rnamespaces, "" );
|
2494 |
+
}
|
2495 |
+
|
2496 |
+
if ( type === "hover" ) {
|
2497 |
+
types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
|
2498 |
+
continue;
|
2499 |
+
}
|
2500 |
+
|
2501 |
+
preType = type;
|
2502 |
+
|
2503 |
+
if ( type === "focus" || type === "blur" ) {
|
2504 |
+
types.push( liveMap[ type ] + namespaces );
|
2505 |
+
type = type + namespaces;
|
2506 |
+
|
2507 |
+
} else {
|
2508 |
+
type = (liveMap[ type ] || type) + namespaces;
|
2509 |
+
}
|
2510 |
+
|
2511 |
+
if ( name === "live" ) {
|
2512 |
+
// bind live handler
|
2513 |
+
context.each(function(){
|
2514 |
+
jQuery.event.add( this, liveConvert( type, selector ),
|
2515 |
+
{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
|
2516 |
+
});
|
2517 |
+
|
2518 |
+
} else {
|
2519 |
+
// unbind live handler
|
2520 |
+
context.unbind( liveConvert( type, selector ), fn );
|
2521 |
+
}
|
2522 |
+
}
|
2523 |
+
|
2524 |
+
return this;
|
2525 |
+
}
|
2526 |
+
});
|
2527 |
+
|
2528 |
+
function liveHandler( event ) {
|
2529 |
+
var stop, elems = [], selectors = [], args = arguments,
|
2530 |
+
related, match, handleObj, elem, j, i, l, data,
|
2531 |
+
events = jQuery.data( this, "events" );
|
2532 |
+
|
2533 |
+
// Make sure we avoid non-left-click bubbling in Firefox (#3861)
|
2534 |
+
if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
|
2535 |
+
return;
|
2536 |
+
}
|
2537 |
+
|
2538 |
+
event.liveFired = this;
|
2539 |
+
|
2540 |
+
var live = events.live.slice(0);
|
2541 |
+
|
2542 |
+
for ( j = 0; j < live.length; j++ ) {
|
2543 |
+
handleObj = live[j];
|
2544 |
+
|
2545 |
+
if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
|
2546 |
+
selectors.push( handleObj.selector );
|
2547 |
+
|
2548 |
+
} else {
|
2549 |
+
live.splice( j--, 1 );
|
2550 |
+
}
|
2551 |
+
}
|
2552 |
+
|
2553 |
+
match = jQuery( event.target ).closest( selectors, event.currentTarget );
|
2554 |
+
|
2555 |
+
for ( i = 0, l = match.length; i < l; i++ ) {
|
2556 |
+
for ( j = 0; j < live.length; j++ ) {
|
2557 |
+
handleObj = live[j];
|
2558 |
+
|
2559 |
+
if ( match[i].selector === handleObj.selector ) {
|
2560 |
+
elem = match[i].elem;
|
2561 |
+
related = null;
|
2562 |
+
|
2563 |
+
// Those two events require additional checking
|
2564 |
+
if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
|
2565 |
+
related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
|
2566 |
+
}
|
2567 |
+
|
2568 |
+
if ( !related || related !== elem ) {
|
2569 |
+
elems.push({ elem: elem, handleObj: handleObj });
|
2570 |
+
}
|
2571 |
+
}
|
2572 |
+
}
|
2573 |
+
}
|
2574 |
+
|
2575 |
+
for ( i = 0, l = elems.length; i < l; i++ ) {
|
2576 |
+
match = elems[i];
|
2577 |
+
event.currentTarget = match.elem;
|
2578 |
+
event.data = match.handleObj.data;
|
2579 |
+
event.handleObj = match.handleObj;
|
2580 |
+
|
2581 |
+
if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
|
2582 |
+
stop = false;
|
2583 |
+
break;
|
2584 |
+
}
|
2585 |
+
}
|
2586 |
+
|
2587 |
+
return stop;
|
2588 |
+
}
|
2589 |
+
|
2590 |
+
function liveConvert( type, selector ) {
|
2591 |
+
return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
|
2592 |
+
}
|
2593 |
+
|
2594 |
+
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
|
2595 |
+
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
|
2596 |
+
"change select submit keydown keypress keyup error").split(" "), function( i, name ) {
|
2597 |
+
|
2598 |
+
// Handle event binding
|
2599 |
+
jQuery.fn[ name ] = function( fn ) {
|
2600 |
+
return fn ? this.bind( name, fn ) : this.trigger( name );
|
2601 |
+
};
|
2602 |
+
|
2603 |
+
if ( jQuery.attrFn ) {
|
2604 |
+
jQuery.attrFn[ name ] = true;
|
2605 |
+
}
|
2606 |
+
});
|
2607 |
+
|
2608 |
+
// Prevent memory leaks in IE
|
2609 |
+
// Window isn't included so as not to unbind existing unload events
|
2610 |
+
// More info:
|
2611 |
+
// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
|
2612 |
+
if ( window.attachEvent && !window.addEventListener ) {
|
2613 |
+
window.attachEvent("onunload", function() {
|
2614 |
+
for ( var id in jQuery.cache ) {
|
2615 |
+
if ( jQuery.cache[ id ].handle ) {
|
2616 |
+
// Try/Catch is to handle iframes being unloaded, see #4280
|
2617 |
+
try {
|
2618 |
+
jQuery.event.remove( jQuery.cache[ id ].handle.elem );
|
2619 |
+
} catch(e) {}
|
2620 |
+
}
|
2621 |
+
}
|
2622 |
+
});
|
2623 |
+
}
|
2624 |
+
/*!
|
2625 |
+
* Sizzle CSS Selector Engine - v1.0
|
2626 |
+
* Copyright 2009, The Dojo Foundation
|
2627 |
+
* Released under the MIT, BSD, and GPL Licenses.
|
2628 |
+
* More information: http://sizzlejs.com/
|
2629 |
+
*/
|
2630 |
+
(function(){
|
2631 |
+
|
2632 |
+
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
2633 |
+
done = 0,
|
2634 |
+
toString = Object.prototype.toString,
|
2635 |
+
hasDuplicate = false,
|
2636 |
+
baseHasDuplicate = true;
|
2637 |
+
|
2638 |
+
// Here we check if the JavaScript engine is using some sort of
|
2639 |
+
// optimization where it does not always call our comparision
|
2640 |
+
// function. If that is the case, discard the hasDuplicate value.
|
2641 |
+
// Thus far that includes Google Chrome.
|
2642 |
+
[0, 0].sort(function(){
|
2643 |
+
baseHasDuplicate = false;
|
2644 |
+
return 0;
|
2645 |
+
});
|
2646 |
+
|
2647 |
+
var Sizzle = function(selector, context, results, seed) {
|
2648 |
+
results = results || [];
|
2649 |
+
var origContext = context = context || document;
|
2650 |
+
|
2651 |
+
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
|
2652 |
+
return [];
|
2653 |
+
}
|
2654 |
+
|
2655 |
+
if ( !selector || typeof selector !== "string" ) {
|
2656 |
+
return results;
|
2657 |
+
}
|
2658 |
+
|
2659 |
+
var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
|
2660 |
+
soFar = selector;
|
2661 |
+
|
2662 |
+
// Reset the position of the chunker regexp (start from head)
|
2663 |
+
while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
|
2664 |
+
soFar = m[3];
|
2665 |
+
|
2666 |
+
parts.push( m[1] );
|
2667 |
+
|
2668 |
+
if ( m[2] ) {
|
2669 |
+
extra = m[3];
|
2670 |
+
break;
|
2671 |
+
}
|
2672 |
+
}
|
2673 |
+
|
2674 |
+
if ( parts.length > 1 && origPOS.exec( selector ) ) {
|
2675 |
+
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
|
2676 |
+
set = posProcess( parts[0] + parts[1], context );
|
2677 |
+
} else {
|
2678 |
+
set = Expr.relative[ parts[0] ] ?
|
2679 |
+
[ context ] :
|
2680 |
+
Sizzle( parts.shift(), context );
|
2681 |
+
|
2682 |
+
while ( parts.length ) {
|
2683 |
+
selector = parts.shift();
|
2684 |
+
|
2685 |
+
if ( Expr.relative[ selector ] ) {
|
2686 |
+
selector += parts.shift();
|
2687 |
+
}
|
2688 |
+
|
2689 |
+
set = posProcess( selector, set );
|
2690 |
+
}
|
2691 |
+
}
|
2692 |
+
} else {
|
2693 |
+
// Take a shortcut and set the context if the root selector is an ID
|
2694 |
+
// (but not if it'll be faster if the inner selector is an ID)
|
2695 |
+
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
|
2696 |
+
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
|
2697 |
+
var ret = Sizzle.find( parts.shift(), context, contextXML );
|
2698 |
+
context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
|
2699 |
+
}
|
2700 |
+
|
2701 |
+
if ( context ) {
|
2702 |
+
var ret = seed ?
|
2703 |
+
{ expr: parts.pop(), set: makeArray(seed) } :
|
2704 |
+
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
|
2705 |
+
set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
|
2706 |
+
|
2707 |
+
if ( parts.length > 0 ) {
|
2708 |
+
checkSet = makeArray(set);
|
2709 |
+
} else {
|
2710 |
+
prune = false;
|
2711 |
+
}
|
2712 |
+
|
2713 |
+
while ( parts.length ) {
|
2714 |
+
var cur = parts.pop(), pop = cur;
|
2715 |
+
|
2716 |
+
if ( !Expr.relative[ cur ] ) {
|
2717 |
+
cur = "";
|
2718 |
+
} else {
|
2719 |
+
pop = parts.pop();
|
2720 |
+
}
|
2721 |
+
|
2722 |
+
if ( pop == null ) {
|
2723 |
+
pop = context;
|
2724 |
+
}
|
2725 |
+
|
2726 |
+
Expr.relative[ cur ]( checkSet, pop, contextXML );
|
2727 |
+
}
|
2728 |
+
} else {
|
2729 |
+
checkSet = parts = [];
|
2730 |
+
}
|
2731 |
+
}
|
2732 |
+
|
2733 |
+
if ( !checkSet ) {
|
2734 |
+
checkSet = set;
|
2735 |
+
}
|
2736 |
+
|
2737 |
+
if ( !checkSet ) {
|
2738 |
+
Sizzle.error( cur || selector );
|
2739 |
+
}
|
2740 |
+
|
2741 |
+
if ( toString.call(checkSet) === "[object Array]" ) {
|
2742 |
+
if ( !prune ) {
|
2743 |
+
results.push.apply( results, checkSet );
|
2744 |
+
} else if ( context && context.nodeType === 1 ) {
|
2745 |
+
for ( var i = 0; checkSet[i] != null; i++ ) {
|
2746 |
+
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
|
2747 |
+
results.push( set[i] );
|
2748 |
+
}
|
2749 |
+
}
|
2750 |
+
} else {
|
2751 |
+
for ( var i = 0; checkSet[i] != null; i++ ) {
|
2752 |
+
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
|
2753 |
+
results.push( set[i] );
|
2754 |
+
}
|
2755 |
+
}
|
2756 |
+
}
|
2757 |
+
} else {
|
2758 |
+
makeArray( checkSet, results );
|
2759 |
+
}
|
2760 |
+
|
2761 |
+
if ( extra ) {
|
2762 |
+
Sizzle( extra, origContext, results, seed );
|
2763 |
+
Sizzle.uniqueSort( results );
|
2764 |
+
}
|
2765 |
+
|
2766 |
+
return results;
|
2767 |
+
};
|
2768 |
+
|
2769 |
+
Sizzle.uniqueSort = function(results){
|
2770 |
+
if ( sortOrder ) {
|
2771 |
+
hasDuplicate = baseHasDuplicate;
|
2772 |
+
results.sort(sortOrder);
|
2773 |
+
|
2774 |
+
if ( hasDuplicate ) {
|
2775 |
+
for ( var i = 1; i < results.length; i++ ) {
|
2776 |
+
if ( results[i] === results[i-1] ) {
|
2777 |
+
results.splice(i--, 1);
|
2778 |
+
}
|
2779 |
+
}
|
2780 |
+
}
|
2781 |
+
}
|
2782 |
+
|
2783 |
+
return results;
|
2784 |
+
};
|
2785 |
+
|
2786 |
+
Sizzle.matches = function(expr, set){
|
2787 |
+
return Sizzle(expr, null, null, set);
|
2788 |
+
};
|
2789 |
+
|
2790 |
+
Sizzle.find = function(expr, context, isXML){
|
2791 |
+
var set, match;
|
2792 |
+
|
2793 |
+
if ( !expr ) {
|
2794 |
+
return [];
|
2795 |
+
}
|
2796 |
+
|
2797 |
+
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
|
2798 |
+
var type = Expr.order[i], match;
|
2799 |
+
|
2800 |
+
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
|
2801 |
+
var left = match[1];
|
2802 |
+
match.splice(1,1);
|
2803 |
+
|
2804 |
+
if ( left.substr( left.length - 1 ) !== "\\" ) {
|
2805 |
+
match[1] = (match[1] || "").replace(/\\/g, "");
|
2806 |
+
set = Expr.find[ type ]( match, context, isXML );
|
2807 |
+
if ( set != null ) {
|
2808 |
+
expr = expr.replace( Expr.match[ type ], "" );
|
2809 |
+
break;
|
2810 |
+
}
|
2811 |
+
}
|
2812 |
+
}
|
2813 |
+
}
|
2814 |
+
|
2815 |
+
if ( !set ) {
|
2816 |
+
set = context.getElementsByTagName("*");
|
2817 |
+
}
|
2818 |
+
|
2819 |
+
return {set: set, expr: expr};
|
2820 |
+
};
|
2821 |
+
|
2822 |
+
Sizzle.filter = function(expr, set, inplace, not){
|
2823 |
+
var old = expr, result = [], curLoop = set, match, anyFound,
|
2824 |
+
isXMLFilter = set && set[0] && isXML(set[0]);
|
2825 |
+
|
2826 |
+
while ( expr && set.length ) {
|
2827 |
+
for ( var type in Expr.filter ) {
|
2828 |
+
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
|
2829 |
+
var filter = Expr.filter[ type ], found, item, left = match[1];
|
2830 |
+
anyFound = false;
|
2831 |
+
|
2832 |
+
match.splice(1,1);
|
2833 |
+
|
2834 |
+
if ( left.substr( left.length - 1 ) === "\\" ) {
|
2835 |
+
continue;
|
2836 |
+
}
|
2837 |
+
|
2838 |
+
if ( curLoop === result ) {
|
2839 |
+
result = [];
|
2840 |
+
}
|
2841 |
+
|
2842 |
+
if ( Expr.preFilter[ type ] ) {
|
2843 |
+
match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
|
2844 |
+
|
2845 |
+
if ( !match ) {
|
2846 |
+
anyFound = found = true;
|
2847 |
+
} else if ( match === true ) {
|
2848 |
+
continue;
|
2849 |
+
}
|
2850 |
+
}
|
2851 |
+
|
2852 |
+
if ( match ) {
|
2853 |
+
for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
|
2854 |
+
if ( item ) {
|
2855 |
+
found = filter( item, match, i, curLoop );
|
2856 |
+
var pass = not ^ !!found;
|
2857 |
+
|
2858 |
+
if ( inplace && found != null ) {
|
2859 |
+
if ( pass ) {
|
2860 |
+
anyFound = true;
|
2861 |
+
} else {
|
2862 |
+
curLoop[i] = false;
|
2863 |
+
}
|
2864 |
+
} else if ( pass ) {
|
2865 |
+
result.push( item );
|
2866 |
+
anyFound = true;
|
2867 |
+
}
|
2868 |
+
}
|
2869 |
+
}
|
2870 |
+
}
|
2871 |
+
|
2872 |
+
if ( found !== undefined ) {
|
2873 |
+
if ( !inplace ) {
|
2874 |
+
curLoop = result;
|
2875 |
+
}
|
2876 |
+
|
2877 |
+
expr = expr.replace( Expr.match[ type ], "" );
|
2878 |
+
|
2879 |
+
if ( !anyFound ) {
|
2880 |
+
return [];
|
2881 |
+
}
|
2882 |
+
|
2883 |
+
break;
|
2884 |
+
}
|
2885 |
+
}
|
2886 |
+
}
|
2887 |
+
|
2888 |
+
// Improper expression
|
2889 |
+
if ( expr === old ) {
|
2890 |
+
if ( anyFound == null ) {
|
2891 |
+
Sizzle.error( expr );
|
2892 |
+
} else {
|
2893 |
+
break;
|
2894 |
+
}
|
2895 |
+
}
|
2896 |
+
|
2897 |
+
old = expr;
|
2898 |
+
}
|
2899 |
+
|
2900 |
+
return curLoop;
|
2901 |
+
};
|
2902 |
+
|
2903 |
+
Sizzle.error = function( msg ) {
|
2904 |
+
throw "Syntax error, unrecognized expression: " + msg;
|
2905 |
+
};
|
2906 |
+
|
2907 |
+
var Expr = Sizzle.selectors = {
|
2908 |
+
order: [ "ID", "NAME", "TAG" ],
|
2909 |
+
match: {
|
2910 |
+
ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
2911 |
+
CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
2912 |
+
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
|
2913 |
+
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
|
2914 |
+
TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
|
2915 |
+
CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
|
2916 |
+
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
|
2917 |
+
PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
|
2918 |
+
},
|
2919 |
+
leftMatch: {},
|
2920 |
+
attrMap: {
|
2921 |
+
"class": "className",
|
2922 |
+
"for": "htmlFor"
|
2923 |
+
},
|
2924 |
+
attrHandle: {
|
2925 |
+
href: function(elem){
|
2926 |
+
return elem.getAttribute("href");
|
2927 |
+
}
|
2928 |
+
},
|
2929 |
+
relative: {
|
2930 |
+
"+": function(checkSet, part){
|
2931 |
+
var isPartStr = typeof part === "string",
|
2932 |
+
isTag = isPartStr && !/\W/.test(part),
|
2933 |
+
isPartStrNotTag = isPartStr && !isTag;
|
2934 |
+
|
2935 |
+
if ( isTag ) {
|
2936 |
+
part = part.toLowerCase();
|
2937 |
+
}
|
2938 |
+
|
2939 |
+
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
|
2940 |
+
if ( (elem = checkSet[i]) ) {
|
2941 |
+
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
|
2942 |
+
|
2943 |
+
checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
|
2944 |
+
elem || false :
|
2945 |
+
elem === part;
|
2946 |
+
}
|
2947 |
+
}
|
2948 |
+
|
2949 |
+
if ( isPartStrNotTag ) {
|
2950 |
+
Sizzle.filter( part, checkSet, true );
|
2951 |
+
}
|
2952 |
+
},
|
2953 |
+
">": function(checkSet, part){
|
2954 |
+
var isPartStr = typeof part === "string";
|
2955 |
+
|
2956 |
+
if ( isPartStr && !/\W/.test(part) ) {
|
2957 |
+
part = part.toLowerCase();
|
2958 |
+
|
2959 |
+
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
|
2960 |
+
var elem = checkSet[i];
|
2961 |
+
if ( elem ) {
|
2962 |
+
var parent = elem.parentNode;
|
2963 |
+
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
|
2964 |
+
}
|
2965 |
+
}
|
2966 |
+
} else {
|
2967 |
+
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
|
2968 |
+
var elem = checkSet[i];
|
2969 |
+
if ( elem ) {
|
2970 |
+
checkSet[i] = isPartStr ?
|
2971 |
+
elem.parentNode :
|
2972 |
+
elem.parentNode === part;
|
2973 |
+
}
|
2974 |
+
}
|
2975 |
+
|
2976 |
+
if ( isPartStr ) {
|
2977 |
+
Sizzle.filter( part, checkSet, true );
|
2978 |
+
}
|
2979 |
+
}
|
2980 |
+
},
|
2981 |
+
"": function(checkSet, part, isXML){
|
2982 |
+
var doneName = done++, checkFn = dirCheck;
|
2983 |
+
|
2984 |
+
if ( typeof part === "string" && !/\W/.test(part) ) {
|
2985 |
+
var nodeCheck = part = part.toLowerCase();
|
2986 |
+
checkFn = dirNodeCheck;
|
2987 |
+
}
|
2988 |
+
|
2989 |
+
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
|
2990 |
+
},
|
2991 |
+
"~": function(checkSet, part, isXML){
|
2992 |
+
var doneName = done++, checkFn = dirCheck;
|
2993 |
+
|
2994 |
+
if ( typeof part === "string" && !/\W/.test(part) ) {
|
2995 |
+
var nodeCheck = part = part.toLowerCase();
|
2996 |
+
checkFn = dirNodeCheck;
|
2997 |
+
}
|
2998 |
+
|
2999 |
+
checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
|
3000 |
+
}
|
3001 |
+
},
|
3002 |
+
find: {
|
3003 |
+
ID: function(match, context, isXML){
|
3004 |
+
if ( typeof context.getElementById !== "undefined" && !isXML ) {
|
3005 |
+
var m = context.getElementById(match[1]);
|
3006 |
+
return m ? [m] : [];
|
3007 |
+
}
|
3008 |
+
},
|
3009 |
+
NAME: function(match, context){
|
3010 |
+
if ( typeof context.getElementsByName !== "undefined" ) {
|
3011 |
+
var ret = [], results = context.getElementsByName(match[1]);
|
3012 |
+
|
3013 |
+
for ( var i = 0, l = results.length; i < l; i++ ) {
|
3014 |
+
if ( results[i].getAttribute("name") === match[1] ) {
|
3015 |
+
ret.push( results[i] );
|
3016 |
+
}
|
3017 |
+
}
|
3018 |
+
|
3019 |
+
return ret.length === 0 ? null : ret;
|
3020 |
+
}
|
3021 |
+
},
|
3022 |
+
TAG: function(match, context){
|
3023 |
+
return context.getElementsByTagName(match[1]);
|
3024 |
+
}
|
3025 |
+
},
|
3026 |
+
preFilter: {
|
3027 |
+
CLASS: function(match, curLoop, inplace, result, not, isXML){
|
3028 |
+
match = " " + match[1].replace(/\\/g, "") + " ";
|
3029 |
+
|
3030 |
+
if ( isXML ) {
|
3031 |
+
return match;
|
3032 |
+
}
|
3033 |
+
|
3034 |
+
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
|
3035 |
+
if ( elem ) {
|
3036 |
+
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
|
3037 |
+
if ( !inplace ) {
|
3038 |
+
result.push( elem );
|
3039 |
+
}
|
3040 |
+
} else if ( inplace ) {
|
3041 |
+
curLoop[i] = false;
|
3042 |
+
}
|
3043 |
+
}
|
3044 |
+
}
|
3045 |
+
|
3046 |
+
return false;
|
3047 |
+
},
|
3048 |
+
ID: function(match){
|
3049 |
+
return match[1].replace(/\\/g, "");
|
3050 |
+
},
|
3051 |
+
TAG: function(match, curLoop){
|
3052 |
+
return match[1].toLowerCase();
|
3053 |
+
},
|
3054 |
+
CHILD: function(match){
|
3055 |
+
if ( match[1] === "nth" ) {
|
3056 |
+
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
|
3057 |
+
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
|
3058 |
+
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
|
3059 |
+
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
|
3060 |
+
|
3061 |
+
// calculate the numbers (first)n+(last) including if they are negative
|
3062 |
+
match[2] = (test[1] + (test[2] || 1)) - 0;
|
3063 |
+
match[3] = test[3] - 0;
|
3064 |
+
}
|
3065 |
+
|
3066 |
+
// TODO: Move to normal caching system
|
3067 |
+
match[0] = done++;
|
3068 |
+
|
3069 |
+
return match;
|
3070 |
+
},
|
3071 |
+
ATTR: function(match, curLoop, inplace, result, not, isXML){
|
3072 |
+
var name = match[1].replace(/\\/g, "");
|
3073 |
+
|
3074 |
+
if ( !isXML && Expr.attrMap[name] ) {
|
3075 |
+
match[1] = Expr.attrMap[name];
|
3076 |
+
}
|
3077 |
+
|
3078 |
+
if ( match[2] === "~=" ) {
|
3079 |
+
match[4] = " " + match[4] + " ";
|
3080 |
+
}
|
3081 |
+
|
3082 |
+
return match;
|
3083 |
+
},
|
3084 |
+
PSEUDO: function(match, curLoop, inplace, result, not){
|
3085 |
+
if ( match[1] === "not" ) {
|
3086 |
+
// If we're dealing with a complex expression, or a simple one
|
3087 |
+
if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
|
3088 |
+
match[3] = Sizzle(match[3], null, null, curLoop);
|
3089 |
+
} else {
|
3090 |
+
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
|
3091 |
+
if ( !inplace ) {
|
3092 |
+
result.push.apply( result, ret );
|
3093 |
+
}
|
3094 |
+
return false;
|
3095 |
+
}
|
3096 |
+
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
|
3097 |
+
return true;
|
3098 |
+
}
|
3099 |
+
|
3100 |
+
return match;
|
3101 |
+
},
|
3102 |
+
POS: function(match){
|
3103 |
+
match.unshift( true );
|
3104 |
+
return match;
|
3105 |
+
}
|
3106 |
+
},
|
3107 |
+
filters: {
|
3108 |
+
enabled: function(elem){
|
3109 |
+
return elem.disabled === false && elem.type !== "hidden";
|
3110 |
+
},
|
3111 |
+
disabled: function(elem){
|
3112 |
+
return elem.disabled === true;
|
3113 |
+
},
|
3114 |
+
checked: function(elem){
|
3115 |
+
return elem.checked === true;
|
3116 |
+
},
|
3117 |
+
selected: function(elem){
|
3118 |
+
// Accessing this property makes selected-by-default
|
3119 |
+
// options in Safari work properly
|
3120 |
+
elem.parentNode.selectedIndex;
|
3121 |
+
return elem.selected === true;
|
3122 |
+
},
|
3123 |
+
parent: function(elem){
|
3124 |
+
return !!elem.firstChild;
|
3125 |
+
},
|
3126 |
+
empty: function(elem){
|
3127 |
+
return !elem.firstChild;
|
3128 |
+
},
|
3129 |
+
has: function(elem, i, match){
|
3130 |
+
return !!Sizzle( match[3], elem ).length;
|
3131 |
+
},
|
3132 |
+
header: function(elem){
|
3133 |
+
return /h\d/i.test( elem.nodeName );
|
3134 |
+
},
|
3135 |
+
text: function(elem){
|
3136 |
+
return "text" === elem.type;
|
3137 |
+
},
|
3138 |
+
radio: function(elem){
|
3139 |
+
return "radio" === elem.type;
|
3140 |
+
},
|
3141 |
+
checkbox: function(elem){
|
3142 |
+
return "checkbox" === elem.type;
|
3143 |
+
},
|
3144 |
+
file: function(elem){
|
3145 |
+
return "file" === elem.type;
|
3146 |
+
},
|
3147 |
+
password: function(elem){
|
3148 |
+
return "password" === elem.type;
|
3149 |
+
},
|
3150 |
+
submit: function(elem){
|
3151 |
+
return "submit" === elem.type;
|
3152 |
+
},
|
3153 |
+
image: function(elem){
|
3154 |
+
return "image" === elem.type;
|
3155 |
+
},
|
3156 |
+
reset: function(elem){
|
3157 |
+
return "reset" === elem.type;
|
3158 |
+
},
|
3159 |
+
button: function(elem){
|
3160 |
+
return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
|
3161 |
+
},
|
3162 |
+
input: function(elem){
|
3163 |
+
return /input|select|textarea|button/i.test(elem.nodeName);
|
3164 |
+
}
|
3165 |
+
},
|
3166 |
+
setFilters: {
|
3167 |
+
first: function(elem, i){
|
3168 |
+
return i === 0;
|
3169 |
+
},
|
3170 |
+
last: function(elem, i, match, array){
|
3171 |
+
return i === array.length - 1;
|
3172 |
+
},
|
3173 |
+
even: function(elem, i){
|
3174 |
+
return i % 2 === 0;
|
3175 |
+
},
|
3176 |
+
odd: function(elem, i){
|
3177 |
+
return i % 2 === 1;
|
3178 |
+
},
|
3179 |
+
lt: function(elem, i, match){
|
3180 |
+
return i < match[3] - 0;
|
3181 |
+
},
|
3182 |
+
gt: function(elem, i, match){
|
3183 |
+
return i > match[3] - 0;
|
3184 |
+
},
|
3185 |
+
nth: function(elem, i, match){
|
3186 |
+
return match[3] - 0 === i;
|
3187 |
+
},
|
3188 |
+
eq: function(elem, i, match){
|
3189 |
+
return match[3] - 0 === i;
|
3190 |
+
}
|
3191 |
+
},
|
3192 |
+
filter: {
|
3193 |
+
PSEUDO: function(elem, match, i, array){
|
3194 |
+
var name = match[1], filter = Expr.filters[ name ];
|
3195 |
+
|
3196 |
+
if ( filter ) {
|
3197 |
+
return filter( elem, i, match, array );
|
3198 |
+
} else if ( name === "contains" ) {
|
3199 |
+
return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
|
3200 |
+
} else if ( name === "not" ) {
|
3201 |
+
var not = match[3];
|
3202 |
+
|
3203 |
+
for ( var i = 0, l = not.length; i < l; i++ ) {
|
3204 |
+
if ( not[i] === elem ) {
|
3205 |
+
return false;
|
3206 |
+
}
|
3207 |
+
}
|
3208 |
+
|
3209 |
+
return true;
|
3210 |
+
} else {
|
3211 |
+
Sizzle.error( "Syntax error, unrecognized expression: " + name );
|
3212 |
+
}
|
3213 |
+
},
|
3214 |
+
CHILD: function(elem, match){
|
3215 |
+
var type = match[1], node = elem;
|
3216 |
+
switch (type) {
|
3217 |
+
case 'only':
|
3218 |
+
case 'first':
|
3219 |
+
while ( (node = node.previousSibling) ) {
|
3220 |
+
if ( node.nodeType === 1 ) {
|
3221 |
+
return false;
|
3222 |
+
}
|
3223 |
+
}
|
3224 |
+
if ( type === "first" ) {
|
3225 |
+
return true;
|
3226 |
+
}
|
3227 |
+
node = elem;
|
3228 |
+
case 'last':
|
3229 |
+
while ( (node = node.nextSibling) ) {
|
3230 |
+
if ( node.nodeType === 1 ) {
|
3231 |
+
return false;
|
3232 |
+
}
|
3233 |
+
}
|
3234 |
+
return true;
|
3235 |
+
case 'nth':
|
3236 |
+
var first = match[2], last = match[3];
|
3237 |
+
|
3238 |
+
if ( first === 1 && last === 0 ) {
|
3239 |
+
return true;
|
3240 |
+
}
|
3241 |
+
|
3242 |
+
var doneName = match[0],
|
3243 |
+
parent = elem.parentNode;
|
3244 |
+
|
3245 |
+
if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
|
3246 |
+
var count = 0;
|
3247 |
+
for ( node = parent.firstChild; node; node = node.nextSibling ) {
|
3248 |
+
if ( node.nodeType === 1 ) {
|
3249 |
+
node.nodeIndex = ++count;
|
3250 |
+
}
|
3251 |
+
}
|
3252 |
+
parent.sizcache = doneName;
|
3253 |
+
}
|
3254 |
+
|
3255 |
+
var diff = elem.nodeIndex - last;
|
3256 |
+
if ( first === 0 ) {
|
3257 |
+
return diff === 0;
|
3258 |
+
} else {
|
3259 |
+
return ( diff % first === 0 && diff / first >= 0 );
|
3260 |
+
}
|
3261 |
+
}
|
3262 |
+
},
|
3263 |
+
ID: function(elem, match){
|
3264 |
+
return elem.nodeType === 1 && elem.getAttribute("id") === match;
|
3265 |
+
},
|
3266 |
+
TAG: function(elem, match){
|
3267 |
+
return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
|
3268 |
+
},
|
3269 |
+
CLASS: function(elem, match){
|
3270 |
+
return (" " + (elem.className || elem.getAttribute("class")) + " ")
|
3271 |
+
.indexOf( match ) > -1;
|
3272 |
+
},
|
3273 |
+
ATTR: function(elem, match){
|
3274 |
+
var name = match[1],
|
3275 |
+
result = Expr.attrHandle[ name ] ?
|
3276 |
+
Expr.attrHandle[ name ]( elem ) :
|
3277 |
+
elem[ name ] != null ?
|
3278 |
+
elem[ name ] :
|
3279 |
+
elem.getAttribute( name ),
|
3280 |
+
value = result + "",
|
3281 |
+
type = match[2],
|
3282 |
+
check = match[4];
|
3283 |
+
|
3284 |
+
return result == null ?
|
3285 |
+
type === "!=" :
|
3286 |
+
type === "=" ?
|
3287 |
+
value === check :
|
3288 |
+
type === "*=" ?
|
3289 |
+
value.indexOf(check) >= 0 :
|
3290 |
+
type === "~=" ?
|
3291 |
+
(" " + value + " ").indexOf(check) >= 0 :
|
3292 |
+
!check ?
|
3293 |
+
value && result !== false :
|
3294 |
+
type === "!=" ?
|
3295 |
+
value !== check :
|
3296 |
+
type === "^=" ?
|
3297 |
+
value.indexOf(check) === 0 :
|
3298 |
+
type === "$=" ?
|
3299 |
+
value.substr(value.length - check.length) === check :
|
3300 |
+
type === "|=" ?
|
3301 |
+
value === check || value.substr(0, check.length + 1) === check + "-" :
|
3302 |
+
false;
|
3303 |
+
},
|
3304 |
+
POS: function(elem, match, i, array){
|
3305 |
+
var name = match[2], filter = Expr.setFilters[ name ];
|
3306 |
+
|
3307 |
+
if ( filter ) {
|
3308 |
+
return filter( elem, i, match, array );
|
3309 |
+
}
|
3310 |
+
}
|
3311 |
+
}
|
3312 |
+
};
|
3313 |
+
|
3314 |
+
var origPOS = Expr.match.POS;
|
3315 |
+
|
3316 |
+
for ( var type in Expr.match ) {
|
3317 |
+
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
|
3318 |
+
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
|
3319 |
+
return "\\" + (num - 0 + 1);
|
3320 |
+
}));
|
3321 |
+
}
|
3322 |
+
|
3323 |
+
var makeArray = function(array, results) {
|
3324 |
+
array = Array.prototype.slice.call( array, 0 );
|
3325 |
+
|
3326 |
+
if ( results ) {
|
3327 |
+
results.push.apply( results, array );
|
3328 |
+
return results;
|
3329 |
+
}
|
3330 |
+
|
3331 |
+
return array;
|
3332 |
+
};
|
3333 |
+
|
3334 |
+
// Perform a simple check to determine if the browser is capable of
|
3335 |
+
// converting a NodeList to an array using builtin methods.
|
3336 |
+
// Also verifies that the returned array holds DOM nodes
|
3337 |
+
// (which is not the case in the Blackberry browser)
|
3338 |
+
try {
|
3339 |
+
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
|
3340 |
+
|
3341 |
+
// Provide a fallback method if it does not work
|
3342 |
+
} catch(e){
|
3343 |
+
makeArray = function(array, results) {
|
3344 |
+
var ret = results || [];
|
3345 |
+
|
3346 |
+
if ( toString.call(array) === "[object Array]" ) {
|
3347 |
+
Array.prototype.push.apply( ret, array );
|
3348 |
+
} else {
|
3349 |
+
if ( typeof array.length === "number" ) {
|
3350 |
+
for ( var i = 0, l = array.length; i < l; i++ ) {
|
3351 |
+
ret.push( array[i] );
|
3352 |
+
}
|
3353 |
+
} else {
|
3354 |
+
for ( var i = 0; array[i]; i++ ) {
|
3355 |
+
ret.push( array[i] );
|
3356 |
+
}
|
3357 |
+
}
|
3358 |
+
}
|
3359 |
+
|
3360 |
+
return ret;
|
3361 |
+
};
|
3362 |
+
}
|
3363 |
+
|
3364 |
+
var sortOrder;
|
3365 |
+
|
3366 |
+
if ( document.documentElement.compareDocumentPosition ) {
|
3367 |
+
sortOrder = function( a, b ) {
|
3368 |
+
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
|
3369 |
+
if ( a == b ) {
|
3370 |
+
hasDuplicate = true;
|
3371 |
+
}
|
3372 |
+
return a.compareDocumentPosition ? -1 : 1;
|
3373 |
+
}
|
3374 |
+
|
3375 |
+
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
|
3376 |
+
if ( ret === 0 ) {
|
3377 |
+
hasDuplicate = true;
|
3378 |
+
}
|
3379 |
+
return ret;
|
3380 |
+
};
|
3381 |
+
} else if ( "sourceIndex" in document.documentElement ) {
|
3382 |
+
sortOrder = function( a, b ) {
|
3383 |
+
if ( !a.sourceIndex || !b.sourceIndex ) {
|
3384 |
+
if ( a == b ) {
|
3385 |
+
hasDuplicate = true;
|
3386 |
+
}
|
3387 |
+
return a.sourceIndex ? -1 : 1;
|
3388 |
+
}
|
3389 |
+
|
3390 |
+
var ret = a.sourceIndex - b.sourceIndex;
|
3391 |
+
if ( ret === 0 ) {
|
3392 |
+
hasDuplicate = true;
|
3393 |
+
}
|
3394 |
+
return ret;
|
3395 |
+
};
|
3396 |
+
} else if ( document.createRange ) {
|
3397 |
+
sortOrder = function( a, b ) {
|
3398 |
+
if ( !a.ownerDocument || !b.ownerDocument ) {
|
3399 |
+
if ( a == b ) {
|
3400 |
+
hasDuplicate = true;
|
3401 |
+
}
|
3402 |
+
return a.ownerDocument ? -1 : 1;
|
3403 |
+
}
|
3404 |
+
|
3405 |
+
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
|
3406 |
+
aRange.setStart(a, 0);
|
3407 |
+
aRange.setEnd(a, 0);
|
3408 |
+
bRange.setStart(b, 0);
|
3409 |
+
bRange.setEnd(b, 0);
|
3410 |
+
var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
|
3411 |
+
if ( ret === 0 ) {
|
3412 |
+
hasDuplicate = true;
|
3413 |
+
}
|
3414 |
+
return ret;
|
3415 |
+
};
|
3416 |
+
}
|
3417 |
+
|
3418 |
+
// Utility function for retreiving the text value of an array of DOM nodes
|
3419 |
+
function getText( elems ) {
|
3420 |
+
var ret = "", elem;
|
3421 |
+
|
3422 |
+
for ( var i = 0; elems[i]; i++ ) {
|
3423 |
+
elem = elems[i];
|
3424 |
+
|
3425 |
+
// Get the text from text nodes and CDATA nodes
|
3426 |
+
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
|
3427 |
+
ret += elem.nodeValue;
|
3428 |
+
|
3429 |
+
// Traverse everything else, except comment nodes
|
3430 |
+
} else if ( elem.nodeType !== 8 ) {
|
3431 |
+
ret += getText( elem.childNodes );
|
3432 |
+
}
|
3433 |
+
}
|
3434 |
+
|
3435 |
+
return ret;
|
3436 |
+
}
|
3437 |
+
|
3438 |
+
// Check to see if the browser returns elements by name when
|
3439 |
+
// querying by getElementById (and provide a workaround)
|
3440 |
+
(function(){
|
3441 |
+
// We're going to inject a fake input element with a specified name
|
3442 |
+
var form = document.createElement("div"),
|
3443 |
+
id = "script" + (new Date).getTime();
|
3444 |
+
form.innerHTML = "<a name='" + id + "'/>";
|
3445 |
+
|
3446 |
+
// Inject it into the root element, check its status, and remove it quickly
|
3447 |
+
var root = document.documentElement;
|
3448 |
+
root.insertBefore( form, root.firstChild );
|
3449 |
+
|
3450 |
+
// The workaround has to do additional checks after a getElementById
|
3451 |
+
// Which slows things down for other browsers (hence the branching)
|
3452 |
+
if ( document.getElementById( id ) ) {
|
3453 |
+
Expr.find.ID = function(match, context, isXML){
|
3454 |
+
if ( typeof context.getElementById !== "undefined" && !isXML ) {
|
3455 |
+
var m = context.getElementById(match[1]);
|
3456 |
+
return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
|
3457 |
+
}
|
3458 |
+
};
|
3459 |
+
|
3460 |
+
Expr.filter.ID = function(elem, match){
|
3461 |
+
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
|
3462 |
+
return elem.nodeType === 1 && node && node.nodeValue === match;
|
3463 |
+
};
|
3464 |
+
}
|
3465 |
+
|
3466 |
+
root.removeChild( form );
|
3467 |
+
root = form = null; // release memory in IE
|
3468 |
+
})();
|
3469 |
+
|
3470 |
+
(function(){
|
3471 |
+
// Check to see if the browser returns only elements
|
3472 |
+
// when doing getElementsByTagName("*")
|
3473 |
+
|
3474 |
+
// Create a fake element
|
3475 |
+
var div = document.createElement("div");
|
3476 |
+
div.appendChild( document.createComment("") );
|
3477 |
+
|
3478 |
+
// Make sure no comments are found
|
3479 |
+
if ( div.getElementsByTagName("*").length > 0 ) {
|
3480 |
+
Expr.find.TAG = function(match, context){
|
3481 |
+
var results = context.getElementsByTagName(match[1]);
|
3482 |
+
|
3483 |
+
// Filter out possible comments
|
3484 |
+
if ( match[1] === "*" ) {
|
3485 |
+
var tmp = [];
|
3486 |
+
|
3487 |
+
for ( var i = 0; results[i]; i++ ) {
|
3488 |
+
if ( results[i].nodeType === 1 ) {
|
3489 |
+
tmp.push( results[i] );
|
3490 |
+
}
|
3491 |
+
}
|
3492 |
+
|
3493 |
+
results = tmp;
|
3494 |
+
}
|
3495 |
+
|
3496 |
+
return results;
|
3497 |
+
};
|
3498 |
+
}
|
3499 |
+
|
3500 |
+
// Check to see if an attribute returns normalized href attributes
|
3501 |
+
div.innerHTML = "<a href='#'></a>";
|
3502 |
+
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
|
3503 |
+
div.firstChild.getAttribute("href") !== "#" ) {
|
3504 |
+
Expr.attrHandle.href = function(elem){
|
3505 |
+
return elem.getAttribute("href", 2);
|
3506 |
+
};
|
3507 |
+
}
|
3508 |
+
|
3509 |
+
div = null; // release memory in IE
|
3510 |
+
})();
|
3511 |
+
|
3512 |
+
if ( document.querySelectorAll ) {
|
3513 |
+
(function(){
|
3514 |
+
var oldSizzle = Sizzle, div = document.createElement("div");
|
3515 |
+
div.innerHTML = "<p class='TEST'></p>";
|
3516 |
+
|
3517 |
+
// Safari can't handle uppercase or unicode characters when
|
3518 |
+
// in quirks mode.
|
3519 |
+
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
|
3520 |
+
return;
|
3521 |
+
}
|
3522 |
+
|
3523 |
+
Sizzle = function(query, context, extra, seed){
|
3524 |
+
context = context || document;
|
3525 |
+
|
3526 |
+
// Only use querySelectorAll on non-XML documents
|
3527 |
+
// (ID selectors don't work in non-HTML documents)
|
3528 |
+
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
|
3529 |
+
try {
|
3530 |
+
return makeArray( context.querySelectorAll(query), extra );
|
3531 |
+
} catch(e){}
|
3532 |
+
}
|
3533 |
+
|
3534 |
+
return oldSizzle(query, context, extra, seed);
|
3535 |
+
};
|
3536 |
+
|
3537 |
+
for ( var prop in oldSizzle ) {
|
3538 |
+
Sizzle[ prop ] = oldSizzle[ prop ];
|
3539 |
+
}
|
3540 |
+
|
3541 |
+
div = null; // release memory in IE
|
3542 |
+
})();
|
3543 |
+
}
|
3544 |
+
|
3545 |
+
(function(){
|
3546 |
+
var div = document.createElement("div");
|
3547 |
+
|
3548 |
+
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
|
3549 |
+
|
3550 |
+
// Opera can't find a second classname (in 9.6)
|
3551 |
+
// Also, make sure that getElementsByClassName actually exists
|
3552 |
+
if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
|
3553 |
+
return;
|
3554 |
+
}
|
3555 |
+
|
3556 |
+
// Safari caches class attributes, doesn't catch changes (in 3.2)
|
3557 |
+
div.lastChild.className = "e";
|
3558 |
+
|
3559 |
+
if ( div.getElementsByClassName("e").length === 1 ) {
|
3560 |
+
return;
|
3561 |
+
}
|
3562 |
+
|
3563 |
+
Expr.order.splice(1, 0, "CLASS");
|
3564 |
+
Expr.find.CLASS = function(match, context, isXML) {
|
3565 |
+
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
|
3566 |
+
return context.getElementsByClassName(match[1]);
|
3567 |
+
}
|
3568 |
+
};
|
3569 |
+
|
3570 |
+
div = null; // release memory in IE
|
3571 |
+
})();
|
3572 |
+
|
3573 |
+
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
|
3574 |
+
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
|
3575 |
+
var elem = checkSet[i];
|
3576 |
+
if ( elem ) {
|
3577 |
+
elem = elem[dir];
|
3578 |
+
var match = false;
|
3579 |
+
|
3580 |
+
while ( elem ) {
|
3581 |
+
if ( elem.sizcache === doneName ) {
|
3582 |
+
match = checkSet[elem.sizset];
|
3583 |
+
break;
|
3584 |
+
}
|
3585 |
+
|
3586 |
+
if ( elem.nodeType === 1 && !isXML ){
|
3587 |
+
elem.sizcache = doneName;
|
3588 |
+
elem.sizset = i;
|
3589 |
+
}
|
3590 |
+
|
3591 |
+
if ( elem.nodeName.toLowerCase() === cur ) {
|
3592 |
+
match = elem;
|
3593 |
+
break;
|
3594 |
+
}
|
3595 |
+
|
3596 |
+
elem = elem[dir];
|
3597 |
+
}
|
3598 |
+
|
3599 |
+
checkSet[i] = match;
|
3600 |
+
}
|
3601 |
+
}
|
3602 |
+
}
|
3603 |
+
|
3604 |
+
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
|
3605 |
+
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
|
3606 |
+
var elem = checkSet[i];
|
3607 |
+
if ( elem ) {
|
3608 |
+
elem = elem[dir];
|
3609 |
+
var match = false;
|
3610 |
+
|
3611 |
+
while ( elem ) {
|
3612 |
+
if ( elem.sizcache === doneName ) {
|
3613 |
+
match = checkSet[elem.sizset];
|
3614 |
+
break;
|
3615 |
+
}
|
3616 |
+
|
3617 |
+
if ( elem.nodeType === 1 ) {
|
3618 |
+
if ( !isXML ) {
|
3619 |
+
elem.sizcache = doneName;
|
3620 |
+
elem.sizset = i;
|
3621 |
+
}
|
3622 |
+
if ( typeof cur !== "string" ) {
|
3623 |
+
if ( elem === cur ) {
|
3624 |
+
match = true;
|
3625 |
+
break;
|
3626 |
+
}
|
3627 |
+
|
3628 |
+
} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
|
3629 |
+
match = elem;
|
3630 |
+
break;
|
3631 |
+
}
|
3632 |
+
}
|
3633 |
+
|
3634 |
+
elem = elem[dir];
|
3635 |
+
}
|
3636 |
+
|
3637 |
+
checkSet[i] = match;
|
3638 |
+
}
|
3639 |
+
}
|
3640 |
+
}
|
3641 |
+
|
3642 |
+
var contains = document.compareDocumentPosition ? function(a, b){
|
3643 |
+
return !!(a.compareDocumentPosition(b) & 16);
|
3644 |
+
} : function(a, b){
|
3645 |
+
return a !== b && (a.contains ? a.contains(b) : true);
|
3646 |
+
};
|
3647 |
+
|
3648 |
+
var isXML = function(elem){
|
3649 |
+
// documentElement is verified for cases where it doesn't yet exist
|
3650 |
+
// (such as loading iframes in IE - #4833)
|
3651 |
+
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
|
3652 |
+
return documentElement ? documentElement.nodeName !== "HTML" : false;
|
3653 |
+
};
|
3654 |
+
|
3655 |
+
var posProcess = function(selector, context){
|
3656 |
+
var tmpSet = [], later = "", match,
|
3657 |
+
root = context.nodeType ? [context] : context;
|
3658 |
+
|
3659 |
+
// Position selectors must be done after the filter
|
3660 |
+
// And so must :not(positional) so we move all PSEUDOs to the end
|
3661 |
+
while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
|
3662 |
+
later += match[0];
|
3663 |
+
selector = selector.replace( Expr.match.PSEUDO, "" );
|
3664 |
+
}
|
3665 |
+
|
3666 |
+
selector = Expr.relative[selector] ? selector + "*" : selector;
|
3667 |
+
|
3668 |
+
for ( var i = 0, l = root.length; i < l; i++ ) {
|
3669 |
+
Sizzle( selector, root[i], tmpSet );
|
3670 |
+
}
|
3671 |
+
|
3672 |
+
return Sizzle.filter( later, tmpSet );
|
3673 |
+
};
|
3674 |
+
|
3675 |
+
// EXPOSE
|
3676 |
+
jQuery.find = Sizzle;
|
3677 |
+
jQuery.expr = Sizzle.selectors;
|
3678 |
+
jQuery.expr[":"] = jQuery.expr.filters;
|
3679 |
+
jQuery.unique = Sizzle.uniqueSort;
|
3680 |
+
jQuery.text = getText;
|
3681 |
+
jQuery.isXMLDoc = isXML;
|
3682 |
+
jQuery.contains = contains;
|
3683 |
+
|
3684 |
+
return;
|
3685 |
+
|
3686 |
+
window.Sizzle = Sizzle;
|
3687 |
+
|
3688 |
+
})();
|
3689 |
+
var runtil = /Until$/,
|
3690 |
+
rparentsprev = /^(?:parents|prevUntil|prevAll)/,
|
3691 |
+
// Note: This RegExp should be improved, or likely pulled from Sizzle
|
3692 |
+
rmultiselector = /,/,
|
3693 |
+
slice = Array.prototype.slice;
|
3694 |
+
|
3695 |
+
// Implement the identical functionality for filter and not
|
3696 |
+
var winnow = function( elements, qualifier, keep ) {
|
3697 |
+
if ( jQuery.isFunction( qualifier ) ) {
|
3698 |
+
return jQuery.grep(elements, function( elem, i ) {
|
3699 |
+
return !!qualifier.call( elem, i, elem ) === keep;
|
3700 |
+
});
|
3701 |
+
|
3702 |
+
} else if ( qualifier.nodeType ) {
|
3703 |
+
return jQuery.grep(elements, function( elem, i ) {
|
3704 |
+
return (elem === qualifier) === keep;
|
3705 |
+
});
|
3706 |
+
|
3707 |
+
} else if ( typeof qualifier === "string" ) {
|
3708 |
+
var filtered = jQuery.grep(elements, function( elem ) {
|
3709 |
+
return elem.nodeType === 1;
|
3710 |
+
});
|
3711 |
+
|
3712 |
+
if ( isSimple.test( qualifier ) ) {
|
3713 |
+
return jQuery.filter(qualifier, filtered, !keep);
|
3714 |
+
} else {
|
3715 |
+
qualifier = jQuery.filter( qualifier, filtered );
|
3716 |
+
}
|
3717 |
+
}
|
3718 |
+
|
3719 |
+
return jQuery.grep(elements, function( elem, i ) {
|
3720 |
+
return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
|
3721 |
+
});
|
3722 |
+
};
|
3723 |
+
|
3724 |
+
jQuery.fn.extend({
|
3725 |
+
find: function( selector ) {
|
3726 |
+
var ret = this.pushStack( "", "find", selector ), length = 0;
|
3727 |
+
|
3728 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
3729 |
+
length = ret.length;
|
3730 |
+
jQuery.find( selector, this[i], ret );
|
3731 |
+
|
3732 |
+
if ( i > 0 ) {
|
3733 |
+
// Make sure that the results are unique
|
3734 |
+
for ( var n = length; n < ret.length; n++ ) {
|
3735 |
+
for ( var r = 0; r < length; r++ ) {
|
3736 |
+
if ( ret[r] === ret[n] ) {
|
3737 |
+
ret.splice(n--, 1);
|
3738 |
+
break;
|
3739 |
+
}
|
3740 |
+
}
|
3741 |
+
}
|
3742 |
+
}
|
3743 |
+
}
|
3744 |
+
|
3745 |
+
return ret;
|
3746 |
+
},
|
3747 |
+
|
3748 |
+
has: function( target ) {
|
3749 |
+
var targets = jQuery( target );
|
3750 |
+
return this.filter(function() {
|
3751 |
+
for ( var i = 0, l = targets.length; i < l; i++ ) {
|
3752 |
+
if ( jQuery.contains( this, targets[i] ) ) {
|
3753 |
+
return true;
|
3754 |
+
}
|
3755 |
+
}
|
3756 |
+
});
|
3757 |
+
},
|
3758 |
+
|
3759 |
+
not: function( selector ) {
|
3760 |
+
return this.pushStack( winnow(this, selector, false), "not", selector);
|
3761 |
+
},
|
3762 |
+
|
3763 |
+
filter: function( selector ) {
|
3764 |
+
return this.pushStack( winnow(this, selector, true), "filter", selector );
|
3765 |
+
},
|
3766 |
+
|
3767 |
+
is: function( selector ) {
|
3768 |
+
return !!selector && jQuery.filter( selector, this ).length > 0;
|
3769 |
+
},
|
3770 |
+
|
3771 |
+
closest: function( selectors, context ) {
|
3772 |
+
if ( jQuery.isArray( selectors ) ) {
|
3773 |
+
var ret = [], cur = this[0], match, matches = {}, selector;
|
3774 |
+
|
3775 |
+
if ( cur && selectors.length ) {
|
3776 |
+
for ( var i = 0, l = selectors.length; i < l; i++ ) {
|
3777 |
+
selector = selectors[i];
|
3778 |
+
|
3779 |
+
if ( !matches[selector] ) {
|
3780 |
+
matches[selector] = jQuery.expr.match.POS.test( selector ) ?
|
3781 |
+
jQuery( selector, context || this.context ) :
|
3782 |
+
selector;
|
3783 |
+
}
|
3784 |
+
}
|
3785 |
+
|
3786 |
+
while ( cur && cur.ownerDocument && cur !== context ) {
|
3787 |
+
for ( selector in matches ) {
|
3788 |
+
match = matches[selector];
|
3789 |
+
|
3790 |
+
if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
|
3791 |
+
ret.push({ selector: selector, elem: cur });
|
3792 |
+
delete matches[selector];
|
3793 |
+
}
|
3794 |
+
}
|
3795 |
+
cur = cur.parentNode;
|
3796 |
+
}
|
3797 |
+
}
|
3798 |
+
|
3799 |
+
return ret;
|
3800 |
+
}
|
3801 |
+
|
3802 |
+
var pos = jQuery.expr.match.POS.test( selectors ) ?
|
3803 |
+
jQuery( selectors, context || this.context ) : null;
|
3804 |
+
|
3805 |
+
return this.map(function( i, cur ) {
|
3806 |
+
while ( cur && cur.ownerDocument && cur !== context ) {
|
3807 |
+
if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
|
3808 |
+
return cur;
|
3809 |
+
}
|
3810 |
+
cur = cur.parentNode;
|
3811 |
+
}
|
3812 |
+
return null;
|
3813 |
+
});
|
3814 |
+
},
|
3815 |
+
|
3816 |
+
// Determine the position of an element within
|
3817 |
+
// the matched set of elements
|
3818 |
+
index: function( elem ) {
|
3819 |
+
if ( !elem || typeof elem === "string" ) {
|
3820 |
+
return jQuery.inArray( this[0],
|
3821 |
+
// If it receives a string, the selector is used
|
3822 |
+
// If it receives nothing, the siblings are used
|
3823 |
+
elem ? jQuery( elem ) : this.parent().children() );
|
3824 |
+
}
|
3825 |
+
// Locate the position of the desired element
|
3826 |
+
return jQuery.inArray(
|
3827 |
+
// If it receives a jQuery object, the first element is used
|
3828 |
+
elem.jquery ? elem[0] : elem, this );
|
3829 |
+
},
|
3830 |
+
|
3831 |
+
add: function( selector, context ) {
|
3832 |
+
var set = typeof selector === "string" ?
|
3833 |
+
jQuery( selector, context || this.context ) :
|
3834 |
+
jQuery.makeArray( selector ),
|
3835 |
+
all = jQuery.merge( this.get(), set );
|
3836 |
+
|
3837 |
+
return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
|
3838 |
+
all :
|
3839 |
+
jQuery.unique( all ) );
|
3840 |
+
},
|
3841 |
+
|
3842 |
+
andSelf: function() {
|
3843 |
+
return this.add( this.prevObject );
|
3844 |
+
}
|
3845 |
+
});
|
3846 |
+
|
3847 |
+
// A painfully simple check to see if an element is disconnected
|
3848 |
+
// from a document (should be improved, where feasible).
|
3849 |
+
function isDisconnected( node ) {
|
3850 |
+
return !node || !node.parentNode || node.parentNode.nodeType === 11;
|
3851 |
+
}
|
3852 |
+
|
3853 |
+
jQuery.each({
|
3854 |
+
parent: function( elem ) {
|
3855 |
+
var parent = elem.parentNode;
|
3856 |
+
return parent && parent.nodeType !== 11 ? parent : null;
|
3857 |
+
},
|
3858 |
+
parents: function( elem ) {
|
3859 |
+
return jQuery.dir( elem, "parentNode" );
|
3860 |
+
},
|
3861 |
+
parentsUntil: function( elem, i, until ) {
|
3862 |
+
return jQuery.dir( elem, "parentNode", until );
|
3863 |
+
},
|
3864 |
+
next: function( elem ) {
|
3865 |
+
return jQuery.nth( elem, 2, "nextSibling" );
|
3866 |
+
},
|
3867 |
+
prev: function( elem ) {
|
3868 |
+
return jQuery.nth( elem, 2, "previousSibling" );
|
3869 |
+
},
|
3870 |
+
nextAll: function( elem ) {
|
3871 |
+
return jQuery.dir( elem, "nextSibling" );
|
3872 |
+
},
|
3873 |
+
prevAll: function( elem ) {
|
3874 |
+
return jQuery.dir( elem, "previousSibling" );
|
3875 |
+
},
|
3876 |
+
nextUntil: function( elem, i, until ) {
|
3877 |
+
return jQuery.dir( elem, "nextSibling", until );
|
3878 |
+
},
|
3879 |
+
prevUntil: function( elem, i, until ) {
|
3880 |
+
return jQuery.dir( elem, "previousSibling", until );
|
3881 |
+
},
|
3882 |
+
siblings: function( elem ) {
|
3883 |
+
return jQuery.sibling( elem.parentNode.firstChild, elem );
|
3884 |
+
},
|
3885 |
+
children: function( elem ) {
|
3886 |
+
return jQuery.sibling( elem.firstChild );
|
3887 |
+
},
|
3888 |
+
contents: function( elem ) {
|
3889 |
+
return jQuery.nodeName( elem, "iframe" ) ?
|
3890 |
+
elem.contentDocument || elem.contentWindow.document :
|
3891 |
+
jQuery.makeArray( elem.childNodes );
|
3892 |
+
}
|
3893 |
+
}, function( name, fn ) {
|
3894 |
+
jQuery.fn[ name ] = function( until, selector ) {
|
3895 |
+
var ret = jQuery.map( this, fn, until );
|
3896 |
+
|
3897 |
+
if ( !runtil.test( name ) ) {
|
3898 |
+
selector = until;
|
3899 |
+
}
|
3900 |
+
|
3901 |
+
if ( selector && typeof selector === "string" ) {
|
3902 |
+
ret = jQuery.filter( selector, ret );
|
3903 |
+
}
|
3904 |
+
|
3905 |
+
ret = this.length > 1 ? jQuery.unique( ret ) : ret;
|
3906 |
+
|
3907 |
+
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
|
3908 |
+
ret = ret.reverse();
|
3909 |
+
}
|
3910 |
+
|
3911 |
+
return this.pushStack( ret, name, slice.call(arguments).join(",") );
|
3912 |
+
};
|
3913 |
+
});
|
3914 |
+
|
3915 |
+
jQuery.extend({
|
3916 |
+
filter: function( expr, elems, not ) {
|
3917 |
+
if ( not ) {
|
3918 |
+
expr = ":not(" + expr + ")";
|
3919 |
+
}
|
3920 |
+
|
3921 |
+
return jQuery.find.matches(expr, elems);
|
3922 |
+
},
|
3923 |
+
|
3924 |
+
dir: function( elem, dir, until ) {
|
3925 |
+
var matched = [], cur = elem[dir];
|
3926 |
+
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
|
3927 |
+
if ( cur.nodeType === 1 ) {
|
3928 |
+
matched.push( cur );
|
3929 |
+
}
|
3930 |
+
cur = cur[dir];
|
3931 |
+
}
|
3932 |
+
return matched;
|
3933 |
+
},
|
3934 |
+
|
3935 |
+
nth: function( cur, result, dir, elem ) {
|
3936 |
+
result = result || 1;
|
3937 |
+
var num = 0;
|
3938 |
+
|
3939 |
+
for ( ; cur; cur = cur[dir] ) {
|
3940 |
+
if ( cur.nodeType === 1 && ++num === result ) {
|
3941 |
+
break;
|
3942 |
+
}
|
3943 |
+
}
|
3944 |
+
|
3945 |
+
return cur;
|
3946 |
+
},
|
3947 |
+
|
3948 |
+
sibling: function( n, elem ) {
|
3949 |
+
var r = [];
|
3950 |
+
|
3951 |
+
for ( ; n; n = n.nextSibling ) {
|
3952 |
+
if ( n.nodeType === 1 && n !== elem ) {
|
3953 |
+
r.push( n );
|
3954 |
+
}
|
3955 |
+
}
|
3956 |
+
|
3957 |
+
return r;
|
3958 |
+
}
|
3959 |
+
});
|
3960 |
+
var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
|
3961 |
+
rleadingWhitespace = /^\s+/,
|
3962 |
+
rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
|
3963 |
+
rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
|
3964 |
+
rtagName = /<([\w:]+)/,
|
3965 |
+
rtbody = /<tbody/i,
|
3966 |
+
rhtml = /<|&#?\w+;/,
|
3967 |
+
rnocache = /<script|<object|<embed|<option|<style/i,
|
3968 |
+
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
|
3969 |
+
fcloseTag = function( all, front, tag ) {
|
3970 |
+
return rselfClosing.test( tag ) ?
|
3971 |
+
all :
|
3972 |
+
front + "></" + tag + ">";
|
3973 |
+
},
|
3974 |
+
wrapMap = {
|
3975 |
+
option: [ 1, "<select multiple='multiple'>", "</select>" ],
|
3976 |
+
legend: [ 1, "<fieldset>", "</fieldset>" ],
|
3977 |
+
thead: [ 1, "<table>", "</table>" ],
|
3978 |
+
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
|
3979 |
+
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
3980 |
+
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
|
3981 |
+
area: [ 1, "<map>", "</map>" ],
|
3982 |
+
_default: [ 0, "", "" ]
|
3983 |
+
};
|
3984 |
+
|
3985 |
+
wrapMap.optgroup = wrapMap.option;
|
3986 |
+
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
|
3987 |
+
wrapMap.th = wrapMap.td;
|
3988 |
+
|
3989 |
+
// IE can't serialize <link> and <script> tags normally
|
3990 |
+
if ( !jQuery.support.htmlSerialize ) {
|
3991 |
+
wrapMap._default = [ 1, "div<div>", "</div>" ];
|
3992 |
+
}
|
3993 |
+
|
3994 |
+
jQuery.fn.extend({
|
3995 |
+
text: function( text ) {
|
3996 |
+
if ( jQuery.isFunction(text) ) {
|
3997 |
+
return this.each(function(i) {
|
3998 |
+
var self = jQuery(this);
|
3999 |
+
self.text( text.call(this, i, self.text()) );
|
4000 |
+
});
|
4001 |
+
}
|
4002 |
+
|
4003 |
+
if ( typeof text !== "object" && text !== undefined ) {
|
4004 |
+
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
|
4005 |
+
}
|
4006 |
+
|
4007 |
+
return jQuery.text( this );
|
4008 |
+
},
|
4009 |
+
|
4010 |
+
wrapAll: function( html ) {
|
4011 |
+
if ( jQuery.isFunction( html ) ) {
|
4012 |
+
return this.each(function(i) {
|
4013 |
+
jQuery(this).wrapAll( html.call(this, i) );
|
4014 |
+
});
|
4015 |
+
}
|
4016 |
+
|
4017 |
+
if ( this[0] ) {
|
4018 |
+
// The elements to wrap the target around
|
4019 |
+
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
|
4020 |
+
|
4021 |
+
if ( this[0].parentNode ) {
|
4022 |
+
wrap.insertBefore( this[0] );
|
4023 |
+
}
|
4024 |
+
|
4025 |
+
wrap.map(function() {
|
4026 |
+
var elem = this;
|
4027 |
+
|
4028 |
+
while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
|
4029 |
+
elem = elem.firstChild;
|
4030 |
+
}
|
4031 |
+
|
4032 |
+
return elem;
|
4033 |
+
}).append(this);
|
4034 |
+
}
|
4035 |
+
|
4036 |
+
return this;
|
4037 |
+
},
|
4038 |
+
|
4039 |
+
wrapInner: function( html ) {
|
4040 |
+
if ( jQuery.isFunction( html ) ) {
|
4041 |
+
return this.each(function(i) {
|
4042 |
+
jQuery(this).wrapInner( html.call(this, i) );
|
4043 |
+
});
|
4044 |
+
}
|
4045 |
+
|
4046 |
+
return this.each(function() {
|
4047 |
+
var self = jQuery( this ), contents = self.contents();
|
4048 |
+
|
4049 |
+
if ( contents.length ) {
|
4050 |
+
contents.wrapAll( html );
|
4051 |
+
|
4052 |
+
} else {
|
4053 |
+
self.append( html );
|
4054 |
+
}
|
4055 |
+
});
|
4056 |
+
},
|
4057 |
+
|
4058 |
+
wrap: function( html ) {
|
4059 |
+
return this.each(function() {
|
4060 |
+
jQuery( this ).wrapAll( html );
|
4061 |
+
});
|
4062 |
+
},
|
4063 |
+
|
4064 |
+
unwrap: function() {
|
4065 |
+
return this.parent().each(function() {
|
4066 |
+
if ( !jQuery.nodeName( this, "body" ) ) {
|
4067 |
+
jQuery( this ).replaceWith( this.childNodes );
|
4068 |
+
}
|
4069 |
+
}).end();
|
4070 |
+
},
|
4071 |
+
|
4072 |
+
append: function() {
|
4073 |
+
return this.domManip(arguments, true, function( elem ) {
|
4074 |
+
if ( this.nodeType === 1 ) {
|
4075 |
+
this.appendChild( elem );
|
4076 |
+
}
|
4077 |
+
});
|
4078 |
+
},
|
4079 |
+
|
4080 |
+
prepend: function() {
|
4081 |
+
return this.domManip(arguments, true, function( elem ) {
|
4082 |
+
if ( this.nodeType === 1 ) {
|
4083 |
+
this.insertBefore( elem, this.firstChild );
|
4084 |
+
}
|
4085 |
+
});
|
4086 |
+
},
|
4087 |
+
|
4088 |
+
before: function() {
|
4089 |
+
if ( this[0] && this[0].parentNode ) {
|
4090 |
+
return this.domManip(arguments, false, function( elem ) {
|
4091 |
+
this.parentNode.insertBefore( elem, this );
|
4092 |
+
});
|
4093 |
+
} else if ( arguments.length ) {
|
4094 |
+
var set = jQuery(arguments[0]);
|
4095 |
+
set.push.apply( set, this.toArray() );
|
4096 |
+
return this.pushStack( set, "before", arguments );
|
4097 |
+
}
|
4098 |
+
},
|
4099 |
+
|
4100 |
+
after: function() {
|
4101 |
+
if ( this[0] && this[0].parentNode ) {
|
4102 |
+
return this.domManip(arguments, false, function( elem ) {
|
4103 |
+
this.parentNode.insertBefore( elem, this.nextSibling );
|
4104 |
+
});
|
4105 |
+
} else if ( arguments.length ) {
|
4106 |
+
var set = this.pushStack( this, "after", arguments );
|
4107 |
+
set.push.apply( set, jQuery(arguments[0]).toArray() );
|
4108 |
+
return set;
|
4109 |
+
}
|
4110 |
+
},
|
4111 |
+
|
4112 |
+
// keepData is for internal use only--do not document
|
4113 |
+
remove: function( selector, keepData ) {
|
4114 |
+
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
|
4115 |
+
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
|
4116 |
+
if ( !keepData && elem.nodeType === 1 ) {
|
4117 |
+
jQuery.cleanData( elem.getElementsByTagName("*") );
|
4118 |
+
jQuery.cleanData( [ elem ] );
|
4119 |
+
}
|
4120 |
+
|
4121 |
+
if ( elem.parentNode ) {
|
4122 |
+
elem.parentNode.removeChild( elem );
|
4123 |
+
}
|
4124 |
+
}
|
4125 |
+
}
|
4126 |
+
|
4127 |
+
return this;
|
4128 |
+
},
|
4129 |
+
|
4130 |
+
empty: function() {
|
4131 |
+
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
|
4132 |
+
// Remove element nodes and prevent memory leaks
|
4133 |
+
if ( elem.nodeType === 1 ) {
|
4134 |
+
jQuery.cleanData( elem.getElementsByTagName("*") );
|
4135 |
+
}
|
4136 |
+
|
4137 |
+
// Remove any remaining nodes
|
4138 |
+
while ( elem.firstChild ) {
|
4139 |
+
elem.removeChild( elem.firstChild );
|
4140 |
+
}
|
4141 |
+
}
|
4142 |
+
|
4143 |
+
return this;
|
4144 |
+
},
|
4145 |
+
|
4146 |
+
clone: function( events ) {
|
4147 |
+
// Do the clone
|
4148 |
+
var ret = this.map(function() {
|
4149 |
+
if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
|
4150 |
+
// IE copies events bound via attachEvent when
|
4151 |
+
// using cloneNode. Calling detachEvent on the
|
4152 |
+
// clone will also remove the events from the orignal
|
4153 |
+
// In order to get around this, we use innerHTML.
|
4154 |
+
// Unfortunately, this means some modifications to
|
4155 |
+
// attributes in IE that are actually only stored
|
4156 |
+
// as properties will not be copied (such as the
|
4157 |
+
// the name attribute on an input).
|
4158 |
+
var html = this.outerHTML, ownerDocument = this.ownerDocument;
|
4159 |
+
if ( !html ) {
|
4160 |
+
var div = ownerDocument.createElement("div");
|
4161 |
+
div.appendChild( this.cloneNode(true) );
|
4162 |
+
html = div.innerHTML;
|
4163 |
+
}
|
4164 |
+
|
4165 |
+
return jQuery.clean([html.replace(rinlinejQuery, "")
|
4166 |
+
// Handle the case in IE 8 where action=/test/> self-closes a tag
|
4167 |
+
.replace(/=([^="'>\s]+\/)>/g, '="$1">')
|
4168 |
+
.replace(rleadingWhitespace, "")], ownerDocument)[0];
|
4169 |
+
} else {
|
4170 |
+
return this.cloneNode(true);
|
4171 |
+
}
|
4172 |
+
});
|
4173 |
+
|
4174 |
+
// Copy the events from the original to the clone
|
4175 |
+
if ( events === true ) {
|
4176 |
+
cloneCopyEvent( this, ret );
|
4177 |
+
cloneCopyEvent( this.find("*"), ret.find("*") );
|
4178 |
+
}
|
4179 |
+
|
4180 |
+
// Return the cloned set
|
4181 |
+
return ret;
|
4182 |
+
},
|
4183 |
+
|
4184 |
+
html: function( value ) {
|
4185 |
+
if ( value === undefined ) {
|
4186 |
+
return this[0] && this[0].nodeType === 1 ?
|
4187 |
+
this[0].innerHTML.replace(rinlinejQuery, "") :
|
4188 |
+
null;
|
4189 |
+
|
4190 |
+
// See if we can take a shortcut and just use innerHTML
|
4191 |
+
} else if ( typeof value === "string" && !rnocache.test( value ) &&
|
4192 |
+
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
|
4193 |
+
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
|
4194 |
+
|
4195 |
+
value = value.replace(rxhtmlTag, fcloseTag);
|
4196 |
+
|
4197 |
+
try {
|
4198 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
4199 |
+
// Remove element nodes and prevent memory leaks
|
4200 |
+
if ( this[i].nodeType === 1 ) {
|
4201 |
+
jQuery.cleanData( this[i].getElementsByTagName("*") );
|
4202 |
+
this[i].innerHTML = value;
|
4203 |
+
}
|
4204 |
+
}
|
4205 |
+
|
4206 |
+
// If using innerHTML throws an exception, use the fallback method
|
4207 |
+
} catch(e) {
|
4208 |
+
this.empty().append( value );
|
4209 |
+
}
|
4210 |
+
|
4211 |
+
} else if ( jQuery.isFunction( value ) ) {
|
4212 |
+
this.each(function(i){
|
4213 |
+
var self = jQuery(this), old = self.html();
|
4214 |
+
self.empty().append(function(){
|
4215 |
+
return value.call( this, i, old );
|
4216 |
+
});
|
4217 |
+
});
|
4218 |
+
|
4219 |
+
} else {
|
4220 |
+
this.empty().append( value );
|
4221 |
+
}
|
4222 |
+
|
4223 |
+
return this;
|
4224 |
+
},
|
4225 |
+
|
4226 |
+
replaceWith: function( value ) {
|
4227 |
+
if ( this[0] && this[0].parentNode ) {
|
4228 |
+
// Make sure that the elements are removed from the DOM before they are inserted
|
4229 |
+
// this can help fix replacing a parent with child elements
|
4230 |
+
if ( jQuery.isFunction( value ) ) {
|
4231 |
+
return this.each(function(i) {
|
4232 |
+
var self = jQuery(this), old = self.html();
|
4233 |
+
self.replaceWith( value.call( this, i, old ) );
|
4234 |
+
});
|
4235 |
+
}
|
4236 |
+
|
4237 |
+
if ( typeof value !== "string" ) {
|
4238 |
+
value = jQuery(value).detach();
|
4239 |
+
}
|
4240 |
+
|
4241 |
+
return this.each(function() {
|
4242 |
+
var next = this.nextSibling, parent = this.parentNode;
|
4243 |
+
|
4244 |
+
jQuery(this).remove();
|
4245 |
+
|
4246 |
+
if ( next ) {
|
4247 |
+
jQuery(next).before( value );
|
4248 |
+
} else {
|
4249 |
+
jQuery(parent).append( value );
|
4250 |
+
}
|
4251 |
+
});
|
4252 |
+
} else {
|
4253 |
+
return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
|
4254 |
+
}
|
4255 |
+
},
|
4256 |
+
|
4257 |
+
detach: function( selector ) {
|
4258 |
+
return this.remove( selector, true );
|
4259 |
+
},
|
4260 |
+
|
4261 |
+
domManip: function( args, table, callback ) {
|
4262 |
+
var results, first, value = args[0], scripts = [], fragment, parent;
|
4263 |
+
|
4264 |
+
// We can't cloneNode fragments that contain checked, in WebKit
|
4265 |
+
if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
|
4266 |
+
return this.each(function() {
|
4267 |
+
jQuery(this).domManip( args, table, callback, true );
|
4268 |
+
});
|
4269 |
+
}
|
4270 |
+
|
4271 |
+
if ( jQuery.isFunction(value) ) {
|
4272 |
+
return this.each(function(i) {
|
4273 |
+
var self = jQuery(this);
|
4274 |
+
args[0] = value.call(this, i, table ? self.html() : undefined);
|
4275 |
+
self.domManip( args, table, callback );
|
4276 |
+
});
|
4277 |
+
}
|
4278 |
+
|
4279 |
+
if ( this[0] ) {
|
4280 |
+
parent = value && value.parentNode;
|
4281 |
+
|
4282 |
+
// If we're in a fragment, just use that instead of building a new one
|
4283 |
+
if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
|
4284 |
+
results = { fragment: parent };
|
4285 |
+
|
4286 |
+
} else {
|
4287 |
+
results = buildFragment( args, this, scripts );
|
4288 |
+
}
|
4289 |
+
|
4290 |
+
fragment = results.fragment;
|
4291 |
+
|
4292 |
+
if ( fragment.childNodes.length === 1 ) {
|
4293 |
+
first = fragment = fragment.firstChild;
|
4294 |
+
} else {
|
4295 |
+
first = fragment.firstChild;
|
4296 |
+
}
|
4297 |
+
|
4298 |
+
if ( first ) {
|
4299 |
+
table = table && jQuery.nodeName( first, "tr" );
|
4300 |
+
|
4301 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
4302 |
+
callback.call(
|
4303 |
+
table ?
|
4304 |
+
root(this[i], first) :
|
4305 |
+
this[i],
|
4306 |
+
i > 0 || results.cacheable || this.length > 1 ?
|
4307 |
+
fragment.cloneNode(true) :
|
4308 |
+
fragment
|
4309 |
+
);
|
4310 |
+
}
|
4311 |
+
}
|
4312 |
+
|
4313 |
+
if ( scripts.length ) {
|
4314 |
+
jQuery.each( scripts, evalScript );
|
4315 |
+
}
|
4316 |
+
}
|
4317 |
+
|
4318 |
+
return this;
|
4319 |
+
|
4320 |
+
function root( elem, cur ) {
|
4321 |
+
return jQuery.nodeName(elem, "table") ?
|
4322 |
+
(elem.getElementsByTagName("tbody")[0] ||
|
4323 |
+
elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
|
4324 |
+
elem;
|
4325 |
+
}
|
4326 |
+
}
|
4327 |
+
});
|
4328 |
+
|
4329 |
+
function cloneCopyEvent(orig, ret) {
|
4330 |
+
var i = 0;
|
4331 |
+
|
4332 |
+
ret.each(function() {
|
4333 |
+
if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
|
4334 |
+
return;
|
4335 |
+
}
|
4336 |
+
|
4337 |
+
var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
|
4338 |
+
|
4339 |
+
if ( events ) {
|
4340 |
+
delete curData.handle;
|
4341 |
+
curData.events = {};
|
4342 |
+
|
4343 |
+
for ( var type in events ) {
|
4344 |
+
for ( var handler in events[ type ] ) {
|
4345 |
+
jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
|
4346 |
+
}
|
4347 |
+
}
|
4348 |
+
}
|
4349 |
+
});
|
4350 |
+
}
|
4351 |
+
|
4352 |
+
function buildFragment( args, nodes, scripts ) {
|
4353 |
+
var fragment, cacheable, cacheresults,
|
4354 |
+
doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
|
4355 |
+
|
4356 |
+
// Only cache "small" (1/2 KB) strings that are associated with the main document
|
4357 |
+
// Cloning options loses the selected state, so don't cache them
|
4358 |
+
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
|
4359 |
+
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
|
4360 |
+
if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
|
4361 |
+
!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
|
4362 |
+
|
4363 |
+
cacheable = true;
|
4364 |
+
cacheresults = jQuery.fragments[ args[0] ];
|
4365 |
+
if ( cacheresults ) {
|
4366 |
+
if ( cacheresults !== 1 ) {
|
4367 |
+
fragment = cacheresults;
|
4368 |
+
}
|
4369 |
+
}
|
4370 |
+
}
|
4371 |
+
|
4372 |
+
if ( !fragment ) {
|
4373 |
+
fragment = doc.createDocumentFragment();
|
4374 |
+
jQuery.clean( args, doc, fragment, scripts );
|
4375 |
+
}
|
4376 |
+
|
4377 |
+
if ( cacheable ) {
|
4378 |
+
jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
|
4379 |
+
}
|
4380 |
+
|
4381 |
+
return { fragment: fragment, cacheable: cacheable };
|
4382 |
+
}
|
4383 |
+
|
4384 |
+
jQuery.fragments = {};
|
4385 |
+
|
4386 |
+
jQuery.each({
|
4387 |
+
appendTo: "append",
|
4388 |
+
prependTo: "prepend",
|
4389 |
+
insertBefore: "before",
|
4390 |
+
insertAfter: "after",
|
4391 |
+
replaceAll: "replaceWith"
|
4392 |
+
}, function( name, original ) {
|
4393 |
+
jQuery.fn[ name ] = function( selector ) {
|
4394 |
+
var ret = [], insert = jQuery( selector ),
|
4395 |
+
parent = this.length === 1 && this[0].parentNode;
|
4396 |
+
|
4397 |
+
if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
|
4398 |
+
insert[ original ]( this[0] );
|
4399 |
+
return this;
|
4400 |
+
|
4401 |
+
} else {
|
4402 |
+
for ( var i = 0, l = insert.length; i < l; i++ ) {
|
4403 |
+
var elems = (i > 0 ? this.clone(true) : this).get();
|
4404 |
+
jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
|
4405 |
+
ret = ret.concat( elems );
|
4406 |
+
}
|
4407 |
+
|
4408 |
+
return this.pushStack( ret, name, insert.selector );
|
4409 |
+
}
|
4410 |
+
};
|
4411 |
+
});
|
4412 |
+
|
4413 |
+
jQuery.extend({
|
4414 |
+
clean: function( elems, context, fragment, scripts ) {
|
4415 |
+
context = context || document;
|
4416 |
+
|
4417 |
+
// !context.createElement fails in IE with an error but returns typeof 'object'
|
4418 |
+
if ( typeof context.createElement === "undefined" ) {
|
4419 |
+
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
|
4420 |
+
}
|
4421 |
+
|
4422 |
+
var ret = [];
|
4423 |
+
|
4424 |
+
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
|
4425 |
+
if ( typeof elem === "number" ) {
|
4426 |
+
elem += "";
|
4427 |
+
}
|
4428 |
+
|
4429 |
+
if ( !elem ) {
|
4430 |
+
continue;
|
4431 |
+
}
|
4432 |
+
|
4433 |
+
// Convert html string into DOM nodes
|
4434 |
+
if ( typeof elem === "string" && !rhtml.test( elem ) ) {
|
4435 |
+
elem = context.createTextNode( elem );
|
4436 |
+
|
4437 |
+
} else if ( typeof elem === "string" ) {
|
4438 |
+
// Fix "XHTML"-style tags in all browsers
|
4439 |
+
elem = elem.replace(rxhtmlTag, fcloseTag);
|
4440 |
+
|
4441 |
+
// Trim whitespace, otherwise indexOf won't work as expected
|
4442 |
+
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
|
4443 |
+
wrap = wrapMap[ tag ] || wrapMap._default,
|
4444 |
+
depth = wrap[0],
|
4445 |
+
div = context.createElement("div");
|
4446 |
+
|
4447 |
+
// Go to html and back, then peel off extra wrappers
|
4448 |
+
div.innerHTML = wrap[1] + elem + wrap[2];
|
4449 |
+
|
4450 |
+
// Move to the right depth
|
4451 |
+
while ( depth-- ) {
|
4452 |
+
div = div.lastChild;
|
4453 |
+
}
|
4454 |
+
|
4455 |
+
// Remove IE's autoinserted <tbody> from table fragments
|
4456 |
+
if ( !jQuery.support.tbody ) {
|
4457 |
+
|
4458 |
+
// String was a <table>, *may* have spurious <tbody>
|
4459 |
+
var hasBody = rtbody.test(elem),
|
4460 |
+
tbody = tag === "table" && !hasBody ?
|
4461 |
+
div.firstChild && div.firstChild.childNodes :
|
4462 |
+
|
4463 |
+
// String was a bare <thead> or <tfoot>
|
4464 |
+
wrap[1] === "<table>" && !hasBody ?
|
4465 |
+
div.childNodes :
|
4466 |
+
[];
|
4467 |
+
|
4468 |
+
for ( var j = tbody.length - 1; j >= 0 ; --j ) {
|
4469 |
+
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
|
4470 |
+
tbody[ j ].parentNode.removeChild( tbody[ j ] );
|
4471 |
+
}
|
4472 |
+
}
|
4473 |
+
|
4474 |
+
}
|
4475 |
+
|
4476 |
+
// IE completely kills leading whitespace when innerHTML is used
|
4477 |
+
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
|
4478 |
+
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
|
4479 |
+
}
|
4480 |
+
|
4481 |
+
elem = div.childNodes;
|
4482 |
+
}
|
4483 |
+
|
4484 |
+
if ( elem.nodeType ) {
|
4485 |
+
ret.push( elem );
|
4486 |
+
} else {
|
4487 |
+
ret = jQuery.merge( ret, elem );
|
4488 |
+
}
|
4489 |
+
}
|
4490 |
+
|
4491 |
+
if ( fragment ) {
|
4492 |
+
for ( var i = 0; ret[i]; i++ ) {
|
4493 |
+
if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
|
4494 |
+
scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
|
4495 |
+
|
4496 |
+
} else {
|
4497 |
+
if ( ret[i].nodeType === 1 ) {
|
4498 |
+
ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
|
4499 |
+
}
|
4500 |
+
fragment.appendChild( ret[i] );
|
4501 |
+
}
|
4502 |
+
}
|
4503 |
+
}
|
4504 |
+
|
4505 |
+
return ret;
|
4506 |
+
},
|
4507 |
+
|
4508 |
+
cleanData: function( elems ) {
|
4509 |
+
var data, id, cache = jQuery.cache,
|
4510 |
+
special = jQuery.event.special,
|
4511 |
+
deleteExpando = jQuery.support.deleteExpando;
|
4512 |
+
|
4513 |
+
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
|
4514 |
+
id = elem[ jQuery.expando ];
|
4515 |
+
|
4516 |
+
if ( id ) {
|
4517 |
+
data = cache[ id ];
|
4518 |
+
|
4519 |
+
if ( data.events ) {
|
4520 |
+
for ( var type in data.events ) {
|
4521 |
+
if ( special[ type ] ) {
|
4522 |
+
jQuery.event.remove( elem, type );
|
4523 |
+
|
4524 |
+
} else {
|
4525 |
+
removeEvent( elem, type, data.handle );
|
4526 |
+
}
|
4527 |
+
}
|
4528 |
+
}
|
4529 |
+
|
4530 |
+
if ( deleteExpando ) {
|
4531 |
+
delete elem[ jQuery.expando ];
|
4532 |
+
|
4533 |
+
} else if ( elem.removeAttribute ) {
|
4534 |
+
elem.removeAttribute( jQuery.expando );
|
4535 |
+
}
|
4536 |
+
|
4537 |
+
delete cache[ id ];
|
4538 |
+
}
|
4539 |
+
}
|
4540 |
+
}
|
4541 |
+
});
|
4542 |
+
// exclude the following css properties to add px
|
4543 |
+
var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
|
4544 |
+
ralpha = /alpha\([^)]*\)/,
|
4545 |
+
ropacity = /opacity=([^)]*)/,
|
4546 |
+
rfloat = /float/i,
|
4547 |
+
rdashAlpha = /-([a-z])/ig,
|
4548 |
+
rupper = /([A-Z])/g,
|
4549 |
+
rnumpx = /^-?\d+(?:px)?$/i,
|
4550 |
+
rnum = /^-?\d/,
|
4551 |
+
|
4552 |
+
cssShow = { position: "absolute", visibility: "hidden", display:"block" },
|
4553 |
+
cssWidth = [ "Left", "Right" ],
|
4554 |
+
cssHeight = [ "Top", "Bottom" ],
|
4555 |
+
|
4556 |
+
// cache check for defaultView.getComputedStyle
|
4557 |
+
getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
|
4558 |
+
// normalize float css property
|
4559 |
+
styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
|
4560 |
+
fcamelCase = function( all, letter ) {
|
4561 |
+
return letter.toUpperCase();
|
4562 |
+
};
|
4563 |
+
|
4564 |
+
jQuery.fn.css = function( name, value ) {
|
4565 |
+
return access( this, name, value, true, function( elem, name, value ) {
|
4566 |
+
if ( value === undefined ) {
|
4567 |
+
return jQuery.curCSS( elem, name );
|
4568 |
+
}
|
4569 |
+
|
4570 |
+
if ( typeof value === "number" && !rexclude.test(name) ) {
|
4571 |
+
value += "px";
|
4572 |
+
}
|
4573 |
+
|
4574 |
+
jQuery.style( elem, name, value );
|
4575 |
+
});
|
4576 |
+
};
|
4577 |
+
|
4578 |
+
jQuery.extend({
|
4579 |
+
style: function( elem, name, value ) {
|
4580 |
+
// don't set styles on text and comment nodes
|
4581 |
+
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
|
4582 |
+
return undefined;
|
4583 |
+
}
|
4584 |
+
|
4585 |
+
// ignore negative width and height values #1599
|
4586 |
+
if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
|
4587 |
+
value = undefined;
|
4588 |
+
}
|
4589 |
+
|
4590 |
+
var style = elem.style || elem, set = value !== undefined;
|
4591 |
+
|
4592 |
+
// IE uses filters for opacity
|
4593 |
+
if ( !jQuery.support.opacity && name === "opacity" ) {
|
4594 |
+
if ( set ) {
|
4595 |
+
// IE has trouble with opacity if it does not have layout
|
4596 |
+
// Force it by setting the zoom level
|
4597 |
+
style.zoom = 1;
|
4598 |
+
|
4599 |
+
// Set the alpha filter to set the opacity
|
4600 |
+
var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
|
4601 |
+
var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
|
4602 |
+
style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
|
4603 |
+
}
|
4604 |
+
|
4605 |
+
return style.filter && style.filter.indexOf("opacity=") >= 0 ?
|
4606 |
+
(parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
|
4607 |
+
"";
|
4608 |
+
}
|
4609 |
+
|
4610 |
+
// Make sure we're using the right name for getting the float value
|
4611 |
+
if ( rfloat.test( name ) ) {
|
4612 |
+
name = styleFloat;
|
4613 |
+
}
|
4614 |
+
|
4615 |
+
name = name.replace(rdashAlpha, fcamelCase);
|
4616 |
+
|
4617 |
+
if ( set ) {
|
4618 |
+
style[ name ] = value;
|
4619 |
+
}
|
4620 |
+
|
4621 |
+
return style[ name ];
|
4622 |
+
},
|
4623 |
+
|
4624 |
+
css: function( elem, name, force, extra ) {
|
4625 |
+
if ( name === "width" || name === "height" ) {
|
4626 |
+
var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
|
4627 |
+
|
4628 |
+
function getWH() {
|
4629 |
+
val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
|
4630 |
+
|
4631 |
+
if ( extra === "border" ) {
|
4632 |
+
return;
|
4633 |
+
}
|
4634 |
+
|
4635 |
+
jQuery.each( which, function() {
|
4636 |
+
if ( !extra ) {
|
4637 |
+
val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
|
4638 |
+
}
|
4639 |
+
|
4640 |
+
if ( extra === "margin" ) {
|
4641 |
+
val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
|
4642 |
+
} else {
|
4643 |
+
val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
|
4644 |
+
}
|
4645 |
+
});
|
4646 |
+
}
|
4647 |
+
|
4648 |
+
if ( elem.offsetWidth !== 0 ) {
|
4649 |
+
getWH();
|
4650 |
+
} else {
|
4651 |
+
jQuery.swap( elem, props, getWH );
|
4652 |
+
}
|
4653 |
+
|
4654 |
+
return Math.max(0, Math.round(val));
|
4655 |
+
}
|
4656 |
+
|
4657 |
+
return jQuery.curCSS( elem, name, force );
|
4658 |
+
},
|
4659 |
+
|
4660 |
+
curCSS: function( elem, name, force ) {
|
4661 |
+
var ret, style = elem.style, filter;
|
4662 |
+
|
4663 |
+
// IE uses filters for opacity
|
4664 |
+
if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
|
4665 |
+
ret = ropacity.test(elem.currentStyle.filter || "") ?
|
4666 |
+
(parseFloat(RegExp.$1) / 100) + "" :
|
4667 |
+
"";
|
4668 |
+
|
4669 |
+
return ret === "" ?
|
4670 |
+
"1" :
|
4671 |
+
ret;
|
4672 |
+
}
|
4673 |
+
|
4674 |
+
// Make sure we're using the right name for getting the float value
|
4675 |
+
if ( rfloat.test( name ) ) {
|
4676 |
+
name = styleFloat;
|
4677 |
+
}
|
4678 |
+
|
4679 |
+
if ( !force && style && style[ name ] ) {
|
4680 |
+
ret = style[ name ];
|
4681 |
+
|
4682 |
+
} else if ( getComputedStyle ) {
|
4683 |
+
|
4684 |
+
// Only "float" is needed here
|
4685 |
+
if ( rfloat.test( name ) ) {
|
4686 |
+
name = "float";
|
4687 |
+
}
|
4688 |
+
|
4689 |
+
name = name.replace( rupper, "-$1" ).toLowerCase();
|
4690 |
+
|
4691 |
+
var defaultView = elem.ownerDocument.defaultView;
|
4692 |
+
|
4693 |
+
if ( !defaultView ) {
|
4694 |
+
return null;
|
4695 |
+
}
|
4696 |
+
|
4697 |
+
var computedStyle = defaultView.getComputedStyle( elem, null );
|
4698 |
+
|
4699 |
+
if ( computedStyle ) {
|
4700 |
+
ret = computedStyle.getPropertyValue( name );
|
4701 |
+
}
|
4702 |
+
|
4703 |
+
// We should always get a number back from opacity
|
4704 |
+
if ( name === "opacity" && ret === "" ) {
|
4705 |
+
ret = "1";
|
4706 |
+
}
|
4707 |
+
|
4708 |
+
} else if ( elem.currentStyle ) {
|
4709 |
+
var camelCase = name.replace(rdashAlpha, fcamelCase);
|
4710 |
+
|
4711 |
+
ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
|
4712 |
+
|
4713 |
+
// From the awesome hack by Dean Edwards
|
4714 |
+
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
4715 |
+
|
4716 |
+
// If we're not dealing with a regular pixel number
|
4717 |
+
// but a number that has a weird ending, we need to convert it to pixels
|
4718 |
+
if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
|
4719 |
+
// Remember the original values
|
4720 |
+
var left = style.left, rsLeft = elem.runtimeStyle.left;
|
4721 |
+
|
4722 |
+
// Put in the new values to get a computed value out
|
4723 |
+
elem.runtimeStyle.left = elem.currentStyle.left;
|
4724 |
+
style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
|
4725 |
+
ret = style.pixelLeft + "px";
|
4726 |
+
|
4727 |
+
// Revert the changed values
|
4728 |
+
style.left = left;
|
4729 |
+
elem.runtimeStyle.left = rsLeft;
|
4730 |
+
}
|
4731 |
+
}
|
4732 |
+
|
4733 |
+
return ret;
|
4734 |
+
},
|
4735 |
+
|
4736 |
+
// A method for quickly swapping in/out CSS properties to get correct calculations
|
4737 |
+
swap: function( elem, options, callback ) {
|
4738 |
+
var old = {};
|
4739 |
+
|
4740 |
+
// Remember the old values, and insert the new ones
|
4741 |
+
for ( var name in options ) {
|
4742 |
+
old[ name ] = elem.style[ name ];
|
4743 |
+
elem.style[ name ] = options[ name ];
|
4744 |
+
}
|
4745 |
+
|
4746 |
+
callback.call( elem );
|
4747 |
+
|
4748 |
+
// Revert the old values
|
4749 |
+
for ( var name in options ) {
|
4750 |
+
elem.style[ name ] = old[ name ];
|
4751 |
+
}
|
4752 |
+
}
|
4753 |
+
});
|
4754 |
+
|
4755 |
+
if ( jQuery.expr && jQuery.expr.filters ) {
|
4756 |
+
jQuery.expr.filters.hidden = function( elem ) {
|
4757 |
+
var width = elem.offsetWidth, height = elem.offsetHeight,
|
4758 |
+
skip = elem.nodeName.toLowerCase() === "tr";
|
4759 |
+
|
4760 |
+
return width === 0 && height === 0 && !skip ?
|
4761 |
+
true :
|
4762 |
+
width > 0 && height > 0 && !skip ?
|
4763 |
+
false :
|
4764 |
+
jQuery.curCSS(elem, "display") === "none";
|
4765 |
+
};
|
4766 |
+
|
4767 |
+
jQuery.expr.filters.visible = function( elem ) {
|
4768 |
+
return !jQuery.expr.filters.hidden( elem );
|
4769 |
+
};
|
4770 |
+
}
|
4771 |
+
var jsc = now(),
|
4772 |
+
rscript = /<script(.|\s)*?\/script>/gi,
|
4773 |
+
rselectTextarea = /select|textarea/i,
|
4774 |
+
rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
|
4775 |
+
jsre = /=\?(&|$)/,
|
4776 |
+
rquery = /\?/,
|
4777 |
+
rts = /(\?|&)_=.*?(&|$)/,
|
4778 |
+
rurl = /^(\w+:)?\/\/([^\/?#]+)/,
|
4779 |
+
r20 = /%20/g,
|
4780 |
+
|
4781 |
+
// Keep a copy of the old load method
|
4782 |
+
_load = jQuery.fn.load;
|
4783 |
+
|
4784 |
+
jQuery.fn.extend({
|
4785 |
+
load: function( url, params, callback ) {
|
4786 |
+
if ( typeof url !== "string" ) {
|
4787 |
+
return _load.call( this, url );
|
4788 |
+
|
4789 |
+
// Don't do a request if no elements are being requested
|
4790 |
+
} else if ( !this.length ) {
|
4791 |
+
return this;
|
4792 |
+
}
|
4793 |
+
|
4794 |
+
var off = url.indexOf(" ");
|
4795 |
+
if ( off >= 0 ) {
|
4796 |
+
var selector = url.slice(off, url.length);
|
4797 |
+
url = url.slice(0, off);
|
4798 |
+
}
|
4799 |
+
|
4800 |
+
// Default to a GET request
|
4801 |
+
var type = "GET";
|
4802 |
+
|
4803 |
+
// If the second parameter was provided
|
4804 |
+
if ( params ) {
|
4805 |
+
// If it's a function
|
4806 |
+
if ( jQuery.isFunction( params ) ) {
|
4807 |
+
// We assume that it's the callback
|
4808 |
+
callback = params;
|
4809 |
+
params = null;
|
4810 |
+
|
4811 |
+
// Otherwise, build a param string
|
4812 |
+
} else if ( typeof params === "object" ) {
|
4813 |
+
params = jQuery.param( params, jQuery.ajaxSettings.traditional );
|
4814 |
+
type = "POST";
|
4815 |
+
}
|
4816 |
+
}
|
4817 |
+
|
4818 |
+
var self = this;
|
4819 |
+
|
4820 |
+
// Request the remote document
|
4821 |
+
jQuery.ajax({
|
4822 |
+
url: url,
|
4823 |
+
type: type,
|
4824 |
+
dataType: "html",
|
4825 |
+
data: params,
|
4826 |
+
complete: function( res, status ) {
|
4827 |
+
// If successful, inject the HTML into all the matched elements
|
4828 |
+
if ( status === "success" || status === "notmodified" ) {
|
4829 |
+
// See if a selector was specified
|
4830 |
+
self.html( selector ?
|
4831 |
+
// Create a dummy div to hold the results
|
4832 |
+
jQuery("<div />")
|
4833 |
+
// inject the contents of the document in, removing the scripts
|
4834 |
+
// to avoid any 'Permission Denied' errors in IE
|
4835 |
+
.append(res.responseText.replace(rscript, ""))
|
4836 |
+
|
4837 |
+
// Locate the specified elements
|
4838 |
+
.find(selector) :
|
4839 |
+
|
4840 |
+
// If not, just inject the full result
|
4841 |
+
res.responseText );
|
4842 |
+
}
|
4843 |
+
|
4844 |
+
if ( callback ) {
|
4845 |
+
self.each( callback, [res.responseText, status, res] );
|
4846 |
+
}
|
4847 |
+
}
|
4848 |
+
});
|
4849 |
+
|
4850 |
+
return this;
|
4851 |
+
},
|
4852 |
+
|
4853 |
+
serialize: function() {
|
4854 |
+
return jQuery.param(this.serializeArray());
|
4855 |
+
},
|
4856 |
+
serializeArray: function() {
|
4857 |
+
return this.map(function() {
|
4858 |
+
return this.elements ? jQuery.makeArray(this.elements) : this;
|
4859 |
+
})
|
4860 |
+
.filter(function() {
|
4861 |
+
return this.name && !this.disabled &&
|
4862 |
+
(this.checked || rselectTextarea.test(this.nodeName) ||
|
4863 |
+
rinput.test(this.type));
|
4864 |
+
})
|
4865 |
+
.map(function( i, elem ) {
|
4866 |
+
var val = jQuery(this).val();
|
4867 |
+
|
4868 |
+
return val == null ?
|
4869 |
+
null :
|
4870 |
+
jQuery.isArray(val) ?
|
4871 |
+
jQuery.map( val, function( val, i ) {
|
4872 |
+
return { name: elem.name, value: val };
|
4873 |
+
}) :
|
4874 |
+
{ name: elem.name, value: val };
|
4875 |
+
}).get();
|
4876 |
+
}
|
4877 |
+
});
|
4878 |
+
|
4879 |
+
// Attach a bunch of functions for handling common AJAX events
|
4880 |
+
jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
|
4881 |
+
jQuery.fn[o] = function( f ) {
|
4882 |
+
return this.bind(o, f);
|
4883 |
+
};
|
4884 |
+
});
|
4885 |
+
|
4886 |
+
jQuery.extend({
|
4887 |
+
|
4888 |
+
get: function( url, data, callback, type ) {
|
4889 |
+
// shift arguments if data argument was omited
|
4890 |
+
if ( jQuery.isFunction( data ) ) {
|
4891 |
+
type = type || callback;
|
4892 |
+
callback = data;
|
4893 |
+
data = null;
|
4894 |
+
}
|
4895 |
+
|
4896 |
+
return jQuery.ajax({
|
4897 |
+
type: "GET",
|
4898 |
+
url: url,
|
4899 |
+
data: data,
|
4900 |
+
success: callback,
|
4901 |
+
dataType: type
|
4902 |
+
});
|
4903 |
+
},
|
4904 |
+
|
4905 |
+
getScript: function( url, callback ) {
|
4906 |
+
return jQuery.get(url, null, callback, "script");
|
4907 |
+
},
|
4908 |
+
|
4909 |
+
getJSON: function( url, data, callback ) {
|
4910 |
+
return jQuery.get(url, data, callback, "json");
|
4911 |
+
},
|
4912 |
+
|
4913 |
+
post: function( url, data, callback, type ) {
|
4914 |
+
// shift arguments if data argument was omited
|
4915 |
+
if ( jQuery.isFunction( data ) ) {
|
4916 |
+
type = type || callback;
|
4917 |
+
callback = data;
|
4918 |
+
data = {};
|
4919 |
+
}
|
4920 |
+
|
4921 |
+
return jQuery.ajax({
|
4922 |
+
type: "POST",
|
4923 |
+
url: url,
|
4924 |
+
data: data,
|
4925 |
+
success: callback,
|
4926 |
+
dataType: type
|
4927 |
+
});
|
4928 |
+
},
|
4929 |
+
|
4930 |
+
ajaxSetup: function( settings ) {
|
4931 |
+
jQuery.extend( jQuery.ajaxSettings, settings );
|
4932 |
+
},
|
4933 |
+
|
4934 |
+
ajaxSettings: {
|
4935 |
+
url: location.href,
|
4936 |
+
global: true,
|
4937 |
+
type: "GET",
|
4938 |
+
contentType: "application/x-www-form-urlencoded",
|
4939 |
+
processData: true,
|
4940 |
+
async: true,
|
4941 |
+
/*
|
4942 |
+
timeout: 0,
|
4943 |
+
data: null,
|
4944 |
+
username: null,
|
4945 |
+
password: null,
|
4946 |
+
traditional: false,
|
4947 |
+
*/
|
4948 |
+
// Create the request object; Microsoft failed to properly
|
4949 |
+
// implement the XMLHttpRequest in IE7 (can't request local files),
|
4950 |
+
// so we use the ActiveXObject when it is available
|
4951 |
+
// This function can be overriden by calling jQuery.ajaxSetup
|
4952 |
+
xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
|
4953 |
+
function() {
|
4954 |
+
return new window.XMLHttpRequest();
|
4955 |
+
} :
|
4956 |
+
function() {
|
4957 |
+
try {
|
4958 |
+
return new window.ActiveXObject("Microsoft.XMLHTTP");
|
4959 |
+
} catch(e) {}
|
4960 |
+
},
|
4961 |
+
accepts: {
|
4962 |
+
xml: "application/xml, text/xml",
|
4963 |
+
html: "text/html",
|
4964 |
+
script: "text/javascript, application/javascript",
|
4965 |
+
json: "application/json, text/javascript",
|
4966 |
+
text: "text/plain",
|
4967 |
+
_default: "*/*"
|
4968 |
+
}
|
4969 |
+
},
|
4970 |
+
|
4971 |
+
// Last-Modified header cache for next request
|
4972 |
+
lastModified: {},
|
4973 |
+
etag: {},
|
4974 |
+
|
4975 |
+
ajax: function( origSettings ) {
|
4976 |
+
var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
|
4977 |
+
|
4978 |
+
var jsonp, status, data,
|
4979 |
+
callbackContext = origSettings && origSettings.context || s,
|
4980 |
+
type = s.type.toUpperCase();
|
4981 |
+
|
4982 |
+
// convert data if not already a string
|
4983 |
+
if ( s.data && s.processData && typeof s.data !== "string" ) {
|
4984 |
+
s.data = jQuery.param( s.data, s.traditional );
|
4985 |
+
}
|
4986 |
+
|
4987 |
+
// Handle JSONP Parameter Callbacks
|
4988 |
+
if ( s.dataType === "jsonp" ) {
|
4989 |
+
if ( type === "GET" ) {
|
4990 |
+
if ( !jsre.test( s.url ) ) {
|
4991 |
+
s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
|
4992 |
+
}
|
4993 |
+
} else if ( !s.data || !jsre.test(s.data) ) {
|
4994 |
+
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
|
4995 |
+
}
|
4996 |
+
s.dataType = "json";
|
4997 |
+
}
|
4998 |
+
|
4999 |
+
// Build temporary JSONP function
|
5000 |
+
if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
|
5001 |
+
jsonp = s.jsonpCallback || ("jsonp" + jsc++);
|
5002 |
+
|
5003 |
+
// Replace the =? sequence both in the query string and the data
|
5004 |
+
if ( s.data ) {
|
5005 |
+
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
|
5006 |
+
}
|
5007 |
+
|
5008 |
+
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
|
5009 |
+
|
5010 |
+
// We need to make sure
|
5011 |
+
// that a JSONP style response is executed properly
|
5012 |
+
s.dataType = "script";
|
5013 |
+
|
5014 |
+
// Handle JSONP-style loading
|
5015 |
+
window[ jsonp ] = window[ jsonp ] || function( tmp ) {
|
5016 |
+
data = tmp;
|
5017 |
+
success();
|
5018 |
+
complete();
|
5019 |
+
// Garbage collect
|
5020 |
+
window[ jsonp ] = undefined;
|
5021 |
+
|
5022 |
+
try {
|
5023 |
+
delete window[ jsonp ];
|
5024 |
+
} catch(e) {}
|
5025 |
+
|
5026 |
+
if ( head ) {
|
5027 |
+
head.removeChild( script );
|
5028 |
+
}
|
5029 |
+
};
|
5030 |
+
}
|
5031 |
+
|
5032 |
+
if ( s.dataType === "script" && s.cache === null ) {
|
5033 |
+
s.cache = false;
|
5034 |
+
}
|
5035 |
+
|
5036 |
+
if ( s.cache === false && type === "GET" ) {
|
5037 |
+
var ts = now();
|
5038 |
+
|
5039 |
+
// try replacing _= if it is there
|
5040 |
+
var ret = s.url.replace(rts, "$1_=" + ts + "$2");
|
5041 |
+
|
5042 |
+
// if nothing was replaced, add timestamp to the end
|
5043 |
+
s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
|
5044 |
+
}
|
5045 |
+
|
5046 |
+
// If data is available, append data to url for get requests
|
5047 |
+
if ( s.data && type === "GET" ) {
|
5048 |
+
s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
|
5049 |
+
}
|
5050 |
+
|
5051 |
+
// Watch for a new set of requests
|
5052 |
+
if ( s.global && ! jQuery.active++ ) {
|
5053 |
+
jQuery.event.trigger( "ajaxStart" );
|
5054 |
+
}
|
5055 |
+
|
5056 |
+
// Matches an absolute URL, and saves the domain
|
5057 |
+
var parts = rurl.exec( s.url ),
|
5058 |
+
remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
|
5059 |
+
|
5060 |
+
// If we're requesting a remote document
|
5061 |
+
// and trying to load JSON or Script with a GET
|
5062 |
+
if ( s.dataType === "script" && type === "GET" && remote ) {
|
5063 |
+
var head = document.getElementsByTagName("head")[0] || document.documentElement;
|
5064 |
+
var script = document.createElement("script");
|
5065 |
+
script.src = s.url;
|
5066 |
+
if ( s.scriptCharset ) {
|
5067 |
+
script.charset = s.scriptCharset;
|
5068 |
+
}
|
5069 |
+
|
5070 |
+
// Handle Script loading
|
5071 |
+
if ( !jsonp ) {
|
5072 |
+
var done = false;
|
5073 |
+
|
5074 |
+
// Attach handlers for all browsers
|
5075 |
+
script.onload = script.onreadystatechange = function() {
|
5076 |
+
if ( !done && (!this.readyState ||
|
5077 |
+
this.readyState === "loaded" || this.readyState === "complete") ) {
|
5078 |
+
done = true;
|
5079 |
+
success();
|
5080 |
+
complete();
|
5081 |
+
|
5082 |
+
// Handle memory leak in IE
|
5083 |
+
script.onload = script.onreadystatechange = null;
|
5084 |
+
if ( head && script.parentNode ) {
|
5085 |
+
head.removeChild( script );
|
5086 |
+
}
|
5087 |
+
}
|
5088 |
+
};
|
5089 |
+
}
|
5090 |
+
|
5091 |
+
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
|
5092 |
+
// This arises when a base node is used (#2709 and #4378).
|
5093 |
+
head.insertBefore( script, head.firstChild );
|
5094 |
+
|
5095 |
+
// We handle everything using the script element injection
|
5096 |
+
return undefined;
|
5097 |
+
}
|
5098 |
+
|
5099 |
+
var requestDone = false;
|
5100 |
+
|
5101 |
+
// Create the request object
|
5102 |
+
var xhr = s.xhr();
|
5103 |
+
|
5104 |
+
if ( !xhr ) {
|
5105 |
+
return;
|
5106 |
+
}
|
5107 |
+
|
5108 |
+
// Open the socket
|
5109 |
+
// Passing null username, generates a login popup on Opera (#2865)
|
5110 |
+
if ( s.username ) {
|
5111 |
+
xhr.open(type, s.url, s.async, s.username, s.password);
|
5112 |
+
} else {
|
5113 |
+
xhr.open(type, s.url, s.async);
|
5114 |
+
}
|
5115 |
+
|
5116 |
+
// Need an extra try/catch for cross domain requests in Firefox 3
|
5117 |
+
try {
|
5118 |
+
// Set the correct header, if data is being sent
|
5119 |
+
if ( s.data || origSettings && origSettings.contentType ) {
|
5120 |
+
xhr.setRequestHeader("Content-Type", s.contentType);
|
5121 |
+
}
|
5122 |
+
|
5123 |
+
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
5124 |
+
if ( s.ifModified ) {
|
5125 |
+
if ( jQuery.lastModified[s.url] ) {
|
5126 |
+
xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
|
5127 |
+
}
|
5128 |
+
|
5129 |
+
if ( jQuery.etag[s.url] ) {
|
5130 |
+
xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
|
5131 |
+
}
|
5132 |
+
}
|
5133 |
+
|
5134 |
+
// Set header so the called script knows that it's an XMLHttpRequest
|
5135 |
+
// Only send the header if it's not a remote XHR
|
5136 |
+
if ( !remote ) {
|
5137 |
+
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
5138 |
+
}
|
5139 |
+
|
5140 |
+
// Set the Accepts header for the server, depending on the dataType
|
5141 |
+
xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
|
5142 |
+
s.accepts[ s.dataType ] + ", */*" :
|
5143 |
+
s.accepts._default );
|
5144 |
+
} catch(e) {}
|
5145 |
+
|
5146 |
+
// Allow custom headers/mimetypes and early abort
|
5147 |
+
if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
|
5148 |
+
// Handle the global AJAX counter
|
5149 |
+
if ( s.global && ! --jQuery.active ) {
|
5150 |
+
jQuery.event.trigger( "ajaxStop" );
|
5151 |
+
}
|
5152 |
+
|
5153 |
+
// close opended socket
|
5154 |
+
xhr.abort();
|
5155 |
+
return false;
|
5156 |
+
}
|
5157 |
+
|
5158 |
+
if ( s.global ) {
|
5159 |
+
trigger("ajaxSend", [xhr, s]);
|
5160 |
+
}
|
5161 |
+
|
5162 |
+
// Wait for a response to come back
|
5163 |
+
var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
|
5164 |
+
// The request was aborted
|
5165 |
+
if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
|
5166 |
+
// Opera doesn't call onreadystatechange before this point
|
5167 |
+
// so we simulate the call
|
5168 |
+
if ( !requestDone ) {
|
5169 |
+
complete();
|
5170 |
+
}
|
5171 |
+
|
5172 |
+
requestDone = true;
|
5173 |
+
if ( xhr ) {
|
5174 |
+
xhr.onreadystatechange = jQuery.noop;
|
5175 |
+
}
|
5176 |
+
|
5177 |
+
// The transfer is complete and the data is available, or the request timed out
|
5178 |
+
} else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
|
5179 |
+
requestDone = true;
|
5180 |
+
xhr.onreadystatechange = jQuery.noop;
|
5181 |
+
|
5182 |
+
status = isTimeout === "timeout" ?
|
5183 |
+
"timeout" :
|
5184 |
+
!jQuery.httpSuccess( xhr ) ?
|
5185 |
+
"error" :
|
5186 |
+
s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
|
5187 |
+
"notmodified" :
|
5188 |
+
"success";
|
5189 |
+
|
5190 |
+
var errMsg;
|
5191 |
+
|
5192 |
+
if ( status === "success" ) {
|
5193 |
+
// Watch for, and catch, XML document parse errors
|
5194 |
+
try {
|
5195 |
+
// process the data (runs the xml through httpData regardless of callback)
|
5196 |
+
data = jQuery.httpData( xhr, s.dataType, s );
|
5197 |
+
} catch(err) {
|
5198 |
+
status = "parsererror";
|
5199 |
+
errMsg = err;
|
5200 |
+
}
|
5201 |
+
}
|
5202 |
+
|
5203 |
+
// Make sure that the request was successful or notmodified
|
5204 |
+
if ( status === "success" || status === "notmodified" ) {
|
5205 |
+
// JSONP handles its own success callback
|
5206 |
+
if ( !jsonp ) {
|
5207 |
+
success();
|
5208 |
+
}
|
5209 |
+
} else {
|
5210 |
+
jQuery.handleError(s, xhr, status, errMsg);
|
5211 |
+
}
|
5212 |
+
|
5213 |
+
// Fire the complete handlers
|
5214 |
+
complete();
|
5215 |
+
|
5216 |
+
if ( isTimeout === "timeout" ) {
|
5217 |
+
xhr.abort();
|
5218 |
+
}
|
5219 |
+
|
5220 |
+
// Stop memory leaks
|
5221 |
+
if ( s.async ) {
|
5222 |
+
xhr = null;
|
5223 |
+
}
|
5224 |
+
}
|
5225 |
+
};
|
5226 |
+
|
5227 |
+
// Override the abort handler, if we can (IE doesn't allow it, but that's OK)
|
5228 |
+
// Opera doesn't fire onreadystatechange at all on abort
|
5229 |
+
try {
|
5230 |
+
var oldAbort = xhr.abort;
|
5231 |
+
xhr.abort = function() {
|
5232 |
+
if ( xhr ) {
|
5233 |
+
oldAbort.call( xhr );
|
5234 |
+
}
|
5235 |
+
|
5236 |
+
onreadystatechange( "abort" );
|
5237 |
+
};
|
5238 |
+
} catch(e) { }
|
5239 |
+
|
5240 |
+
// Timeout checker
|
5241 |
+
if ( s.async && s.timeout > 0 ) {
|
5242 |
+
setTimeout(function() {
|
5243 |
+
// Check to see if the request is still happening
|
5244 |
+
if ( xhr && !requestDone ) {
|
5245 |
+
onreadystatechange( "timeout" );
|
5246 |
+
}
|
5247 |
+
}, s.timeout);
|
5248 |
+
}
|
5249 |
+
|
5250 |
+
// Send the data
|
5251 |
+
try {
|
5252 |
+
xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
|
5253 |
+
} catch(e) {
|
5254 |
+
jQuery.handleError(s, xhr, null, e);
|
5255 |
+
// Fire the complete handlers
|
5256 |
+
complete();
|
5257 |
+
}
|
5258 |
+
|
5259 |
+
// firefox 1.5 doesn't fire statechange for sync requests
|
5260 |
+
if ( !s.async ) {
|
5261 |
+
onreadystatechange();
|
5262 |
+
}
|
5263 |
+
|
5264 |
+
function success() {
|
5265 |
+
// If a local callback was specified, fire it and pass it the data
|
5266 |
+
if ( s.success ) {
|
5267 |
+
s.success.call( callbackContext, data, status, xhr );
|
5268 |
+
}
|
5269 |
+
|
5270 |
+
// Fire the global callback
|
5271 |
+
if ( s.global ) {
|
5272 |
+
trigger( "ajaxSuccess", [xhr, s] );
|
5273 |
+
}
|
5274 |
+
}
|
5275 |
+
|
5276 |
+
function complete() {
|
5277 |
+
// Process result
|
5278 |
+
if ( s.complete ) {
|
5279 |
+
s.complete.call( callbackContext, xhr, status);
|
5280 |
+
}
|
5281 |
+
|
5282 |
+
// The request was completed
|
5283 |
+
if ( s.global ) {
|
5284 |
+
trigger( "ajaxComplete", [xhr, s] );
|
5285 |
+
}
|
5286 |
+
|
5287 |
+
// Handle the global AJAX counter
|
5288 |
+
if ( s.global && ! --jQuery.active ) {
|
5289 |
+
jQuery.event.trigger( "ajaxStop" );
|
5290 |
+
}
|
5291 |
+
}
|
5292 |
+
|
5293 |
+
function trigger(type, args) {
|
5294 |
+
(s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
|
5295 |
+
}
|
5296 |
+
|
5297 |
+
// return XMLHttpRequest to allow aborting the request etc.
|
5298 |
+
return xhr;
|
5299 |
+
},
|
5300 |
+
|
5301 |
+
handleError: function( s, xhr, status, e ) {
|
5302 |
+
// If a local callback was specified, fire it
|
5303 |
+
if ( s.error ) {
|
5304 |
+
s.error.call( s.context || s, xhr, status, e );
|
5305 |
+
}
|
5306 |
+
|
5307 |
+
// Fire the global callback
|
5308 |
+
if ( s.global ) {
|
5309 |
+
(s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
|
5310 |
+
}
|
5311 |
+
},
|
5312 |
+
|
5313 |
+
// Counter for holding the number of active queries
|
5314 |
+
active: 0,
|
5315 |
+
|
5316 |
+
// Determines if an XMLHttpRequest was successful or not
|
5317 |
+
httpSuccess: function( xhr ) {
|
5318 |
+
try {
|
5319 |
+
// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
|
5320 |
+
return !xhr.status && location.protocol === "file:" ||
|
5321 |
+
// Opera returns 0 when status is 304
|
5322 |
+
( xhr.status >= 200 && xhr.status < 300 ) ||
|
5323 |
+
xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
|
5324 |
+
} catch(e) {}
|
5325 |
+
|
5326 |
+
return false;
|
5327 |
+
},
|
5328 |
+
|
5329 |
+
// Determines if an XMLHttpRequest returns NotModified
|
5330 |
+
httpNotModified: function( xhr, url ) {
|
5331 |
+
var lastModified = xhr.getResponseHeader("Last-Modified"),
|
5332 |
+
etag = xhr.getResponseHeader("Etag");
|
5333 |
+
|
5334 |
+
if ( lastModified ) {
|
5335 |
+
jQuery.lastModified[url] = lastModified;
|
5336 |
+
}
|
5337 |
+
|
5338 |
+
if ( etag ) {
|
5339 |
+
jQuery.etag[url] = etag;
|
5340 |
+
}
|
5341 |
+
|
5342 |
+
// Opera returns 0 when status is 304
|
5343 |
+
return xhr.status === 304 || xhr.status === 0;
|
5344 |
+
},
|
5345 |
+
|
5346 |
+
httpData: function( xhr, type, s ) {
|
5347 |
+
var ct = xhr.getResponseHeader("content-type") || "",
|
5348 |
+
xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
|
5349 |
+
data = xml ? xhr.responseXML : xhr.responseText;
|
5350 |
+
|
5351 |
+
if ( xml && data.documentElement.nodeName === "parsererror" ) {
|
5352 |
+
jQuery.error( "parsererror" );
|
5353 |
+
}
|
5354 |
+
|
5355 |
+
// Allow a pre-filtering function to sanitize the response
|
5356 |
+
// s is checked to keep backwards compatibility
|
5357 |
+
if ( s && s.dataFilter ) {
|
5358 |
+
data = s.dataFilter( data, type );
|
5359 |
+
}
|
5360 |
+
|
5361 |
+
// The filter can actually parse the response
|
5362 |
+
if ( typeof data === "string" ) {
|
5363 |
+
// Get the JavaScript object, if JSON is used.
|
5364 |
+
if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
|
5365 |
+
data = jQuery.parseJSON( data );
|
5366 |
+
|
5367 |
+
// If the type is "script", eval it in global context
|
5368 |
+
} else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
|
5369 |
+
jQuery.globalEval( data );
|
5370 |
+
}
|
5371 |
+
}
|
5372 |
+
|
5373 |
+
return data;
|
5374 |
+
},
|
5375 |
+
|
5376 |
+
// Serialize an array of form elements or a set of
|
5377 |
+
// key/values into a query string
|
5378 |
+
param: function( a, traditional ) {
|
5379 |
+
var s = [];
|
5380 |
+
|
5381 |
+
// Set traditional to true for jQuery <= 1.3.2 behavior.
|
5382 |
+
if ( traditional === undefined ) {
|
5383 |
+
traditional = jQuery.ajaxSettings.traditional;
|
5384 |
+
}
|
5385 |
+
|
5386 |
+
// If an array was passed in, assume that it is an array of form elements.
|
5387 |
+
if ( jQuery.isArray(a) || a.jquery ) {
|
5388 |
+
// Serialize the form elements
|
5389 |
+
jQuery.each( a, function() {
|
5390 |
+
add( this.name, this.value );
|
5391 |
+
});
|
5392 |
+
|
5393 |
+
} else {
|
5394 |
+
// If traditional, encode the "old" way (the way 1.3.2 or older
|
5395 |
+
// did it), otherwise encode params recursively.
|
5396 |
+
for ( var prefix in a ) {
|
5397 |
+
buildParams( prefix, a[prefix] );
|
5398 |
+
}
|
5399 |
+
}
|
5400 |
+
|
5401 |
+
// Return the resulting serialization
|
5402 |
+
return s.join("&").replace(r20, "+");
|
5403 |
+
|
5404 |
+
function buildParams( prefix, obj ) {
|
5405 |
+
if ( jQuery.isArray(obj) ) {
|
5406 |
+
// Serialize array item.
|
5407 |
+
jQuery.each( obj, function( i, v ) {
|
5408 |
+
if ( traditional || /\[\]$/.test( prefix ) ) {
|
5409 |
+
// Treat each array item as a scalar.
|
5410 |
+
add( prefix, v );
|
5411 |
+
} else {
|
5412 |
+
// If array item is non-scalar (array or object), encode its
|
5413 |
+
// numeric index to resolve deserialization ambiguity issues.
|
5414 |
+
// Note that rack (as of 1.0.0) can't currently deserialize
|
5415 |
+
// nested arrays properly, and attempting to do so may cause
|
5416 |
+
// a server error. Possible fixes are to modify rack's
|
5417 |
+
// deserialization algorithm or to provide an option or flag
|
5418 |
+
// to force array serialization to be shallow.
|
5419 |
+
buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
|
5420 |
+
}
|
5421 |
+
});
|
5422 |
+
|
5423 |
+
} else if ( !traditional && obj != null && typeof obj === "object" ) {
|
5424 |
+
// Serialize object item.
|
5425 |
+
jQuery.each( obj, function( k, v ) {
|
5426 |
+
buildParams( prefix + "[" + k + "]", v );
|
5427 |
+
});
|
5428 |
+
|
5429 |
+
} else {
|
5430 |
+
// Serialize scalar item.
|
5431 |
+
add( prefix, obj );
|
5432 |
+
}
|
5433 |
+
}
|
5434 |
+
|
5435 |
+
function add( key, value ) {
|
5436 |
+
// If value is a function, invoke it and return its value
|
5437 |
+
value = jQuery.isFunction(value) ? value() : value;
|
5438 |
+
s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
|
5439 |
+
}
|
5440 |
+
}
|
5441 |
+
});
|
5442 |
+
var elemdisplay = {},
|
5443 |
+
rfxtypes = /toggle|show|hide/,
|
5444 |
+
rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
|
5445 |
+
timerId,
|
5446 |
+
fxAttrs = [
|
5447 |
+
// height animations
|
5448 |
+
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
|
5449 |
+
// width animations
|
5450 |
+
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
|
5451 |
+
// opacity animations
|
5452 |
+
[ "opacity" ]
|
5453 |
+
];
|
5454 |
+
|
5455 |
+
jQuery.fn.extend({
|
5456 |
+
show: function( speed, callback ) {
|
5457 |
+
if ( speed || speed === 0) {
|
5458 |
+
return this.animate( genFx("show", 3), speed, callback);
|
5459 |
+
|
5460 |
+
} else {
|
5461 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
5462 |
+
var old = jQuery.data(this[i], "olddisplay");
|
5463 |
+
|
5464 |
+
this[i].style.display = old || "";
|
5465 |
+
|
5466 |
+
if ( jQuery.css(this[i], "display") === "none" ) {
|
5467 |
+
var nodeName = this[i].nodeName, display;
|
5468 |
+
|
5469 |
+
if ( elemdisplay[ nodeName ] ) {
|
5470 |
+
display = elemdisplay[ nodeName ];
|
5471 |
+
|
5472 |
+
} else {
|
5473 |
+
var elem = jQuery("<" + nodeName + " />").appendTo("body");
|
5474 |
+
|
5475 |
+
display = elem.css("display");
|
5476 |
+
|
5477 |
+
if ( display === "none" ) {
|
5478 |
+
display = "block";
|
5479 |
+
}
|
5480 |
+
|
5481 |
+
elem.remove();
|
5482 |
+
|
5483 |
+
elemdisplay[ nodeName ] = display;
|
5484 |
+
}
|
5485 |
+
|
5486 |
+
jQuery.data(this[i], "olddisplay", display);
|
5487 |
+
}
|
5488 |
+
}
|
5489 |
+
|
5490 |
+
// Set the display of the elements in a second loop
|
5491 |
+
// to avoid the constant reflow
|
5492 |
+
for ( var j = 0, k = this.length; j < k; j++ ) {
|
5493 |
+
this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
|
5494 |
+
}
|
5495 |
+
|
5496 |
+
return this;
|
5497 |
+
}
|
5498 |
+
},
|
5499 |
+
|
5500 |
+
hide: function( speed, callback ) {
|
5501 |
+
if ( speed || speed === 0 ) {
|
5502 |
+
return this.animate( genFx("hide", 3), speed, callback);
|
5503 |
+
|
5504 |
+
} else {
|
5505 |
+
for ( var i = 0, l = this.length; i < l; i++ ) {
|
5506 |
+
var old = jQuery.data(this[i], "olddisplay");
|
5507 |
+
if ( !old && old !== "none" ) {
|
5508 |
+
jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
|
5509 |
+
}
|
5510 |
+
}
|
5511 |
+
|
5512 |
+
// Set the display of the elements in a second loop
|
5513 |
+
// to avoid the constant reflow
|
5514 |
+
for ( var j = 0, k = this.length; j < k; j++ ) {
|
5515 |
+
this[j].style.display = "none";
|
5516 |
+
}
|
5517 |
+
|
5518 |
+
return this;
|
5519 |
+
}
|
5520 |
+
},
|
5521 |
+
|
5522 |
+
// Save the old toggle function
|
5523 |
+
_toggle: jQuery.fn.toggle,
|
5524 |
+
|
5525 |
+
toggle: function( fn, fn2 ) {
|
5526 |
+
var bool = typeof fn === "boolean";
|
5527 |
+
|
5528 |
+
if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
|
5529 |
+
this._toggle.apply( this, arguments );
|
5530 |
+
|
5531 |
+
} else if ( fn == null || bool ) {
|
5532 |
+
this.each(function() {
|
5533 |
+
var state = bool ? fn : jQuery(this).is(":hidden");
|
5534 |
+
jQuery(this)[ state ? "show" : "hide" ]();
|
5535 |
+
});
|
5536 |
+
|
5537 |
+
} else {
|
5538 |
+
this.animate(genFx("toggle", 3), fn, fn2);
|
5539 |
+
}
|
5540 |
+
|
5541 |
+
return this;
|
5542 |
+
},
|
5543 |
+
|
5544 |
+
fadeTo: function( speed, to, callback ) {
|
5545 |
+
return this.filter(":hidden").css("opacity", 0).show().end()
|
5546 |
+
.animate({opacity: to}, speed, callback);
|
5547 |
+
},
|
5548 |
+
|
5549 |
+
animate: function( prop, speed, easing, callback ) {
|
5550 |
+
var optall = jQuery.speed(speed, easing, callback);
|
5551 |
+
|
5552 |
+
if ( jQuery.isEmptyObject( prop ) ) {
|
5553 |
+
return this.each( optall.complete );
|
5554 |
+
}
|
5555 |
+
|
5556 |
+
return this[ optall.queue === false ? "each" : "queue" ](function() {
|
5557 |
+
var opt = jQuery.extend({}, optall), p,
|
5558 |
+
hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
|
5559 |
+
self = this;
|
5560 |
+
|
5561 |
+
for ( p in prop ) {
|
5562 |
+
var name = p.replace(rdashAlpha, fcamelCase);
|
5563 |
+
|
5564 |
+
if ( p !== name ) {
|
5565 |
+
prop[ name ] = prop[ p ];
|
5566 |
+
delete prop[ p ];
|
5567 |
+
p = name;
|
5568 |
+
}
|
5569 |
+
|
5570 |
+
if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
|
5571 |
+
return opt.complete.call(this);
|
5572 |
+
}
|
5573 |
+
|
5574 |
+
if ( ( p === "height" || p === "width" ) && this.style ) {
|
5575 |
+
// Store display property
|
5576 |
+
opt.display = jQuery.css(this, "display");
|
5577 |
+
|
5578 |
+
// Make sure that nothing sneaks out
|
5579 |
+
opt.overflow = this.style.overflow;
|
5580 |
+
}
|
5581 |
+
|
5582 |
+
if ( jQuery.isArray( prop[p] ) ) {
|
5583 |
+
// Create (if needed) and add to specialEasing
|
5584 |
+
(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
|
5585 |
+
prop[p] = prop[p][0];
|
5586 |
+
}
|
5587 |
+
}
|
5588 |
+
|
5589 |
+
if ( opt.overflow != null ) {
|
5590 |
+
this.style.overflow = "hidden";
|
5591 |
+
}
|
5592 |
+
|
5593 |
+
opt.curAnim = jQuery.extend({}, prop);
|
5594 |
+
|
5595 |
+
jQuery.each( prop, function( name, val ) {
|
5596 |
+
var e = new jQuery.fx( self, opt, name );
|
5597 |
+
|
5598 |
+
if ( rfxtypes.test(val) ) {
|
5599 |
+
e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
|
5600 |
+
|
5601 |
+
} else {
|
5602 |
+
var parts = rfxnum.exec(val),
|
5603 |
+
start = e.cur(true) || 0;
|
5604 |
+
|
5605 |
+
if ( parts ) {
|
5606 |
+
var end = parseFloat( parts[2] ),
|
5607 |
+
unit = parts[3] || "px";
|
5608 |
+
|
5609 |
+
// We need to compute starting value
|
5610 |
+
if ( unit !== "px" ) {
|
5611 |
+
self.style[ name ] = (end || 1) + unit;
|
5612 |
+
start = ((end || 1) / e.cur(true)) * start;
|
5613 |
+
self.style[ name ] = start + unit;
|
5614 |
+
}
|
5615 |
+
|
5616 |
+
// If a +=/-= token was provided, we're doing a relative animation
|
5617 |
+
if ( parts[1] ) {
|
5618 |
+
end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
|
5619 |
+
}
|
5620 |
+
|
5621 |
+
e.custom( start, end, unit );
|
5622 |
+
|
5623 |
+
} else {
|
5624 |
+
e.custom( start, val, "" );
|
5625 |
+
}
|
5626 |
+
}
|
5627 |
+
});
|
5628 |
+
|
5629 |
+
// For JS strict compliance
|
5630 |
+
return true;
|
5631 |
+
});
|
5632 |
+
},
|
5633 |
+
|
5634 |
+
stop: function( clearQueue, gotoEnd ) {
|
5635 |
+
var timers = jQuery.timers;
|
5636 |
+
|
5637 |
+
if ( clearQueue ) {
|
5638 |
+
this.queue([]);
|
5639 |
+
}
|
5640 |
+
|
5641 |
+
this.each(function() {
|
5642 |
+
// go in reverse order so anything added to the queue during the loop is ignored
|
5643 |
+
for ( var i = timers.length - 1; i >= 0; i-- ) {
|
5644 |
+
if ( timers[i].elem === this ) {
|
5645 |
+
if (gotoEnd) {
|
5646 |
+
// force the next step to be the last
|
5647 |
+
timers[i](true);
|
5648 |
+
}
|
5649 |
+
|
5650 |
+
timers.splice(i, 1);
|
5651 |
+
}
|
5652 |
+
}
|
5653 |
+
});
|
5654 |
+
|
5655 |
+
// start the next in the queue if the last step wasn't forced
|
5656 |
+
if ( !gotoEnd ) {
|
5657 |
+
this.dequeue();
|
5658 |
+
}
|
5659 |
+
|
5660 |
+
return this;
|
5661 |
+
}
|
5662 |
+
|
5663 |
+
});
|
5664 |
+
|
5665 |
+
// Generate shortcuts for custom animations
|
5666 |
+
jQuery.each({
|
5667 |
+
slideDown: genFx("show", 1),
|
5668 |
+
slideUp: genFx("hide", 1),
|
5669 |
+
slideToggle: genFx("toggle", 1),
|
5670 |
+
fadeIn: { opacity: "show" },
|
5671 |
+
fadeOut: { opacity: "hide" }
|
5672 |
+
}, function( name, props ) {
|
5673 |
+
jQuery.fn[ name ] = function( speed, callback ) {
|
5674 |
+
return this.animate( props, speed, callback );
|
5675 |
+
};
|
5676 |
+
});
|
5677 |
+
|
5678 |
+
jQuery.extend({
|
5679 |
+
speed: function( speed, easing, fn ) {
|
5680 |
+
var opt = speed && typeof speed === "object" ? speed : {
|
5681 |
+
complete: fn || !fn && easing ||
|
5682 |
+
jQuery.isFunction( speed ) && speed,
|
5683 |
+
duration: speed,
|
5684 |
+
easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
|
5685 |
+
};
|
5686 |
+
|
5687 |
+
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
|
5688 |
+
jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
|
5689 |
+
|
5690 |
+
// Queueing
|
5691 |
+
opt.old = opt.complete;
|
5692 |
+
opt.complete = function() {
|
5693 |
+
if ( opt.queue !== false ) {
|
5694 |
+
jQuery(this).dequeue();
|
5695 |
+
}
|
5696 |
+
if ( jQuery.isFunction( opt.old ) ) {
|
5697 |
+
opt.old.call( this );
|
5698 |
+
}
|
5699 |
+
};
|
5700 |
+
|
5701 |
+
return opt;
|
5702 |
+
},
|
5703 |
+
|
5704 |
+
easing: {
|
5705 |
+
linear: function( p, n, firstNum, diff ) {
|
5706 |
+
return firstNum + diff * p;
|
5707 |
+
},
|
5708 |
+
swing: function( p, n, firstNum, diff ) {
|
5709 |
+
return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
|
5710 |
+
}
|
5711 |
+
},
|
5712 |
+
|
5713 |
+
timers: [],
|
5714 |
+
|
5715 |
+
fx: function( elem, options, prop ) {
|
5716 |
+
this.options = options;
|
5717 |
+
this.elem = elem;
|
5718 |
+
this.prop = prop;
|
5719 |
+
|
5720 |
+
if ( !options.orig ) {
|
5721 |
+
options.orig = {};
|
5722 |
+
}
|
5723 |
+
}
|
5724 |
+
|
5725 |
+
});
|
5726 |
+
|
5727 |
+
jQuery.fx.prototype = {
|
5728 |
+
// Simple function for setting a style value
|
5729 |
+
update: function() {
|
5730 |
+
if ( this.options.step ) {
|
5731 |
+
this.options.step.call( this.elem, this.now, this );
|
5732 |
+
}
|
5733 |
+
|
5734 |
+
(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
|
5735 |
+
|
5736 |
+
// Set display property to block for height/width animations
|
5737 |
+
if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
|
5738 |
+
this.elem.style.display = "block";
|
5739 |
+
}
|
5740 |
+
},
|
5741 |
+
|
5742 |
+
// Get the current size
|
5743 |
+
cur: function( force ) {
|
5744 |
+
if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
|
5745 |
+
return this.elem[ this.prop ];
|
5746 |
+
}
|
5747 |
+
|
5748 |
+
var r = parseFloat(jQuery.css(this.elem, this.prop, force));
|
5749 |
+
return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
|
5750 |
+
},
|
5751 |
+
|
5752 |
+
// Start an animation from one number to another
|
5753 |
+
custom: function( from, to, unit ) {
|
5754 |
+
this.startTime = now();
|
5755 |
+
this.start = from;
|
5756 |
+
this.end = to;
|
5757 |
+
this.unit = unit || this.unit || "px";
|
5758 |
+
this.now = this.start;
|
5759 |
+
this.pos = this.state = 0;
|
5760 |
+
|
5761 |
+
var self = this;
|
5762 |
+
function t( gotoEnd ) {
|
5763 |
+
return self.step(gotoEnd);
|
5764 |
+
}
|
5765 |
+
|
5766 |
+
t.elem = this.elem;
|
5767 |
+
|
5768 |
+
if ( t() && jQuery.timers.push(t) && !timerId ) {
|
5769 |
+
timerId = setInterval(jQuery.fx.tick, 13);
|
5770 |
+
}
|
5771 |
+
},
|
5772 |
+
|
5773 |
+
// Simple 'show' function
|
5774 |
+
show: function() {
|
5775 |
+
// Remember where we started, so that we can go back to it later
|
5776 |
+
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
|
5777 |
+
this.options.show = true;
|
5778 |
+
|
5779 |
+
// Begin the animation
|
5780 |
+
// Make sure that we start at a small width/height to avoid any
|
5781 |
+
// flash of content
|
5782 |
+
this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
|
5783 |
+
|
5784 |
+
// Start by showing the element
|
5785 |
+
jQuery( this.elem ).show();
|
5786 |
+
},
|
5787 |
+
|
5788 |
+
// Simple 'hide' function
|
5789 |
+
hide: function() {
|
5790 |
+
// Remember where we started, so that we can go back to it later
|
5791 |
+
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
|
5792 |
+
this.options.hide = true;
|
5793 |
+
|
5794 |
+
// Begin the animation
|
5795 |
+
this.custom(this.cur(), 0);
|
5796 |
+
},
|
5797 |
+
|
5798 |
+
// Each step of an animation
|
5799 |
+
step: function( gotoEnd ) {
|
5800 |
+
var t = now(), done = true;
|
5801 |
+
|
5802 |
+
if ( gotoEnd || t >= this.options.duration + this.startTime ) {
|
5803 |
+
this.now = this.end;
|
5804 |
+
this.pos = this.state = 1;
|
5805 |
+
this.update();
|
5806 |
+
|
5807 |
+
this.options.curAnim[ this.prop ] = true;
|
5808 |
+
|
5809 |
+
for ( var i in this.options.curAnim ) {
|
5810 |
+
if ( this.options.curAnim[i] !== true ) {
|
5811 |
+
done = false;
|
5812 |
+
}
|
5813 |
+
}
|
5814 |
+
|
5815 |
+
if ( done ) {
|
5816 |
+
if ( this.options.display != null ) {
|
5817 |
+
// Reset the overflow
|
5818 |
+
this.elem.style.overflow = this.options.overflow;
|
5819 |
+
|
5820 |
+
// Reset the display
|
5821 |
+
var old = jQuery.data(this.elem, "olddisplay");
|
5822 |
+
this.elem.style.display = old ? old : this.options.display;
|
5823 |
+
|
5824 |
+
if ( jQuery.css(this.elem, "display") === "none" ) {
|
5825 |
+
this.elem.style.display = "block";
|
5826 |
+
}
|
5827 |
+
}
|
5828 |
+
|
5829 |
+
// Hide the element if the "hide" operation was done
|
5830 |
+
if ( this.options.hide ) {
|
5831 |
+
jQuery(this.elem).hide();
|
5832 |
+
}
|
5833 |
+
|
5834 |
+
// Reset the properties, if the item has been hidden or shown
|
5835 |
+
if ( this.options.hide || this.options.show ) {
|
5836 |
+
for ( var p in this.options.curAnim ) {
|
5837 |
+
jQuery.style(this.elem, p, this.options.orig[p]);
|
5838 |
+
}
|
5839 |
+
}
|
5840 |
+
|
5841 |
+
// Execute the complete function
|
5842 |
+
this.options.complete.call( this.elem );
|
5843 |
+
}
|
5844 |
+
|
5845 |
+
return false;
|
5846 |
+
|
5847 |
+
} else {
|
5848 |
+
var n = t - this.startTime;
|
5849 |
+
this.state = n / this.options.duration;
|
5850 |
+
|
5851 |
+
// Perform the easing function, defaults to swing
|
5852 |
+
var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
|
5853 |
+
var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
|
5854 |
+
this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
|
5855 |
+
this.now = this.start + ((this.end - this.start) * this.pos);
|
5856 |
+
|
5857 |
+
// Perform the next step of the animation
|
5858 |
+
this.update();
|
5859 |
+
}
|
5860 |
+
|
5861 |
+
return true;
|
5862 |
+
}
|
5863 |
+
};
|
5864 |
+
|
5865 |
+
jQuery.extend( jQuery.fx, {
|
5866 |
+
tick: function() {
|
5867 |
+
var timers = jQuery.timers;
|
5868 |
+
|
5869 |
+
for ( var i = 0; i < timers.length; i++ ) {
|
5870 |
+
if ( !timers[i]() ) {
|
5871 |
+
timers.splice(i--, 1);
|
5872 |
+
}
|
5873 |
+
}
|
5874 |
+
|
5875 |
+
if ( !timers.length ) {
|
5876 |
+
jQuery.fx.stop();
|
5877 |
+
}
|
5878 |
+
},
|
5879 |
+
|
5880 |
+
stop: function() {
|
5881 |
+
clearInterval( timerId );
|
5882 |
+
timerId = null;
|
5883 |
+
},
|
5884 |
+
|
5885 |
+
speeds: {
|
5886 |
+
slow: 600,
|
5887 |
+
fast: 200,
|
5888 |
+
// Default speed
|
5889 |
+
_default: 400
|
5890 |
+
},
|
5891 |
+
|
5892 |
+
step: {
|
5893 |
+
opacity: function( fx ) {
|
5894 |
+
jQuery.style(fx.elem, "opacity", fx.now);
|
5895 |
+
},
|
5896 |
+
|
5897 |
+
_default: function( fx ) {
|
5898 |
+
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
|
5899 |
+
fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
|
5900 |
+
} else {
|
5901 |
+
fx.elem[ fx.prop ] = fx.now;
|
5902 |
+
}
|
5903 |
+
}
|
5904 |
+
}
|
5905 |
+
});
|
5906 |
+
|
5907 |
+
if ( jQuery.expr && jQuery.expr.filters ) {
|
5908 |
+
jQuery.expr.filters.animated = function( elem ) {
|
5909 |
+
return jQuery.grep(jQuery.timers, function( fn ) {
|
5910 |
+
return elem === fn.elem;
|
5911 |
+
}).length;
|
5912 |
+
};
|
5913 |
+
}
|
5914 |
+
|
5915 |
+
function genFx( type, num ) {
|
5916 |
+
var obj = {};
|
5917 |
+
|
5918 |
+
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
|
5919 |
+
obj[ this ] = type;
|
5920 |
+
});
|
5921 |
+
|
5922 |
+
return obj;
|
5923 |
+
}
|
5924 |
+
if ( "getBoundingClientRect" in document.documentElement ) {
|
5925 |
+
jQuery.fn.offset = function( options ) {
|
5926 |
+
var elem = this[0];
|
5927 |
+
|
5928 |
+
if ( options ) {
|
5929 |
+
return this.each(function( i ) {
|
5930 |
+
jQuery.offset.setOffset( this, options, i );
|
5931 |
+
});
|
5932 |
+
}
|
5933 |
+
|
5934 |
+
if ( !elem || !elem.ownerDocument ) {
|
5935 |
+
return null;
|
5936 |
+
}
|
5937 |
+
|
5938 |
+
if ( elem === elem.ownerDocument.body ) {
|
5939 |
+
return jQuery.offset.bodyOffset( elem );
|
5940 |
+
}
|
5941 |
+
|
5942 |
+
var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
|
5943 |
+
clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
|
5944 |
+
top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
|
5945 |
+
left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
|
5946 |
+
|
5947 |
+
return { top: top, left: left };
|
5948 |
+
};
|
5949 |
+
|
5950 |
+
} else {
|
5951 |
+
jQuery.fn.offset = function( options ) {
|
5952 |
+
var elem = this[0];
|
5953 |
+
|
5954 |
+
if ( options ) {
|
5955 |
+
return this.each(function( i ) {
|
5956 |
+
jQuery.offset.setOffset( this, options, i );
|
5957 |
+
});
|
5958 |
+
}
|
5959 |
+
|
5960 |
+
if ( !elem || !elem.ownerDocument ) {
|
5961 |
+
return null;
|
5962 |
+
}
|
5963 |
+
|
5964 |
+
if ( elem === elem.ownerDocument.body ) {
|
5965 |
+
return jQuery.offset.bodyOffset( elem );
|
5966 |
+
}
|
5967 |
+
|
5968 |
+
jQuery.offset.initialize();
|
5969 |
+
|
5970 |
+
var offsetParent = elem.offsetParent, prevOffsetParent = elem,
|
5971 |
+
doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
|
5972 |
+
body = doc.body, defaultView = doc.defaultView,
|
5973 |
+
prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
|
5974 |
+
top = elem.offsetTop, left = elem.offsetLeft;
|
5975 |
+
|
5976 |
+
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
|
5977 |
+
if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
|
5978 |
+
break;
|
5979 |
+
}
|
5980 |
+
|
5981 |
+
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
|
5982 |
+
top -= elem.scrollTop;
|
5983 |
+
left -= elem.scrollLeft;
|
5984 |
+
|
5985 |
+
if ( elem === offsetParent ) {
|
5986 |
+
top += elem.offsetTop;
|
5987 |
+
left += elem.offsetLeft;
|
5988 |
+
|
5989 |
+
if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
|
5990 |
+
top += parseFloat( computedStyle.borderTopWidth ) || 0;
|
5991 |
+
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
|
5992 |
+
}
|
5993 |
+
|
5994 |
+
prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
|
5995 |
+
}
|
5996 |
+
|
5997 |
+
if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
|
5998 |
+
top += parseFloat( computedStyle.borderTopWidth ) || 0;
|
5999 |
+
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
|
6000 |
+
}
|
6001 |
+
|
6002 |
+
prevComputedStyle = computedStyle;
|
6003 |
+
}
|
6004 |
+
|
6005 |
+
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
|
6006 |
+
top += body.offsetTop;
|
6007 |
+
left += body.offsetLeft;
|
6008 |
+
}
|
6009 |
+
|
6010 |
+
if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
|
6011 |
+
top += Math.max( docElem.scrollTop, body.scrollTop );
|
6012 |
+
left += Math.max( docElem.scrollLeft, body.scrollLeft );
|
6013 |
+
}
|
6014 |
+
|
6015 |
+
return { top: top, left: left };
|
6016 |
+
};
|
6017 |
+
}
|
6018 |
+
|
6019 |
+
jQuery.offset = {
|
6020 |
+
initialize: function() {
|
6021 |
+
var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
|
6022 |
+
html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
6023 |
+
|
6024 |
+
jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
|
6025 |
+
|
6026 |
+
container.innerHTML = html;
|
6027 |
+
body.insertBefore( container, body.firstChild );
|
6028 |
+
innerDiv = container.firstChild;
|
6029 |
+
checkDiv = innerDiv.firstChild;
|
6030 |
+
td = innerDiv.nextSibling.firstChild.firstChild;
|
6031 |
+
|
6032 |
+
this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
|
6033 |
+
this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
|
6034 |
+
|
6035 |
+
checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
|
6036 |
+
// safari subtracts parent border width here which is 5px
|
6037 |
+
this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
|
6038 |
+
checkDiv.style.position = checkDiv.style.top = "";
|
6039 |
+
|
6040 |
+
innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
|
6041 |
+
this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
|
6042 |
+
|
6043 |
+
this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
|
6044 |
+
|
6045 |
+
body.removeChild( container );
|
6046 |
+
body = container = innerDiv = checkDiv = table = td = null;
|
6047 |
+
jQuery.offset.initialize = jQuery.noop;
|
6048 |
+
},
|
6049 |
+
|
6050 |
+
bodyOffset: function( body ) {
|
6051 |
+
var top = body.offsetTop, left = body.offsetLeft;
|
6052 |
+
|
6053 |
+
jQuery.offset.initialize();
|
6054 |
+
|
6055 |
+
if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
|
6056 |
+
top += parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0;
|
6057 |
+
left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
|
6058 |
+
}
|
6059 |
+
|
6060 |
+
return { top: top, left: left };
|
6061 |
+
},
|
6062 |
+
|
6063 |
+
setOffset: function( elem, options, i ) {
|
6064 |
+
// set position first, in-case top/left are set even on static elem
|
6065 |
+
if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
|
6066 |
+
elem.style.position = "relative";
|
6067 |
+
}
|
6068 |
+
var curElem = jQuery( elem ),
|
6069 |
+
curOffset = curElem.offset(),
|
6070 |
+
curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
|
6071 |
+
curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
|
6072 |
+
|
6073 |
+
if ( jQuery.isFunction( options ) ) {
|
6074 |
+
options = options.call( elem, i, curOffset );
|
6075 |
+
}
|
6076 |
+
|
6077 |
+
var props = {
|
6078 |
+
top: (options.top - curOffset.top) + curTop,
|
6079 |
+
left: (options.left - curOffset.left) + curLeft
|
6080 |
+
};
|
6081 |
+
|
6082 |
+
if ( "using" in options ) {
|
6083 |
+
options.using.call( elem, props );
|
6084 |
+
} else {
|
6085 |
+
curElem.css( props );
|
6086 |
+
}
|
6087 |
+
}
|
6088 |
+
};
|
6089 |
+
|
6090 |
+
|
6091 |
+
jQuery.fn.extend({
|
6092 |
+
position: function() {
|
6093 |
+
if ( !this[0] ) {
|
6094 |
+
return null;
|
6095 |
+
}
|
6096 |
+
|
6097 |
+
var elem = this[0],
|
6098 |
+
|
6099 |
+
// Get *real* offsetParent
|
6100 |
+
offsetParent = this.offsetParent(),
|
6101 |
+
|
6102 |
+
// Get correct offsets
|
6103 |
+
offset = this.offset(),
|
6104 |
+
parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
|
6105 |
+
|
6106 |
+
// Subtract element margins
|
6107 |
+
// note: when an element has margin: auto the offsetLeft and marginLeft
|
6108 |
+
// are the same in Safari causing offset.left to incorrectly be 0
|
6109 |
+
offset.top -= parseFloat( jQuery.curCSS(elem, "marginTop", true) ) || 0;
|
6110 |
+
offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
|
6111 |
+
|
6112 |
+
// Add offsetParent borders
|
6113 |
+
parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
|
6114 |
+
parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
|
6115 |
+
|
6116 |
+
// Subtract the two offsets
|
6117 |
+
return {
|
6118 |
+
top: offset.top - parentOffset.top,
|
6119 |
+
left: offset.left - parentOffset.left
|
6120 |
+
};
|
6121 |
+
},
|
6122 |
+
|
6123 |
+
offsetParent: function() {
|
6124 |
+
return this.map(function() {
|
6125 |
+
var offsetParent = this.offsetParent || document.body;
|
6126 |
+
while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
|
6127 |
+
offsetParent = offsetParent.offsetParent;
|
6128 |
+
}
|
6129 |
+
return offsetParent;
|
6130 |
+
});
|
6131 |
+
}
|
6132 |
+
});
|
6133 |
+
|
6134 |
+
|
6135 |
+
// Create scrollLeft and scrollTop methods
|
6136 |
+
jQuery.each( ["Left", "Top"], function( i, name ) {
|
6137 |
+
var method = "scroll" + name;
|
6138 |
+
|
6139 |
+
jQuery.fn[ method ] = function(val) {
|
6140 |
+
var elem = this[0], win;
|
6141 |
+
|
6142 |
+
if ( !elem ) {
|
6143 |
+
return null;
|
6144 |
+
}
|
6145 |
+
|
6146 |
+
if ( val !== undefined ) {
|
6147 |
+
// Set the scroll offset
|
6148 |
+
return this.each(function() {
|
6149 |
+
win = getWindow( this );
|
6150 |
+
|
6151 |
+
if ( win ) {
|
6152 |
+
win.scrollTo(
|
6153 |
+
!i ? val : jQuery(win).scrollLeft(),
|
6154 |
+
i ? val : jQuery(win).scrollTop()
|
6155 |
+
);
|
6156 |
+
|
6157 |
+
} else {
|
6158 |
+
this[ method ] = val;
|
6159 |
+
}
|
6160 |
+
});
|
6161 |
+
} else {
|
6162 |
+
win = getWindow( elem );
|
6163 |
+
|
6164 |
+
// Return the scroll offset
|
6165 |
+
return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
|
6166 |
+
jQuery.support.boxModel && win.document.documentElement[ method ] ||
|
6167 |
+
win.document.body[ method ] :
|
6168 |
+
elem[ method ];
|
6169 |
+
}
|
6170 |
+
};
|
6171 |
+
});
|
6172 |
+
|
6173 |
+
function getWindow( elem ) {
|
6174 |
+
return ("scrollTo" in elem && elem.document) ?
|
6175 |
+
elem :
|
6176 |
+
elem.nodeType === 9 ?
|
6177 |
+
elem.defaultView || elem.parentWindow :
|
6178 |
+
false;
|
6179 |
+
}
|
6180 |
+
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
|
6181 |
+
jQuery.each([ "Height", "Width" ], function( i, name ) {
|
6182 |
+
|
6183 |
+
var type = name.toLowerCase();
|
6184 |
+
|
6185 |
+
// innerHeight and innerWidth
|
6186 |
+
jQuery.fn["inner" + name] = function() {
|
6187 |
+
return this[0] ?
|
6188 |
+
jQuery.css( this[0], type, false, "padding" ) :
|
6189 |
+
null;
|
6190 |
+
};
|
6191 |
+
|
6192 |
+
// outerHeight and outerWidth
|
6193 |
+
jQuery.fn["outer" + name] = function( margin ) {
|
6194 |
+
return this[0] ?
|
6195 |
+
jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
|
6196 |
+
null;
|
6197 |
+
};
|
6198 |
+
|
6199 |
+
jQuery.fn[ type ] = function( size ) {
|
6200 |
+
// Get window width or height
|
6201 |
+
var elem = this[0];
|
6202 |
+
if ( !elem ) {
|
6203 |
+
return size == null ? null : this;
|
6204 |
+
}
|
6205 |
+
|
6206 |
+
if ( jQuery.isFunction( size ) ) {
|
6207 |
+
return this.each(function( i ) {
|
6208 |
+
var self = jQuery( this );
|
6209 |
+
self[ type ]( size.call( this, i, self[ type ]() ) );
|
6210 |
+
});
|
6211 |
+
}
|
6212 |
+
|
6213 |
+
return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
|
6214 |
+
// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
|
6215 |
+
elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
|
6216 |
+
elem.document.body[ "client" + name ] :
|
6217 |
+
|
6218 |
+
// Get document width or height
|
6219 |
+
(elem.nodeType === 9) ? // is it a document
|
6220 |
+
// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
|
6221 |
+
Math.max(
|
6222 |
+
elem.documentElement["client" + name],
|
6223 |
+
elem.body["scroll" + name], elem.documentElement["scroll" + name],
|
6224 |
+
elem.body["offset" + name], elem.documentElement["offset" + name]
|
6225 |
+
) :
|
6226 |
+
|
6227 |
+
// Get or set width or height on the element
|
6228 |
+
size === undefined ?
|
6229 |
+
// Get width or height on the element
|
6230 |
+
jQuery.css( elem, type ) :
|
6231 |
+
|
6232 |
+
// Set the width or height on the element (default to pixels if value is unitless)
|
6233 |
+
this.css( type, typeof size === "string" ? size : size + "px" );
|
6234 |
+
};
|
6235 |
+
|
6236 |
+
});
|
6237 |
+
// Expose jQuery to the global object
|
6238 |
+
window.jQuery = window.$ = jQuery;
|
6239 |
+
|
6240 |
+
})(window);
|
6241 |
+
|
6242 |
+
|
6243 |
+
|
6244 |
+
|
6245 |
+
|
6246 |
+
/*!
|
6247 |
+
* jQuery postMessage - v0.5 - 9/11/2009
|
6248 |
+
* http://benalman.com/projects/jquery-postmessage-plugin/
|
6249 |
+
*
|
6250 |
+
* Copyright (c) 2009 "Cowboy" Ben Alman
|
6251 |
+
* Dual licensed under the MIT and GPL licenses.
|
6252 |
+
* http://benalman.com/about/license/
|
6253 |
+
*/
|
6254 |
+
|
6255 |
+
// Script: jQuery postMessage: Cross-domain scripting goodness
|
6256 |
+
//
|
6257 |
+
// *Version: 0.5, Last updated: 9/11/2009*
|
6258 |
+
//
|
6259 |
+
// Project Home - http://benalman.com/projects/jquery-postmessage-plugin/
|
6260 |
+
// GitHub - http://github.com/cowboy/jquery-postmessage/
|
6261 |
+
// Source - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.js
|
6262 |
+
// (Minified) - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.min.js (0.9kb)
|
6263 |
+
//
|
6264 |
+
// About: License
|
6265 |
+
//
|
6266 |
+
// Copyright (c) 2009 "Cowboy" Ben Alman,
|
6267 |
+
// Dual licensed under the MIT and GPL licenses.
|
6268 |
+
// http://benalman.com/about/license/
|
6269 |
+
//
|
6270 |
+
// About: Examples
|
6271 |
+
//
|
6272 |
+
// This working example, complete with fully commented code, illustrates one
|
6273 |
+
// way in which this plugin can be used.
|
6274 |
+
//
|
6275 |
+
// Iframe resizing - http://benalman.com/code/projects/jquery-postmessage/examples/iframe/
|
6276 |
+
//
|
6277 |
+
// About: Support and Testing
|
6278 |
+
//
|
6279 |
+
// Information about what version or versions of jQuery this plugin has been
|
6280 |
+
// tested with and what browsers it has been tested in.
|
6281 |
+
//
|
6282 |
+
// jQuery Versions - 1.3.2
|
6283 |
+
// Browsers Tested - Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9.
|
6284 |
+
//
|
6285 |
+
// About: Release History
|
6286 |
+
//
|
6287 |
+
// 0.5 - (9/11/2009) Improved cache-busting
|
6288 |
+
// 0.4 - (8/25/2009) Initial release
|
6289 |
+
|
6290 |
+
(function($){
|
6291 |
+
'$:nomunge'; // Used by YUI compressor.
|
6292 |
+
|
6293 |
+
// A few vars used in non-awesome browsers.
|
6294 |
+
var interval_id,
|
6295 |
+
last_hash,
|
6296 |
+
cache_bust = 1,
|
6297 |
+
|
6298 |
+
// A var used in awesome browsers.
|
6299 |
+
rm_callback,
|
6300 |
+
|
6301 |
+
// A few convenient shortcuts.
|
6302 |
+
window = this,
|
6303 |
+
FALSE = !1,
|
6304 |
+
|
6305 |
+
// Reused internal strings.
|
6306 |
+
postMessage = 'postMessage',
|
6307 |
+
addEventListener = 'addEventListener',
|
6308 |
+
|
6309 |
+
p_receiveMessage,
|
6310 |
+
|
6311 |
+
has_postMessage = window[postMessage];
|
6312 |
+
|
6313 |
+
// Method: jQuery.postMessage
|
6314 |
+
//
|
6315 |
+
// This method will call window.postMessage if available, setting the
|
6316 |
+
// targetOrigin parameter to the base of the target_url parameter for maximum
|
6317 |
+
// security in browsers that support it. If window.postMessage is not available,
|
6318 |
+
// the target window's location.hash will be used to pass the message. If an
|
6319 |
+
// object is passed as the message param, it will be serialized into a string
|
6320 |
+
// using the jQuery.param method.
|
6321 |
+
//
|
6322 |
+
// Usage:
|
6323 |
+
//
|
6324 |
+
// > jQuery.postMessage( message, target_url [, target ] );
|
6325 |
+
//
|
6326 |
+
// Arguments:
|
6327 |
+
//
|
6328 |
+
// message - (String) A message to be passed to the other frame.
|
6329 |
+
// message - (Object) An object to be serialized into a params string, using
|
6330 |
+
// the jQuery.param method.
|
6331 |
+
// target_url - (String) The URL of the other frame this window is
|
6332 |
+
// attempting to communicate with. This must be the exact URL (including
|
6333 |
+
// any query string) of the other window for this script to work in
|
6334 |
+
// browsers that don't support window.postMessage.
|
6335 |
+
// target - (Object) A reference to the other frame this window is
|
6336 |
+
// attempting to communicate with. If omitted, defaults to `parent`.
|
6337 |
+
//
|
6338 |
+
// Returns:
|
6339 |
+
//
|
6340 |
+
// Nothing.
|
6341 |
+
|
6342 |
+
$[postMessage] = function( message, target_url, target ) {
|
6343 |
+
if ( !target_url ) { return; }
|
6344 |
+
|
6345 |
+
// Serialize the message if not a string. Note that this is the only real
|
6346 |
+
// jQuery dependency for this script. If removed, this script could be
|
6347 |
+
// written as very basic JavaScript.
|
6348 |
+
message = typeof message === 'string' ? message : $.param( message );
|
6349 |
+
|
6350 |
+
// Default to parent if unspecified.
|
6351 |
+
target = target || parent;
|
6352 |
+
|
6353 |
+
if ( has_postMessage ) {
|
6354 |
+
// The browser supports window.postMessage, so call it with a targetOrigin
|
6355 |
+
// set appropriately, based on the target_url parameter.
|
6356 |
+
target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
|
6357 |
+
|
6358 |
+
} else if ( target_url ) {
|
6359 |
+
// The browser does not support window.postMessage, so set the location
|
6360 |
+
// of the target to target_url#message. A bit ugly, but it works! A cache
|
6361 |
+
// bust parameter is added to ensure that repeat messages trigger the
|
6362 |
+
// callback.
|
6363 |
+
target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (cache_bust++) + '&' + message;
|
6364 |
+
}
|
6365 |
+
};
|
6366 |
+
|
6367 |
+
// Method: jQuery.receiveMessage
|
6368 |
+
//
|
6369 |
+
// Register a single callback for either a window.postMessage call, if
|
6370 |
+
// supported, or if unsupported, for any change in the current window
|
6371 |
+
// location.hash. If window.postMessage is supported and source_origin is
|
6372 |
+
// specified, the source window will be checked against this for maximum
|
6373 |
+
// security. If window.postMessage is unsupported, a polling loop will be
|
6374 |
+
// started to watch for changes to the location.hash.
|
6375 |
+
//
|
6376 |
+
// Note that for simplicity's sake, only a single callback can be registered
|
6377 |
+
// at one time. Passing no params will unbind this event (or stop the polling
|
6378 |
+
// loop), and calling this method a second time with another callback will
|
6379 |
+
// unbind the event (or stop the polling loop) first, before binding the new
|
6380 |
+
// callback.
|
6381 |
+
//
|
6382 |
+
// Also note that if window.postMessage is available, the optional
|
6383 |
+
// source_origin param will be used to test the event.origin property. From
|
6384 |
+
// the MDC window.postMessage docs: This string is the concatenation of the
|
6385 |
+
// protocol and "://", the host name if one exists, and ":" followed by a port
|
6386 |
+
// number if a port is present and differs from the default port for the given
|
6387 |
+
// protocol. Examples of typical origins are https://example.org (implying
|
6388 |
+
// port 443), http://example.net (implying port 80), and http://example.com:8080.
|
6389 |
+
//
|
6390 |
+
// Usage:
|
6391 |
+
//
|
6392 |
+
// > jQuery.receiveMessage( callback [, source_origin ] [, delay ] );
|
6393 |
+
//
|
6394 |
+
// Arguments:
|
6395 |
+
//
|
6396 |
+
// callback - (Function) This callback will execute whenever a <jQuery.postMessage>
|
6397 |
+
// message is received, provided the source_origin matches. If callback is
|
6398 |
+
// omitted, any existing receiveMessage event bind or polling loop will be
|
6399 |
+
// canceled.
|
6400 |
+
// source_origin - (String) If window.postMessage is available and this value
|
6401 |
+
// is not equal to the event.origin property, the callback will not be
|
6402 |
+
// called.
|
6403 |
+
// source_origin - (Function) If window.postMessage is available and this
|
6404 |
+
// function returns false when passed the event.origin property, the
|
6405 |
+
// callback will not be called.
|
6406 |
+
// delay - (Number) An optional zero-or-greater delay in milliseconds at
|
6407 |
+
// which the polling loop will execute (for browser that don't support
|
6408 |
+
// window.postMessage). If omitted, defaults to 100.
|
6409 |
+
//
|
6410 |
+
// Returns:
|
6411 |
+
//
|
6412 |
+
// Nothing!
|
6413 |
+
|
6414 |
+
$.receiveMessage = p_receiveMessage = function( callback, source_origin, delay ) {
|
6415 |
+
if ( has_postMessage ) {
|
6416 |
+
// Since the browser supports window.postMessage, the callback will be
|
6417 |
+
// bound to the actual event associated with window.postMessage.
|
6418 |
+
|
6419 |
+
if ( callback ) {
|
6420 |
+
// Unbind an existing callback if it exists.
|
6421 |
+
rm_callback && p_receiveMessage();
|
6422 |
+
|
6423 |
+
// Bind the callback. A reference to the callback is stored for ease of
|
6424 |
+
// unbinding.
|
6425 |
+
rm_callback = function(e) {
|
6426 |
+
if ( ( typeof source_origin === 'string' && e.origin !== source_origin )
|
6427 |
+
|| ( $.isFunction( source_origin ) && source_origin( e.origin ) === FALSE ) ) {
|
6428 |
+
return FALSE;
|
6429 |
+
}
|
6430 |
+
callback( e );
|
6431 |
+
};
|
6432 |
+
}
|
6433 |
+
|
6434 |
+
if ( window[addEventListener] ) {
|
6435 |
+
window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
|
6436 |
+
} else {
|
6437 |
+
window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
|
6438 |
+
}
|
6439 |
+
|
6440 |
+
} else {
|
6441 |
+
// Since the browser sucks, a polling loop will be started, and the
|
6442 |
+
// callback will be called whenever the location.hash changes.
|
6443 |
+
|
6444 |
+
interval_id && clearInterval( interval_id );
|
6445 |
+
interval_id = null;
|
6446 |
+
|
6447 |
+
if ( callback ) {
|
6448 |
+
delay = typeof source_origin === 'number'
|
6449 |
+
? source_origin
|
6450 |
+
: typeof delay === 'number'
|
6451 |
+
? delay
|
6452 |
+
: 100;
|
6453 |
+
|
6454 |
+
interval_id = setInterval(function(){
|
6455 |
+
var hash = document.location.hash,
|
6456 |
+
re = /^#?\d+&/;
|
6457 |
+
if ( hash !== last_hash && re.test( hash ) ) {
|
6458 |
+
last_hash = hash;
|
6459 |
+
callback({ data: hash.replace( re, '' ) });
|
6460 |
+
}
|
6461 |
+
}, delay );
|
6462 |
+
}
|
6463 |
+
}
|
6464 |
+
};
|
6465 |
+
|
6466 |
+
})(jQuery);
|
6467 |
+
|
6468 |
+
|
6469 |
+
|
6470 |
+
var D = $.noConflict();
|
6471 |
+
|
6472 |
+
|
6473 |
+
/*
|
6474 |
+
* Duo Web SDK v1
|
6475 |
+
* Copyright 2011, Duo Security
|
6476 |
+
*/
|
6477 |
+
|
6478 |
+
var Duo = {
|
6479 |
+
init: function(options) {
|
6480 |
+
/* sanity check for argument dict */
|
6481 |
+
if (!options) {
|
6482 |
+
alert('Error: missing arguments in Duo.init()');
|
6483 |
+
return;
|
6484 |
+
}
|
6485 |
+
|
6486 |
+
/* API hostname is a required argument */
|
6487 |
+
if (!options.host) {
|
6488 |
+
alert('Error: missing \'host\' argument in Duo.init()');
|
6489 |
+
return;
|
6490 |
+
}
|
6491 |
+
Duo._host = options.host;
|
6492 |
+
|
6493 |
+
/* sig_request is a required argument */
|
6494 |
+
if (!options.sig_request) {
|
6495 |
+
alert('Error: missing \'sig_request\' argument in Duo.init()');
|
6496 |
+
return;
|
6497 |
+
}
|
6498 |
+
Duo._sig_request = options.sig_request;
|
6499 |
+
|
6500 |
+
/* check for sig_request errors */
|
6501 |
+
if (Duo._sig_request.indexOf('ERR|') == 0) {
|
6502 |
+
var err_array = Duo._sig_request.split('|');
|
6503 |
+
alert('Error: ' + err_array[1]);
|
6504 |
+
return;
|
6505 |
+
}
|
6506 |
+
|
6507 |
+
/* validate and parse the sig_request */
|
6508 |
+
if (Duo._sig_request.indexOf(':') == -1) {
|
6509 |
+
alert('Invalid sig_request value');
|
6510 |
+
return;
|
6511 |
+
}
|
6512 |
+
var sig_array = Duo._sig_request.split(':');
|
6513 |
+
if (sig_array.length != 2) {
|
6514 |
+
alert('Invalid sig_request value');
|
6515 |
+
return;
|
6516 |
+
}
|
6517 |
+
Duo._duo_sig = sig_array[0];
|
6518 |
+
Duo._app_sig = sig_array[1];
|
6519 |
+
|
6520 |
+
/* allow override of the POST-back action URI */
|
6521 |
+
if (!options.post_action) {
|
6522 |
+
options.post_action = '';
|
6523 |
+
}
|
6524 |
+
Duo._post_action = options.post_action;
|
6525 |
+
|
6526 |
+
/* allow override of the POST-back argument name */
|
6527 |
+
if (!options.post_argument) {
|
6528 |
+
options.post_argument = 'sig_response';
|
6529 |
+
}
|
6530 |
+
Duo._post_argument = options.post_argument;
|
6531 |
+
|
6532 |
+
Duo._mage_form_key = options.form_key;
|
6533 |
+
},
|
6534 |
+
|
6535 |
+
ready: function() {
|
6536 |
+
var iframe = D('#duo_iframe');
|
6537 |
+
|
6538 |
+
/* sanity check for a duo_iframe element */
|
6539 |
+
if (!iframe.length) {
|
6540 |
+
alert('Error: missing IFRAME element with id \'duo_iframe\'');
|
6541 |
+
return;
|
6542 |
+
}
|
6543 |
+
|
6544 |
+
var args = D.param({
|
6545 |
+
'tx': Duo._duo_sig,
|
6546 |
+
'parent': document.location.href
|
6547 |
+
});
|
6548 |
+
|
6549 |
+
var src = 'https://' + Duo._host + '/frame/web/v1/auth?' + args;
|
6550 |
+
iframe.attr('src', src);
|
6551 |
+
|
6552 |
+
D.receiveMessage(function(msg) {
|
6553 |
+
var sig_response = msg.data + ':' + Duo._app_sig;
|
6554 |
+
var input = D('<input type="hidden">').attr('name', Duo._post_argument).val(sig_response);
|
6555 |
+
|
6556 |
+
var form = D('#duo_form');
|
6557 |
+
if (!form.length) {
|
6558 |
+
form = D('<form>');
|
6559 |
+
form.insertAfter(iframe);
|
6560 |
+
}
|
6561 |
+
|
6562 |
+
form.attr('method', 'POST');
|
6563 |
+
form.attr('action', Duo._post_action);
|
6564 |
+
form.append(input);
|
6565 |
+
form.append('<input type="hidden" name="form_key" value="'+ Duo._mage_form_key +'" />');
|
6566 |
+
form.submit();
|
6567 |
+
}, 'https://' + Duo._host);
|
6568 |
+
}
|
6569 |
+
};
|
6570 |
+
|
6571 |
+
D(document).ready(function() {
|
6572 |
+
Duo.ready();
|
6573 |
+
});
|
js/he_twofactor/Duo-Web-v1.bundled.min.js
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* jQuery JavaScript Library v1.4.2
|
3 |
+
* http://jquery.com/
|
4 |
+
*
|
5 |
+
* Copyright 2010, John Resig
|
6 |
+
* Dual licensed under the MIT or GPL Version 2 licenses.
|
7 |
+
* http://jquery.org/license
|
8 |
+
*
|
9 |
+
* Includes Sizzle.js
|
10 |
+
* http://sizzlejs.com/
|
11 |
+
* Copyright 2010, The Dojo Foundation
|
12 |
+
* Released under the MIT, BSD, and GPL Licenses.
|
13 |
+
*
|
14 |
+
* Date: Sat Feb 13 22:33:48 2010 -0500
|
15 |
+
*/
|
16 |
+
(function(aN,C){var a=function(aZ,a0){return new a.fn.init(aZ,a0)},n=aN.jQuery,S=aN.$,ac=aN.document,Y,Q=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,aX=/^.[^:#\[\.,]*$/,ay=/\S/,N=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,e=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,b=navigator.userAgent,u,L=false,ae=[],aH,au=Object.prototype.toString,aq=Object.prototype.hasOwnProperty,g=Array.prototype.push,G=Array.prototype.slice,s=Array.prototype.indexOf;a.fn=a.prototype={init:function(aZ,a2){var a1,a3,a0,a4;if(!aZ){return this}if(aZ.nodeType){this.context=this[0]=aZ;this.length=1;return this}if(aZ==="body"&&!a2){this.context=ac;this[0]=ac.body;this.selector="body";this.length=1;return this}if(typeof aZ==="string"){a1=Q.exec(aZ);if(a1&&(a1[1]||!a2)){if(a1[1]){a4=(a2?a2.ownerDocument||a2:ac);a0=e.exec(aZ);if(a0){if(a.isPlainObject(a2)){aZ=[ac.createElement(a0[1])];a.fn.attr.call(aZ,a2,true)}else{aZ=[a4.createElement(a0[1])]}}else{a0=K([a1[1]],[a4]);aZ=(a0.cacheable?a0.fragment.cloneNode(true):a0.fragment).childNodes}return a.merge(this,aZ)}else{a3=ac.getElementById(a1[2]);if(a3){if(a3.id!==a1[2]){return Y.find(aZ)}this.length=1;this[0]=a3}this.context=ac;this.selector=aZ;return this}}else{if(!a2&&/^\w+$/.test(aZ)){this.selector=aZ;this.context=ac;aZ=ac.getElementsByTagName(aZ);return a.merge(this,aZ)}else{if(!a2||a2.jquery){return(a2||Y).find(aZ)}else{return a(a2).find(aZ)}}}}else{if(a.isFunction(aZ)){return Y.ready(aZ)}}if(aZ.selector!==C){this.selector=aZ.selector;this.context=aZ.context}return a.makeArray(aZ,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(aZ){return aZ==null?this.toArray():(aZ<0?this.slice(aZ)[0]:this[aZ])},pushStack:function(a0,a2,aZ){var a1=a();if(a.isArray(a0)){g.apply(a1,a0)}else{a.merge(a1,a0)}a1.prevObject=this;a1.context=this.context;if(a2==="find"){a1.selector=this.selector+(this.selector?" ":"")+aZ}else{if(a2){a1.selector=this.selector+"."+a2+"("+aZ+")"}}return a1},each:function(a0,aZ){return a.each(this,a0,aZ)},ready:function(aZ){a.bindReady();if(a.isReady){aZ.call(ac,a)}else{if(ae){ae.push(aZ)}}return this},eq:function(aZ){return aZ===-1?this.slice(aZ):this.slice(aZ,+aZ+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(aZ){return this.pushStack(a.map(this,function(a1,a0){return aZ.call(a1,a0,a1)}))},end:function(){return this.prevObject||a(null)},push:g,sort:[].sort,splice:[].splice};a.fn.init.prototype=a.fn;a.extend=a.fn.extend=function(){var a4=arguments[0]||{},a3=1,a2=arguments.length,a6=false,a7,a1,aZ,a0;if(typeof a4==="boolean"){a6=a4;a4=arguments[1]||{};a3=2}if(typeof a4!=="object"&&!a.isFunction(a4)){a4={}}if(a2===a3){a4=this;--a3}for(;a3<a2;a3++){if((a7=arguments[a3])!=null){for(a1 in a7){aZ=a4[a1];a0=a7[a1];if(a4===a0){continue}if(a6&&a0&&(a.isPlainObject(a0)||a.isArray(a0))){var a5=aZ&&(a.isPlainObject(aZ)||a.isArray(aZ))?aZ:a.isArray(a0)?[]:{};a4[a1]=a.extend(a6,a5,a0)}else{if(a0!==C){a4[a1]=a0}}}}}return a4};a.extend({noConflict:function(aZ){aN.$=S;if(aZ){aN.jQuery=n}return a},isReady:false,ready:function(){if(!a.isReady){if(!ac.body){return setTimeout(a.ready,13)}a.isReady=true;if(ae){var a0,aZ=0;while((a0=ae[aZ++])){a0.call(ac,a)}ae=null}if(a.fn.triggerHandler){a(ac).triggerHandler("ready")}}},bindReady:function(){if(L){return}L=true;if(ac.readyState==="complete"){return a.ready()}if(ac.addEventListener){ac.addEventListener("DOMContentLoaded",aH,false);aN.addEventListener("load",a.ready,false)}else{if(ac.attachEvent){ac.attachEvent("onreadystatechange",aH);aN.attachEvent("onload",a.ready);var aZ=false;try{aZ=aN.frameElement==null}catch(a0){}if(ac.documentElement.doScroll&&aZ){x()}}}},isFunction:function(aZ){return au.call(aZ)==="[object Function]"},isArray:function(aZ){return au.call(aZ)==="[object Array]"},isPlainObject:function(a0){if(!a0||au.call(a0)!=="[object Object]"||a0.nodeType||a0.setInterval){return false}if(a0.constructor&&!aq.call(a0,"constructor")&&!aq.call(a0.constructor.prototype,"isPrototypeOf")){return false}var aZ;for(aZ in a0){}return aZ===C||aq.call(a0,aZ)},isEmptyObject:function(a0){for(var aZ in a0){return false}return true},error:function(aZ){throw aZ},parseJSON:function(aZ){if(typeof aZ!=="string"||!aZ){return null}aZ=a.trim(aZ);if(/^[\],:{}\s]*$/.test(aZ.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){return aN.JSON&&aN.JSON.parse?aN.JSON.parse(aZ):(new Function("return "+aZ))()}else{a.error("Invalid JSON: "+aZ)}},noop:function(){},globalEval:function(a1){if(a1&&ay.test(a1)){var a0=ac.getElementsByTagName("head")[0]||ac.documentElement,aZ=ac.createElement("script");aZ.type="text/javascript";if(a.support.scriptEval){aZ.appendChild(ac.createTextNode(a1))}else{aZ.text=a1}a0.insertBefore(aZ,a0.firstChild);a0.removeChild(aZ)}},nodeName:function(a0,aZ){return a0.nodeName&&a0.nodeName.toUpperCase()===aZ.toUpperCase()},each:function(a2,a6,a1){var a0,a3=0,a4=a2.length,aZ=a4===C||a.isFunction(a2);if(a1){if(aZ){for(a0 in a2){if(a6.apply(a2[a0],a1)===false){break}}}else{for(;a3<a4;){if(a6.apply(a2[a3++],a1)===false){break}}}}else{if(aZ){for(a0 in a2){if(a6.call(a2[a0],a0,a2[a0])===false){break}}}else{for(var a5=a2[0];a3<a4&&a6.call(a5,a3,a5)!==false;a5=a2[++a3]){}}}return a2},trim:function(aZ){return(aZ||"").replace(N,"")},makeArray:function(a1,a0){var aZ=a0||[];if(a1!=null){if(a1.length==null||typeof a1==="string"||a.isFunction(a1)||(typeof a1!=="function"&&a1.setInterval)){g.call(aZ,a1)}else{a.merge(aZ,a1)}}return aZ},inArray:function(a1,a2){if(a2.indexOf){return a2.indexOf(a1)}for(var aZ=0,a0=a2.length;aZ<a0;aZ++){if(a2[aZ]===a1){return aZ}}return -1},merge:function(a3,a1){var a2=a3.length,a0=0;if(typeof a1.length==="number"){for(var aZ=a1.length;a0<aZ;a0++){a3[a2++]=a1[a0]}}else{while(a1[a0]!==C){a3[a2++]=a1[a0++]}}a3.length=a2;return a3},grep:function(a0,a4,aZ){var a1=[];for(var a2=0,a3=a0.length;a2<a3;a2++){if(!aZ!==!a4(a0[a2],a2)){a1.push(a0[a2])}}return a1},map:function(a0,a5,aZ){var a1=[],a4;for(var a2=0,a3=a0.length;a2<a3;a2++){a4=a5(a0[a2],a2,aZ);if(a4!=null){a1[a1.length]=a4}}return a1.concat.apply([],a1)},guid:1,proxy:function(a1,a0,aZ){if(arguments.length===2){if(typeof a0==="string"){aZ=a1;a1=aZ[a0];a0=C}else{if(a0&&!a.isFunction(a0)){aZ=a0;a0=C}}}if(!a0&&a1){a0=function(){return a1.apply(aZ||this,arguments)}}if(a1){a0.guid=a1.guid=a1.guid||a0.guid||a.guid++}return a0},uaMatch:function(a0){a0=a0.toLowerCase();var aZ=/(webkit)[ \/]([\w.]+)/.exec(a0)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a0)||/(msie) ([\w.]+)/.exec(a0)||!/compatible/.test(a0)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a0)||[];return{browser:aZ[1]||"",version:aZ[2]||"0"}},browser:{}});u=a.uaMatch(b);if(u.browser){a.browser[u.browser]=true;a.browser.version=u.version}if(a.browser.webkit){a.browser.safari=true}if(s){a.inArray=function(aZ,a0){return s.call(a0,aZ)}}Y=a(ac);if(ac.addEventListener){aH=function(){ac.removeEventListener("DOMContentLoaded",aH,false);a.ready()}}else{if(ac.attachEvent){aH=function(){if(ac.readyState==="complete"){ac.detachEvent("onreadystatechange",aH);a.ready()}}}}function x(){if(a.isReady){return}try{ac.documentElement.doScroll("left")}catch(aZ){setTimeout(x,1);return}a.ready()}function aW(aZ,a0){if(a0.src){a.ajax({url:a0.src,async:false,dataType:"script"})}else{a.globalEval(a0.text||a0.textContent||a0.innerHTML||"")}if(a0.parentNode){a0.parentNode.removeChild(a0)}}function ao(aZ,a7,a5,a1,a4,a6){var a0=aZ.length;if(typeof a7==="object"){for(var a2 in a7){ao(aZ,a2,a7[a2],a1,a4,a5)}return aZ}if(a5!==C){a1=!a6&&a1&&a.isFunction(a5);for(var a3=0;a3<a0;a3++){a4(aZ[a3],a7,a1?a5.call(aZ[a3],a3,a4(aZ[a3],a7)):a5,a6)}return aZ}return a0?a4(aZ[0],a7):C}function aQ(){return(new Date).getTime()}(function(){a.support={};var a5=ac.documentElement,a4=ac.createElement("script"),aZ=ac.createElement("div"),a0="script"+aQ();aZ.style.display="none";aZ.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var a7=aZ.getElementsByTagName("*"),a6=aZ.getElementsByTagName("a")[0];if(!a7||!a7.length||!a6){return}a.support={leadingWhitespace:aZ.firstChild.nodeType===3,tbody:!aZ.getElementsByTagName("tbody").length,htmlSerialize:!!aZ.getElementsByTagName("link").length,style:/red/.test(a6.getAttribute("style")),hrefNormalized:a6.getAttribute("href")==="/a",opacity:/^0.55$/.test(a6.style.opacity),cssFloat:!!a6.style.cssFloat,checkOn:aZ.getElementsByTagName("input")[0].value==="on",optSelected:ac.createElement("select").appendChild(ac.createElement("option")).selected,parentNode:aZ.removeChild(aZ.appendChild(ac.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};a4.type="text/javascript";try{a4.appendChild(ac.createTextNode("window."+a0+"=1;"))}catch(a2){}a5.insertBefore(a4,a5.firstChild);if(aN[a0]){a.support.scriptEval=true;delete aN[a0]}try{delete a4.test}catch(a2){a.support.deleteExpando=false}a5.removeChild(a4);if(aZ.attachEvent&&aZ.fireEvent){aZ.attachEvent("onclick",function a8(){a.support.noCloneEvent=false;aZ.detachEvent("onclick",a8)});aZ.cloneNode(true).fireEvent("onclick")}aZ=ac.createElement("div");aZ.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";var a1=ac.createDocumentFragment();a1.appendChild(aZ.firstChild);a.support.checkClone=a1.cloneNode(true).cloneNode(true).lastChild.checked;a(function(){var a9=ac.createElement("div");a9.style.width=a9.style.paddingLeft="1px";ac.body.appendChild(a9);a.boxModel=a.support.boxModel=a9.offsetWidth===2;ac.body.removeChild(a9).style.display="none";a9=null});var a3=function(a9){var bb=ac.createElement("div");a9="on"+a9;var ba=(a9 in bb);if(!ba){bb.setAttribute(a9,"return;");ba=typeof bb[a9]==="function"}bb=null;return ba};a.support.submitBubbles=a3("submit");a.support.changeBubbles=a3("change");a5=a4=aZ=a7=a6=null})();a.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var aJ="jQuery"+aQ(),aI=0,aU={};a.extend({cache:{},expando:aJ,noData:{embed:true,object:true,applet:true},data:function(a1,a0,a3){if(a1.nodeName&&a.noData[a1.nodeName.toLowerCase()]){return}a1=a1==aN?aU:a1;var a4=a1[aJ],aZ=a.cache,a2;if(!a4&&typeof a0==="string"&&a3===C){return null}if(!a4){a4=++aI}if(typeof a0==="object"){a1[aJ]=a4;a2=aZ[a4]=a.extend(true,{},a0)}else{if(!aZ[a4]){a1[aJ]=a4;aZ[a4]={}}}a2=aZ[a4];if(a3!==C){a2[a0]=a3}return typeof a0==="string"?a2[a0]:a2},removeData:function(a1,a0){if(a1.nodeName&&a.noData[a1.nodeName.toLowerCase()]){return}a1=a1==aN?aU:a1;var a3=a1[aJ],aZ=a.cache,a2=aZ[a3];if(a0){if(a2){delete a2[a0];if(a.isEmptyObject(a2)){a.removeData(a1)}}}else{if(a.support.deleteExpando){delete a1[a.expando]}else{if(a1.removeAttribute){a1.removeAttribute(a.expando)}}delete aZ[a3]}}});a.fn.extend({data:function(aZ,a1){if(typeof aZ==="undefined"&&this.length){return a.data(this[0])}else{if(typeof aZ==="object"){return this.each(function(){a.data(this,aZ)})}}var a2=aZ.split(".");a2[1]=a2[1]?"."+a2[1]:"";if(a1===C){var a0=this.triggerHandler("getData"+a2[1]+"!",[a2[0]]);if(a0===C&&this.length){a0=a.data(this[0],aZ)}return a0===C&&a2[1]?this.data(a2[0]):a0}else{return this.trigger("setData"+a2[1]+"!",[a2[0],a1]).each(function(){a.data(this,aZ,a1)})}},removeData:function(aZ){return this.each(function(){a.removeData(this,aZ)})}});a.extend({queue:function(a0,aZ,a2){if(!a0){return}aZ=(aZ||"fx")+"queue";var a1=a.data(a0,aZ);if(!a2){return a1||[]}if(!a1||a.isArray(a2)){a1=a.data(a0,aZ,a.makeArray(a2))}else{a1.push(a2)}return a1},dequeue:function(a2,a1){a1=a1||"fx";var aZ=a.queue(a2,a1),a0=aZ.shift();if(a0==="inprogress"){a0=aZ.shift()}if(a0){if(a1==="fx"){aZ.unshift("inprogress")}a0.call(a2,function(){a.dequeue(a2,a1)})}}});a.fn.extend({queue:function(aZ,a0){if(typeof aZ!=="string"){a0=aZ;aZ="fx"}if(a0===C){return a.queue(this[0],aZ)}return this.each(function(a2,a3){var a1=a.queue(this,aZ,a0);if(aZ==="fx"&&a1[0]!=="inprogress"){a.dequeue(this,aZ)}})},dequeue:function(aZ){return this.each(function(){a.dequeue(this,aZ)})},delay:function(a0,aZ){a0=a.fx?a.fx.speeds[a0]||a0:a0;aZ=aZ||"fx";return this.queue(aZ,function(){var a1=this;setTimeout(function(){a.dequeue(a1,aZ)},a0)})},clearQueue:function(aZ){return this.queue(aZ||"fx",[])}});var ap=/[\n\t]/g,T=/\s+/,aw=/\r/g,aR=/href|src|style/,d=/(button|input)/i,z=/(button|input|object|select|textarea)/i,j=/^(a|area)$/i,J=/radio|checkbox/;a.fn.extend({attr:function(aZ,a0){return ao(this,aZ,a0,true,a.attr)},removeAttr:function(aZ,a0){return this.each(function(){a.attr(this,aZ,"");if(this.nodeType===1){this.removeAttribute(aZ)}})},addClass:function(a6){if(a.isFunction(a6)){return this.each(function(a9){var a8=a(this);a8.addClass(a6.call(this,a9,a8.attr("class")))})}if(a6&&typeof a6==="string"){var aZ=(a6||"").split(T);for(var a2=0,a1=this.length;a2<a1;a2++){var a0=this[a2];if(a0.nodeType===1){if(!a0.className){a0.className=a6}else{var a3=" "+a0.className+" ",a5=a0.className;for(var a4=0,a7=aZ.length;a4<a7;a4++){if(a3.indexOf(" "+aZ[a4]+" ")<0){a5+=" "+aZ[a4]}}a0.className=a.trim(a5)}}}}return this},removeClass:function(a4){if(a.isFunction(a4)){return this.each(function(a8){var a7=a(this);a7.removeClass(a4.call(this,a8,a7.attr("class")))})}if((a4&&typeof a4==="string")||a4===C){var a5=(a4||"").split(T);for(var a1=0,a0=this.length;a1<a0;a1++){var a3=this[a1];if(a3.nodeType===1&&a3.className){if(a4){var a2=(" "+a3.className+" ").replace(ap," ");for(var a6=0,aZ=a5.length;a6<aZ;a6++){a2=a2.replace(" "+a5[a6]+" "," ")}a3.className=a.trim(a2)}else{a3.className=""}}}}return this},toggleClass:function(a2,a0){var a1=typeof a2,aZ=typeof a0==="boolean";if(a.isFunction(a2)){return this.each(function(a4){var a3=a(this);a3.toggleClass(a2.call(this,a4,a3.attr("class"),a0),a0)})}return this.each(function(){if(a1==="string"){var a5,a4=0,a3=a(this),a6=a0,a7=a2.split(T);while((a5=a7[a4++])){a6=aZ?a6:!a3.hasClass(a5);a3[a6?"addClass":"removeClass"](a5)}}else{if(a1==="undefined"||a1==="boolean"){if(this.className){a.data(this,"__className__",this.className)}this.className=this.className||a2===false?"":a.data(this,"__className__")||""}}})},hasClass:function(aZ){var a2=" "+aZ+" ";for(var a1=0,a0=this.length;a1<a0;a1++){if((" "+this[a1].className+" ").replace(ap," ").indexOf(a2)>-1){return true}}return false},val:function(a6){if(a6===C){var a0=this[0];if(a0){if(a.nodeName(a0,"option")){return(a0.attributes.value||{}).specified?a0.value:a0.text}if(a.nodeName(a0,"select")){var a4=a0.selectedIndex,a7=[],a8=a0.options,a3=a0.type==="select-one";if(a4<0){return null}for(var a1=a3?a4:0,a5=a3?a4+1:a8.length;a1<a5;a1++){var a2=a8[a1];if(a2.selected){a6=a(a2).val();if(a3){return a6}a7.push(a6)}}return a7}if(J.test(a0.type)&&!a.support.checkOn){return a0.getAttribute("value")===null?"on":a0.value}return(a0.value||"").replace(aw,"")}return C}var aZ=a.isFunction(a6);return this.each(function(bb){var ba=a(this),bc=a6;if(this.nodeType!==1){return}if(aZ){bc=a6.call(this,bb,ba.val())}if(typeof bc==="number"){bc+=""}if(a.isArray(bc)&&J.test(this.type)){this.checked=a.inArray(ba.val(),bc)>=0}else{if(a.nodeName(this,"select")){var a9=a.makeArray(bc);a("option",this).each(function(){this.selected=a.inArray(a(this).val(),a9)>=0});if(!a9.length){this.selectedIndex=-1}}else{this.value=bc}}})}});a.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a0,aZ,a5,a8){if(!a0||a0.nodeType===3||a0.nodeType===8){return C}if(a8&&aZ in a.attrFn){return a(a0)[aZ](a5)}var a1=a0.nodeType!==1||!a.isXMLDoc(a0),a4=a5!==C;aZ=a1&&a.props[aZ]||aZ;if(a0.nodeType===1){var a3=aR.test(aZ);if(aZ==="selected"&&!a.support.optSelected){var a6=a0.parentNode;if(a6){a6.selectedIndex;if(a6.parentNode){a6.parentNode.selectedIndex}}}if(aZ in a0&&a1&&!a3){if(a4){if(aZ==="type"&&d.test(a0.nodeName)&&a0.parentNode){a.error("type property can't be changed")}a0[aZ]=a5}if(a.nodeName(a0,"form")&&a0.getAttributeNode(aZ)){return a0.getAttributeNode(aZ).nodeValue}if(aZ==="tabIndex"){var a7=a0.getAttributeNode("tabIndex");return a7&&a7.specified?a7.value:z.test(a0.nodeName)||j.test(a0.nodeName)&&a0.href?0:C}return a0[aZ]}if(!a.support.style&&a1&&aZ==="style"){if(a4){a0.style.cssText=""+a5}return a0.style.cssText}if(a4){a0.setAttribute(aZ,""+a5)}var a2=!a.support.hrefNormalized&&a1&&a3?a0.getAttribute(aZ,2):a0.getAttribute(aZ);return a2===null?C:a2}return a.style(a0,aZ,a5)}});var aD=/\.(.*)$/,A=function(aZ){return aZ.replace(/[^\w\s\.\|`]/g,function(a0){return"\\"+a0})};a.event={add:function(a2,a6,bb,a4){if(a2.nodeType===3||a2.nodeType===8){return}if(a2.setInterval&&(a2!==aN&&!a2.frameElement)){a2=aN}var a0,ba;if(bb.handler){a0=bb;bb=a0.handler}if(!bb.guid){bb.guid=a.guid++}var a7=a.data(a2);if(!a7){return}var bc=a7.events=a7.events||{},a5=a7.handle,a5;if(!a5){a7.handle=a5=function(){return typeof a!=="undefined"&&!a.event.triggered?a.event.handle.apply(a5.elem,arguments):C}}a5.elem=a2;a6=a6.split(" ");var a9,a3=0,aZ;while((a9=a6[a3++])){ba=a0?a.extend({},a0):{handler:bb,data:a4};if(a9.indexOf(".")>-1){aZ=a9.split(".");a9=aZ.shift();ba.namespace=aZ.slice(0).sort().join(".")}else{aZ=[];ba.namespace=""}ba.type=a9;ba.guid=bb.guid;var a1=bc[a9],a8=a.event.special[a9]||{};if(!a1){a1=bc[a9]=[];if(!a8.setup||a8.setup.call(a2,a4,aZ,a5)===false){if(a2.addEventListener){a2.addEventListener(a9,a5,false)}else{if(a2.attachEvent){a2.attachEvent("on"+a9,a5)}}}}if(a8.add){a8.add.call(a2,ba);if(!ba.handler.guid){ba.handler.guid=bb.guid}}a1.push(ba);a.event.global[a9]=true}a2=null},global:{},remove:function(be,a9,a0,a5){if(be.nodeType===3||be.nodeType===8){return}var bh,a4,a6,bc=0,a2,a7,ba,a3,a8,aZ,bg,bd=a.data(be),a1=bd&&bd.events;if(!bd||!a1){return}if(a9&&a9.type){a0=a9.handler;a9=a9.type}if(!a9||typeof a9==="string"&&a9.charAt(0)==="."){a9=a9||"";for(a4 in a1){a.event.remove(be,a4+a9)}return}a9=a9.split(" ");while((a4=a9[bc++])){bg=a4;aZ=null;a2=a4.indexOf(".")<0;a7=[];if(!a2){a7=a4.split(".");a4=a7.shift();ba=new RegExp("(^|\\.)"+a.map(a7.slice(0).sort(),A).join("\\.(?:.*\\.)?")+"(\\.|$)")}a8=a1[a4];if(!a8){continue}if(!a0){for(var bb=0;bb<a8.length;bb++){aZ=a8[bb];if(a2||ba.test(aZ.namespace)){a.event.remove(be,bg,aZ.handler,bb);a8.splice(bb--,1)}}continue}a3=a.event.special[a4]||{};for(var bb=a5||0;bb<a8.length;bb++){aZ=a8[bb];if(a0.guid===aZ.guid){if(a2||ba.test(aZ.namespace)){if(a5==null){a8.splice(bb--,1)}if(a3.remove){a3.remove.call(be,aZ)}}if(a5!=null){break}}}if(a8.length===0||a5!=null&&a8.length===1){if(!a3.teardown||a3.teardown.call(be,a7)===false){ah(be,a4,bd.handle)}bh=null;delete a1[a4]}}if(a.isEmptyObject(a1)){var bf=bd.handle;if(bf){bf.elem=null}delete bd.events;delete bd.handle;if(a.isEmptyObject(bd)){a.removeData(be)}}},trigger:function(aZ,a3,a1){var a8=aZ.type||aZ,a2=arguments[3];if(!a2){aZ=typeof aZ==="object"?aZ[aJ]?aZ:a.extend(a.Event(a8),aZ):a.Event(a8);if(a8.indexOf("!")>=0){aZ.type=a8=a8.slice(0,-1);aZ.exclusive=true}if(!a1){aZ.stopPropagation();if(a.event.global[a8]){a.each(a.cache,function(){if(this.events&&this.events[a8]){a.event.trigger(aZ,a3,this.handle.elem)}})}}if(!a1||a1.nodeType===3||a1.nodeType===8){return C}aZ.result=C;aZ.target=a1;a3=a.makeArray(a3);a3.unshift(aZ)}aZ.currentTarget=a1;var a4=a.data(a1,"handle");if(a4){a4.apply(a1,a3)}var a9=a1.parentNode||a1.ownerDocument;try{if(!(a1&&a1.nodeName&&a.noData[a1.nodeName.toLowerCase()])){if(a1["on"+a8]&&a1["on"+a8].apply(a1,a3)===false){aZ.result=false}}}catch(a6){}if(!aZ.isPropagationStopped()&&a9){a.event.trigger(aZ,a3,a9,true)}else{if(!aZ.isDefaultPrevented()){var a5=aZ.target,a0,ba=a.nodeName(a5,"a")&&a8==="click",a7=a.event.special[a8]||{};if((!a7._default||a7._default.call(a1,aZ)===false)&&!ba&&!(a5&&a5.nodeName&&a.noData[a5.nodeName.toLowerCase()])){try{if(a5[a8]){a0=a5["on"+a8];if(a0){a5["on"+a8]=null}a.event.triggered=true;a5[a8]()}}catch(a6){}if(a0){a5["on"+a8]=a0}a.event.triggered=false}}}},handle:function(aZ){var a7,a1,a0,a2,a8;aZ=arguments[0]=a.event.fix(aZ||aN.event);aZ.currentTarget=this;a7=aZ.type.indexOf(".")<0&&!aZ.exclusive;if(!a7){a0=aZ.type.split(".");aZ.type=a0.shift();a2=new RegExp("(^|\\.)"+a0.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}var a8=a.data(this,"events"),a1=a8[aZ.type];if(a8&&a1){a1=a1.slice(0);for(var a4=0,a3=a1.length;a4<a3;a4++){var a6=a1[a4];if(a7||a2.test(a6.namespace)){aZ.handler=a6.handler;aZ.data=a6.data;aZ.handleObj=a6;var a5=a6.handler.apply(this,arguments);if(a5!==C){aZ.result=a5;if(a5===false){aZ.preventDefault();aZ.stopPropagation()}}if(aZ.isImmediatePropagationStopped()){break}}}}return aZ.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a2){if(a2[aJ]){return a2}var a0=a2;a2=a.Event(a0);for(var a1=this.props.length,a4;a1;){a4=this.props[--a1];a2[a4]=a0[a4]}if(!a2.target){a2.target=a2.srcElement||ac}if(a2.target.nodeType===3){a2.target=a2.target.parentNode}if(!a2.relatedTarget&&a2.fromElement){a2.relatedTarget=a2.fromElement===a2.target?a2.toElement:a2.fromElement}if(a2.pageX==null&&a2.clientX!=null){var a3=ac.documentElement,aZ=ac.body;a2.pageX=a2.clientX+(a3&&a3.scrollLeft||aZ&&aZ.scrollLeft||0)-(a3&&a3.clientLeft||aZ&&aZ.clientLeft||0);a2.pageY=a2.clientY+(a3&&a3.scrollTop||aZ&&aZ.scrollTop||0)-(a3&&a3.clientTop||aZ&&aZ.clientTop||0)}if(!a2.which&&((a2.charCode||a2.charCode===0)?a2.charCode:a2.keyCode)){a2.which=a2.charCode||a2.keyCode}if(!a2.metaKey&&a2.ctrlKey){a2.metaKey=a2.ctrlKey}if(!a2.which&&a2.button!==C){a2.which=(a2.button&1?1:(a2.button&2?3:(a2.button&4?2:0)))}return a2},guid:100000000,proxy:a.proxy,special:{ready:{setup:a.bindReady,teardown:a.noop},live:{add:function(aZ){a.event.add(this,aZ.origType,a.extend({},aZ,{handler:W}))},remove:function(a0){var aZ=true,a1=a0.origType.replace(aD,"");a.each(a.data(this,"events").live||[],function(){if(a1===this.origType.replace(aD,"")){aZ=false;return false}});if(aZ){a.event.remove(this,a0.origType,W)}}},beforeunload:{setup:function(a1,a0,aZ){if(this.setInterval){this.onbeforeunload=aZ}return false},teardown:function(a0,aZ){if(this.onbeforeunload===aZ){this.onbeforeunload=null}}}}};var ah=ac.removeEventListener?function(a0,aZ,a1){a0.removeEventListener(aZ,a1,false)}:function(a0,aZ,a1){a0.detachEvent("on"+aZ,a1)};a.Event=function(aZ){if(!this.preventDefault){return new a.Event(aZ)}if(aZ&&aZ.type){this.originalEvent=aZ;this.type=aZ.type}else{this.type=aZ}this.timeStamp=aQ();this[aJ]=true};function aS(){return false}function f(){return true}a.Event.prototype={preventDefault:function(){this.isDefaultPrevented=f;var aZ=this.originalEvent;if(!aZ){return}if(aZ.preventDefault){aZ.preventDefault()}aZ.returnValue=false},stopPropagation:function(){this.isPropagationStopped=f;var aZ=this.originalEvent;if(!aZ){return}if(aZ.stopPropagation){aZ.stopPropagation()}aZ.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=f;this.stopPropagation()},isDefaultPrevented:aS,isPropagationStopped:aS,isImmediatePropagationStopped:aS};var R=function(a0){var aZ=a0.relatedTarget;try{while(aZ&&aZ!==this){aZ=aZ.parentNode}if(aZ!==this){a0.type=a0.data;a.event.handle.apply(this,arguments)}}catch(a1){}},az=function(aZ){aZ.type=aZ.data;a.event.handle.apply(this,arguments)};a.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a0,aZ){a.event.special[a0]={setup:function(a1){a.event.add(this,aZ,a1&&a1.selector?az:R,a0)},teardown:function(a1){a.event.remove(this,aZ,a1&&a1.selector?az:R)}}});if(!a.support.submitBubbles){a.event.special.submit={setup:function(a0,aZ){if(this.nodeName.toLowerCase()!=="form"){a.event.add(this,"click.specialSubmit",function(a3){var a2=a3.target,a1=a2.type;if((a1==="submit"||a1==="image")&&a(a2).closest("form").length){return aB("submit",this,arguments)}});a.event.add(this,"keypress.specialSubmit",function(a3){var a2=a3.target,a1=a2.type;if((a1==="text"||a1==="password")&&a(a2).closest("form").length&&a3.keyCode===13){return aB("submit",this,arguments)}})}else{return false}},teardown:function(aZ){a.event.remove(this,".specialSubmit")}}}if(!a.support.changeBubbles){var ar=/textarea|input|select/i,aT,i=function(a0){var aZ=a0.type,a1=a0.value;if(aZ==="radio"||aZ==="checkbox"){a1=a0.checked}else{if(aZ==="select-multiple"){a1=a0.selectedIndex>-1?a.map(a0.options,function(a2){return a2.selected}).join("-"):""}else{if(a0.nodeName.toLowerCase()==="select"){a1=a0.selectedIndex}}}return a1},P=function P(a1){var aZ=a1.target,a0,a2;if(!ar.test(aZ.nodeName)||aZ.readOnly){return}a0=a.data(aZ,"_change_data");a2=i(aZ);if(a1.type!=="focusout"||aZ.type!=="radio"){a.data(aZ,"_change_data",a2)}if(a0===C||a2===a0){return}if(a0!=null||a2){a1.type="change";return a.event.trigger(a1,arguments[1],aZ)}};a.event.special.change={filters:{focusout:P,click:function(a1){var a0=a1.target,aZ=a0.type;if(aZ==="radio"||aZ==="checkbox"||a0.nodeName.toLowerCase()==="select"){return P.call(this,a1)}},keydown:function(a1){var a0=a1.target,aZ=a0.type;if((a1.keyCode===13&&a0.nodeName.toLowerCase()!=="textarea")||(a1.keyCode===32&&(aZ==="checkbox"||aZ==="radio"))||aZ==="select-multiple"){return P.call(this,a1)}},beforeactivate:function(a0){var aZ=a0.target;a.data(aZ,"_change_data",i(aZ))}},setup:function(a1,a0){if(this.type==="file"){return false}for(var aZ in aT){a.event.add(this,aZ+".specialChange",aT[aZ])}return ar.test(this.nodeName)},teardown:function(aZ){a.event.remove(this,".specialChange");return ar.test(this.nodeName)}};aT=a.event.special.change.filters}function aB(a0,a1,aZ){aZ[0].type=a0;return a.event.handle.apply(a1,aZ)}if(ac.addEventListener){a.each({focus:"focusin",blur:"focusout"},function(a1,aZ){a.event.special[aZ]={setup:function(){this.addEventListener(a1,a0,true)},teardown:function(){this.removeEventListener(a1,a0,true)}};function a0(a2){a2=a.event.fix(a2);a2.type=aZ;return a.event.handle.call(this,a2)}})}a.each(["bind","one"],function(a0,aZ){a.fn[aZ]=function(a6,a7,a5){if(typeof a6==="object"){for(var a3 in a6){this[aZ](a3,a7,a6[a3],a5)}return this}if(a.isFunction(a7)){a5=a7;a7=C}var a4=aZ==="one"?a.proxy(a5,function(a8){a(this).unbind(a8,a4);return a5.apply(this,arguments)}):a5;if(a6==="unload"&&aZ!=="one"){this.one(a6,a7,a5)}else{for(var a2=0,a1=this.length;a2<a1;a2++){a.event.add(this[a2],a6,a4,a7)}}return this}});a.fn.extend({unbind:function(a3,a2){if(typeof a3==="object"&&!a3.preventDefault){for(var a1 in a3){this.unbind(a1,a3[a1])}}else{for(var a0=0,aZ=this.length;a0<aZ;a0++){a.event.remove(this[a0],a3,a2)}}return this},delegate:function(aZ,a0,a2,a1){return this.live(a0,a2,a1,aZ)},undelegate:function(aZ,a0,a1){if(arguments.length===0){return this.unbind("live")}else{return this.die(a0,null,a1,aZ)}},trigger:function(aZ,a0){return this.each(function(){a.event.trigger(aZ,a0,this)})},triggerHandler:function(aZ,a1){if(this[0]){var a0=a.Event(aZ);a0.preventDefault();a0.stopPropagation();a.event.trigger(a0,a1,this[0]);return a0.result}},toggle:function(a1){var aZ=arguments,a0=1;while(a0<aZ.length){a.proxy(a1,aZ[a0++])}return this.click(a.proxy(a1,function(a2){var a3=(a.data(this,"lastToggle"+a1.guid)||0)%a0;a.data(this,"lastToggle"+a1.guid,a3+1);a2.preventDefault();return aZ[a3].apply(this,arguments)||false}))},hover:function(aZ,a0){return this.mouseenter(aZ).mouseleave(a0||aZ)}});var ax={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};a.each(["live","die"],function(a0,aZ){a.fn[aZ]=function(a8,a5,ba,a3){var a9,a6=0,a7,a2,bb,a4=a3||this.selector,a1=a3?this:a(this.context);if(a.isFunction(a5)){ba=a5;a5=C}a8=(a8||"").split(" ");while((a9=a8[a6++])!=null){a7=aD.exec(a9);a2="";if(a7){a2=a7[0];a9=a9.replace(aD,"")}if(a9==="hover"){a8.push("mouseenter"+a2,"mouseleave"+a2);continue}bb=a9;if(a9==="focus"||a9==="blur"){a8.push(ax[a9]+a2);a9=a9+a2}else{a9=(ax[a9]||a9)+a2}if(aZ==="live"){a1.each(function(){a.event.add(this,m(a9,a4),{data:a5,selector:a4,handler:ba,origType:a9,origHandler:ba,preType:bb})})}else{a1.unbind(m(a9,a4),ba)}}return this}});function W(aZ){var a9,a0=[],bc=[],a8=arguments,bb,a7,ba,a2,a4,a6,a3,a5,bd=a.data(this,"events");if(aZ.liveFired===this||!bd||!bd.live||aZ.button&&aZ.type==="click"){return}aZ.liveFired=this;var a1=bd.live.slice(0);for(a4=0;a4<a1.length;a4++){ba=a1[a4];if(ba.origType.replace(aD,"")===aZ.type){bc.push(ba.selector)}else{a1.splice(a4--,1)}}a7=a(aZ.target).closest(bc,aZ.currentTarget);for(a6=0,a3=a7.length;a6<a3;a6++){for(a4=0;a4<a1.length;a4++){ba=a1[a4];if(a7[a6].selector===ba.selector){a2=a7[a6].elem;bb=null;if(ba.preType==="mouseenter"||ba.preType==="mouseleave"){bb=a(aZ.relatedTarget).closest(ba.selector)[0]}if(!bb||bb!==a2){a0.push({elem:a2,handleObj:ba})}}}}for(a6=0,a3=a0.length;a6<a3;a6++){a7=a0[a6];aZ.currentTarget=a7.elem;aZ.data=a7.handleObj.data;aZ.handleObj=a7.handleObj;if(a7.handleObj.origHandler.apply(a7.elem,a8)===false){a9=false;break}}return a9}function m(a0,aZ){return"live."+(a0&&a0!=="*"?a0+".":"")+aZ.replace(/\./g,"`").replace(/ /g,"&")}a.each(("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error").split(" "),function(a0,aZ){a.fn[aZ]=function(a1){return a1?this.bind(aZ,a1):this.trigger(aZ)};if(a.attrFn){a.attrFn[aZ]=true}});if(aN.attachEvent&&!aN.addEventListener){aN.attachEvent("onunload",function(){for(var a0 in a.cache){if(a.cache[a0].handle){try{a.event.remove(a.cache[a0].handle.elem)}catch(aZ){}}}})}
|
17 |
+
/*!
|
18 |
+
* Sizzle CSS Selector Engine - v1.0
|
19 |
+
* Copyright 2009, The Dojo Foundation
|
20 |
+
* Released under the MIT, BSD, and GPL Licenses.
|
21 |
+
* More information: http://sizzlejs.com/
|
22 |
+
*/
|
23 |
+
(function(){var ba=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,bb=0,bd=Object.prototype.toString,a5=false,a4=true;[0,0].sort(function(){a4=false;return 0});var a1=function(bm,bh,bp,bq){bp=bp||[];var bs=bh=bh||ac;if(bh.nodeType!==1&&bh.nodeType!==9){return[]}if(!bm||typeof bm!=="string"){return bp}var bn=[],bj,bu,bx,bi,bl=true,bk=a2(bh),br=bm;while((ba.exec(""),bj=ba.exec(br))!==null){br=bj[3];bn.push(bj[1]);if(bj[2]){bi=bj[3];break}}if(bn.length>1&&a6.exec(bm)){if(bn.length===2&&a7.relative[bn[0]]){bu=be(bn[0]+bn[1],bh)}else{bu=a7.relative[bn[0]]?[bh]:a1(bn.shift(),bh);while(bn.length){bm=bn.shift();if(a7.relative[bm]){bm+=bn.shift()}bu=be(bm,bu)}}}else{if(!bq&&bn.length>1&&bh.nodeType===9&&!bk&&a7.match.ID.test(bn[0])&&!a7.match.ID.test(bn[bn.length-1])){var bt=a1.find(bn.shift(),bh,bk);bh=bt.expr?a1.filter(bt.expr,bt.set)[0]:bt.set[0]}if(bh){var bt=bq?{expr:bn.pop(),set:a9(bq)}:a1.find(bn.pop(),bn.length===1&&(bn[0]==="~"||bn[0]==="+")&&bh.parentNode?bh.parentNode:bh,bk);bu=bt.expr?a1.filter(bt.expr,bt.set):bt.set;if(bn.length>0){bx=a9(bu)}else{bl=false}while(bn.length){var bw=bn.pop(),bv=bw;if(!a7.relative[bw]){bw=""}else{bv=bn.pop()}if(bv==null){bv=bh}a7.relative[bw](bx,bv,bk)}}else{bx=bn=[]}}if(!bx){bx=bu}if(!bx){a1.error(bw||bm)}if(bd.call(bx)==="[object Array]"){if(!bl){bp.push.apply(bp,bx)}else{if(bh&&bh.nodeType===1){for(var bo=0;bx[bo]!=null;bo++){if(bx[bo]&&(bx[bo]===true||bx[bo].nodeType===1&&a8(bh,bx[bo]))){bp.push(bu[bo])}}}else{for(var bo=0;bx[bo]!=null;bo++){if(bx[bo]&&bx[bo].nodeType===1){bp.push(bu[bo])}}}}}else{a9(bx,bp)}if(bi){a1(bi,bs,bp,bq);a1.uniqueSort(bp)}return bp};a1.uniqueSort=function(bi){if(bc){a5=a4;bi.sort(bc);if(a5){for(var bh=1;bh<bi.length;bh++){if(bi[bh]===bi[bh-1]){bi.splice(bh--,1)}}}}return bi};a1.matches=function(bh,bi){return a1(bh,null,null,bi)};a1.find=function(bo,bh,bp){var bn,bl;if(!bo){return[]}for(var bk=0,bj=a7.order.length;bk<bj;bk++){var bm=a7.order[bk],bl;if((bl=a7.leftMatch[bm].exec(bo))){var bi=bl[1];bl.splice(1,1);if(bi.substr(bi.length-1)!=="\\"){bl[1]=(bl[1]||"").replace(/\\/g,"");bn=a7.find[bm](bl,bh,bp);if(bn!=null){bo=bo.replace(a7.match[bm],"");break}}}}if(!bn){bn=bh.getElementsByTagName("*")}return{set:bn,expr:bo}};a1.filter=function(bs,br,bv,bl){var bj=bs,bx=[],bp=br,bn,bh,bo=br&&br[0]&&a2(br[0]);while(bs&&br.length){for(var bq in a7.filter){if((bn=a7.leftMatch[bq].exec(bs))!=null&&bn[2]){var bi=a7.filter[bq],bw,bu,bk=bn[1];bh=false;bn.splice(1,1);if(bk.substr(bk.length-1)==="\\"){continue}if(bp===bx){bx=[]}if(a7.preFilter[bq]){bn=a7.preFilter[bq](bn,bp,bv,bx,bl,bo);if(!bn){bh=bw=true}else{if(bn===true){continue}}}if(bn){for(var bm=0;(bu=bp[bm])!=null;bm++){if(bu){bw=bi(bu,bn,bm,bp);var bt=bl^!!bw;if(bv&&bw!=null){if(bt){bh=true}else{bp[bm]=false}}else{if(bt){bx.push(bu);bh=true}}}}}if(bw!==C){if(!bv){bp=bx}bs=bs.replace(a7.match[bq],"");if(!bh){return[]}break}}}if(bs===bj){if(bh==null){a1.error(bs)}else{break}}bj=bs}return bp};a1.error=function(bh){throw"Syntax error, unrecognized expression: "+bh};var a7=a1.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(bh){return bh.getAttribute("href")}},relative:{"+":function(bn,bi){var bk=typeof bi==="string",bm=bk&&!/\W/.test(bi),bo=bk&&!bm;if(bm){bi=bi.toLowerCase()}for(var bj=0,bh=bn.length,bl;bj<bh;bj++){if((bl=bn[bj])){while((bl=bl.previousSibling)&&bl.nodeType!==1){}bn[bj]=bo||bl&&bl.nodeName.toLowerCase()===bi?bl||false:bl===bi}}if(bo){a1.filter(bi,bn,true)}},">":function(bn,bi){var bl=typeof bi==="string";if(bl&&!/\W/.test(bi)){bi=bi.toLowerCase();for(var bj=0,bh=bn.length;bj<bh;bj++){var bm=bn[bj];if(bm){var bk=bm.parentNode;bn[bj]=bk.nodeName.toLowerCase()===bi?bk:false}}}else{for(var bj=0,bh=bn.length;bj<bh;bj++){var bm=bn[bj];if(bm){bn[bj]=bl?bm.parentNode:bm.parentNode===bi}}if(bl){a1.filter(bi,bn,true)}}},"":function(bk,bi,bm){var bj=bb++,bh=bf;if(typeof bi==="string"&&!/\W/.test(bi)){var bl=bi=bi.toLowerCase();bh=aZ}bh("parentNode",bi,bj,bk,bl,bm)},"~":function(bk,bi,bm){var bj=bb++,bh=bf;if(typeof bi==="string"&&!/\W/.test(bi)){var bl=bi=bi.toLowerCase();bh=aZ}bh("previousSibling",bi,bj,bk,bl,bm)}},find:{ID:function(bi,bj,bk){if(typeof bj.getElementById!=="undefined"&&!bk){var bh=bj.getElementById(bi[1]);return bh?[bh]:[]}},NAME:function(bj,bm){if(typeof bm.getElementsByName!=="undefined"){var bi=[],bl=bm.getElementsByName(bj[1]);for(var bk=0,bh=bl.length;bk<bh;bk++){if(bl[bk].getAttribute("name")===bj[1]){bi.push(bl[bk])}}return bi.length===0?null:bi}},TAG:function(bh,bi){return bi.getElementsByTagName(bh[1])}},preFilter:{CLASS:function(bk,bi,bj,bh,bn,bo){bk=" "+bk[1].replace(/\\/g,"")+" ";if(bo){return bk}for(var bl=0,bm;(bm=bi[bl])!=null;bl++){if(bm){if(bn^(bm.className&&(" "+bm.className+" ").replace(/[\t\n]/g," ").indexOf(bk)>=0)){if(!bj){bh.push(bm)}}else{if(bj){bi[bl]=false}}}}return false},ID:function(bh){return bh[1].replace(/\\/g,"")},TAG:function(bi,bh){return bi[1].toLowerCase()},CHILD:function(bh){if(bh[1]==="nth"){var bi=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(bh[2]==="even"&&"2n"||bh[2]==="odd"&&"2n+1"||!/\D/.test(bh[2])&&"0n+"+bh[2]||bh[2]);bh[2]=(bi[1]+(bi[2]||1))-0;bh[3]=bi[3]-0}bh[0]=bb++;return bh},ATTR:function(bl,bi,bj,bh,bm,bn){var bk=bl[1].replace(/\\/g,"");if(!bn&&a7.attrMap[bk]){bl[1]=a7.attrMap[bk]}if(bl[2]==="~="){bl[4]=" "+bl[4]+" "}return bl},PSEUDO:function(bl,bi,bj,bh,bm){if(bl[1]==="not"){if((ba.exec(bl[3])||"").length>1||/^\w/.test(bl[3])){bl[3]=a1(bl[3],null,null,bi)}else{var bk=a1.filter(bl[3],bi,bj,true^bm);if(!bj){bh.push.apply(bh,bk)}return false}}else{if(a7.match.POS.test(bl[0])||a7.match.CHILD.test(bl[0])){return true}}return bl},POS:function(bh){bh.unshift(true);return bh}},filters:{enabled:function(bh){return bh.disabled===false&&bh.type!=="hidden"},disabled:function(bh){return bh.disabled===true},checked:function(bh){return bh.checked===true},selected:function(bh){bh.parentNode.selectedIndex;return bh.selected===true},parent:function(bh){return !!bh.firstChild},empty:function(bh){return !bh.firstChild},has:function(bj,bi,bh){return !!a1(bh[3],bj).length},header:function(bh){return/h\d/i.test(bh.nodeName)},text:function(bh){return"text"===bh.type},radio:function(bh){return"radio"===bh.type},checkbox:function(bh){return"checkbox"===bh.type},file:function(bh){return"file"===bh.type},password:function(bh){return"password"===bh.type},submit:function(bh){return"submit"===bh.type},image:function(bh){return"image"===bh.type},reset:function(bh){return"reset"===bh.type},button:function(bh){return"button"===bh.type||bh.nodeName.toLowerCase()==="button"},input:function(bh){return/input|select|textarea|button/i.test(bh.nodeName)}},setFilters:{first:function(bi,bh){return bh===0},last:function(bj,bi,bh,bk){return bi===bk.length-1},even:function(bi,bh){return bh%2===0},odd:function(bi,bh){return bh%2===1},lt:function(bj,bi,bh){return bi<bh[3]-0},gt:function(bj,bi,bh){return bi>bh[3]-0},nth:function(bj,bi,bh){return bh[3]-0===bi},eq:function(bj,bi,bh){return bh[3]-0===bi}},filter:{PSEUDO:function(bn,bj,bk,bo){var bi=bj[1],bl=a7.filters[bi];if(bl){return bl(bn,bk,bj,bo)}else{if(bi==="contains"){return(bn.textContent||bn.innerText||a0([bn])||"").indexOf(bj[3])>=0}else{if(bi==="not"){var bm=bj[3];for(var bk=0,bh=bm.length;bk<bh;bk++){if(bm[bk]===bn){return false}}return true}else{a1.error("Syntax error, unrecognized expression: "+bi)}}}},CHILD:function(bh,bk){var bn=bk[1],bi=bh;switch(bn){case"only":case"first":while((bi=bi.previousSibling)){if(bi.nodeType===1){return false}}if(bn==="first"){return true}bi=bh;case"last":while((bi=bi.nextSibling)){if(bi.nodeType===1){return false}}return true;case"nth":var bj=bk[2],bq=bk[3];if(bj===1&&bq===0){return true}var bm=bk[0],bp=bh.parentNode;if(bp&&(bp.sizcache!==bm||!bh.nodeIndex)){var bl=0;for(bi=bp.firstChild;bi;bi=bi.nextSibling){if(bi.nodeType===1){bi.nodeIndex=++bl}}bp.sizcache=bm}var bo=bh.nodeIndex-bq;if(bj===0){return bo===0}else{return(bo%bj===0&&bo/bj>=0)}}},ID:function(bi,bh){return bi.nodeType===1&&bi.getAttribute("id")===bh},TAG:function(bi,bh){return(bh==="*"&&bi.nodeType===1)||bi.nodeName.toLowerCase()===bh},CLASS:function(bi,bh){return(" "+(bi.className||bi.getAttribute("class"))+" ").indexOf(bh)>-1},ATTR:function(bm,bk){var bj=bk[1],bh=a7.attrHandle[bj]?a7.attrHandle[bj](bm):bm[bj]!=null?bm[bj]:bm.getAttribute(bj),bn=bh+"",bl=bk[2],bi=bk[4];return bh==null?bl==="!=":bl==="="?bn===bi:bl==="*="?bn.indexOf(bi)>=0:bl==="~="?(" "+bn+" ").indexOf(bi)>=0:!bi?bn&&bh!==false:bl==="!="?bn!==bi:bl==="^="?bn.indexOf(bi)===0:bl==="$="?bn.substr(bn.length-bi.length)===bi:bl==="|="?bn===bi||bn.substr(0,bi.length+1)===bi+"-":false},POS:function(bl,bi,bj,bm){var bh=bi[2],bk=a7.setFilters[bh];if(bk){return bk(bl,bj,bi,bm)}}}};var a6=a7.match.POS;for(var a3 in a7.match){a7.match[a3]=new RegExp(a7.match[a3].source+/(?![^\[]*\])(?![^\(]*\))/.source);a7.leftMatch[a3]=new RegExp(/(^(?:.|\r|\n)*?)/.source+a7.match[a3].source.replace(/\\(\d+)/g,function(bi,bh){return"\\"+(bh-0+1)}))}var a9=function(bi,bh){bi=Array.prototype.slice.call(bi,0);if(bh){bh.push.apply(bh,bi);return bh}return bi};try{Array.prototype.slice.call(ac.documentElement.childNodes,0)[0].nodeType}catch(bg){a9=function(bl,bk){var bi=bk||[];if(bd.call(bl)==="[object Array]"){Array.prototype.push.apply(bi,bl)}else{if(typeof bl.length==="number"){for(var bj=0,bh=bl.length;bj<bh;bj++){bi.push(bl[bj])}}else{for(var bj=0;bl[bj];bj++){bi.push(bl[bj])}}}return bi}}var bc;if(ac.documentElement.compareDocumentPosition){bc=function(bi,bh){if(!bi.compareDocumentPosition||!bh.compareDocumentPosition){if(bi==bh){a5=true}return bi.compareDocumentPosition?-1:1}var bj=bi.compareDocumentPosition(bh)&4?-1:bi===bh?0:1;if(bj===0){a5=true}return bj}}else{if("sourceIndex" in ac.documentElement){bc=function(bi,bh){if(!bi.sourceIndex||!bh.sourceIndex){if(bi==bh){a5=true}return bi.sourceIndex?-1:1}var bj=bi.sourceIndex-bh.sourceIndex;if(bj===0){a5=true}return bj}}else{if(ac.createRange){bc=function(bk,bi){if(!bk.ownerDocument||!bi.ownerDocument){if(bk==bi){a5=true}return bk.ownerDocument?-1:1}var bj=bk.ownerDocument.createRange(),bh=bi.ownerDocument.createRange();bj.setStart(bk,0);bj.setEnd(bk,0);bh.setStart(bi,0);bh.setEnd(bi,0);var bl=bj.compareBoundaryPoints(Range.START_TO_END,bh);if(bl===0){a5=true}return bl}}}}function a0(bh){var bi="",bk;for(var bj=0;bh[bj];bj++){bk=bh[bj];if(bk.nodeType===3||bk.nodeType===4){bi+=bk.nodeValue}else{if(bk.nodeType!==8){bi+=a0(bk.childNodes)}}}return bi}(function(){var bi=ac.createElement("div"),bj="script"+(new Date).getTime();bi.innerHTML="<a name='"+bj+"'/>";var bh=ac.documentElement;bh.insertBefore(bi,bh.firstChild);if(ac.getElementById(bj)){a7.find.ID=function(bl,bm,bn){if(typeof bm.getElementById!=="undefined"&&!bn){var bk=bm.getElementById(bl[1]);return bk?bk.id===bl[1]||typeof bk.getAttributeNode!=="undefined"&&bk.getAttributeNode("id").nodeValue===bl[1]?[bk]:C:[]}};a7.filter.ID=function(bm,bk){var bl=typeof bm.getAttributeNode!=="undefined"&&bm.getAttributeNode("id");return bm.nodeType===1&&bl&&bl.nodeValue===bk}}bh.removeChild(bi);bh=bi=null})();(function(){var bh=ac.createElement("div");bh.appendChild(ac.createComment(""));if(bh.getElementsByTagName("*").length>0){a7.find.TAG=function(bi,bm){var bl=bm.getElementsByTagName(bi[1]);if(bi[1]==="*"){var bk=[];for(var bj=0;bl[bj];bj++){if(bl[bj].nodeType===1){bk.push(bl[bj])}}bl=bk}return bl}}bh.innerHTML="<a href='#'></a>";if(bh.firstChild&&typeof bh.firstChild.getAttribute!=="undefined"&&bh.firstChild.getAttribute("href")!=="#"){a7.attrHandle.href=function(bi){return bi.getAttribute("href",2)}}bh=null})();if(ac.querySelectorAll){(function(){var bh=a1,bj=ac.createElement("div");bj.innerHTML="<p class='TEST'></p>";if(bj.querySelectorAll&&bj.querySelectorAll(".TEST").length===0){return}a1=function(bn,bm,bk,bl){bm=bm||ac;if(!bl&&bm.nodeType===9&&!a2(bm)){try{return a9(bm.querySelectorAll(bn),bk)}catch(bo){}}return bh(bn,bm,bk,bl)};for(var bi in bh){a1[bi]=bh[bi]}bj=null})()}(function(){var bh=ac.createElement("div");bh.innerHTML="<div class='test e'></div><div class='test'></div>";if(!bh.getElementsByClassName||bh.getElementsByClassName("e").length===0){return}bh.lastChild.className="e";if(bh.getElementsByClassName("e").length===1){return}a7.order.splice(1,0,"CLASS");a7.find.CLASS=function(bi,bj,bk){if(typeof bj.getElementsByClassName!=="undefined"&&!bk){return bj.getElementsByClassName(bi[1])}};bh=null})();function aZ(bi,bn,bm,bq,bo,bp){for(var bk=0,bj=bq.length;bk<bj;bk++){var bh=bq[bk];if(bh){bh=bh[bi];var bl=false;while(bh){if(bh.sizcache===bm){bl=bq[bh.sizset];break}if(bh.nodeType===1&&!bp){bh.sizcache=bm;bh.sizset=bk}if(bh.nodeName.toLowerCase()===bn){bl=bh;break}bh=bh[bi]}bq[bk]=bl}}}function bf(bi,bn,bm,bq,bo,bp){for(var bk=0,bj=bq.length;bk<bj;bk++){var bh=bq[bk];if(bh){bh=bh[bi];var bl=false;while(bh){if(bh.sizcache===bm){bl=bq[bh.sizset];break}if(bh.nodeType===1){if(!bp){bh.sizcache=bm;bh.sizset=bk}if(typeof bn!=="string"){if(bh===bn){bl=true;break}}else{if(a1.filter(bn,[bh]).length>0){bl=bh;break}}}bh=bh[bi]}bq[bk]=bl}}}var a8=ac.compareDocumentPosition?function(bi,bh){return !!(bi.compareDocumentPosition(bh)&16)}:function(bi,bh){return bi!==bh&&(bi.contains?bi.contains(bh):true)};var a2=function(bh){var bi=(bh?bh.ownerDocument||bh:0).documentElement;return bi?bi.nodeName!=="HTML":false};var be=function(bh,bo){var bk=[],bl="",bm,bj=bo.nodeType?[bo]:bo;while((bm=a7.match.PSEUDO.exec(bh))){bl+=bm[0];bh=bh.replace(a7.match.PSEUDO,"")}bh=a7.relative[bh]?bh+"*":bh;for(var bn=0,bi=bj.length;bn<bi;bn++){a1(bh,bj[bn],bk)}return a1.filter(bl,bk)};a.find=a1;a.expr=a1.selectors;a.expr[":"]=a.expr.filters;a.unique=a1.uniqueSort;a.text=a0;a.isXMLDoc=a2;a.contains=a8;return;aN.Sizzle=a1})();var O=/Until$/,Z=/^(?:parents|prevUntil|prevAll)/,aM=/,/,G=Array.prototype.slice;var aj=function(a2,a1,aZ){if(a.isFunction(a1)){return a.grep(a2,function(a4,a3){return !!a1.call(a4,a3,a4)===aZ})}else{if(a1.nodeType){return a.grep(a2,function(a4,a3){return(a4===a1)===aZ})}else{if(typeof a1==="string"){var a0=a.grep(a2,function(a3){return a3.nodeType===1});if(aX.test(a1)){return a.filter(a1,a0,!aZ)}else{a1=a.filter(a1,a0)}}}}return a.grep(a2,function(a4,a3){return(a.inArray(a4,a1)>=0)===aZ})};a.fn.extend({find:function(aZ){var a1=this.pushStack("","find",aZ),a4=0;for(var a2=0,a0=this.length;a2<a0;a2++){a4=a1.length;a.find(aZ,this[a2],a1);if(a2>0){for(var a5=a4;a5<a1.length;a5++){for(var a3=0;a3<a4;a3++){if(a1[a3]===a1[a5]){a1.splice(a5--,1);break}}}}}return a1},has:function(a0){var aZ=a(a0);return this.filter(function(){for(var a2=0,a1=aZ.length;a2<a1;a2++){if(a.contains(this,aZ[a2])){return true}}})},not:function(aZ){return this.pushStack(aj(this,aZ,false),"not",aZ)},filter:function(aZ){return this.pushStack(aj(this,aZ,true),"filter",aZ)},is:function(aZ){return !!aZ&&a.filter(aZ,this).length>0},closest:function(a8,aZ){if(a.isArray(a8)){var a5=[],a7=this[0],a4,a3={},a1;if(a7&&a8.length){for(var a2=0,a0=a8.length;a2<a0;a2++){a1=a8[a2];if(!a3[a1]){a3[a1]=a.expr.match.POS.test(a1)?a(a1,aZ||this.context):a1}}while(a7&&a7.ownerDocument&&a7!==aZ){for(a1 in a3){a4=a3[a1];if(a4.jquery?a4.index(a7)>-1:a(a7).is(a4)){a5.push({selector:a1,elem:a7});delete a3[a1]}}a7=a7.parentNode}}return a5}var a6=a.expr.match.POS.test(a8)?a(a8,aZ||this.context):null;return this.map(function(a9,ba){while(ba&&ba.ownerDocument&&ba!==aZ){if(a6?a6.index(ba)>-1:a(ba).is(a8)){return ba}ba=ba.parentNode}return null})},index:function(aZ){if(!aZ||typeof aZ==="string"){return a.inArray(this[0],aZ?a(aZ):this.parent().children())}return a.inArray(aZ.jquery?aZ[0]:aZ,this)},add:function(aZ,a0){var a2=typeof aZ==="string"?a(aZ,a0||this.context):a.makeArray(aZ),a1=a.merge(this.get(),a2);return this.pushStack(y(a2[0])||y(a1[0])?a1:a.unique(a1))},andSelf:function(){return this.add(this.prevObject)}});function y(aZ){return !aZ||!aZ.parentNode||aZ.parentNode.nodeType===11}a.each({parent:function(a0){var aZ=a0.parentNode;return aZ&&aZ.nodeType!==11?aZ:null},parents:function(aZ){return a.dir(aZ,"parentNode")},parentsUntil:function(a0,aZ,a1){return a.dir(a0,"parentNode",a1)},next:function(aZ){return a.nth(aZ,2,"nextSibling")},prev:function(aZ){return a.nth(aZ,2,"previousSibling")},nextAll:function(aZ){return a.dir(aZ,"nextSibling")},prevAll:function(aZ){return a.dir(aZ,"previousSibling")},nextUntil:function(a0,aZ,a1){return a.dir(a0,"nextSibling",a1)},prevUntil:function(a0,aZ,a1){return a.dir(a0,"previousSibling",a1)},siblings:function(aZ){return a.sibling(aZ.parentNode.firstChild,aZ)},children:function(aZ){return a.sibling(aZ.firstChild)},contents:function(aZ){return a.nodeName(aZ,"iframe")?aZ.contentDocument||aZ.contentWindow.document:a.makeArray(aZ.childNodes)}},function(aZ,a0){a.fn[aZ]=function(a3,a1){var a2=a.map(this,a0,a3);if(!O.test(aZ)){a1=a3}if(a1&&typeof a1==="string"){a2=a.filter(a1,a2)}a2=this.length>1?a.unique(a2):a2;if((this.length>1||aM.test(a1))&&Z.test(aZ)){a2=a2.reverse()}return this.pushStack(a2,aZ,G.call(arguments).join(","))}});a.extend({filter:function(a1,aZ,a0){if(a0){a1=":not("+a1+")"}return a.find.matches(a1,aZ)},dir:function(a1,a0,a3){var aZ=[],a2=a1[a0];while(a2&&a2.nodeType!==9&&(a3===C||a2.nodeType!==1||!a(a2).is(a3))){if(a2.nodeType===1){aZ.push(a2)}a2=a2[a0]}return aZ},nth:function(a3,aZ,a1,a2){aZ=aZ||1;var a0=0;for(;a3;a3=a3[a1]){if(a3.nodeType===1&&++a0===aZ){break}}return a3},sibling:function(a1,a0){var aZ=[];for(;a1;a1=a1.nextSibling){if(a1.nodeType===1&&a1!==a0){aZ.push(a1)}}return aZ}});var U=/ jQuery\d+="(?:\d+|null)"/g,aa=/^\s+/,I=/(<([\w:]+)[^>]*?)\/>/g,am=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,c=/<([\w:]+)/,t=/<tbody/i,M=/<|&#?\w+;/,F=/<script|<object|<embed|<option|<style/i,l=/checked\s*(?:[^=]|=\s*.checked.)/i,p=function(a0,a1,aZ){return am.test(aZ)?a0:a1+"></"+aZ+">"},ad={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};ad.optgroup=ad.option;ad.tbody=ad.tfoot=ad.colgroup=ad.caption=ad.thead;ad.th=ad.td;if(!a.support.htmlSerialize){ad._default=[1,"div<div>","</div>"]}a.fn.extend({text:function(aZ){if(a.isFunction(aZ)){return this.each(function(a1){var a0=a(this);a0.text(aZ.call(this,a1,a0.text()))})}if(typeof aZ!=="object"&&aZ!==C){return this.empty().append((this[0]&&this[0].ownerDocument||ac).createTextNode(aZ))}return a.text(this)},wrapAll:function(aZ){if(a.isFunction(aZ)){return this.each(function(a1){a(this).wrapAll(aZ.call(this,a1))})}if(this[0]){var a0=a(aZ,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){a0.insertBefore(this[0])}a0.map(function(){var a1=this;while(a1.firstChild&&a1.firstChild.nodeType===1){a1=a1.firstChild}return a1}).append(this)}return this},wrapInner:function(aZ){if(a.isFunction(aZ)){return this.each(function(a0){a(this).wrapInner(aZ.call(this,a0))})}return this.each(function(){var a0=a(this),a1=a0.contents();if(a1.length){a1.wrapAll(aZ)}else{a0.append(aZ)}})},wrap:function(aZ){return this.each(function(){a(this).wrapAll(aZ)})},unwrap:function(){return this.parent().each(function(){if(!a.nodeName(this,"body")){a(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(aZ){if(this.nodeType===1){this.appendChild(aZ)}})},prepend:function(){return this.domManip(arguments,true,function(aZ){if(this.nodeType===1){this.insertBefore(aZ,this.firstChild)}})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(a0){this.parentNode.insertBefore(a0,this)})}else{if(arguments.length){var aZ=a(arguments[0]);aZ.push.apply(aZ,this.toArray());return this.pushStack(aZ,"before",arguments)}}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(a0){this.parentNode.insertBefore(a0,this.nextSibling)})}else{if(arguments.length){var aZ=this.pushStack(this,"after",arguments);aZ.push.apply(aZ,a(arguments[0]).toArray());return aZ}}},remove:function(aZ,a2){for(var a0=0,a1;(a1=this[a0])!=null;a0++){if(!aZ||a.filter(aZ,[a1]).length){if(!a2&&a1.nodeType===1){a.cleanData(a1.getElementsByTagName("*"));a.cleanData([a1])}if(a1.parentNode){a1.parentNode.removeChild(a1)}}}return this},empty:function(){for(var aZ=0,a0;(a0=this[aZ])!=null;aZ++){if(a0.nodeType===1){a.cleanData(a0.getElementsByTagName("*"))}while(a0.firstChild){a0.removeChild(a0.firstChild)}}return this},clone:function(a0){var aZ=this.map(function(){if(!a.support.noCloneEvent&&!a.isXMLDoc(this)){var a2=this.outerHTML,a1=this.ownerDocument;if(!a2){var a3=a1.createElement("div");a3.appendChild(this.cloneNode(true));a2=a3.innerHTML}return a.clean([a2.replace(U,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(aa,"")],a1)[0]}else{return this.cloneNode(true)}});if(a0===true){q(this,aZ);q(this.find("*"),aZ.find("*"))}return aZ},html:function(a1){if(a1===C){return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(U,""):null}else{if(typeof a1==="string"&&!F.test(a1)&&(a.support.leadingWhitespace||!aa.test(a1))&&!ad[(c.exec(a1)||["",""])[1].toLowerCase()]){a1=a1.replace(I,p);try{for(var a0=0,aZ=this.length;a0<aZ;a0++){if(this[a0].nodeType===1){a.cleanData(this[a0].getElementsByTagName("*"));this[a0].innerHTML=a1}}}catch(a2){this.empty().append(a1)}}else{if(a.isFunction(a1)){this.each(function(a5){var a4=a(this),a3=a4.html();a4.empty().append(function(){return a1.call(this,a5,a3)})})}else{this.empty().append(a1)}}}return this},replaceWith:function(aZ){if(this[0]&&this[0].parentNode){if(a.isFunction(aZ)){return this.each(function(a2){var a1=a(this),a0=a1.html();a1.replaceWith(aZ.call(this,a2,a0))})}if(typeof aZ!=="string"){aZ=a(aZ).detach()}return this.each(function(){var a1=this.nextSibling,a0=this.parentNode;a(this).remove();if(a1){a(a1).before(aZ)}else{a(a0).append(aZ)}})}else{return this.pushStack(a(a.isFunction(aZ)?aZ():aZ),"replaceWith",aZ)}},detach:function(aZ){return this.remove(aZ,true)},domManip:function(a5,ba,a9){var a2,a3,a8=a5[0],a0=[],a4,a7;if(!a.support.checkClone&&arguments.length===3&&typeof a8==="string"&&l.test(a8)){return this.each(function(){a(this).domManip(a5,ba,a9,true)})}if(a.isFunction(a8)){return this.each(function(bc){var bb=a(this);a5[0]=a8.call(this,bc,ba?bb.html():C);bb.domManip(a5,ba,a9)})}if(this[0]){a7=a8&&a8.parentNode;if(a.support.parentNode&&a7&&a7.nodeType===11&&a7.childNodes.length===this.length){a2={fragment:a7}}else{a2=K(a5,this,a0)}a4=a2.fragment;if(a4.childNodes.length===1){a3=a4=a4.firstChild}else{a3=a4.firstChild}if(a3){ba=ba&&a.nodeName(a3,"tr");for(var a1=0,aZ=this.length;a1<aZ;a1++){a9.call(ba?a6(this[a1],a3):this[a1],a1>0||a2.cacheable||this.length>1?a4.cloneNode(true):a4)}}if(a0.length){a.each(a0,aW)}}return this;function a6(bb,bc){return a.nodeName(bb,"table")?(bb.getElementsByTagName("tbody")[0]||bb.appendChild(bb.ownerDocument.createElement("tbody"))):bb}}});function q(a1,aZ){var a0=0;aZ.each(function(){if(this.nodeName!==(a1[a0]&&a1[a0].nodeName)){return}var a6=a.data(a1[a0++]),a5=a.data(this,a6),a2=a6&&a6.events;if(a2){delete a5.handle;a5.events={};for(var a4 in a2){for(var a3 in a2[a4]){a.event.add(this,a4,a2[a4][a3],a2[a4][a3].data)}}}})}function K(a4,a2,a0){var a3,aZ,a1,a5=(a2&&a2[0]?a2[0].ownerDocument||a2[0]:ac);if(a4.length===1&&typeof a4[0]==="string"&&a4[0].length<512&&a5===ac&&!F.test(a4[0])&&(a.support.checkClone||!l.test(a4[0]))){aZ=true;a1=a.fragments[a4[0]];if(a1){if(a1!==1){a3=a1}}}if(!a3){a3=a5.createDocumentFragment();a.clean(a4,a5,a3,a0)}if(aZ){a.fragments[a4[0]]=a1?a3:1}return{fragment:a3,cacheable:aZ}}a.fragments={};a.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(aZ,a0){a.fn[aZ]=function(a1){var a4=[],a7=a(a1),a6=this.length===1&&this[0].parentNode;if(a6&&a6.nodeType===11&&a6.childNodes.length===1&&a7.length===1){a7[a0](this[0]);return this}else{for(var a5=0,a2=a7.length;a5<a2;a5++){var a3=(a5>0?this.clone(true):this).get();a.fn[a0].apply(a(a7[a5]),a3);a4=a4.concat(a3)}return this.pushStack(a4,aZ,a7.selector)}}});a.extend({clean:function(a1,a3,ba,a5){a3=a3||ac;if(typeof a3.createElement==="undefined"){a3=a3.ownerDocument||a3[0]&&a3[0].ownerDocument||ac}var bb=[];for(var a9=0,a4;(a4=a1[a9])!=null;a9++){if(typeof a4==="number"){a4+=""}if(!a4){continue}if(typeof a4==="string"&&!M.test(a4)){a4=a3.createTextNode(a4)}else{if(typeof a4==="string"){a4=a4.replace(I,p);var bc=(c.exec(a4)||["",""])[1].toLowerCase(),a2=ad[bc]||ad._default,a8=a2[0],a0=a3.createElement("div");a0.innerHTML=a2[1]+a4+a2[2];while(a8--){a0=a0.lastChild}if(!a.support.tbody){var aZ=t.test(a4),a7=bc==="table"&&!aZ?a0.firstChild&&a0.firstChild.childNodes:a2[1]==="<table>"&&!aZ?a0.childNodes:[];for(var a6=a7.length-1;a6>=0;--a6){if(a.nodeName(a7[a6],"tbody")&&!a7[a6].childNodes.length){a7[a6].parentNode.removeChild(a7[a6])}}}if(!a.support.leadingWhitespace&&aa.test(a4)){a0.insertBefore(a3.createTextNode(aa.exec(a4)[0]),a0.firstChild)}a4=a0.childNodes}}if(a4.nodeType){bb.push(a4)}else{bb=a.merge(bb,a4)}}if(ba){for(var a9=0;bb[a9];a9++){if(a5&&a.nodeName(bb[a9],"script")&&(!bb[a9].type||bb[a9].type.toLowerCase()==="text/javascript")){a5.push(bb[a9].parentNode?bb[a9].parentNode.removeChild(bb[a9]):bb[a9])}else{if(bb[a9].nodeType===1){bb.splice.apply(bb,[a9+1,0].concat(a.makeArray(bb[a9].getElementsByTagName("script"))))}ba.appendChild(bb[a9])}}}return bb},cleanData:function(a0){var a3,a1,aZ=a.cache,a6=a.event.special,a5=a.support.deleteExpando;for(var a4=0,a2;(a2=a0[a4])!=null;a4++){a1=a2[a.expando];if(a1){a3=aZ[a1];if(a3.events){for(var a7 in a3.events){if(a6[a7]){a.event.remove(a2,a7)}else{ah(a2,a7,a3.handle)}}}if(a5){delete a2[a.expando]}else{if(a2.removeAttribute){a2.removeAttribute(a.expando)}}delete aZ[a1]}}}});var at=/z-?index|font-?weight|opacity|zoom|line-?height/i,V=/alpha\([^)]*\)/,ab=/opacity=([^)]*)/,ai=/float/i,aA=/-([a-z])/ig,v=/([A-Z])/g,aP=/^-?\d+(?:px)?$/i,aV=/^-?\d/,aL={position:"absolute",visibility:"hidden",display:"block"},X=["Left","Right"],aF=["Top","Bottom"],al=ac.defaultView&&ac.defaultView.getComputedStyle,aO=a.support.cssFloat?"cssFloat":"styleFloat",k=function(aZ,a0){return a0.toUpperCase()};a.fn.css=function(aZ,a0){return ao(this,aZ,a0,true,function(a2,a1,a3){if(a3===C){return a.curCSS(a2,a1)}if(typeof a3==="number"&&!at.test(a1)){a3+="px"}a.style(a2,a1,a3)})};a.extend({style:function(a3,a0,a4){if(!a3||a3.nodeType===3||a3.nodeType===8){return C}if((a0==="width"||a0==="height")&&parseFloat(a4)<0){a4=C}var a2=a3.style||a3,a5=a4!==C;if(!a.support.opacity&&a0==="opacity"){if(a5){a2.zoom=1;var aZ=parseInt(a4,10)+""==="NaN"?"":"alpha(opacity="+a4*100+")";var a1=a2.filter||a.curCSS(a3,"filter")||"";a2.filter=V.test(a1)?a1.replace(V,aZ):aZ}return a2.filter&&a2.filter.indexOf("opacity=")>=0?(parseFloat(ab.exec(a2.filter)[1])/100)+"":""}if(ai.test(a0)){a0=aO}a0=a0.replace(aA,k);if(a5){a2[a0]=a4}return a2[a0]},css:function(a2,a0,a4,aZ){if(a0==="width"||a0==="height"){var a6,a1=aL,a5=a0==="width"?X:aF;function a3(){a6=a0==="width"?a2.offsetWidth:a2.offsetHeight;if(aZ==="border"){return}a.each(a5,function(){if(!aZ){a6-=parseFloat(a.curCSS(a2,"padding"+this,true))||0}if(aZ==="margin"){a6+=parseFloat(a.curCSS(a2,"margin"+this,true))||0}else{a6-=parseFloat(a.curCSS(a2,"border"+this+"Width",true))||0}})}if(a2.offsetWidth!==0){a3()}else{a.swap(a2,a1,a3)}return Math.max(0,Math.round(a6))}return a.curCSS(a2,a0,a4)},curCSS:function(a5,a0,a1){var a8,aZ=a5.style,a2;if(!a.support.opacity&&a0==="opacity"&&a5.currentStyle){a8=ab.test(a5.currentStyle.filter||"")?(parseFloat(RegExp.$1)/100)+"":"";return a8===""?"1":a8}if(ai.test(a0)){a0=aO}if(!a1&&aZ&&aZ[a0]){a8=aZ[a0]}else{if(al){if(ai.test(a0)){a0="float"}a0=a0.replace(v,"-$1").toLowerCase();var a7=a5.ownerDocument.defaultView;if(!a7){return null}var a9=a7.getComputedStyle(a5,null);if(a9){a8=a9.getPropertyValue(a0)}if(a0==="opacity"&&a8===""){a8="1"}}else{if(a5.currentStyle){var a4=a0.replace(aA,k);a8=a5.currentStyle[a0]||a5.currentStyle[a4];if(!aP.test(a8)&&aV.test(a8)){var a3=aZ.left,a6=a5.runtimeStyle.left;a5.runtimeStyle.left=a5.currentStyle.left;aZ.left=a4==="fontSize"?"1em":(a8||0);a8=aZ.pixelLeft+"px";aZ.left=a3;a5.runtimeStyle.left=a6}}}}return a8},swap:function(a2,a1,a3){var aZ={};for(var a0 in a1){aZ[a0]=a2.style[a0];a2.style[a0]=a1[a0]}a3.call(a2);for(var a0 in a1){a2.style[a0]=aZ[a0]}}});if(a.expr&&a.expr.filters){a.expr.filters.hidden=function(a2){var a0=a2.offsetWidth,aZ=a2.offsetHeight,a1=a2.nodeName.toLowerCase()==="tr";return a0===0&&aZ===0&&!a1?true:a0>0&&aZ>0&&!a1?false:a.curCSS(a2,"display")==="none"};a.expr.filters.visible=function(aZ){return !a.expr.filters.hidden(aZ)}}var ag=aQ(),aK=/<script(.|\s)*?\/script>/gi,o=/select|textarea/i,aC=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,r=/=\?(&|$)/,E=/\?/,aY=/(\?|&)_=.*?(&|$)/,B=/^(\w+:)?\/\/([^\/?#]+)/,h=/%20/g,w=a.fn.load;a.fn.extend({load:function(a1,a4,a5){if(typeof a1!=="string"){return w.call(this,a1)}else{if(!this.length){return this}}var a3=a1.indexOf(" ");if(a3>=0){var aZ=a1.slice(a3,a1.length);a1=a1.slice(0,a3)}var a2="GET";if(a4){if(a.isFunction(a4)){a5=a4;a4=null}else{if(typeof a4==="object"){a4=a.param(a4,a.ajaxSettings.traditional);a2="POST"}}}var a0=this;a.ajax({url:a1,type:a2,dataType:"html",data:a4,complete:function(a7,a6){if(a6==="success"||a6==="notmodified"){a0.html(aZ?a("<div />").append(a7.responseText.replace(aK,"")).find(aZ):a7.responseText)}if(a5){a0.each(a5,[a7.responseText,a6,a7])}}});return this},serialize:function(){return a.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?a.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||o.test(this.nodeName)||aC.test(this.type))}).map(function(aZ,a0){var a1=a(this).val();return a1==null?null:a.isArray(a1)?a.map(a1,function(a3,a2){return{name:a0.name,value:a3}}):{name:a0.name,value:a1}}).get()}});a.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(aZ,a0){a.fn[a0]=function(a1){return this.bind(a0,a1)}});a.extend({get:function(aZ,a1,a2,a0){if(a.isFunction(a1)){a0=a0||a2;a2=a1;a1=null}return a.ajax({type:"GET",url:aZ,data:a1,success:a2,dataType:a0})},getScript:function(aZ,a0){return a.get(aZ,null,a0,"script")},getJSON:function(aZ,a0,a1){return a.get(aZ,a0,a1,"json")},post:function(aZ,a1,a2,a0){if(a.isFunction(a1)){a0=a0||a2;a2=a1;a1={}}return a.ajax({type:"POST",url:aZ,data:a1,success:a2,dataType:a0})},ajaxSetup:function(aZ){a.extend(a.ajaxSettings,aZ)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:aN.XMLHttpRequest&&(aN.location.protocol!=="file:"||!aN.ActiveXObject)?function(){return new aN.XMLHttpRequest()}:function(){try{return new aN.ActiveXObject("Microsoft.XMLHTTP")}catch(aZ){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(be){var a9=a.extend(true,{},a.ajaxSettings,be);var bj,bd,bi,bk=be&&be.context||a9,a1=a9.type.toUpperCase();if(a9.data&&a9.processData&&typeof a9.data!=="string"){a9.data=a.param(a9.data,a9.traditional)}if(a9.dataType==="jsonp"){if(a1==="GET"){if(!r.test(a9.url)){a9.url+=(E.test(a9.url)?"&":"?")+(a9.jsonp||"callback")+"=?"}}else{if(!a9.data||!r.test(a9.data)){a9.data=(a9.data?a9.data+"&":"")+(a9.jsonp||"callback")+"=?"}}a9.dataType="json"}if(a9.dataType==="json"&&(a9.data&&r.test(a9.data)||r.test(a9.url))){bj=a9.jsonpCallback||("jsonp"+ag++);if(a9.data){a9.data=(a9.data+"").replace(r,"="+bj+"$1")}a9.url=a9.url.replace(r,"="+bj+"$1");a9.dataType="script";aN[bj]=aN[bj]||function(bl){bi=bl;a4();a7();aN[bj]=C;try{delete aN[bj]}catch(bm){}if(a2){a2.removeChild(bg)}}}if(a9.dataType==="script"&&a9.cache===null){a9.cache=false}if(a9.cache===false&&a1==="GET"){var aZ=aQ();var bh=a9.url.replace(aY,"$1_="+aZ+"$2");a9.url=bh+((bh===a9.url)?(E.test(a9.url)?"&":"?")+"_="+aZ:"")}if(a9.data&&a1==="GET"){a9.url+=(E.test(a9.url)?"&":"?")+a9.data}if(a9.global&&!a.active++){a.event.trigger("ajaxStart")}var bc=B.exec(a9.url),a3=bc&&(bc[1]&&bc[1]!==location.protocol||bc[2]!==location.host);if(a9.dataType==="script"&&a1==="GET"&&a3){var a2=ac.getElementsByTagName("head")[0]||ac.documentElement;var bg=ac.createElement("script");bg.src=a9.url;if(a9.scriptCharset){bg.charset=a9.scriptCharset}if(!bj){var bb=false;bg.onload=bg.onreadystatechange=function(){if(!bb&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){bb=true;a4();a7();bg.onload=bg.onreadystatechange=null;if(a2&&bg.parentNode){a2.removeChild(bg)}}}}a2.insertBefore(bg,a2.firstChild);return C}var a6=false;var a5=a9.xhr();if(!a5){return}if(a9.username){a5.open(a1,a9.url,a9.async,a9.username,a9.password)}else{a5.open(a1,a9.url,a9.async)}try{if(a9.data||be&&be.contentType){a5.setRequestHeader("Content-Type",a9.contentType)}if(a9.ifModified){if(a.lastModified[a9.url]){a5.setRequestHeader("If-Modified-Since",a.lastModified[a9.url])}if(a.etag[a9.url]){a5.setRequestHeader("If-None-Match",a.etag[a9.url])}}if(!a3){a5.setRequestHeader("X-Requested-With","XMLHttpRequest")}a5.setRequestHeader("Accept",a9.dataType&&a9.accepts[a9.dataType]?a9.accepts[a9.dataType]+", */*":a9.accepts._default)}catch(bf){}if(a9.beforeSend&&a9.beforeSend.call(bk,a5,a9)===false){if(a9.global&&!--a.active){a.event.trigger("ajaxStop")}a5.abort();return false}if(a9.global){ba("ajaxSend",[a5,a9])}var a8=a5.onreadystatechange=function(bl){if(!a5||a5.readyState===0||bl==="abort"){if(!a6){a7()}a6=true;if(a5){a5.onreadystatechange=a.noop}}else{if(!a6&&a5&&(a5.readyState===4||bl==="timeout")){a6=true;a5.onreadystatechange=a.noop;bd=bl==="timeout"?"timeout":!a.httpSuccess(a5)?"error":a9.ifModified&&a.httpNotModified(a5,a9.url)?"notmodified":"success";var bn;if(bd==="success"){try{bi=a.httpData(a5,a9.dataType,a9)}catch(bm){bd="parsererror";bn=bm}}if(bd==="success"||bd==="notmodified"){if(!bj){a4()}}else{a.handleError(a9,a5,bd,bn)}a7();if(bl==="timeout"){a5.abort()}if(a9.async){a5=null}}}};try{var a0=a5.abort;a5.abort=function(){if(a5){a0.call(a5)}a8("abort")}}catch(bf){}if(a9.async&&a9.timeout>0){setTimeout(function(){if(a5&&!a6){a8("timeout")}},a9.timeout)}try{a5.send(a1==="POST"||a1==="PUT"||a1==="DELETE"?a9.data:null)}catch(bf){a.handleError(a9,a5,null,bf);a7()}if(!a9.async){a8()}function a4(){if(a9.success){a9.success.call(bk,bi,bd,a5)}if(a9.global){ba("ajaxSuccess",[a5,a9])}}function a7(){if(a9.complete){a9.complete.call(bk,a5,bd)}if(a9.global){ba("ajaxComplete",[a5,a9])}if(a9.global&&!--a.active){a.event.trigger("ajaxStop")}}function ba(bm,bl){(a9.context?a(a9.context):a.event).trigger(bm,bl)}return a5},handleError:function(a0,a2,aZ,a1){if(a0.error){a0.error.call(a0.context||a0,a2,aZ,a1)}if(a0.global){(a0.context?a(a0.context):a.event).trigger("ajaxError",[a2,a0,a1])}},active:0,httpSuccess:function(a0){try{return !a0.status&&location.protocol==="file:"||(a0.status>=200&&a0.status<300)||a0.status===304||a0.status===1223||a0.status===0}catch(aZ){}return false},httpNotModified:function(a2,aZ){var a1=a2.getResponseHeader("Last-Modified"),a0=a2.getResponseHeader("Etag");if(a1){a.lastModified[aZ]=a1}if(a0){a.etag[aZ]=a0}return a2.status===304||a2.status===0},httpData:function(a4,a2,a1){var a0=a4.getResponseHeader("content-type")||"",aZ=a2==="xml"||!a2&&a0.indexOf("xml")>=0,a3=aZ?a4.responseXML:a4.responseText;if(aZ&&a3.documentElement.nodeName==="parsererror"){a.error("parsererror")}if(a1&&a1.dataFilter){a3=a1.dataFilter(a3,a2)}if(typeof a3==="string"){if(a2==="json"||!a2&&a0.indexOf("json")>=0){a3=a.parseJSON(a3)}else{if(a2==="script"||!a2&&a0.indexOf("javascript")>=0){a.globalEval(a3)}}}return a3},param:function(aZ,a2){var a0=[];if(a2===C){a2=a.ajaxSettings.traditional}if(a.isArray(aZ)||aZ.jquery){a.each(aZ,function(){a4(this.name,this.value)})}else{for(var a3 in aZ){a1(a3,aZ[a3])}}return a0.join("&").replace(h,"+");function a1(a5,a6){if(a.isArray(a6)){a.each(a6,function(a8,a7){if(a2||/\[\]$/.test(a5)){a4(a5,a7)}else{a1(a5+"["+(typeof a7==="object"||a.isArray(a7)?a8:"")+"]",a7)}})}else{if(!a2&&a6!=null&&typeof a6==="object"){a.each(a6,function(a8,a7){a1(a5+"["+a8+"]",a7)})}else{a4(a5,a6)}}}function a4(a5,a6){a6=a.isFunction(a6)?a6():a6;a0[a0.length]=encodeURIComponent(a5)+"="+encodeURIComponent(a6)}}});var H={},af=/toggle|show|hide/,av=/^([+-]=)?([\d+-.]+)(.*)$/,aG,ak=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];a.fn.extend({show:function(a0,a8){if(a0||a0===0){return this.animate(aE("show",3),a0,a8)}else{for(var a5=0,a2=this.length;a5<a2;a5++){var aZ=a.data(this[a5],"olddisplay");this[a5].style.display=aZ||"";if(a.css(this[a5],"display")==="none"){var a7=this[a5].nodeName,a6;if(H[a7]){a6=H[a7]}else{var a1=a("<"+a7+" />").appendTo("body");a6=a1.css("display");if(a6==="none"){a6="block"}a1.remove();H[a7]=a6}a.data(this[a5],"olddisplay",a6)}}for(var a4=0,a3=this.length;a4<a3;a4++){this[a4].style.display=a.data(this[a4],"olddisplay")||""}return this}},hide:function(a4,a5){if(a4||a4===0){return this.animate(aE("hide",3),a4,a5)}else{for(var a3=0,a0=this.length;a3<a0;a3++){var aZ=a.data(this[a3],"olddisplay");if(!aZ&&aZ!=="none"){a.data(this[a3],"olddisplay",a.css(this[a3],"display"))}}for(var a2=0,a1=this.length;a2<a1;a2++){this[a2].style.display="none"}return this}},_toggle:a.fn.toggle,toggle:function(a1,a0){var aZ=typeof a1==="boolean";if(a.isFunction(a1)&&a.isFunction(a0)){this._toggle.apply(this,arguments)}else{if(a1==null||aZ){this.each(function(){var a2=aZ?a1:a(this).is(":hidden");a(this)[a2?"show":"hide"]()})}else{this.animate(aE("toggle",3),a1,a0)}}return this},fadeTo:function(aZ,a1,a0){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:a1},aZ,a0)},animate:function(a3,a0,a2,a1){var aZ=a.speed(a0,a2,a1);if(a.isEmptyObject(a3)){return this.each(aZ.complete)}return this[aZ.queue===false?"each":"queue"](function(){var a6=a.extend({},aZ),a8,a7=this.nodeType===1&&a(this).is(":hidden"),a4=this;for(a8 in a3){var a5=a8.replace(aA,k);if(a8!==a5){a3[a5]=a3[a8];delete a3[a8];a8=a5}if(a3[a8]==="hide"&&a7||a3[a8]==="show"&&!a7){return a6.complete.call(this)}if((a8==="height"||a8==="width")&&this.style){a6.display=a.css(this,"display");a6.overflow=this.style.overflow}if(a.isArray(a3[a8])){(a6.specialEasing=a6.specialEasing||{})[a8]=a3[a8][1];a3[a8]=a3[a8][0]}}if(a6.overflow!=null){this.style.overflow="hidden"}a6.curAnim=a.extend({},a3);a.each(a3,function(ba,be){var bd=new a.fx(a4,a6,ba);if(af.test(be)){bd[be==="toggle"?a7?"show":"hide":be](a3)}else{var bc=av.exec(be),bf=bd.cur(true)||0;if(bc){var a9=parseFloat(bc[2]),bb=bc[3]||"px";if(bb!=="px"){a4.style[ba]=(a9||1)+bb;bf=((a9||1)/bd.cur(true))*bf;a4.style[ba]=bf+bb}if(bc[1]){a9=((bc[1]==="-="?-1:1)*a9)+bf}bd.custom(bf,a9,bb)}else{bd.custom(bf,be,"")}}});return true})},stop:function(a0,aZ){var a1=a.timers;if(a0){this.queue([])}this.each(function(){for(var a2=a1.length-1;a2>=0;a2--){if(a1[a2].elem===this){if(aZ){a1[a2](true)}a1.splice(a2,1)}}});if(!aZ){this.dequeue()}return this}});a.each({slideDown:aE("show",1),slideUp:aE("hide",1),slideToggle:aE("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(aZ,a0){a.fn[aZ]=function(a1,a2){return this.animate(a0,a1,a2)}});a.extend({speed:function(a1,a2,a0){var aZ=a1&&typeof a1==="object"?a1:{complete:a0||!a0&&a2||a.isFunction(a1)&&a1,duration:a1,easing:a0&&a2||a2&&!a.isFunction(a2)&&a2};aZ.duration=a.fx.off?0:typeof aZ.duration==="number"?aZ.duration:a.fx.speeds[aZ.duration]||a.fx.speeds._default;aZ.old=aZ.complete;aZ.complete=function(){if(aZ.queue!==false){a(this).dequeue()}if(a.isFunction(aZ.old)){aZ.old.call(this)}};return aZ},easing:{linear:function(a1,a2,aZ,a0){return aZ+a0*a1},swing:function(a1,a2,aZ,a0){return((-Math.cos(a1*Math.PI)/2)+0.5)*a0+aZ}},timers:[],fx:function(a0,aZ,a1){this.options=aZ;this.elem=a0;this.prop=a1;if(!aZ.orig){aZ.orig={}}}});a.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(a.fx.step[this.prop]||a.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(a0){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var aZ=parseFloat(a.css(this.elem,this.prop,a0));return aZ&&aZ>-10000?aZ:parseFloat(a.curCSS(this.elem,this.prop))||0},custom:function(a3,a2,a1){this.startTime=aQ();this.start=a3;this.end=a2;this.unit=a1||this.unit||"px";this.now=this.start;this.pos=this.state=0;var aZ=this;function a0(a4){return aZ.step(a4)}a0.elem=this.elem;if(a0()&&a.timers.push(a0)&&!aG){aG=setInterval(a.fx.tick,13)}},show:function(){this.options.orig[this.prop]=a.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());a(this.elem).show()},hide:function(){this.options.orig[this.prop]=a.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a2){var a7=aQ(),a3=true;if(a2||a7>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var a4 in this.options.curAnim){if(this.options.curAnim[a4]!==true){a3=false}}if(a3){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;var a1=a.data(this.elem,"olddisplay");this.elem.style.display=a1?a1:this.options.display;if(a.css(this.elem,"display")==="none"){this.elem.style.display="block"}}if(this.options.hide){a(this.elem).hide()}if(this.options.hide||this.options.show){for(var aZ in this.options.curAnim){a.style(this.elem,aZ,this.options.orig[aZ])}}this.options.complete.call(this.elem)}return false}else{var a0=a7-this.startTime;this.state=a0/this.options.duration;var a5=this.options.specialEasing&&this.options.specialEasing[this.prop];var a6=this.options.easing||(a.easing.swing?"swing":"linear");this.pos=a.easing[a5||a6](this.state,a0,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};a.extend(a.fx,{tick:function(){var a0=a.timers;for(var aZ=0;aZ<a0.length;aZ++){if(!a0[aZ]()){a0.splice(aZ--,1)}}if(!a0.length){a.fx.stop()}},stop:function(){clearInterval(aG);aG=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(aZ){a.style(aZ.elem,"opacity",aZ.now)},_default:function(aZ){if(aZ.elem.style&&aZ.elem.style[aZ.prop]!=null){aZ.elem.style[aZ.prop]=(aZ.prop==="width"||aZ.prop==="height"?Math.max(0,aZ.now):aZ.now)+aZ.unit}else{aZ.elem[aZ.prop]=aZ.now}}}});if(a.expr&&a.expr.filters){a.expr.filters.animated=function(aZ){return a.grep(a.timers,function(a0){return aZ===a0.elem}).length}}function aE(a0,aZ){var a1={};a.each(ak.concat.apply([],ak.slice(0,aZ)),function(){a1[this]=a0});return a1}if("getBoundingClientRect" in ac.documentElement){a.fn.offset=function(a8){var a1=this[0];if(a8){return this.each(function(a9){a.offset.setOffset(this,a8,a9)})}if(!a1||!a1.ownerDocument){return null}if(a1===a1.ownerDocument.body){return a.offset.bodyOffset(a1)}var a3=a1.getBoundingClientRect(),a7=a1.ownerDocument,a4=a7.body,aZ=a7.documentElement,a2=aZ.clientTop||a4.clientTop||0,a5=aZ.clientLeft||a4.clientLeft||0,a6=a3.top+(self.pageYOffset||a.support.boxModel&&aZ.scrollTop||a4.scrollTop)-a2,a0=a3.left+(self.pageXOffset||a.support.boxModel&&aZ.scrollLeft||a4.scrollLeft)-a5;return{top:a6,left:a0}}}else{a.fn.offset=function(ba){var a4=this[0];if(ba){return this.each(function(bb){a.offset.setOffset(this,ba,bb)})}if(!a4||!a4.ownerDocument){return null}if(a4===a4.ownerDocument.body){return a.offset.bodyOffset(a4)}a.offset.initialize();var a1=a4.offsetParent,a0=a4,a9=a4.ownerDocument,a7,a2=a9.documentElement,a5=a9.body,a6=a9.defaultView,aZ=a6?a6.getComputedStyle(a4,null):a4.currentStyle,a8=a4.offsetTop,a3=a4.offsetLeft;while((a4=a4.parentNode)&&a4!==a5&&a4!==a2){if(a.offset.supportsFixedPosition&&aZ.position==="fixed"){break}a7=a6?a6.getComputedStyle(a4,null):a4.currentStyle;a8-=a4.scrollTop;a3-=a4.scrollLeft;if(a4===a1){a8+=a4.offsetTop;a3+=a4.offsetLeft;if(a.offset.doesNotAddBorder&&!(a.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(a4.nodeName))){a8+=parseFloat(a7.borderTopWidth)||0;a3+=parseFloat(a7.borderLeftWidth)||0}a0=a1,a1=a4.offsetParent}if(a.offset.subtractsBorderForOverflowNotVisible&&a7.overflow!=="visible"){a8+=parseFloat(a7.borderTopWidth)||0;a3+=parseFloat(a7.borderLeftWidth)||0}aZ=a7}if(aZ.position==="relative"||aZ.position==="static"){a8+=a5.offsetTop;a3+=a5.offsetLeft}if(a.offset.supportsFixedPosition&&aZ.position==="fixed"){a8+=Math.max(a2.scrollTop,a5.scrollTop);a3+=Math.max(a2.scrollLeft,a5.scrollLeft)}return{top:a8,left:a3}}}a.offset={initialize:function(){var aZ=ac.body,a0=ac.createElement("div"),a3,a5,a4,a6,a1=parseFloat(a.curCSS(aZ,"marginTop",true))||0,a2="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.extend(a0.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});a0.innerHTML=a2;aZ.insertBefore(a0,aZ.firstChild);a3=a0.firstChild;a5=a3.firstChild;a6=a3.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(a5.offsetTop!==5);this.doesAddBorderForTableAndCells=(a6.offsetTop===5);a5.style.position="fixed",a5.style.top="20px";this.supportsFixedPosition=(a5.offsetTop===20||a5.offsetTop===15);a5.style.position=a5.style.top="";a3.style.overflow="hidden",a3.style.position="relative";this.subtractsBorderForOverflowNotVisible=(a5.offsetTop===-5);this.doesNotIncludeMarginInBodyOffset=(aZ.offsetTop!==a1);aZ.removeChild(a0);aZ=a0=a3=a5=a4=a6=null;a.offset.initialize=a.noop},bodyOffset:function(aZ){var a1=aZ.offsetTop,a0=aZ.offsetLeft;a.offset.initialize();if(a.offset.doesNotIncludeMarginInBodyOffset){a1+=parseFloat(a.curCSS(aZ,"marginTop",true))||0;a0+=parseFloat(a.curCSS(aZ,"marginLeft",true))||0}return{top:a1,left:a0}},setOffset:function(a4,a0,a1){if(/static/.test(a.curCSS(a4,"position"))){a4.style.position="relative"}var a3=a(a4),a6=a3.offset(),aZ=parseInt(a.curCSS(a4,"top",true),10)||0,a5=parseInt(a.curCSS(a4,"left",true),10)||0;if(a.isFunction(a0)){a0=a0.call(a4,a1,a6)}var a2={top:(a0.top-a6.top)+aZ,left:(a0.left-a6.left)+a5};if("using" in a0){a0.using.call(a4,a2)}else{a3.css(a2)}}};a.fn.extend({position:function(){if(!this[0]){return null}var a1=this[0],a0=this.offsetParent(),a2=this.offset(),aZ=/^body|html$/i.test(a0[0].nodeName)?{top:0,left:0}:a0.offset();a2.top-=parseFloat(a.curCSS(a1,"marginTop",true))||0;a2.left-=parseFloat(a.curCSS(a1,"marginLeft",true))||0;aZ.top+=parseFloat(a.curCSS(a0[0],"borderTopWidth",true))||0;aZ.left+=parseFloat(a.curCSS(a0[0],"borderLeftWidth",true))||0;return{top:a2.top-aZ.top,left:a2.left-aZ.left}},offsetParent:function(){return this.map(function(){var aZ=this.offsetParent||ac.body;while(aZ&&(!/^body|html$/i.test(aZ.nodeName)&&a.css(aZ,"position")==="static")){aZ=aZ.offsetParent}return aZ})}});a.each(["Left","Top"],function(a0,aZ){var a1="scroll"+aZ;a.fn[a1]=function(a4){var a2=this[0],a3;if(!a2){return null}if(a4!==C){return this.each(function(){a3=an(this);if(a3){a3.scrollTo(!a0?a4:a(a3).scrollLeft(),a0?a4:a(a3).scrollTop())}else{this[a1]=a4}})}else{a3=an(a2);return a3?("pageXOffset" in a3)?a3[a0?"pageYOffset":"pageXOffset"]:a.support.boxModel&&a3.document.documentElement[a1]||a3.document.body[a1]:a2[a1]}}});function an(aZ){return("scrollTo" in aZ&&aZ.document)?aZ:aZ.nodeType===9?aZ.defaultView||aZ.parentWindow:false}a.each(["Height","Width"],function(a0,aZ){var a1=aZ.toLowerCase();a.fn["inner"+aZ]=function(){return this[0]?a.css(this[0],a1,false,"padding"):null};a.fn["outer"+aZ]=function(a2){return this[0]?a.css(this[0],a1,false,a2?"margin":"border"):null};a.fn[a1]=function(a2){var a3=this[0];if(!a3){return a2==null?null:this}if(a.isFunction(a2)){return this.each(function(a5){var a4=a(this);a4[a1](a2.call(this,a5,a4[a1]()))})}return("scrollTo" in a3&&a3.document)?a3.document.compatMode==="CSS1Compat"&&a3.document.documentElement["client"+aZ]||a3.document.body["client"+aZ]:(a3.nodeType===9)?Math.max(a3.documentElement["client"+aZ],a3.body["scroll"+aZ],a3.documentElement["scroll"+aZ],a3.body["offset"+aZ],a3.documentElement["offset"+aZ]):a2===C?a.css(a3,a1):this.css(a1,typeof a2==="string"?a2:a2+"px")}});aN.jQuery=aN.$=a})(window);
|
24 |
+
/*!
|
25 |
+
* jQuery postMessage - v0.5 - 9/11/2009
|
26 |
+
* http://benalman.com/projects/jquery-postmessage-plugin/
|
27 |
+
*
|
28 |
+
* Copyright (c) 2009 "Cowboy" Ben Alman
|
29 |
+
* Dual licensed under the MIT and GPL licenses.
|
30 |
+
* http://benalman.com/about/license/
|
31 |
+
*/
|
32 |
+
(function($){var b,d,j=1,a,f=this,g=!1,h="postMessage",c="addEventListener",e,i=f[h];$[h]=function(k,m,l){if(!m){return}k=typeof k==="string"?k:$.param(k);l=l||parent;if(i){l[h](k,m.replace(/([^:]+:\/\/[^\/]+).*/,"$1"))}else{if(m){l.location=m.replace(/#.*$/,"")+"#"+(+new Date)+(j++)+"&"+k}}};$.receiveMessage=e=function(m,l,k){if(i){if(m){a&&e();a=function(n){if((typeof l==="string"&&n.origin!==l)||($.isFunction(l)&&l(n.origin)===g)){return g}m(n)}}if(f[c]){f[m?c:"removeEventListener"]("message",a,g)}else{f[m?"attachEvent":"detachEvent"]("onmessage",a)}}else{b&&clearInterval(b);b=null;if(m){k=typeof l==="number"?l:typeof k==="number"?k:100;b=setInterval(function(){var o=document.location.hash,n=/^#?\d+&/;if(o!==d&&n.test(o)){d=o;m({data:o.replace(n,"")})}},k)}}}})(jQuery);var D=$.noConflict();var Duo={init:function(b){if(!b){alert("Error: missing arguments in Duo.init()");return}if(!b.host){alert("Error: missing 'host' argument in Duo.init()");return}Duo._host=b.host;if(!b.sig_request){alert("Error: missing 'sig_request' argument in Duo.init()");return}Duo._sig_request=b.sig_request;if(Duo._sig_request.indexOf("ERR|")==0){var c=Duo._sig_request.split("|");alert("Error: "+c[1]);return}if(Duo._sig_request.indexOf(":")==-1){alert("Invalid sig_request value");return}var a=Duo._sig_request.split(":");if(a.length!=2){alert("Invalid sig_request value");return}Duo._duo_sig=a[0];Duo._app_sig=a[1];if(!b.post_action){b.post_action=""}Duo._post_action=b.post_action;if(!b.post_argument){b.post_argument="sig_response"}Duo._post_argument=b.post_argument;Duo._mage_form_key=b.form_key},ready:function(){var b=D("#duo_iframe");if(!b.length){alert("Error: missing IFRAME element with id 'duo_iframe'");return}var a=D.param({tx:Duo._duo_sig,parent:document.location.href});var c="https://"+Duo._host+"/frame/web/v1/auth?"+a;b.attr("src",c);D.receiveMessage(function(g){var f=g.data+":"+Duo._app_sig;var d=D('<input type="hidden">').attr("name",Duo._post_argument).val(f);var e=D("#duo_form");if(!e.length){e=D("<form>");e.insertAfter(b)}e.attr("method","POST");e.attr("action",Duo._post_action);e.append(d);e.append('<input type="hidden" name="form_key" value="'+Duo._mage_form_key+'" />');e.submit()},"https://"+Duo._host)}};D(document).ready(function(){Duo.ready()});
|
js/he_twofactor/Duo-Web-v1.js
ADDED
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* jQuery postMessage - v0.5 - 9/11/2009
|
3 |
+
* http://benalman.com/projects/jquery-postmessage-plugin/
|
4 |
+
*
|
5 |
+
* Copyright (c) 2009 "Cowboy" Ben Alman
|
6 |
+
* Dual licensed under the MIT and GPL licenses.
|
7 |
+
* http://benalman.com/about/license/
|
8 |
+
*/
|
9 |
+
|
10 |
+
// Script: jQuery postMessage: Cross-domain scripting goodness
|
11 |
+
//
|
12 |
+
// *Version: 0.5, Last updated: 9/11/2009*
|
13 |
+
//
|
14 |
+
// Project Home - http://benalman.com/projects/jquery-postmessage-plugin/
|
15 |
+
// GitHub - http://github.com/cowboy/jquery-postmessage/
|
16 |
+
// Source - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.js
|
17 |
+
// (Minified) - http://github.com/cowboy/jquery-postmessage/raw/master/jquery.ba-postmessage.min.js (0.9kb)
|
18 |
+
//
|
19 |
+
// About: License
|
20 |
+
//
|
21 |
+
// Copyright (c) 2009 "Cowboy" Ben Alman,
|
22 |
+
// Dual licensed under the MIT and GPL licenses.
|
23 |
+
// http://benalman.com/about/license/
|
24 |
+
//
|
25 |
+
// About: Examples
|
26 |
+
//
|
27 |
+
// This working example, complete with fully commented code, illustrates one
|
28 |
+
// way in which this plugin can be used.
|
29 |
+
//
|
30 |
+
// Iframe resizing - http://benalman.com/code/projects/jquery-postmessage/examples/iframe/
|
31 |
+
//
|
32 |
+
// About: Support and Testing
|
33 |
+
//
|
34 |
+
// Information about what version or versions of jQuery this plugin has been
|
35 |
+
// tested with and what browsers it has been tested in.
|
36 |
+
//
|
37 |
+
// jQuery Versions - 1.3.2
|
38 |
+
// Browsers Tested - Internet Explorer 6-8, Firefox 3, Safari 3-4, Chrome, Opera 9.
|
39 |
+
//
|
40 |
+
// About: Release History
|
41 |
+
//
|
42 |
+
// 0.5 - (9/11/2009) Improved cache-busting
|
43 |
+
// 0.4 - (8/25/2009) Initial release
|
44 |
+
|
45 |
+
(function($){
|
46 |
+
'$:nomunge'; // Used by YUI compressor.
|
47 |
+
|
48 |
+
// A few vars used in non-awesome browsers.
|
49 |
+
var interval_id,
|
50 |
+
last_hash,
|
51 |
+
cache_bust = 1,
|
52 |
+
|
53 |
+
// A var used in awesome browsers.
|
54 |
+
rm_callback,
|
55 |
+
|
56 |
+
// A few convenient shortcuts.
|
57 |
+
window = this,
|
58 |
+
FALSE = !1,
|
59 |
+
|
60 |
+
// Reused internal strings.
|
61 |
+
postMessage = 'postMessage',
|
62 |
+
addEventListener = 'addEventListener',
|
63 |
+
|
64 |
+
p_receiveMessage,
|
65 |
+
|
66 |
+
has_postMessage = window[postMessage];
|
67 |
+
|
68 |
+
// Method: jQuery.postMessage
|
69 |
+
//
|
70 |
+
// This method will call window.postMessage if available, setting the
|
71 |
+
// targetOrigin parameter to the base of the target_url parameter for maximum
|
72 |
+
// security in browsers that support it. If window.postMessage is not available,
|
73 |
+
// the target window's location.hash will be used to pass the message. If an
|
74 |
+
// object is passed as the message param, it will be serialized into a string
|
75 |
+
// using the jQuery.param method.
|
76 |
+
//
|
77 |
+
// Usage:
|
78 |
+
//
|
79 |
+
// > jQuery.postMessage( message, target_url [, target ] );
|
80 |
+
//
|
81 |
+
// Arguments:
|
82 |
+
//
|
83 |
+
// message - (String) A message to be passed to the other frame.
|
84 |
+
// message - (Object) An object to be serialized into a params string, using
|
85 |
+
// the jQuery.param method.
|
86 |
+
// target_url - (String) The URL of the other frame this window is
|
87 |
+
// attempting to communicate with. This must be the exact URL (including
|
88 |
+
// any query string) of the other window for this script to work in
|
89 |
+
// browsers that don't support window.postMessage.
|
90 |
+
// target - (Object) A reference to the other frame this window is
|
91 |
+
// attempting to communicate with. If omitted, defaults to `parent`.
|
92 |
+
//
|
93 |
+
// Returns:
|
94 |
+
//
|
95 |
+
// Nothing.
|
96 |
+
|
97 |
+
$[postMessage] = function( message, target_url, target ) {
|
98 |
+
if ( !target_url ) { return; }
|
99 |
+
|
100 |
+
// Serialize the message if not a string. Note that this is the only real
|
101 |
+
// jQuery dependency for this script. If removed, this script could be
|
102 |
+
// written as very basic JavaScript.
|
103 |
+
message = typeof message === 'string' ? message : $.param( message );
|
104 |
+
|
105 |
+
// Default to parent if unspecified.
|
106 |
+
target = target || parent;
|
107 |
+
|
108 |
+
if ( has_postMessage ) {
|
109 |
+
// The browser supports window.postMessage, so call it with a targetOrigin
|
110 |
+
// set appropriately, based on the target_url parameter.
|
111 |
+
target[postMessage]( message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
|
112 |
+
|
113 |
+
} else if ( target_url ) {
|
114 |
+
// The browser does not support window.postMessage, so set the location
|
115 |
+
// of the target to target_url#message. A bit ugly, but it works! A cache
|
116 |
+
// bust parameter is added to ensure that repeat messages trigger the
|
117 |
+
// callback.
|
118 |
+
target.location = target_url.replace( /#.*$/, '' ) + '#' + (+new Date) + (cache_bust++) + '&' + message;
|
119 |
+
}
|
120 |
+
};
|
121 |
+
|
122 |
+
// Method: jQuery.receiveMessage
|
123 |
+
//
|
124 |
+
// Register a single callback for either a window.postMessage call, if
|
125 |
+
// supported, or if unsupported, for any change in the current window
|
126 |
+
// location.hash. If window.postMessage is supported and source_origin is
|
127 |
+
// specified, the source window will be checked against this for maximum
|
128 |
+
// security. If window.postMessage is unsupported, a polling loop will be
|
129 |
+
// started to watch for changes to the location.hash.
|
130 |
+
//
|
131 |
+
// Note that for simplicity's sake, only a single callback can be registered
|
132 |
+
// at one time. Passing no params will unbind this event (or stop the polling
|
133 |
+
// loop), and calling this method a second time with another callback will
|
134 |
+
// unbind the event (or stop the polling loop) first, before binding the new
|
135 |
+
// callback.
|
136 |
+
//
|
137 |
+
// Also note that if window.postMessage is available, the optional
|
138 |
+
// source_origin param will be used to test the event.origin property. From
|
139 |
+
// the MDC window.postMessage docs: This string is the concatenation of the
|
140 |
+
// protocol and "://", the host name if one exists, and ":" followed by a port
|
141 |
+
// number if a port is present and differs from the default port for the given
|
142 |
+
// protocol. Examples of typical origins are https://example.org (implying
|
143 |
+
// port 443), http://example.net (implying port 80), and http://example.com:8080.
|
144 |
+
//
|
145 |
+
// Usage:
|
146 |
+
//
|
147 |
+
// > jQuery.receiveMessage( callback [, source_origin ] [, delay ] );
|
148 |
+
//
|
149 |
+
// Arguments:
|
150 |
+
//
|
151 |
+
// callback - (Function) This callback will execute whenever a <jQuery.postMessage>
|
152 |
+
// message is received, provided the source_origin matches. If callback is
|
153 |
+
// omitted, any existing receiveMessage event bind or polling loop will be
|
154 |
+
// canceled.
|
155 |
+
// source_origin - (String) If window.postMessage is available and this value
|
156 |
+
// is not equal to the event.origin property, the callback will not be
|
157 |
+
// called.
|
158 |
+
// source_origin - (Function) If window.postMessage is available and this
|
159 |
+
// function returns false when passed the event.origin property, the
|
160 |
+
// callback will not be called.
|
161 |
+
// delay - (Number) An optional zero-or-greater delay in milliseconds at
|
162 |
+
// which the polling loop will execute (for browser that don't support
|
163 |
+
// window.postMessage). If omitted, defaults to 100.
|
164 |
+
//
|
165 |
+
// Returns:
|
166 |
+
//
|
167 |
+
// Nothing!
|
168 |
+
|
169 |
+
$.receiveMessage = p_receiveMessage = function( callback, source_origin, delay ) {
|
170 |
+
if ( has_postMessage ) {
|
171 |
+
// Since the browser supports window.postMessage, the callback will be
|
172 |
+
// bound to the actual event associated with window.postMessage.
|
173 |
+
|
174 |
+
if ( callback ) {
|
175 |
+
// Unbind an existing callback if it exists.
|
176 |
+
rm_callback && p_receiveMessage();
|
177 |
+
|
178 |
+
// Bind the callback. A reference to the callback is stored for ease of
|
179 |
+
// unbinding.
|
180 |
+
rm_callback = function(e) {
|
181 |
+
if ( ( typeof source_origin === 'string' && e.origin !== source_origin )
|
182 |
+
|| ( $.isFunction( source_origin ) && source_origin( e.origin ) === FALSE ) ) {
|
183 |
+
return FALSE;
|
184 |
+
}
|
185 |
+
callback( e );
|
186 |
+
};
|
187 |
+
}
|
188 |
+
|
189 |
+
if ( window[addEventListener] ) {
|
190 |
+
window[ callback ? addEventListener : 'removeEventListener' ]( 'message', rm_callback, FALSE );
|
191 |
+
} else {
|
192 |
+
window[ callback ? 'attachEvent' : 'detachEvent' ]( 'onmessage', rm_callback );
|
193 |
+
}
|
194 |
+
|
195 |
+
} else {
|
196 |
+
// Since the browser sucks, a polling loop will be started, and the
|
197 |
+
// callback will be called whenever the location.hash changes.
|
198 |
+
|
199 |
+
interval_id && clearInterval( interval_id );
|
200 |
+
interval_id = null;
|
201 |
+
|
202 |
+
if ( callback ) {
|
203 |
+
delay = typeof source_origin === 'number'
|
204 |
+
? source_origin
|
205 |
+
: typeof delay === 'number'
|
206 |
+
? delay
|
207 |
+
: 100;
|
208 |
+
|
209 |
+
interval_id = setInterval(function(){
|
210 |
+
var hash = document.location.hash,
|
211 |
+
re = /^#?\d+&/;
|
212 |
+
if ( hash !== last_hash && re.test( hash ) ) {
|
213 |
+
last_hash = hash;
|
214 |
+
callback({ data: hash.replace( re, '' ) });
|
215 |
+
}
|
216 |
+
}, delay );
|
217 |
+
}
|
218 |
+
}
|
219 |
+
};
|
220 |
+
|
221 |
+
})(jQuery);
|
222 |
+
|
223 |
+
|
224 |
+
var D = jQuery;
|
225 |
+
|
226 |
+
|
227 |
+
/*
|
228 |
+
* Duo Web SDK v1
|
229 |
+
* Copyright 2011, Duo Security
|
230 |
+
*/
|
231 |
+
|
232 |
+
var Duo = {
|
233 |
+
init: function(options) {
|
234 |
+
/* sanity check for argument dict */
|
235 |
+
if (!options) {
|
236 |
+
alert('Error: missing arguments in Duo.init()');
|
237 |
+
return;
|
238 |
+
}
|
239 |
+
|
240 |
+
/* API hostname is a required argument */
|
241 |
+
if (!options.host) {
|
242 |
+
alert('Error: missing \'host\' argument in Duo.init()');
|
243 |
+
return;
|
244 |
+
}
|
245 |
+
Duo._host = options.host;
|
246 |
+
|
247 |
+
/* sig_request is a required argument */
|
248 |
+
if (!options.sig_request) {
|
249 |
+
alert('Error: missing \'sig_request\' argument in Duo.init()');
|
250 |
+
return;
|
251 |
+
}
|
252 |
+
Duo._sig_request = options.sig_request;
|
253 |
+
|
254 |
+
/* check for sig_request errors */
|
255 |
+
if (Duo._sig_request.indexOf('ERR|') == 0) {
|
256 |
+
var err_array = Duo._sig_request.split('|');
|
257 |
+
alert('Error: ' + err_array[1]);
|
258 |
+
return;
|
259 |
+
}
|
260 |
+
|
261 |
+
/* validate and parse the sig_request */
|
262 |
+
if (Duo._sig_request.indexOf(':') == -1) {
|
263 |
+
alert('Invalid sig_request value');
|
264 |
+
return;
|
265 |
+
}
|
266 |
+
var sig_array = Duo._sig_request.split(':');
|
267 |
+
if (sig_array.length != 2) {
|
268 |
+
alert('Invalid sig_request value');
|
269 |
+
return;
|
270 |
+
}
|
271 |
+
Duo._duo_sig = sig_array[0];
|
272 |
+
Duo._app_sig = sig_array[1];
|
273 |
+
|
274 |
+
/* allow override of the POST-back action URI */
|
275 |
+
if (!options.post_action) {
|
276 |
+
options.post_action = '';
|
277 |
+
}
|
278 |
+
Duo._post_action = options.post_action;
|
279 |
+
|
280 |
+
/* allow override of the POST-back argument name */
|
281 |
+
if (!options.post_argument) {
|
282 |
+
options.post_argument = 'sig_response';
|
283 |
+
}
|
284 |
+
Duo._post_argument = options.post_argument;
|
285 |
+
},
|
286 |
+
|
287 |
+
ready: function() {
|
288 |
+
var iframe = D('#duo_iframe');
|
289 |
+
|
290 |
+
/* sanity check for a duo_iframe element */
|
291 |
+
if (!iframe.length) {
|
292 |
+
alert('Error: missing IFRAME element with id \'duo_iframe\'');
|
293 |
+
return;
|
294 |
+
}
|
295 |
+
|
296 |
+
var args = D.param({
|
297 |
+
'tx': Duo._duo_sig,
|
298 |
+
'parent': document.location.href
|
299 |
+
});
|
300 |
+
|
301 |
+
var src = 'https://' + Duo._host + '/frame/web/v1/auth?' + args;
|
302 |
+
iframe.attr('src', src);
|
303 |
+
|
304 |
+
D.receiveMessage(function(msg) {
|
305 |
+
var sig_response = msg.data + ':' + Duo._app_sig;
|
306 |
+
var input = D('<input type="hidden">').attr('name', Duo._post_argument).val(sig_response);
|
307 |
+
|
308 |
+
var form = D('#duo_form');
|
309 |
+
if (!form.length) {
|
310 |
+
form = D('<form>');
|
311 |
+
form.insertAfter(iframe);
|
312 |
+
}
|
313 |
+
|
314 |
+
form.attr('method', 'POST');
|
315 |
+
form.attr('action', Duo._post_action);
|
316 |
+
form.append(input);
|
317 |
+
form.append('<input type="hidden" name="form_key" value="'+ options.form_key +'" />');
|
318 |
+
form.submit();
|
319 |
+
}, 'https://' + Duo._host);
|
320 |
+
}
|
321 |
+
};
|
322 |
+
|
323 |
+
D(document).ready(function() {
|
324 |
+
Duo.ready();
|
325 |
+
});
|
js/he_twofactor/adminpanel.js
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
//jquery substitute for document ready (no guarantee that either jquery OR prototype will be available)
|
2 |
+
(function(g,b){function c(){if(!e){e=!0;for(var a=0;a<d.length;a++)d[a].fn.call(window,d[a].ctx);d=[]}}function h(){"complete"===document.readyState&&c()}b=b||window;var d=[],e=!1,f=!1;b[g||"he_twofactor_ready"]=function(a,b){e?setTimeout(function(){a(b)},1):(d.push({fn:a,ctx:b}),"complete"===document.readyState?setTimeout(c,1):f||(document.addEventListener?(document.addEventListener("DOMContentLoaded",c,!1),window.addEventListener("load",c,!1)):(document.attachEvent("onreadystatechange",h),window.attachEvent("onload",
|
3 |
+
c)),f=!0))}})("he_twofactor_ready",window);
|
4 |
+
//on document ready
|
5 |
+
he_twofactor_ready(function() {
|
6 |
+
//if the provider selector is on this page
|
7 |
+
var providerSelect=document.getElementById('he2faconfig_control_provider');
|
8 |
+
if(providerSelect!=undefined){
|
9 |
+
//FUNCTION: cache the toggle-able elements on initial page load
|
10 |
+
var toggleElems;
|
11 |
+
var cacheToggleSections=function(){
|
12 |
+
toggleElems=[];
|
13 |
+
//for each available select option
|
14 |
+
var options=providerSelect.getElementsByTagName('option');
|
15 |
+
for(var s=0;s<options.length;s++){
|
16 |
+
//if this is NOT the first (disabled option)
|
17 |
+
var oVal=options[s].value;
|
18 |
+
if(oVal!=undefined&&oVal.length>0&&oVal.toLowerCase().indexOf('disable')!=0){
|
19 |
+
//get the a link for this toggle-able section (if exists)
|
20 |
+
var alink=document.getElementById('he2faconfig_'+oVal+'-head');
|
21 |
+
//if this link exists
|
22 |
+
if(alink!=undefined){
|
23 |
+
//get the section wrap
|
24 |
+
var wrap=alink.parentNode.parentNode;
|
25 |
+
//if the wrap exists
|
26 |
+
if(wrap!=undefined){
|
27 |
+
var hideShowElems=[];
|
28 |
+
//if this wrap is the entry-edit div (older version of magento has different html)
|
29 |
+
if(wrap.className.indexOf('entry-edit')!==-1){
|
30 |
+
//toggle hide/show link and fieldset elements separately because there is no common parent element in this version of magento
|
31 |
+
hideShowElems.push(alink.parentNode);
|
32 |
+
var fieldset=document.getElementById('he2faconfig_'+oVal);
|
33 |
+
hideShowElems.push(fieldset);
|
34 |
+
}else{
|
35 |
+
//toggle hiding and showing this wrapper for this version of magento
|
36 |
+
hideShowElems.push(wrap);
|
37 |
+
}
|
38 |
+
//cache the toggle-able elements for this select item
|
39 |
+
toggleElems.push({'key':oVal,'wrap':wrap,'alink':alink,'toggleList':hideShowElems});
|
40 |
+
}
|
41 |
+
}else{
|
42 |
+
console.log('"'+oVal+'" does NOT appear as an admin section although it appears in the provider selection dropdown!');
|
43 |
+
}
|
44 |
+
}
|
45 |
+
}
|
46 |
+
};
|
47 |
+
//FUNCTION: update which wrapped section is visible, based on current provider selection
|
48 |
+
var updateVisibleWrap=function(){
|
49 |
+
//if the toggle-able wraps are cached
|
50 |
+
if(toggleElems!=undefined){
|
51 |
+
//if providerSelect is loaded (if exists)
|
52 |
+
if(providerSelect!=undefined){
|
53 |
+
//get the selected value
|
54 |
+
var selKey=providerSelect.value;
|
55 |
+
//for each toggle-able wrap
|
56 |
+
for(var w=0;w<toggleElems.length;w++){
|
57 |
+
//get the toggle-able wrap's data
|
58 |
+
var dat=toggleElems[w];
|
59 |
+
//if this wrap is selected to be visible
|
60 |
+
if(dat['key']==selKey){
|
61 |
+
//show each of the elements in the toggle list
|
62 |
+
for(var i=0;i<dat['toggleList'].length;i++){
|
63 |
+
//show this toggle list element
|
64 |
+
dat['toggleList'][i].style.display='block';
|
65 |
+
}
|
66 |
+
//if this browser supports classList.contains
|
67 |
+
if(dat['wrap'].classList){
|
68 |
+
if(dat['wrap'].classList.contains){
|
69 |
+
//if the class list does NOT contain active
|
70 |
+
if(!dat['wrap'].classList.contains('active')){
|
71 |
+
//open the panel
|
72 |
+
dat['alink'].onclick();
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}else{
|
77 |
+
//DESELECTED section... hide the panel wrapper
|
78 |
+
|
79 |
+
//hide each of the elements in the toggle list
|
80 |
+
for(var i=0;i<dat['toggleList'].length;i++){
|
81 |
+
//hide this toggle list element
|
82 |
+
dat['toggleList'][i].style.display='none';
|
83 |
+
}
|
84 |
+
}
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
};
|
89 |
+
//==STUFF TO DO ON PAGE LOAD==
|
90 |
+
//attach the onchange event to the provier dropdown
|
91 |
+
providerSelect.onchange=function(){
|
92 |
+
//make the selected section visible (if any) and hide any (deselected) sections
|
93 |
+
updateVisibleWrap();
|
94 |
+
};
|
95 |
+
//cache list of toggle-able wraps
|
96 |
+
cacheToggleSections();
|
97 |
+
//make the selected section visible (if any) and hide any (deselected) sections
|
98 |
+
updateVisibleWrap();
|
99 |
+
}
|
100 |
+
});
|
js/he_twofactor/readme.txt
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
These scripts have been modified from the Duo supplied originals to include Magento's formkey value.
|
lib/Duo/duo_web.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Duo {
|
4 |
+
const DUO_PREFIX = "TX";
|
5 |
+
const APP_PREFIX = "APP";
|
6 |
+
const AUTH_PREFIX = "AUTH";
|
7 |
+
|
8 |
+
const DUO_EXPIRE = 300;
|
9 |
+
const APP_EXPIRE = 3600;
|
10 |
+
|
11 |
+
const IKEY_LEN = 20;
|
12 |
+
const SKEY_LEN = 40;
|
13 |
+
const AKEY_LEN = 40; // if this changes you have to change ERR_AKEY
|
14 |
+
|
15 |
+
const ERR_USER = 'ERR|The username passed to sign_request() is invalid.';
|
16 |
+
const ERR_IKEY = 'ERR|The Duo integration key passed to sign_request() is invalid.';
|
17 |
+
const ERR_SKEY = 'ERR|The Duo secret key passed to sign_request() is invalid.';
|
18 |
+
const ERR_AKEY = 'ERR|The application secret key passed to sign_request() must be at least 40 characters.';
|
19 |
+
|
20 |
+
private static function sign_vals($key, $vals, $prefix, $expire, $time=NULL) {
|
21 |
+
$exp = ($time ? $time : time()) + $expire;
|
22 |
+
$val = $vals . '|' . $exp;
|
23 |
+
$b64 = base64_encode($val);
|
24 |
+
$cookie = $prefix . '|' . $b64;
|
25 |
+
|
26 |
+
$sig = hash_hmac("sha1", $cookie, $key);
|
27 |
+
return $cookie . '|' . $sig;
|
28 |
+
}
|
29 |
+
|
30 |
+
private static function parse_vals($key, $val, $prefix, $time=NULL) {
|
31 |
+
$ts = ($time ? $time : time());
|
32 |
+
list($u_prefix, $u_b64, $u_sig) = explode('|', $val);
|
33 |
+
|
34 |
+
$sig = hash_hmac("sha1", $u_prefix . '|' . $u_b64, $key);
|
35 |
+
if (hash_hmac("sha1", $sig, $key) != hash_hmac("sha1", $u_sig, $key)) {
|
36 |
+
return null;
|
37 |
+
}
|
38 |
+
|
39 |
+
if ($u_prefix != $prefix) {
|
40 |
+
return null;
|
41 |
+
}
|
42 |
+
|
43 |
+
list($user, $ikey, $exp) = explode('|', base64_decode($u_b64));
|
44 |
+
|
45 |
+
if ($ts >= intval($exp)) {
|
46 |
+
return null;
|
47 |
+
}
|
48 |
+
|
49 |
+
return $user;
|
50 |
+
}
|
51 |
+
|
52 |
+
public static function signRequest($ikey, $skey, $akey, $username, $time=NULL) {
|
53 |
+
if (!isset($username) || strlen($username) == 0){
|
54 |
+
return self::ERR_USER;
|
55 |
+
}
|
56 |
+
if (!isset($ikey) || strlen($ikey) != self::IKEY_LEN) {
|
57 |
+
return self::ERR_IKEY;
|
58 |
+
}
|
59 |
+
if (!isset($skey) || strlen($skey) != self::SKEY_LEN) {
|
60 |
+
return self::ERR_SKEY;
|
61 |
+
}
|
62 |
+
if (!isset($akey) || strlen($akey) < self::AKEY_LEN) {
|
63 |
+
return self::ERR_AKEY;
|
64 |
+
}
|
65 |
+
|
66 |
+
$vals = $username . '|' . $ikey;
|
67 |
+
|
68 |
+
$duo_sig = self::sign_vals($skey, $vals, self::DUO_PREFIX, self::DUO_EXPIRE, $time);
|
69 |
+
$app_sig = self::sign_vals($akey, $vals, self::APP_PREFIX, self::APP_EXPIRE, $time);
|
70 |
+
|
71 |
+
return $duo_sig . ':' . $app_sig;
|
72 |
+
}
|
73 |
+
|
74 |
+
public static function verifyResponse($ikey, $skey, $akey, $sig_response, $time=NULL) {
|
75 |
+
list($auth_sig, $app_sig) = explode(':', $sig_response);
|
76 |
+
|
77 |
+
$auth_user = self::parse_vals($skey, $auth_sig, self::AUTH_PREFIX, $time);
|
78 |
+
$app_user = self::parse_vals($akey, $app_sig, self::APP_PREFIX, $time);
|
79 |
+
|
80 |
+
if ($auth_user != $app_user) {
|
81 |
+
return null;
|
82 |
+
}
|
83 |
+
|
84 |
+
return $auth_user;
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
?>
|
lib/GoogleAuthenticator/PHPGangsta/GoogleAuthenticator.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* PHP Class for handling Google Authenticator 2-factor authentication
|
5 |
+
*
|
6 |
+
* @author Michael Kliewe
|
7 |
+
* @copyright 2012 Michael Kliewe
|
8 |
+
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
9 |
+
* @link http://www.phpgangsta.de/
|
10 |
+
*/
|
11 |
+
|
12 |
+
class PHPGangsta_GoogleAuthenticator
|
13 |
+
{
|
14 |
+
protected $_codeLength = 6;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Create new secret.
|
18 |
+
* 16 characters, randomly chosen from the allowed base32 characters.
|
19 |
+
*
|
20 |
+
* @param int $secretLength
|
21 |
+
* @return string
|
22 |
+
*/
|
23 |
+
public function createSecret($secretLength = 16)
|
24 |
+
{
|
25 |
+
$validChars = $this->_getBase32LookupTable();
|
26 |
+
unset($validChars[32]);
|
27 |
+
|
28 |
+
$secret = '';
|
29 |
+
for ($i = 0; $i < $secretLength; $i++) {
|
30 |
+
$secret .= $validChars[array_rand($validChars)];
|
31 |
+
}
|
32 |
+
return $secret;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Calculate the code, with given secret and point in time
|
37 |
+
*
|
38 |
+
* @param string $secret
|
39 |
+
* @param int|null $timeSlice
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
+
public function getCode($secret, $timeSlice = null)
|
43 |
+
{
|
44 |
+
if ($timeSlice === null) {
|
45 |
+
$timeSlice = floor(time() / 30);
|
46 |
+
}
|
47 |
+
|
48 |
+
$secretkey = $this->_base32Decode($secret);
|
49 |
+
|
50 |
+
// Pack time into binary string
|
51 |
+
$time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
|
52 |
+
// Hash it with users secret key
|
53 |
+
$hm = hash_hmac('SHA1', $time, $secretkey, true);
|
54 |
+
// Use last nipple of result as index/offset
|
55 |
+
$offset = ord(substr($hm, -1)) & 0x0F;
|
56 |
+
// grab 4 bytes of the result
|
57 |
+
$hashpart = substr($hm, $offset, 4);
|
58 |
+
|
59 |
+
// Unpak binary value
|
60 |
+
$value = unpack('N', $hashpart);
|
61 |
+
$value = $value[1];
|
62 |
+
// Only 32 bits
|
63 |
+
$value = $value & 0x7FFFFFFF;
|
64 |
+
|
65 |
+
$modulo = pow(10, $this->_codeLength);
|
66 |
+
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Get QR-Code URL for image, from google charts
|
71 |
+
*
|
72 |
+
* @param string $name
|
73 |
+
* @param string $secret
|
74 |
+
* @param string $title
|
75 |
+
* @return string
|
76 |
+
*/
|
77 |
+
public function getQRCodeGoogleUrl($name, $secret, $title = null) {
|
78 |
+
$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
|
79 |
+
if(isset($title)) {
|
80 |
+
$urlencoded .= urlencode('&issuer='.$title);
|
81 |
+
}
|
82 |
+
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now
|
87 |
+
*
|
88 |
+
* @param string $secret
|
89 |
+
* @param string $code
|
90 |
+
* @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
|
91 |
+
* @return bool
|
92 |
+
*/
|
93 |
+
public function verifyCode($secret, $code, $discrepancy = 1)
|
94 |
+
{
|
95 |
+
$currentTimeSlice = floor(time() / 30);
|
96 |
+
|
97 |
+
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
|
98 |
+
$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
|
99 |
+
if ($calculatedCode == $code ) {
|
100 |
+
return true;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
return false;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Set the code length, should be >=6
|
109 |
+
*
|
110 |
+
* @param int $length
|
111 |
+
* @return PHPGangsta_GoogleAuthenticator
|
112 |
+
*/
|
113 |
+
public function setCodeLength($length)
|
114 |
+
{
|
115 |
+
$this->_codeLength = $length;
|
116 |
+
return $this;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Helper class to decode base32
|
121 |
+
*
|
122 |
+
* @param $secret
|
123 |
+
* @return bool|string
|
124 |
+
*/
|
125 |
+
protected function _base32Decode($secret)
|
126 |
+
{
|
127 |
+
if (empty($secret)) return '';
|
128 |
+
|
129 |
+
$base32chars = $this->_getBase32LookupTable();
|
130 |
+
$base32charsFlipped = array_flip($base32chars);
|
131 |
+
|
132 |
+
$paddingCharCount = substr_count($secret, $base32chars[32]);
|
133 |
+
$allowedValues = array(6, 4, 3, 1, 0);
|
134 |
+
if (!in_array($paddingCharCount, $allowedValues)) return false;
|
135 |
+
for ($i = 0; $i < 4; $i++){
|
136 |
+
if ($paddingCharCount == $allowedValues[$i] &&
|
137 |
+
substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false;
|
138 |
+
}
|
139 |
+
$secret = str_replace('=','', $secret);
|
140 |
+
$secret = str_split($secret);
|
141 |
+
$binaryString = "";
|
142 |
+
for ($i = 0; $i < count($secret); $i = $i+8) {
|
143 |
+
$x = "";
|
144 |
+
if (!in_array($secret[$i], $base32chars)) return false;
|
145 |
+
for ($j = 0; $j < 8; $j++) {
|
146 |
+
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
|
147 |
+
}
|
148 |
+
$eightBits = str_split($x, 8);
|
149 |
+
for ($z = 0; $z < count($eightBits); $z++) {
|
150 |
+
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
|
151 |
+
}
|
152 |
+
}
|
153 |
+
return $binaryString;
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Helper class to encode base32
|
158 |
+
*
|
159 |
+
* @param string $secret
|
160 |
+
* @param bool $padding
|
161 |
+
* @return string
|
162 |
+
*/
|
163 |
+
protected function _base32Encode($secret, $padding = true)
|
164 |
+
{
|
165 |
+
if (empty($secret)) return '';
|
166 |
+
|
167 |
+
$base32chars = $this->_getBase32LookupTable();
|
168 |
+
|
169 |
+
$secret = str_split($secret);
|
170 |
+
$binaryString = "";
|
171 |
+
for ($i = 0; $i < count($secret); $i++) {
|
172 |
+
$binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
|
173 |
+
}
|
174 |
+
$fiveBitBinaryArray = str_split($binaryString, 5);
|
175 |
+
$base32 = "";
|
176 |
+
$i = 0;
|
177 |
+
while ($i < count($fiveBitBinaryArray)) {
|
178 |
+
$base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)];
|
179 |
+
$i++;
|
180 |
+
}
|
181 |
+
if ($padding && ($x = strlen($binaryString) % 40) != 0) {
|
182 |
+
if ($x == 8) $base32 .= str_repeat($base32chars[32], 6);
|
183 |
+
elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4);
|
184 |
+
elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3);
|
185 |
+
elseif ($x == 32) $base32 .= $base32chars[32];
|
186 |
+
}
|
187 |
+
return $base32;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Get array with all 32 characters for decoding from/encoding to base32
|
192 |
+
*
|
193 |
+
* @return array
|
194 |
+
*/
|
195 |
+
protected function _getBase32LookupTable()
|
196 |
+
{
|
197 |
+
return array(
|
198 |
+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
199 |
+
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
200 |
+
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
201 |
+
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
202 |
+
'=' // padding char
|
203 |
+
);
|
204 |
+
}
|
205 |
+
}
|
package.xml
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?xml version='1.0' encoding='utf-8'?>
|
2 |
+
<package><name>HE_TwoFactorAuth</name><license uri="http://opensource.org/licenses/GPL-2.0">GPLv2</license><notes>Supports Magento v1.6 and later</notes><time>10:35:36</time><__packager>build_package.py v0.0.3</__packager><summary>Magento Two Factor Authentication Module</summary><stability>stable</stability><__commit_hash>1c1757d37d3a13460d6c7714783ea5be8cabb238</__commit_hash><version>1.0.4</version><extends /><contents><target name="magelib"><dir name="Duo"><file hash="137f969b558b3b4484f4f1b900dee785" name="duo_web.php" /></dir><dir name="GoogleAuthenticator"><dir name="PHPGangsta"><file hash="960348e708b1af4f3fa3af0a61c5b480" name="GoogleAuthenticator.php" /></dir></dir></target><target name="magejs"><dir name="he_twofactor"><file hash="f35271c9f9dcd891f9762ecdabe42a7e" name="Duo-Web-v1.js" /><file hash="f56ed95edec91d991c47ca7acd040f14" name="adminpanel.js" /><file hash="d4de57510e0db3879fe4ac40157a2a03" name="Duo-Web-v1.bundled.js" /><file hash="8495205cca2327781c35694c49be0cbb" name="readme.txt" /><file hash="d2d0b9006b59184e0fb4e63746d7dcea" name="Duo-Web-v1.bundled.min.js" /></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="he_twofactor"><file hash="297a5b794bd02fe99c750e27b72a4771" name="auth.xml" /></dir></dir><dir name="template"><dir name="he_twofactor"><dir name="system"><dir name="config"><dir name="fieldset"><file hash="48d67f8b5c78245152a311db167f63af" name="hint.phtml" /></dir></dir></dir><dir name="google"><file hash="5f81e5a6e51998197ee18055f6909c3f" name="auth.phtml" /></dir><dir name="duo"><file hash="730180c796e68c4d41e84bae33e78715" name="auth.phtml" /></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file hash="a23cb0085dfa2ebaba0c3963626ceec2" name="HE_TwoFactorAuth.xml" /></dir></target><target name="magecommunity"><dir name="HE"><dir name="TwoFactorAuth"><file hash="03e96a4e9a95384a62006dd1bec410be" name="readme.txt" /><dir name="controllers"><dir name="Adminhtml"><file hash="c4d8abc157c5ea274595b01c06339dcc" name="TwofactorController.php" /></dir></dir><dir name="Helper"><file hash="f83f52a46de8988840ce13e347560382" name="Data.php" /></dir><dir name="etc"><file hash="5e70d73d44219f6a5ad3d67c8c9f1217" name="adminhtml.xml" /><file hash="dc3414541125ee6fba8b0e388ddd1791" name="config.xml" /><file hash="c4e20d03a436f0551ba2e5fdc7d3eb84" name="system.xml" /></dir><dir name="Block"><file hash="b1213fa6e743592396022ad746218d09" name="Validate.php" /><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Fieldset"><file hash="c9d4c1b6ec475c8f67100c2362c06c6b" name="Hint.php" /></dir></dir></dir></dir></dir><dir name="sql"><dir name="he_twofactorauth"><file hash="cb51f34efe763446991650d94fc12fee" name="mysql4-install-1.0.2.php" /></dir></dir><dir name="Model"><file hash="5c2fde9c75fa7412719b2831f29ec13b" name="Observer.php" /><file hash="874e8d5f9c17a597e6dc287c61d8a812" name="Validate.php" /><dir name="Validate"><file hash="e3c1aa5712e3dc8f4f4b777ad444c84e" name="Duo.php" /><file hash="d3824a3f8a76b9547eee2e9ed9f74744" name="Google.php" /><dir name="Duo"><file hash="097290c1ce66a46a112e8c0581b9a97d" name="Request.php" /></dir></dir><dir name="Sysconfig"><file hash="13d17e2e07a819db52715476bfb5f968" name="Provider.php" /><file hash="32a98775f48b3e5ca65a56d7a3f66661" name="Appkey.php" /></dir><dir name="Resource"><dir name="Mysql4"><file hash="27ac2539835f54ed028b177b5b624309" name="Setup.php" /></dir></dir></dir></dir></dir></target></contents><dependencies><required><php><min>5.2.13</min><max>7.1.0</max></php></required></dependencies><authors><author><name>Aric Watson</name><user>aricwatson</user><email>awatson@nexcess.net</email></author><author><name>Gregg Milligan</name><user>-</user><email>-</email></author><author><name>Greg Croasdill</name><user>-</user><email>-</email></author></authors><date>2016-06-14</date><compatibile /><channel>community</channel><description>Sentry Two-Factor Authentication will protect your Magento store and customer data by adding an extra check to authenticate your Admin users before allowing them access. Developed as a partnership between the Human Element Magento Development team and Nexcess Hosting, Sentry Two-Factor Authentication for Magento is easy to setup and admin users can quickly login.</description></package>
|